11

My app runs on Android 5.0. I use method getExternalFilesDirs() to check if external SD card is available. If it returns more than 1 File, that means external SD card exists.

But on some devices (for example Elephone G2), method getExternalFilesDirs() returns only one directory of primary storage. I'm sure that device has external SD card (/storage/sdcard1/).

Can any one give me the answer?

TOP
  • 2,574
  • 5
  • 35
  • 60
  • Presumably, the manufacturer of the Elephone decided not to allow developers to have access to removable media. Or, perhaps they screwed up when configuring their ROM. – CommonsWare Oct 26 '15 at 16:01
  • @CommonsWare It should be the answer. I really don't understand why `getExternalFilesDirs()` doesn't work. It works on almost devices. – TOP Oct 26 '15 at 16:05
  • Do you have android.permission.WRITE_EXTERNAL_STORAGE in your AndroidManifest.xml file? – Shark Oct 26 '15 at 16:19
  • @Shark Of course yes. As I said, it works on almost devices. – TOP Oct 26 '15 at 16:26
  • @Sunshinetpu looks like you stumled on a half-assed vendor ROM :/ – Shark Oct 26 '15 at 16:34
  • Yes that happens on many kitkat devices. Try `System.getenv("EXTERNAL_STORAGE");` and `System.getenv("SECONDARY_STORAGE");`. – greenapps Oct 26 '15 at 18:39
  • @greenapps It even happens on some Lollipop devices. – TOP Oct 27 '15 at 03:15
  • @greenapps I tried with `System.getenv("EXTERNAL_STORAGE")` and it returned */storage/emulated/legacy*. What is it? In my device I have */storage/sdcard1*. They are different folders. – TOP Oct 27 '15 at 03:32
  • I wait to comment until you tried the one with secondary storage too. For the rest it looks as if you never browsed with a file explorer app on your device because then you would know these paths. – greenapps Oct 27 '15 at 05:56

3 Answers3

15

For getExternalFilesDirs to return the path of the sdcard, the OEM must have set the SECONDARY_STORAGE environment variable in the device specific init.rc file as mentioned here: https://source.android.com/devices/storage/config-example.html

Look at the source of getExternalFilesDirs here: http://androidxref.com/5.1.1_r6/xref/frameworks/base/core/java/android/app/ContextImpl.java#1039

The value is obtained from Environment.buildExternalStorageAppFilesDirs. Look at that source here: http://androidxref.com/5.1.1_r6/xref/frameworks/base/core/java/android/os/Environment.java#206

The value is dependent on mExternalDirsForApp, which in turn is populated by reading the contents of SECONDARY_STORAGE variable: http://androidxref.com/5.1.1_r6/xref/frameworks/base/core/java/android/os/Environment.java#136

As you can see, if the SECONDARY_STORAGE variable is not set, the sdcard path will not be returned. You can cross-check this by going to adb shell and looking at the output of echo $SECONDARY_STORAGE

somesh
  • 2,509
  • 3
  • 16
  • 24
  • 2
    Thank you. So if the SECONDARY_STORAGE variable is not set, we cannot access the external sd card? – TOP Oct 29 '15 at 03:24
  • 1
    If that environment variable is not set, you can't write to the sdcard by making use of getExternalFilesDirs since its not going to return the sdcard path. You can however ask the user permissions to write to sdcard through SAF. Have a look at this link: http://stackoverflow.com/questions/26744842/how-to-use-the-new-sd-card-access-api-presented-for-lollipop/26765884#26765884 – somesh Oct 30 '15 at 04:35
  • Actually, if the device doesn't support `$SECONDARY_STORAGE`, all you need to do is ask for **WRITE_EXTERNAL_STORAGE** permission. No other modification is needed, and I tested this. – IgorGanapolsky Sep 09 '16 at 14:08
8

In my projects using this code & i don't have any problem.

method of getExternalFilesDirs return array with 2 length.

Dirs[0] ==> Internal Sorage Dirs[1] ==> External Storage

 File[] Dirs = ContextCompat.getExternalFilesDirs(MyApp.GetContext(), null);
Mohammad Hossein Gerami
  • 1,360
  • 1
  • 10
  • 26
  • 1
    Do you have WRITE_EXTERNAL_STORAGE permission? Can you create files using your method? – Shark Oct 26 '15 at 16:21
  • 2
    The OP specifically ask for a method not working...that you are using in your answer... – Fustigador Oct 26 '15 at 16:29
  • 1
    @MHossein I will test your code. Could you please explain why you use `ContextCompat`? I use method `getExternalFilesDirs(String fileName)` of `Context`. It works on almost devices. – TOP Oct 26 '15 at 16:31
  • 1
    It shoul dbe noted that, MHossein is using `ContextCompat.getExternalFilesDirs` not `Context. getExternalFilesDirs`. [Src link](https://android.googlesource.com/platform/frameworks/support/+/refs/heads/master/v4/java/android/support/v4/content/ContextCompat.java#219) – petey Oct 26 '15 at 16:31
  • @MHossein Why does the filePath has to contain "sdcard"? – TOP Oct 26 '15 at 17:00
  • @Sunshinetpu for my device(huawei) Context returns internal storage. – Mohammad Hossein Gerami Oct 26 '15 at 17:12
  • @MHossein ContextCompat.getExternalFilesDirs(MyApp.GetContext(), null) still returns only one directory of internal storage. – TOP Oct 27 '15 at 03:15
  • `getExternalFilesDirs` shouldn't need **WRITE_EXTERNAL_STORAGE** permission. Only `getExternalStorageDirectory` needs this permission. – IgorGanapolsky Sep 09 '16 at 13:55
  • It works at 5.0 android tv (XiaoMi tv box 3C) but doesn't work at 4.4.4 (XiaoMi gen 1) – Johnny Dec 15 '17 at 13:49
2

this issue there is in some of Lenovo device too.

my solution is this.

String EXTERNAL_SD_PATH1;
String EXTERNAL_SD_PATH2;

public boolean hasExternalSDCard()
{
    try
    {
        String state = Environment.getExternalStorageState();
        if(Environment.MEDIA_MOUNTED.equals(state) || Environment.MEDIA_MOUNTED_READ_ONLY.equals(state))
        return true;
    }
    catch (Throwable e)
    {}

    return false;
}

        @SuppressLint("SdCardPath")
        protected synchronized void _prepareStorage()
        {
            EXTERNAL_SD_PATH1 = null;
            EXTERNAL_SD_PATH2 = null;
            if (hasExternalSDCard())
            {
                try
                {
                    if(VERSION_SDK_INT > 18)
                    {
                        Context context = getContext();
                        File[]  sds = getExternalFilesDirs("");

                        if(sds == null)
                            return;

                        if(sds.length >= 2)
                        {
                            EXTERNAL_SD_PATH1 = TextWorker.getSubStringBeforeLastMark(sds[1].getAbsolutePath(),"/Android/");
                            if(sds.length > 2)
                                EXTERNAL_SD_PATH2 = TextWorker.getSubStringBeforeLastMark(sds[2].getAbsolutePath(),"/Android/");
                        }
                        else
                        {
                            String internal = sds[0].getAbsolutePath();
                            internal = TextWorker.getSubStringBeforeLastMark(internal,"/Android/");
                            int len = internal.length();
                            int num = Integer.valueOf(internal.substring(len - 1));

                            String ex1 = internal.substring(0, len-1) + (num+1);
                            File sd1 = new File(ex1);
                            if(sd1.exists())
                                EXTERNAL_SD_PATH1 = sd1.getAbsolutePath();

                            String ex2 = internal.substring(0, len-1) + (num+2);
                            File sd2 = new File(ex2);
                            if(sd2.exists())
                                EXTERNAL_SD_PATH2 = sd2.getAbsolutePath();
                        }
                    }

                    else
                    {
                        File sd = Environment.getExternalStorageDirectory();
                        String path = sd.getAbsolutePath();
                        if (sd.exists() && (path.contains("/mnt/") || path.contains("/storage") || path.contains("/sdcard")) && (!path.contains("emulate")))
                        {
                            EXTERNAL_SD_PATH1 = path;
                        }
                    }
                }
                catch (Throwable e)
                {}
            }

        }


        public static String getSubStringBeforeLastMark(String str,String mark)
    { 
        int l = str.lastIndexOf(mark);
        if(l == -1 || l == 0)
            return "";

        return str.substring(0, l);
    }
Ali Bagheri
  • 3,068
  • 27
  • 28