andrdoiddev
andrdoiddev

Reputation: 369

Having trouble implementing ACTION_OPEN_DOCUMENT to my project

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

Answers (4)

guness
guness

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

Booo
Booo

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

Richard Muvirimi
Richard Muvirimi

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

CommonsWare
CommonsWare

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

Related Questions