Reputation: 369
I have AddActivity, which lets you get the URI from either a picture you can take from the camera, or an image that you can select from the gallery. Then you can go to DetailsActivity to view the image. I have it working right now until you restart the device. After you restart and try to go to DetailsActivity for that image, this is the error:
Caused by: java.lang.SecurityException: Permission Denial: opening provider com.android.providers.media.MediaDocumentsProvider from ProcessRecord{3a5e86d 2915:jeremy.com.wineofmine/u0a321} (pid=2915, uid=10321) requires that you obtain access using ACTION_OPEN_DOCUMENT or related APIs
I went to the "Open Files Using Storage Access Framework" Android Development page and read up on the Persist Permissions section. I'm having trouble applying it to my project though.
I think the main thing I don't understand is that it looks like you need to call an intent (in my case inside the DetailsActivity), but I don't even have an intent there.
Here is the intent that lets you pick the gallery image. This is in AddActivity:
Intent intentGallery = new Intent(Intent.ACTION_OPEN_DOCUMENT);
intentGallery.addCategory(Intent.CATEGORY_OPENABLE);
intentGallery.setType("image/*");
intentGallery.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
intentGallery.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
startActivityForResult(intentGallery, SELECT_IMAGE);
In the DetailsActivity, this is where it actually crashes:
imageURI = Uri.parse(cursor.getString(cursor.getColumnIndexOrThrow(WineContract.WineEntry.COLUMN_WINE_IMAGE)));
bitmap = null;
try {
//If the cursor does not have anything in the image column, set the image to null, with a height so the textviews look decent
if (cursor.isNull(cursor.getColumnIndexOrThrow(WineContract.WineEntry.COLUMN_WINE_IMAGE))){
mFullImage.setImageBitmap(null);
mFullImage.setMaxHeight(300);
}else{
//remake the bitmap from the URI in the image column
//********This next line is where the program crashes**********
bitmap = MediaStore.Images.Media.getBitmap(this.getContentResolver(), imageURI);
mFullImage.setImageBitmap(bitmap);
}
Could I get some help with figuring out how to apply this to my project?
Upvotes: 17
Views: 27844
Reputation: 6636
a bit late but...
We need to provide persistent Uri permission. Instead of doing it on onActivityResult
as oppose to prior answers, I prefer to add it as a flag:
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT).also {
it.addCategory(Intent.CATEGORY_OPENABLE)
it.type = "image/*"
it.addFlags(Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION)
it.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
}
Also, a note worth mentioning is persistent permission is available only to Intent.ACTION_OPEN_DOCUMENT
and NOT Intent.ACTION_GET_CONTENT
whereas the latter one is like a one-time thing.
Upvotes: 7
Reputation: 89
The permission denial issue needs to be dealt with the first time you receive a URI. If you use registerForActivityResult() instead of startActivityForResult()
// Kotlin
private val pickImage = registerForActivityResult(
ActivityResultContracts.StartActivityForResult()
) { result: ActivityResult ->
if (result.resultCode == Activity.RESULT_OK) {
// you will get result here in result.data
val uri = result.data?.data!!
requireActivity().contentResolver.takePersistableUriPermission(
uri,
Intent.FLAG_GRANT_READ_URI_PERMISSION
)
// Do something else with the URI. E.g, save the URI as a string in the database
}
}
Upvotes: 5
Reputation: 844
To handle permission results overide onRequestPermissionsResult as below
@Override
public void onRequestPermissionsResult (int requestCode, String[] permissions, int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
switch (requestCode) {
case General.REQUESTPERMISSION:
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
//reload my activity with permission granted or use the features that required the permission
} else {
Messenger.makeToast(getContext(), R.string.noPermissionMarshmallow);
}
break;
}
}
and to persist the permission implement as shown below in your onActivityResult method
@Override
public void onActivityResult (int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
//if ok user selected a file
if (resultCode == RESULT_OK) {
Uri sourceTreeUri = data.getData();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
getContext().getContentResolver().takePersistableUriPermission(sourceTreeUri, Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
}
}
}
Upvotes: 14
Reputation: 1006944
In onActivityResult()
, call takePersistableUriPermission()
on a ContentResolver
, passing in the Uri
that you got along with the mode flag(s) that indicate what access you want (read, write, both).
Upvotes: 12