What I want to do: After the user chooses a directory/folder (and thus allows the app access), I want to write a bitmap to that directory. However, I get this error when writing that bitmap:
Caused by: java.io.FileNotFoundException: /storage/emulated/0/ChosenFolderByUser/output.jpg: open failed: EPERM (Operation not permitted)
Why does this happen since the user has granted access to files in the chosen directory? Here's my code:
private val dirPickerHandler = registerForActivityResult(
ActivityResultContracts.StartActivityForResult()
) {
if (it.data != null) {
val uri = it.data?.data!!
contentResolver.takePersistableUriPermission(
uri,
Intent.FLAG_GRANT_READ_URI_PERMISSION or
Intent.FLAG_GRANT_WRITE_URI_PERMISSION
)
grantUriPermission(
packageName,
uri,
Intent.FLAG_GRANT_READ_URI_PERMISSION or
Intent.FLAG_GRANT_WRITE_URI_PERMISSION or
Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION
)
val path = URLDecoder.decode(uri.toString(), "UTF-8")
val dPath = cleanPath(path)
showMessage("Storage location successfully updated to $dPath")
val mIcon = BitmapFactory.decodeResource(getResources(), R.drawable.image_to_save)
// Write bitmap to directory chosen by user - Error here:
File(Environment.getExternalStorageDirectory().toString() "/" dPath, "output.jpg").writeBitmap(mIcon, Bitmap.CompressFormat.JPEG, 85)
} else {
showMessage(
getString(R.string.no_directory_selected)
)
}
}
companion object {
fun cleanPath(path: String): String {
if (path.isEmpty()) {
return "DCIM/Camera"
}
val s = URLDecoder.decode(path, "UTF-8")
return s.substring(s.lastIndexOf(":") 1)
}
}
private fun File.writeBitmap(bitmap: Bitmap, format: Bitmap.CompressFormat, quality: Int) {
outputStream().use { out ->
bitmap.compress(format, quality, out)
out.flush()
}
}
CodePudding user response:
Remove the call for grantUriPermission() as it does not make sense.
Dont try to get a path for an uri.
Dont use the File class for an Uri.
You are not showing where the used output stream comes from.
CodePudding user response:
You cannot use Environment.getExternalStorageDirectory()
for target SDK 31. You should use scope storage. Please read more about it here: https://developer.android.com/about/versions/11/privacy/storage
If you want to use the legacy way, you should have the target SDK 30 (max) and in case it is 30, then you should enable requestLegacyExternalStorage.