我的目标是从Camera应用程序请求图片,将其保存在我的应用程序数据中,然后将同一张图片写到Gallery中,因此,如果用户删除应用程序,它仍然具有已拍摄的图片(与Messenger一样,我们的实用工具图片被认为对用户非常有价值即使没有该应用程序也是如此。
我面临的问题与29 API级别巫婆密切相关,这是构建目标(提供降级服务是不可接受的)。
所以基本上我得到的是:
第一个问题:即使授予Manifest.permission.WRITE_EXTERNAL_STORAGE
权限,我也无法访问使用Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES)
构造的文件,但我得到IOException(权限被拒绝)
第二个问题:contentResolver.insert(contentUri, contentValues)
在pre Q设备(诺基亚8 Android Pie)上抛出了这个问题:
java.lang.SecurityException: Permission Denial: writing com.android.providers.media.MediaProvider uri content://media/external/images/media from .... requires android.permission.WRITE_EXTERNAL_STORAGE, or grantUriPermission()
如果我在Android 10设备(在我的情况下为模拟器)上运行,则使用contentResolver.insert(..)
的选项效果很好。
[主要问题在定位29Api级别时如何将图片正确写入android Managed Gallery文件夹,使其可以在android 10之前的设备上使用?
额外参考
用于将图片从应用缓存存储写入到Android 10的图库的代码是:
fun writePictureToGalleryQ(context: Context, pictureUri: Uri, pictureName: String) {
val contentResolver = context.contentResolver
val relativeLocation = "${Environment.DIRECTORY_PICTURES}${File.separator}WaiJuDuDisAndroid"
val contentValues = ContentValues().apply {
put(MediaStore.Images.ImageColumns.DISPLAY_NAME, pictureName)
put(MediaStore.MediaColumns.MIME_TYPE, "image/jpeg")
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
put(MediaStore.Images.ImageColumns.RELATIVE_PATH, relativeLocation)
}
}
val contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI
var uri: Uri? = null
uri = contentResolver.insert(contentUri, contentValues)
uri?.let { galleryPartUri ->
val outStream: OutputStream = contentResolver.openOutputStream(galleryPartUri)!!
val inStream: InputStream = contentResolver.openInputStream(pictureUri)!!
outStream.use {out ->
inStream.use { inpt ->
inpt.copyTo(out)
}
}
}
}
P.s。在清单以及所需的权限和文件路径xml中设置内容提供程序。
应该为每个Q android版本执行的代码,但是看起来不是这样(假设我已经获得Manifest.permission.WRITE_EXTERNAL_STORAGE
)
fun writePictureToGalleryLegacy(context: Context, pictureUri: Uri, pictureName: String) {
val directory = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES)
val correctDir = File("${directory.absolutePath}${File.separator}WaiJuDuDisAndroid")
correctDir.mkdirs()
val file = File("${correctDir.absolutePath}${File.separator}${pictureUri.lastPathSegment}")
val contentResolver = context.contentResolver
file.createNewFile() // this is probably unnecessary but doesn't work either way.
val outStream: OutputStream = file.outputStream()
val inStream: InputStream = contentResolver.openInputStream(pictureUri)!!
outStream.use {out ->
inStream.use { inpt ->
inpt.copyTo(out)
}
}
val mimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension(file.extension)
MediaScannerConnection.scanFile(
context,
arrayOf(file.absolutePath),
arrayOf(mimeType?:"image/*"), // probably incorrect but i get exception way before even getting here
null
)
}