My goal is to Request picture from Camera app, save it in my app data and also write the same picture to Gallery so if user deletes app it still has the taken pictures (unlike messenger our utility tool pictures are considered highly valuable to user even without the app).
The problem that i'm facing is tightly related to 29 API level witch is the build target (offers to downgrade it not acceptable).
So basically what i get is:
First problem: Even with Manifest.permission.WRITE_EXTERNAL_STORAGE
granted I can't access files constructed with Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES)
i get IOException (permission denied)
Second problem: contentResolver.insert(contentUri, contentValues)
throws me this on pre Q device (Nokia 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()
If i Run on Android 10 device (emulator in my case) the option using contentResolver.insert(..)
works fine.
Main Question Is how to write picture to android managed gallery folder properly when targeting 29Api level so it would work on pre android 10 devices?
Extra references
My code for writing picture from my App cache storage to gallery for android 10 is:
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")
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. Content providers are set up in manifest as well as needed permissions and file paths xml.
Code that should do for per Q android versions, but it doesn't looks like this (Assuming that i already got 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
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/*"),
null
)
}
Update on issue and edit
I tweaked my code chunks to be a bit more correct since they actually works.
Rebuilding project solved my issue in the end. Not sure what i hate more - Google or myself.
Of course rebuilding project was only part of the solution.
I was having two problems:
- Stated by some guy here who removed his answer was that i might be lacking
<application... android:requestLegacyExternalStorage="true" ....>
- Some library in my dependencies has
android:maxSdkVersion
in it's manifest that basically causes to ignore my permission on both Android 10 and Android 9 devices and probably everything starting with Android 4.4.
So accordingly i changed my app manifest:
<manifest>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" tools:node="replace"/>
<application android:requestLegacyExternalStorage="true" ...>
...
</application>
</manifest>