19

Compiling for Android N I've faced an issue of FileProvider. I need to let user to pick image from gallery/take picture with camera then crop it to square.

I've managed to implement a FileProvider for taking image with camera, but I have serious problem with picking image from gallery. The problem is that in the gallery there are lot of files from different places and I've got the Exception for example:

  java.lang.IllegalArgumentException: Failed to find configured root that contains /storage/6133-3766/DCIM/100ANDRO/DSC_0035.JPG

So the question is, what can I put to file_paths.xml to get access to anywhere in /storage/. I can't rely on exact path, since there maybe pictures from WhatsApp and similar apps, for example the WhatsApp image gets this path:

/storage/emulated/0/WhatsApp/Media/WhatsApp Images/IMG-20160821-WA0000.jpg

which I've managed to resolve with empty path:

<external-path name="external_storage" path=""/>

which is similar to Environment.getExternalStorageDirectory() according to documentation.

But still cannot figure out how to deal with images that stored in /storage/SOME_DIR/. Please help.

Evgeniy Mishustin
  • 3,343
  • 3
  • 42
  • 81
  • what is the minSDKversion of your app ? – Chintan Soni Sep 12 '16 at 12:32
  • min is 14, target is 24 – Evgeniy Mishustin Sep 12 '16 at 12:37
  • 2
    If it was 19, i think SAF (Storage Access Framework) would be your solution. https://developer.android.com/guide/topics/providers/document-provider.html and demo shown here: http://www.techotopia.com/index.php/An_Android_Storage_Access_Framework_Example – Chintan Soni Sep 12 '16 at 12:51
  • Also, there are so many questions related to this error. Check this search: https://www.google.co.in/webhp?sourceid=chrome-instant&ion=1&espv=2&ie=UTF-8#q=Failed+to+find+configured+root+that+contains – Chintan Soni Sep 12 '16 at 12:54
  • just clicked and I saw it all visited links ) thank you, my friend, but I was looking many time before posting my question ) Currently, I've found the way to get over it. But I need the correct way. – Evgeniy Mishustin Sep 12 '16 at 12:57
  • You may post your solution as your answer till there is some correct way to do it. – Chintan Soni Sep 12 '16 at 13:27
  • from gallery, u are choosing content w/ mime type "image/*" you are not choosing 'files'.. suggest a review of intent filters for content type of photo. by going that way, you can choose a photo, get the response as type URI, ask the api for the absolute path of the URI object IF U really need a path. example in this answer http://stackoverflow.com/questions/12208498/what-intent-filters-must-my-app-cover-to-appear-in-chooser-when-requestion-an-im – Robert Rowntree Sep 12 '16 at 13:28
  • http://stackoverflow.com/questions/4455558/allow-user-to-select-camera-or-gallery-for-image/12347567#12347567 -> gallery or camera as a chooser from intent filter without making the fileprovider central to a solution... – Robert Rowntree Sep 12 '16 at 23:40
  • 1
    prereq to a crop is that u load bitmap. prereq to bitmap is that resolve content from correct 'volume' on the mediastore. Details of how contentResolver works VARY depending on media source ( exampl: sdCard vs internalStore vs gallery ) ... https://developer.android.com/reference/android/provider/MediaStore.Images.Media.html see 'getBitmap' ... http://stackoverflow.com/questions/29803924/android-how-to-set-the-photo-selected-from-gallery-to-a-bitmap contentResolve-to-bitmap sample IMO u r getting closer – Robert Rowntree Sep 13 '16 at 14:28
  • http://stackoverflow.com/questions/3401579/get-filename-and-path-from-uri-from-mediastore ... understanding contentResolver <--> MediaStore – Robert Rowntree Sep 13 '16 at 14:40

2 Answers2

5

I think this question is based on a misunderstanding.

The purpose of a FileProvider is to grant access (to an external app), to a file that your app already controls.

You will never succeed in using your own file provider to gain access to a file owned by an external app.

It is up to the external app to grant you that access using a file provider, if it chooses to.

That seems to be what the question is asking for. If I have not understood your question, let me know, but if I do understand it, what you are trying to do just won't work.

Alejandro Moya
  • 434
  • 6
  • 11
x-code
  • 2,940
  • 1
  • 18
  • 19
  • I am trying to pick a picture from gallery and crop it with crop intent. Some pictures work, some pictures don't. – Evgeniy Mishustin Sep 13 '16 at 06:42
  • I believe, that all the images I see when I trigger PICK intent are those with granted access for my application, but when I trigger crop Intent - I should grant access to those images. And there are some pathes I can't grant access to. – Evgeniy Mishustin Sep 13 '16 at 06:55
4

Agreed with @x-code's answer you are not described very clear about issue although if you try to access another app's internal data then you must have permissions to do so.

Files that rightfully belong to your app and should be deleted when the user uninstalls your app. Although these files are technically accessible by the user and other apps because they are on the external storage, they are files that realistically don't provide value to the user outside your app.

Actually i have found on documentation that SDK version 24 is now updated with many schemes and have massive changes in working with Files,from documentation the problem with file:// is described as..

Passing file:// URIs outside the package domain may leave the receiver with an unaccessible path. Therefore, attempts to pass a file:// URI trigger a FileUriExposedException. The recommended way to share the content of a private file is using the FileProvider.

Due to security reasons it is highly recommended to use Content:// instead of using file:// so basically use ContentProvider instead of FileProvider.

A simple example of using it is below,

in AndroidMenifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    ...
    <application
        ...
        <provider
            android:name="android.support.v4.content.FileProvider"
            android:authorities="${applicationId}.provider"
            android:exported="false"
            android:grantUriPermissions="true">
            <meta-data
                android:name="android.support.FILE_PROVIDER_PATHS"
                android:resource="@xml/provider_paths"/>
        </provider>
    </application>
</manifest> 

And then create a provider_paths.xml file in xml folder under res folder. Folder may be needed to create if it doesn't exist.

The content of the file is shown below. It describes that we would like to share access to the External Storage at root folder (path=".") with the name external_files.

res/xml/provider_paths.xml

<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
    <external-path name="external_files" path="."/>
</paths>

now to use it,

Uri photoURI = FileProvider.getUriForFile(MainActivity.this,
        BuildConfig.APPLICATION_ID + ".provider",
        createImageFile());

I have taken this from this blog so please read it for full understanding.Hope it helps everyone.

TapanHP
  • 5,969
  • 6
  • 37
  • 66
  • 2
    Careful on the code inside `createImageFile()` as it should use [Environment.getExternalStorageDirectory()](https://developer.android.com/reference/android/os/Environment.html#getExternalStorageDirectory()) which is not the same as the external root. refer to [FileProvider](https://developer.android.com/reference/android/support/v4/content/FileProvider.html) for details on this. – Alejandro Moya Oct 27 '17 at 03:14