Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Android 10/11 Scoped Storage #190

Open
DennyWeinberg opened this issue Jan 18, 2020 · 7 comments
Open

Android 10/11 Scoped Storage #190

DennyWeinberg opened this issue Jan 18, 2020 · 7 comments

Comments

@DennyWeinberg
Copy link

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.

@DennyWeinberg
Copy link
Author

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;

@Piasy
Copy link
Owner

Piasy commented Jan 19, 2020

Thanks for your reporting, could you please open a pull request for it?

@DennyWeinberg
Copy link
Author

DennyWeinberg commented Jan 19, 2020

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...

@Piasy
Copy link
Owner

Piasy commented Feb 23, 2020

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.

@DennyWeinberg
Copy link
Author

Android 11 can be installed as virtual device

@DennyWeinberg
Copy link
Author

hey, you should work on this!

@Piasy
Copy link
Owner

Piasy commented May 24, 2020

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants