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

Issue on Android Q #543

Closed
raylee4204 opened this issue Jul 16, 2019 · 25 comments
Closed

Issue on Android Q #543

raylee4204 opened this issue Jul 16, 2019 · 25 comments

Comments

@raylee4204
Copy link

Launching Ucrop with image URI from a device image on Android Q throws Permission denied error:

E/TransformImageView: onFailure: setImageUri
    java.io.FileNotFoundException: open failed: EACCES (Permission denied)
        at android.os.ParcelFileDescriptor.openInternal(ParcelFileDescriptor.java:315)
        at android.os.ParcelFileDescriptor.open(ParcelFileDescriptor.java:220)
        at android.content.ContentResolver.openAssetFileDescriptor(ContentResolver.java:1498)
        at android.content.ContentResolver.openFileDescriptor(ContentResolver.java:1338)
        at android.content.ContentResolver.openFileDescriptor(ContentResolver.java:1286)
        at com.yalantis.ucrop.task.BitmapLoadTask.doInBackground(BitmapLoadTask.java:100)
        at com.yalantis.ucrop.task.BitmapLoadTask.doInBackground(BitmapLoadTask.java:44)
        at android.os.AsyncTask$3.call(AsyncTask.java:378)
        at java.util.concurrent.FutureTask.run(FutureTask.java:266)
        at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:289)
        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)

This only occurs on android Q devices when the target sdk version is 29

@mark-software
Copy link

@raylee4204 have you handled runtime permissions?
Can you provide sample code for how you are using uCrop?
Are you attempting to capture a photo or select an existing photo?

I'm on Android Q as well and I don't get a crash.

@raylee4204
Copy link
Author

raylee4204 commented Jul 18, 2019

I have handled runtime permissions.

This occurs when I pass a device image's uri from my app to uCrop.

val newFile = File.createTempFile(imageFileName,  /* prefix */
     ".jpg",         /* suffix */
     storageDir   /* directory */);
val resultUri = Uri.fromFile(newFile)
UCrop
     .of(imageUri, resultUri)
     .start(activity!!, fragment)

@mark-software
Copy link

The error looks like it's saying the file does not exist FileNotFoundException.

Does the following work for you?

//Select photo

val mediaSelector = Intent(Intent.ACTION_GET_CONTENT)
mediaSelector.setTypeAndNormalize("image/*")
fragment.startActivityForResult(mediaSelector, requestCode)

Inside onActivityResult()

var newFile = File(Environment.getExternalStoragePublicDirectory(
    Environment.DIRECTORY_PICTURES), "YourFolderNameHere")

if (!newFile.exists() && !newFile.mkdir()) {
    return 
}

newFile = File(newFile.path + File.separator + imgName)
val destinationUri = Uri.fromFile(newFile)

UCrop.of(result.data!!, destinationUri)
    .withAspectRatio(1f, 1f)
    .start(context!!, this)

@raylee4204
Copy link
Author

Is your build target android Q?
The code I posted works on any devices below android Q

The following is from the android developers documents about scoped storage on Q:

Even with the Storage permission, such an app that accesses the raw file-system view of an external storage device has access only to the app's raw, package-specific path. If an app attempts to open files outside of its package-specific path using a raw file-system view, an error occurs:

In managed code, a FileNotFoundException occurs.

@mark-software
Copy link

Yes, my build target is Q targetSdkVersion 29 and it works for me.

Did you use the code to select a photo

val mediaSelector = Intent(Intent.ACTION_GET_CONTENT)
mediaSelector.setTypeAndNormalize("image/*")
fragment.startActivityForResult(mediaSelector, requestCode)

Or are you using a hardcoded file path?
Note that result.data in the code snippet from my previous comment is from onActivityResult(requestCode: Int, resultCode: Int, result: Intent?).

@raylee4204
Copy link
Author

Selecting photo via intent works fine.

I have a custom bottom sheet that shows a grid of photos located on device via following

val projection = arrayOf(
                MediaStore.Images.ImageColumns._ID,
                MediaStore.Images.ImageColumns.WIDTH,
                MediaStore.Images.ImageColumns.HEIGHT,
                MediaStore.Images.ImageColumns.ORIENTATION,
                MediaStore.Images.ImageColumns.DATE_MODIFIED,
                MediaStore.Images.ImageColumns.SIZE
            )

            val resolver = context.contentResolver
            val cursor = resolver.query(
                MediaStore.Images.Media.EXTERNAL_CONTENT_URI, projection, null, null,
                MediaStore.Images.ImageColumns.DATE_TAKEN + " DESC"
            ) ?: return null

            val count = 0
            val images = ArrayList<ImagePickerTile>(cursor.columnCount)

            if (cursor.moveToFirst()) {
                val idColumn = cursor.getColumnIndex(MediaStore.Images.Media._ID)
                if (idColumn < 0) {
                    cursor.close()
                    return null
                }

                do {
                    val imageLocation = Uri.withAppendedPath(
                        MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
                        cursor.getString(idColumn)
                    )
               
                    val tile = ImagePickerTile(
                        cursor.getString(idColumn),//id
                        imageLocation,//Image Uri
                        cursor.getInt(if (landscape) 2 else 1),//Width
                        cursor.getInt(if (landscape) 1 else 2)//Height
                    )
                    tile.dateModified = dateModified
                    tile.orientation = orientation
                    images.add(tile)
                } while (cursor.moveToNext() && count < maxItems)
            }

@mark-software
Copy link

Hmm, not sure. I'm actually not in any way associated with uCrop - was just trying to help.
May need to use scoped storage. Think it's in beta still though.

This library doesn't seem to be too actively maintained although it's a great library.

@raylee4204
Copy link
Author

I currently turned on legacy mode because of this. Although Q is still in beta, their API has been finalized. I'm hoping I can hear from the steam

@TermLog
Copy link

TermLog commented Sep 13, 2019

@raylee4204 did you try to launch the demo app on the same device where you got an error?

@clownba0t
Copy link

clownba0t commented Sep 30, 2019

I encountered this issue recently, also on Android 10. I managed to reproduce it locally and trace it through with the debugger. As @raylee4204 mentioned, the root cause does seem to be the new scoped file access in Android 10.

In particular, when dealing with a content:// based input URI, BitmapLoadTask in the library calls FileUtils.getPath, which attempts to extract the path to the file from the URI. While this may result in a path that references a valid file, Android 10 may not let the app access it due to the new scoping mechanism. This could certainly explain where the EACCES comes from.

Interestingly, this issue doesn't seem to occur if you haven't granted READ_EXTERNAL_STORAGE permission, because in that case FileUtils.getPath isn't called. Rather, the data referenced by the input URI is copied to the output URI (which is presumably backed by an accessible file), and that output URI is then used as the input URI.

It would seem that the easiest fix would be to stop using FileUtils.getPath internally to try to get the file path, and to instead either use the input URI as is (e.g. getContentResolver().openInputStream(...)) or copy the data it references to a temporary location for processing (e.g. the output URI, as per above, or an intermediate temporary file).

Disclaimer: I haven't studied the library code in depth, so I could easily be missing something.

@clownba0t
Copy link

clownba0t commented Sep 30, 2019

@TermLog I installed the sample app on the same device that I was experiencing issues with when using the uCrop library with my app that targets API 29. There were no problems when selecting the same files that caused my app to crash. However, I noticed that the sample app targets API 28. When I modified it to API 29, it did crash on those same files.

@droidluv
Copy link

droidluv commented Oct 2, 2019

Yup it throws the FileNotFoundException without legacy mode in Android Q

@GamelyAnthony
Copy link

I can confirm as well that this worked previous on Android 8, targeting API level 29. But on Android 10, targeting API 29, it fails with the same callstack.
I added android:requestLegacyExternalStorage="true" to my app manifest, and that made uCrop work again, but that's only a temporary fix.

Is there a timeline on a fix for this?

@phantomVK
Copy link

Same issue:

  1. Google Pixel 2 with Android 10.
  2. 'compileSdkVersion 29' and 'targetSdkVersion 29'

@AnwarShahriar
Copy link

Same issue here:

  1. Google Pixel 3 XL, Android 10
  2. compileSdkVersion 29 and targetSdkVersion 29

@miguelaboliveira
Copy link

Any update on this issue? @GamelyAnthony solution does work, but even the documentation specifies that it should only be used while app is not compatible with scoped storage.

@Shirwee
Copy link

Shirwee commented Nov 22, 2019

@Yalantis hope to adapt soon,thks!

@HanGao1
Copy link

HanGao1 commented Dec 6, 2019

Same issue here:

Google Pixel 3 XL, Android 10
compileSdkVersion 29 and targetSdkVersion 29

@abdulrehmank7
Copy link

I was facing the same problem. Now i have found the solution. Check out here:

https://medium.com/@arkapp/accessing-images-on-android-10-scoped-storage-bbe65160c3f4?source=friends_link&sk=675c33e4595709bce43461ad01154391

@meikaiss
Copy link

meikaiss commented Dec 9, 2019

@blessedCode07
The solution is effective. I parse the media uri to Bitmap, then save the bitmap to a File in app externalCacheDir, and get the file uri by Uri.fromFile(new File(bitmapFilePath));. and last transform the uri to ucrop library.

It seems to be redundancy, but it is effective.

thks, hope it helps somebody.

@githubzjh
Copy link

Same issue here:

Google Pixel 3 XL, Android 10
compileSdkVersion 29 and targetSdkVersion 29

@HanGao1 这个不给适配android 10,目前你们咋处理的,是暂时使用legacy mode?

@HanGao1
Copy link

HanGao1 commented Dec 13, 2019

Same issue here:
Google Pixel 3 XL, Android 10
compileSdkVersion 29 and targetSdkVersion 29

@HanGao1 这个不给适配android 10,目前你们咋处理的,是暂时使用legacy mode?

The solution is effective. I parse the media uri to Bitmap, then save the bitmap to a File in app externalCacheDir, and get the file uri by Uri.fromFile(new File(bitmapFilePath));. and last transform the uri to ucrop library.

It seems to be redundancy, but it is effective.

thks, hope it helps somebody.

@mark-software
Copy link

I'm now using this library. Seems to be second best in terms of design and at least it works with scoped storage. https://github.com/ArthurHub/Android-Image-Cropper

@Marina24
Copy link

Duplicate to #598

@tdkhoasg
Copy link

<manifest ... >
<application android:requestLegacyExternalStorage="true" ... >
...

from: https://medium.com/@sriramaripirala/android-10-open-failed-eacces-permission-denied-da8b630a89df

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

No branches or pull requests