2

I have no problem with internal storage but I want to create a directory on the external SD card, with versions prior to KitKat, File.mkdirs() works.

There is part of my code:

//external sd card
DocumentFile.fromFile(new File("/storage/sdcard1/")).exists(); //returns true
DocumentFile.fromFile(new File("/storage/sdcard1/")).canRead(); //returns true
DocumentFile.fromFile(new File("/storage/sdcard1/")).canWrite(); //returns true
DocumentFile.fromFile(new File("/storage/sdcard1/test")).exists(); //returns false
DocumentFile.fromFile(new File("/storage/sdcard1/")).createDirectory("test"); //returns null

//internal storage
DocumentFile.fromFile(new File("/storage/emulated/0/")).exists(); //returns true
DocumentFile.fromFile(new File("/storage/emulated/0/test")).exists(); //returns false
DocumentFile.fromFile(new File("/storage/emulated/0/")).createDirectory("test"); //it works
DocumentFile.fromFile(new File("/storage/emulated/0/test")).exists(); //returns true
DocumentFile.fromFile(new File("/storage/emulated/0/test")).createFile("text", "file.txt"); //it works

//external sd card
(new File("/storage/sdcard1/test2/subfolder")).exists(); //returns false
(new File("/storage/sdcard1/test2/subfolder")).mkdirs(); //returns false

//internal storage
(new File("/storage/emulated/0/test2/subfolder")).exists(); //returns false
(new File("/storage/emulated/0/test2/subfolder")).mkdirs(); //returns true

In AndroidManifest.xml:

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

Testing with an BQ Aquaris e4, Android Lollipop 5.0 and a 1GB micro SD card.

UPDATE: This is how I get the volume list:

private static ArrayList<StorageInfo> listAvaliableStorage(Context context) {
        ArrayList<StorageInfo> storagges = new ArrayList<>();
        StorageManager storageManager = (StorageManager) context.getSystemService(Context.STORAGE_SERVICE);
        try {
            Class<?>[] paramClasses = {};
            Method getVolumeList = StorageManager.class.getMethod("getVolumeList", paramClasses);
            getVolumeList.setAccessible(true);
            Object[] params = {};
            Object[] invokes = (Object[]) getVolumeList.invoke(storageManager, params);
            if (invokes != null) {
                StorageInfo info;
                for (Object obj : invokes) {
                    Method getPath = obj.getClass().getMethod("getPath");
                    String path = (String) getPath.invoke(obj);
                    info = new StorageInfo(path);
                    File file = new File(info.getPath());
                    if ((file.exists()) && (file.isDirectory())
                        //&& (file.canWrite())
                            ) {
                        info.setTotalStorage(file.getTotalSpace());
                        info.setFreeStorage(file.getUsableSpace());
                        Method isRemovable = obj.getClass().getMethod("isRemovable");
                        String state;
                        try {
                            Method getVolumeState = StorageManager.class.getMethod("getVolumeState", String.class);
                            state = (String) getVolumeState.invoke(storageManager, info.getPath());
                            info.setState(state);
                            info.setRemovable((Boolean) isRemovable.invoke(obj));
                        } catch (Exception e) {
                            e.printStackTrace();
                        }

                        storagges.add(info);
                    }
                }
            }
        } catch (NoSuchMethodException | IllegalArgumentException | IllegalAccessException | InvocationTargetException e) {
            e.printStackTrace();
        }
        storagges.trimToSize();

        return storagges;
    }

The hard coded paths is only for this example

UPDATE 2: I've created the directory: "test" in SD Card with a file explorer. When I execute this code:

File file = new File("/storage/sdcard1/test/", "file.txt");
fileOutput = new FileOutputStream(file);

The second line throws FileNotFoundException: /storage/sdcard1/test/file.txt: open failed: EACCES (Permission denied)

What am I doing wrong?

Floern
  • 33,559
  • 24
  • 104
  • 119
cgr
  • 519
  • 1
  • 7
  • 20
  • Any error message is returned in Logcat? – Giancarlo Abel Giulian Sep 17 '15 at 12:01
  • Can you also post the error you are getting? Also, try using Environment.getExternalStorageDirectory().getPath() instead of directly pointing. Check this - >http://stackoverflow.com/questions/32055815/android-mkdirs-not-working-for-me-not-external-storage – Techidiot Sep 17 '15 at 12:03
  • No error, no exception. Environment.getExternalStorageDirectory.getPath() returns "/storage/emulated/0/", this is not the external sdcard – cgr Sep 17 '15 at 12:21

2 Answers2

2

That is due to the design of Android Lollipop. Environment.getExternalStorageDirectory() will return only the path of emulated external storage. Android does not expose any API that gives you the path of the sdcard.

Also, in Lollipop, apps cannot write to location outside of their own directory in the sdcard, unless user permission is obtained through Storage Access Framework.

Try context.getExternalFilesDirs(). That gives you a list of paths where your app can write data to. One of it will be in the sdcard and will go like /storage/sdcardname/Android/data/yourAppName/files

somesh
  • 2,509
  • 3
  • 16
  • 24
  • getExternalFilesDirs() returns "/storage/emulated/0/Android/data/packagename/files" but this is not the external sdcard. Solved with Storage Access Framework, here a good explanation http://stackoverflow.com/questions/26744842/how-to-use-the-new-sd-card-access-api-presented-for-lollipop/26765884#26765884 – cgr Oct 01 '15 at 08:35
  • Are you sure you used getExternalFilesDirs and not getExternalFilesDir? Dir will return only the emulated path. Dirs will return emulated path and sdcard path. – somesh Oct 03 '15 at 05:52
  • Any Solutions? @cgr – Deep Dave Dec 05 '16 at 13:38
  • yes @DeepDave, using Storage Access Framework, see this answer http://stackoverflow.com/questions/26744842/how-to-use-the-new-sd-card-access-api-presented-for-lollipop/26765884#26765884 – cgr Dec 13 '16 at 13:41
0

you have to explicitly define the location, here is a short script I made to create a folder and a folder inside that folder, hope this helps

    import java.io.File;
    import android.app.Activity;
    import android.content.Context;
    import android.os.Bundle;
    import android.os.Environment;
    import android.widget.Toast;

public class DirectoryPicker extends Activity {

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.chooser_list);//your xml layout file name
}
@Override
public void onStart() {
    super.onStart();
    String state = Environment.getExternalStorageState();
    if (Environment.MEDIA_MOUNTED.equals(state)) { // **Check that we have access to create the folder(s)**
        File Root = Environment.getExternalStorageDirectory(); //**Get the path and hold it**
//**Alternativly you can select a certain directory with**
//File Root = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES);
        File folder = new File(Root.getAbsolutePath() + "/main_folder_to_add/second_folder/");//**This puts everything together CORRECTLY**
        folder.mkdirs();//**Makes the directory folders, make sure to use mkdirs not mkdir**
        if (!folder.exists()) {
            folder.mkdirs();
        }
        Context context = getApplicationContext();//**Prove we were successful**
        String msg = folder.toString();
        Toast toast = Toast.makeText(context, msg, Toast.LENGTH_LONG);
        toast.show();
        return;
    }
}
public void onBackPressed() {

finish();
}
}
Wraithious
  • 385
  • 2
  • 11
  • Environment.getExternalStorageDirectory.getPath() returns "/storage/emulated/0/", this is not the external sdcard – cgr Aug 12 '16 at 12:10
  • correct that is why i did not use that in my above code, first i got the Environment.getExternalStorageDirectory() and then put the string together with File folder = new File(Root.getAbsolutePath() , not getPath, I tested it on api 4.1 and 6, it worked and made the folders that the user can easily find whereas all other methods I've seen posted on here didn't work (for me at least) – Wraithious Aug 12 '16 at 15:13