Android 10/11 Scoped Storage
The library should not use File, but always work with the Uri. Doing that, Glide is allowed to open the input stream via the Android Content Provider/MediaStore Uri and all is fine.
Scoped Storage is enabled in Android 10, as long as you don't use android:requestLegacyExternalStorage="true" in your manifest. In Android 11 it will probably be the default, so your library won't work anymore then.
Right now on Android 10, without the specified option, the following error is raised:
2020-01-18 17:58:20.005 7712-7774/com.levionsoftware.instagram_map E/SubsamplingScaleImageView: Failed to initialise bitmap decoder
java.io.FileNotFoundException: /storage/emulated/0/DCIM/Camera/20191130_173206.jpg: open failed: EACCES (Permission denied)
at libcore.io.IoBridge.open(IoBridge.java:496)
at java.io.FileInputStream.<init>(FileInputStream.java:159)
at java.io.FileInputStream.<init>(FileInputStream.java:115)
at android.graphics.BitmapRegionDecoder.newInstance(BitmapRegionDecoder.java:151)
at com.davemorrissey.labs.subscaleview.decoder.SkiaImageRegionDecoder.init(SkiaImageRegionDecoder.java:96)
at com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView$TilesInitTask.doInBackground(SubsamplingScaleImageView.java:1559)
at com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView$TilesInitTask.doInBackground(SubsamplingScaleImageView.java:1534)
at android.os.AsyncTask$3.call(AsyncTask.java:378)
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:919)
Caused by: android.system.ErrnoException: open failed: EACCES (Permission denied)
at libcore.io.Linux.open(Native Method)
at libcore.io.ForwardingOs.open(ForwardingOs.java:167)
at libcore.io.BlockGuardOs.open(BlockGuardOs.java:252)
at libcore.io.ForwardingOs.open(ForwardingOs.java:167)
at android.app.ActivityThread$AndroidOs.open(ActivityThread.java:7255)
at libcore.io.IoBridge.open(IoBridge.java:482)
at java.io.FileInputStream.<init>(FileInputStream.java:159)
at java.io.FileInputStream.<init>(FileInputStream.java:115)
at android.graphics.BitmapRegionDecoder.newInstance(BitmapRegionDecoder.java:151)
at com.davemorrissey.labs.subscaleview.decoder.SkiaImageRegionDecoder.init(SkiaImageRegionDecoder.java:96)
at com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView$TilesInitTask.doInBackground(SubsamplingScaleImageView.java:1559)
at com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView$TilesInitTask.doInBackground(SubsamplingScaleImageView.java:1534)
at android.os.AsyncTask$3.call(AsyncTask.java:378)
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:919)
Glide in combination with a default ImageView, or even com.github.chrisbanes.photoview.PhotoView however displayes my local images without errors by using the content Uri.
Here is a patch with the modifications I've done to make it work. Note that ImageInfoExtractor.TYPE_ANIMATED_WEBP isn't available anymore.
Index: app/src/main/java/com/github/piasy/biv/loader/ImageLoader.java
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
--- app/src/main/java/com/github/piasy/biv/loader/ImageLoader.java (date 1579367514000)
+++ app/src/main/java/com/github/piasy/biv/loader/ImageLoader.java (date 1579367527000)
@@ -47,11 +47,11 @@
@UiThread
interface Callback {
- void onCacheHit(int imageType, File image);
+ void onCacheHit(int imageType, Uri uri);
- void onCacheMiss(int imageType, File image);
+ void onCacheMiss(int imageType, Uri uri);
- void onBeforeSetImage(int imageType, File image, SubsamplingScaleImageView ssv);
+ void onBeforeSetImage(int imageType, Uri uri, SubsamplingScaleImageView ssv);
void onStart();
@@ -59,7 +59,7 @@
void onFinish();
- void onSuccess(File image);
+ void onSuccess(Uri uri);
void onFail(Exception error);
}
Index: app/src/main/java/com/github/piasy/biv/view/BigImageView.java
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
--- app/src/main/java/com/github/piasy/biv/view/BigImageView.java (date 1579367514000)
+++ app/src/main/java/com/github/piasy/biv/view/BigImageView.java (date 1579367527000)
@@ -100,7 +100,6 @@
private ImageSaveCallback mImageSaveCallback;
private ImageLoader.Callback mUserCallback;
- private File mCurrentImageFile;
private Uri mUri;
private Uri mThumbnail;
@@ -294,37 +293,6 @@
mUserCallback = imageLoaderCallback;
}
- public File getCurrentImageFile() {
- return mCurrentImageFile;
- }
-
- @RequiresPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE)
- public void saveImageIntoGallery() {
- if (mCurrentImageFile == null) {
- if (mImageSaveCallback != null) {
- mImageSaveCallback.onFail(new IllegalStateException("image not downloaded yet"));
- }
-
- return;
- }
-
- try {
- String result = MediaStore.Images.Media.insertImage(getContext().getContentResolver(),
- mCurrentImageFile.getAbsolutePath(), mCurrentImageFile.getName(), "");
- if (mImageSaveCallback != null) {
- if (!TextUtils.isEmpty(result)) {
- mImageSaveCallback.onSuccess(result);
- } else {
- mImageSaveCallback.onFail(new RuntimeException("saveImageIntoGallery fail"));
- }
- }
- } catch (FileNotFoundException e) {
- if (mImageSaveCallback != null) {
- mImageSaveCallback.onFail(e);
- }
- }
- }
-
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
@@ -362,28 +330,25 @@
}
@Override
- public void onCacheHit(final int imageType, File image) {
- mCurrentImageFile = image;
- doShowImage(imageType, image);
+ public void onCacheHit(final int imageType, Uri uri) {
+ doShowImage(imageType, uri);
if (mUserCallback != null) {
- mUserCallback.onCacheHit(imageType, image);
+ mUserCallback.onCacheHit(imageType, uri);
}
}
@Override
- public void onCacheMiss(final int imageType, final File image) {
- mCurrentImageFile = image;
- mTempImages.add(image);
- doShowImage(imageType, image);
+ public void onCacheMiss(final int imageType, final Uri uri) {
+ doShowImage(imageType, uri);
if (mUserCallback != null) {
- mUserCallback.onCacheMiss(imageType, image);
+ mUserCallback.onCacheMiss(imageType, uri);
}
}
@Override
- public void onBeforeSetImage(int imageType, File image, SubsamplingScaleImageView ssv) {
+ public void onBeforeSetImage(int imageType, Uri uri, SubsamplingScaleImageView ssv) {
}
@@ -431,9 +396,9 @@
}
@Override
- public void onSuccess(final File image) {
+ public void onSuccess(final Uri uri) {
if (mUserCallback != null) {
- mUserCallback.onSuccess(image);
+ mUserCallback.onSuccess(uri);
}
}
@@ -507,12 +472,12 @@
}
@UiThread
- private void doShowImage(final int imageType, final File image) {
+ private void doShowImage(final int imageType, final Uri uri) {
if (mMainView != null) {
removeView(mMainView);
}
- mMainView = mViewFactory.createMainView(getContext(), imageType, image, mInitScaleType);
+ mMainView = mViewFactory.createMainView(getContext(), imageType, uri, mInitScaleType);
if (mMainView == null) {
onFail(new RuntimeException("Image type not supported: "
+ ImageInfoExtractor.typeName(imageType)));
@@ -533,10 +498,10 @@
setInitScaleType(mInitScaleType);
if (mUserCallback != null) {
- mUserCallback.onBeforeSetImage(imageType, image, mSSIV);
+ mUserCallback.onBeforeSetImage(imageType, uri, mSSIV);
}
- mSSIV.setImage(ImageSource.uri(Uri.fromFile(image)));
+ mSSIV.setImage(ImageSource.uri(uri));
}
if (mFailureImageView != null) {
Index: app/src/main/java/com/github/piasy/biv/view/ImageViewFactory.java
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
--- app/src/main/java/com/github/piasy/biv/view/ImageViewFactory.java (date 1579367514000)
+++ app/src/main/java/com/github/piasy/biv/view/ImageViewFactory.java (date 1579367527000)
@@ -37,12 +37,12 @@
*/
public class ImageViewFactory {
- public final View createMainView(Context context, int imageType, File imageFile,
- int initScaleType) {
+ public final View createMainView(Context context, int imageType, Uri uri, int initScaleType) {
switch (imageType) {
case ImageInfoExtractor.TYPE_GIF:
case ImageInfoExtractor.TYPE_ANIMATED_WEBP:
- return createAnimatedImageView(context, imageType, imageFile, initScaleType);
+ //return createAnimatedImageView(context, imageType, imageFile, initScaleType);
+ throw new RuntimeException("Removed because of Scoped Storage");
case ImageInfoExtractor.TYPE_STILL_WEBP:
case ImageInfoExtractor.TYPE_STILL_IMAGE:
default:
@@ -54,10 +54,10 @@
return new SubsamplingScaleImageView(context);
}
- protected View createAnimatedImageView(Context context, int imageType, File imageFile,
- int initScaleType) {
- return null;
- }
+// protected View createAnimatedImageView(Context context, int imageType, File imageFile,
+// int initScaleType) {
+// return null;
+// }
public View createThumbnailView(Context context, Uri thumbnail, ImageView.ScaleType scaleType) {
return null;
Thanks for your reporting, could you please open a pull request for it?
I modified the Version 1.5.7, because I was unable to use your master version yesterday. I had an issue with the GlideAppModule class: GeneratedAppGlideModuleImpl is implemented incorrectly. If you've manually implemented this class, remove your implementation.
I cant do it the official way, also because animated webp isn't supported after my manipulation... I used a quite hacky way of resolving my issue: I use my own fork of 1.5.7 because you rejected another pull request some months ago and copied 3 source files into my project because of the present issue. I didn't want to change the fork again because it would have taken more time to test my changes and so on.
I know that it's a mess and I cant update anymore...
Currently at Android 10, it works fine without specifying android:requestLegacyExternalStorage="true". I'll check Android 11 when it comes out.
I think your error is caused by permission issue.
Android 11 can be installed as virtual device
hey, you should work on this!
I checked documents about scoped storage earlier, and I believe current implementation works fine, please provide more info if it doesn't work in your case.