Android FileProvider for ext sdcard
Asked Answered
C

5

8

I'm using FileProvider for my internal files to be exposed to the Gallery for example. To make it more uniform, I also put my my external files into the provider (via external-path) but for the files in removable sd card it doesn't work. Saying something like that folder is not authorized.

Any help will be greatly appreciated.

Thx

Creese answered 1/9, 2015 at 13:35 Comment(0)
P
1

starting from android 4.4, normal applications are not allowed to access secondary external storage devices, i.e. sd card, except in their package-specific directories, even if you have requested WRITE_EXTERNAL_STORAGE permission.

The WRITE_EXTERNAL_STORAGE permission must only grant write access to the primary external storage on a device. Apps must not be allowed to write to secondary external storage devices, except in their package-specific directories as allowed by synthesized permissions. Restricting writes in this way ensures the system can clean up files when applications are uninstalled.

https://source.android.com/devices/storage/

Peppermint answered 1/9, 2015 at 14:13 Comment(5)
Thank you! But it seems to me a bit vouge though. See, in the docs it's said that external storage should be surfaced if it's semi-permanant like sd card slot in a battery compartment. But you're saying sd card is a secondary extarnal storage I guess and as of 4.4 it's not surfaced through the storage API.Creese
Actually in android's term, both "internal memory" and "sd card" can be "external storage". Use old phone Motorola Defy as an example, it has only 1 "external storage", which is sd card, so the sd card is primary external storage. Moto G, or some Nexus phone, it has only got "internal memory" but no sd card, so "internal memory" is the primary external storage here. For samsung phones, they have both internal memory and sd card, this time, internal memory is the primary external storage, while sd card is the secondary external storage.Peppermint
To confuse the matter even more, it comes internal storage, the truth is all phone must have internal MEMORY, just large or not. Use the same example, Defy's internal MEMORY is small, and used only as internal storage, while new model usually have larger internal memory, so only part of it is assigned as internal storagePeppermint
Ok got it. One more... I come across a term sometimes which is "built-in external storage". I believe that's referring to the external storage inside internal storage then?Creese
Yes, you are right. I would say it is external storage on interal MEMORY using my terms above.Peppermint
P
27

i added this root-path as suggested by @Gubatron in my XML and it works.

<?xml version="1.0" encoding="utf-8"?>
<paths>
    <external-path name="external_files" path="."/>
    <root-path name="external_files" path="/storage/" />
</paths>
Purificator answered 13/6, 2017 at 5:4 Comment(2)
amazing solution, saved my crashlytics!Pish
Paths should have different names, otherwise FileProvider will be initialized with only one of them.Griner
C
7

Let's take a look at FileProvider code:

    private static PathStrategy parsePathStrategy(Context context, String authority)
        ...
        int type;
        while ((type = in.next()) != END_DOCUMENT) {
            if (type == START_TAG) {
                final String tag = in.getName();
                final String name = in.getAttributeValue(null, ATTR_NAME);
                String path = in.getAttributeValue(null, ATTR_PATH);
                File target = null;
                if (TAG_ROOT_PATH.equals(tag)) {
                    target = buildPath(DEVICE_ROOT, path);
                } else if (TAG_FILES_PATH.equals(tag)) {
                    target = buildPath(context.getFilesDir(), path);
                } else if (TAG_CACHE_PATH.equals(tag)) {
                    target = buildPath(context.getCacheDir(), path);
                } else if (TAG_EXTERNAL.equals(tag)) {
                    target = buildPath(Environment.getExternalStorageDirectory(), path);
                }
                if (target != null) {
                    strat.addRoot(name, target);
                }
            }
        }
        return strat;
    }

FileProvider accepted absolute pathes to directory with help of tag root-path (DEVICE_ROOT constant). So just add absolute path to your files folder in secondary external disc like below:

<root-path path="/storage/extSdCard/Android/data/com.edufii/files/image/" name="image-ext2" />
<root-path path="/storage/extSdCard/Android/data/com.edufii/files/video/" name="video-ext2" />
<root-path path="/storage/extSdCard/Android/data/com.edufii/files/datafile/" name="datafile-ext2" />
<root-path path="/storage/extSdCard/Android/data/com.edufii/files/audio/" name="audio-ext2" />

Note official documentation doesn't say anything about <root-path>, so it may changed in a future.

Carhart answered 14/6, 2016 at 9:18 Comment(0)
H
2

FileProvider does not support secondary external storage (like removable sd cards). This is even more an issue in Android 7 and above - because you can't use the file:// uris anymore.

I already issued a bug report here.

Hoes answered 20/11, 2016 at 9:38 Comment(1)
tried this on my AVD sd card storage XML paths file and it worked <?xml version="1.0" encoding="utf-8"?> <paths xmlns:android="schemas.android.com/apk/res/android"> <external-path name="external_files" path="."/> <root-path name="external_files" path="/storage/" /> </paths>Midian
P
1

starting from android 4.4, normal applications are not allowed to access secondary external storage devices, i.e. sd card, except in their package-specific directories, even if you have requested WRITE_EXTERNAL_STORAGE permission.

The WRITE_EXTERNAL_STORAGE permission must only grant write access to the primary external storage on a device. Apps must not be allowed to write to secondary external storage devices, except in their package-specific directories as allowed by synthesized permissions. Restricting writes in this way ensures the system can clean up files when applications are uninstalled.

https://source.android.com/devices/storage/

Peppermint answered 1/9, 2015 at 14:13 Comment(5)
Thank you! But it seems to me a bit vouge though. See, in the docs it's said that external storage should be surfaced if it's semi-permanant like sd card slot in a battery compartment. But you're saying sd card is a secondary extarnal storage I guess and as of 4.4 it's not surfaced through the storage API.Creese
Actually in android's term, both "internal memory" and "sd card" can be "external storage". Use old phone Motorola Defy as an example, it has only 1 "external storage", which is sd card, so the sd card is primary external storage. Moto G, or some Nexus phone, it has only got "internal memory" but no sd card, so "internal memory" is the primary external storage here. For samsung phones, they have both internal memory and sd card, this time, internal memory is the primary external storage, while sd card is the secondary external storage.Peppermint
To confuse the matter even more, it comes internal storage, the truth is all phone must have internal MEMORY, just large or not. Use the same example, Defy's internal MEMORY is small, and used only as internal storage, while new model usually have larger internal memory, so only part of it is assigned as internal storagePeppermint
Ok got it. One more... I come across a term sometimes which is "built-in external storage". I believe that's referring to the external storage inside internal storage then?Creese
Yes, you are right. I would say it is external storage on interal MEMORY using my terms above.Peppermint
C
0

To access external sdcard . First call uri.getEncodedPath() to get the encoded path

" /external_files/3161-3330/WhatsApp/Media/WhatsApp%20Documents/All%20Currency.pdf "

Then use below logic to get file path of external storage

public String getFilePath(){
    if (isKitKat && DocumentsContract.isDocumentUri(mContext, uri)) {
                // ExternalStorageProvider
                if (com.android.externalstorage.documents.equals(uri.getAuthority())) {
                    final String docId = DocumentsContract.getDocumentId(uri);
                    final String[] split = docId.split(":");
                    final String type = split[0];
    
                    if ("primary".equalsIgnoreCase(type)) {
                        return Environment.getExternalStorageDirectory() + "/" + split[1];
                    } else {
                        return "/storage" + "/" + split[0] + "/" + split[1];
                    }
               }
    }
}

getFilePath() will give :

/storage/emulated/0/3161-3330/WhatsApp/Media/WhatsApp Documents/All Currency.pdf

where uri path starts with

/external_files/

and file path starts with

/storage/

Hence , we have to add below line

 <root-path name="external_files" path="/storage/" />

in

@xml/provider_paths.xml

Curfew answered 25/7, 2019 at 10:48 Comment(1)
Did you even read the question?Backfire

© 2022 - 2024 — McMap. All rights reserved.