I am using a photo crop library (version 2.7 here: https://github.com/ArthurHub/Android-Image-Cropper) to pick and crop an image using gallery apps (like samsung gallery, google photos, files etc). The library works by launching the picker intent with Intent.ACTION_GET_CONTENT and after the user chooses an image, the Uri is passed to the cropping view whcich opens the bitmap. In some situations I need to reset the cropping, by automatically using the Uri from the photo last chosen by the user. So, after the user chooses the image, I remember the Uri, then use the same Uri to start the cropping step (that decodes the bitmap).
This works of for Samsung Gallery, but I have an issue with Google Photos, which throws a SecurityException when the uri is re-opened. So the image pick with google photos works correctly on first time the user chooses the image only. When I try to pass the same Uri (when I reset the crop), I get the exception below (this also happenes with "Files" - I tested on emulator and motorola one):
java.lang.RuntimeException: Failed to load sampled bitmap: content://com.google.android.apps.photos.contentprovider/0/1/mediakey%3A%2Flocal%253Ace62f6ca-6054-486b-bbb1-b6145ac50272/ORIGINAL/NONE/27402515
Permission Denial: opening provider com.google.android.apps.photos.contentprovider.impl.MediaContentProvider from ProcessRecord{82bb7a9 12497:app.myapp/u0a188} (pid=12497, uid=10188) that is not exported from UID 10093
at com.theartofdev.edmodo.cropper.BitmapUtils.decodeSampledBitmap(BitmapUtils.java:130)
at com.theartofdev.edmodo.cropper.BitmapLoadingWorkerTask.doInBackground(BitmapLoadingWorkerTask.java:73)
at com.theartofdev.edmodo.cropper.BitmapLoadingWorkerTask.doInBackground(BitmapLoadingWorkerTask.java:24)
at android.os.AsyncTask$2.call(AsyncTask.java:333)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
at java.lang.Thread.run(Thread.java:764)
Caused by: java.lang.SecurityException: Permission Denial: opening provider com.google.android.apps.photos.contentprovider.impl.MediaContentProvider from ProcessRecord{82bb7a9 12497:app.myapp/u0a188} (pid=12497, uid=10188) that is not exported from UID 10093
at android.os.Parcel.createException(Parcel.java:1950)
at android.os.Parcel.readException(Parcel.java:1918)
at android.os.Parcel.readException(Parcel.java:1868)
at android.app.IActivityManager$Stub$Proxy.getContentProvider(IActivityManager.java:4118)
The code used for showing the user with which app to choose photo is:
List<Intent> allIntents = new ArrayList<>();
PackageManager packageManager = this.getPackageManager();
boolean includeCamera = useCamera;
// collect all camera intents if Camera permission is available
if (!CropImage.isExplicitCameraPermissionRequired(this) && includeCamera) {
allIntents.addAll(CropImage.getCameraIntents(this, packageManager));
}
boolean includeDocuments = false;
if (useGallery) {
List<Intent> galleryIntents =
CropImage.getGalleryIntents(packageManager, Intent.ACTION_GET_CONTENT, includeDocuments);
if (galleryIntents.size() == 0) {
// if no intents found for get-content try pick intent action (Huawei P9).
galleryIntents = CropImage.getGalleryIntents(packageManager, Intent.ACTION_PICK, includeDocuments);
}
allIntents.addAll(galleryIntents);
}
Intent target;
if (allIntents.isEmpty()) {
target = new Intent();
} else {
target = allIntents.get(allIntents.size() - 1);
allIntents.remove(allIntents.size() - 1);
}
// Create a chooser from the main intent
Intent chooserIntent = Intent.createChooser(target, "Choose");
// Add all other intents
chooserIntent.putExtra(
Intent.EXTRA_INITIAL_INTENTS, allIntents.toArray(new Parcelable[allIntents.size()]));
this.startActivityForResult(
chooserIntent
, CropImage.PICK_IMAGE_CHOOSER_REQUEST_CODE);
And the method CropImage.getGalleryIntents:
public static List<Intent> getGalleryIntents(
@NonNull PackageManager packageManager, String action, boolean includeDocuments) {
List<Intent> intents = new ArrayList<>();
Intent galleryIntent =
action == Intent.ACTION_GET_CONTENT
? new Intent(action)
: new Intent(action, MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
galleryIntent.setType("image/*");
List<ResolveInfo> listGallery = packageManager.queryIntentActivities(galleryIntent, 0);
for (ResolveInfo res : listGallery) {
Intent intent = new Intent(galleryIntent);
intent.setComponent(new ComponentName(res.activityInfo.packageName, res.activityInfo.name));
intent.setPackage(res.activityInfo.packageName);
intents.add(intent);
}
// remove documents intent
if (!includeDocuments) {
for (Intent intent : intents) {
if (intent
.getComponent()
.getClassName()
.equals("com.android.documentsui.DocumentsActivity")) {
intents.remove(intent);
break;
}
}
}
return intents;
}
I there a correct solution or a workaround for this? Thank you.
Use ACTION_OPEN_DOCUMENT
for your galleryIntent
.
But beware that even ACTION_OPEN_DOCUMENT
will still be invalidated after a few hours.
The only way to have an unlimited access to an image is to save it to your app's own storage and access it there for later use.