Search code examples
androidandroid-permissionsandroid-fileproviderandroid-11

Android 11 URI usage (file:// content://)


We have some old issues with similar words, but most of them are about converting one or the other.

What I'm looking here is the "Right" behaviour of URI usage with the new changes. Let me give some context:

Before when we get an image URI this would return file://... format. But since the new OS permissions changes, where we should not use WRITE_EXTERNAL_STORAGE anymore we should use getUriForFile(..) that return content://... path.(Scope Storage usage Android 11 Storage FAQ)

This can be spot on some Android guides, like: taken photos guide

The "problem" is that many users got used to use the URI of a crop image (for example) to create a file of it and save it.

Now, with this changes come the question:

How should we use the URI?

  1. Make some code to check Android version and if more than 29 we should create a new file path for the URI?
  2. Let the URI be the path to the image (content of file) and if someone wanna save it would need to create it own file path
  3. Something else that I don't get yet about how to use URI right.

Obs: Asking this, because of a Android Image Crop open source project handover, where we need to upgrade the permissions for Android 10/11 but now we have this content/file issue. More here

Edit: As pointed on the comments

Code returning file:// (not valid anymore since the changes)

Uri outputFileUri = null;
outputFileUri = Uri.fromFile(
                  new File(context.getExternalCacheDir().getPath(), 
                  "pickImageResult.jpeg")
                );
    

Code returning content://

outputFileUri = FileProvider.getUriForFile(
                        context,
                        context.getPackageName() + CommonValues.authority,
                        File.createTempFile("pickImageResult", ".jpeg", getImage)
                );

Solution

  • The "problem" is that many users got used to use the URI of a crop image (for example) to create a file of it and save it.

    In the end, this is your library, and you need to document what any Uri that you return is suitable for. After all, a Uri could point to:

    • A file on the filesystem (file)
    • A Web resource (https, or possibly http)
    • An Android resource (android.resource)
    • An asset in the app (file://android_asset)
    • Some arbitrary set of bytes (content)

    Your library is for image cropping. While I have not examined the implementation, I assume that it all works inside the app itself. If so, there is nothing wrong with returning a file Uri, if you want to do so. Your code is writing a file somewhere (e.g., getCacheDir() on Context). The app using your library must have access to that file, or else you would have crashed trying to write it. A Uri created via Uri.fromFile(), for that file, is perfectly fine... in that app.

    Where Uri.fromFile() becomes a problem is in passing the Uri to another app. However, your library is for cropping images, not sharing content with other apps. Your job, IMHO, is to give a cropped image back to the app. What the app does with it is up to that app, subject to whatever limitations there are in the Uri that you hand over.

    The two options that you seem to be considering have different issues:

    Uri Source Advantages Disadvantages
    Uri.fromFile() Cheap, easy Can only be used within the app itself; cannot be passed to other apps
    FileProvider Uri can be passed to other apps Requires a library and manifest configuration; cannot readily get to the underlying file

    Since IMHO an image cropper is not an image sharing solution, Uri.fromFile() seems reasonable. If the app using your library wants to turn around and share the cropped image, they would set up FileProvider themselves and use FileProvider.getUriForFile(). The only catch is that either you need to document where the file will be written or give them an option to tell you what directory to use — that information will be needed to set up the FileProvider metadata.

    Someday, if you elect to change the API, you might consider returning an ordinary File instead of a Uri. That way, there is no confusion about what it represents.

    But, in the end, this is all your decision. If you want to use FileProvider, or you want to upload images to your own Web server and use https, that is all up to you. However, you should document what you are doing and what the Uri represents.