I have spent literally hours now looking for (and trying) many different ways to write some files to my 4.4 Android KitKat's removable SD card after getting countless "eacces permission denied" errors. Seems now that Google has limited access to the filesystem on SD cards you are forced to only be able to write to directories owned by the application (eg: /Android/data/com.mycompany.myapp/files/).
Finally I was able to get something working that creates a directory on the removable SD card that can be written to. However I am curious why in order to gain access to this owned directory on the removable SD card I first had to create a new file object using a path to the external directory?
My implementation:
First I create two globals to house the strings of both the external and removable paths.
MainActivity
public class MainActivity extends ActionBarActivity {
String externalStorageDirectory;//path to owned external storage files
String secondaryStorageDirectory ;//path to owned removable storage files
Then I ask the system what the manufacturer specific directories are called and start concatenating absolute paths for both the externalStorage and removableStorage variables. I also create a new File object initialized with the path to the external app owned directory.
onCreate()
externalStorageDirectory = this.getExternalFilesDir(null).toString();//build absolute path to the app owned external directory
File folder = new File(externalStorageDirectory ); //THIS LINE IS CRUCIAL!!
Log.d("DEBUG", " - External Path" + externalStorageDirectory );// "/storage/emulated/0/Android/data/com.mycompany.myapp/files"
String ownedDirectory = "/Android/data/" + this.getPackageName() + "/files/";
secondaryStorageDirectory = System.getenv("SECONDARY_STORAGE").toString() + ownedDirectory;//build absolute path to the app owned removable directory
Log.d("DEBUG", " - Removable Path"+secondaryStorageDirectory );// "/storage/external/Android/data/com.mycompany.myapp/files/"
Finally I was able to write a file to my application's owned removable SD card directory. And more specifically I was using an AsyncTask to download my files and save them, method #1 of this post.
doInBackground()
output = new FileOutputStream(secondaryStorageDirectory + "myawesomefile.mp4");
Then I was able to navigate to the removable SD card directory via adb shell and I could see my file.
Adb shell output:
//internal storage
shell@QTAQZ3:/storage/emulated/0/Android/data/com.mycompany.myapp/files $ ls
s <
//removable SD card
shell@QTAQZ3:/storage/external/Android/data/com.mycompany.myapp/files $ ls
ls
myawesomefile.mp4
<
And just to reiterate a little:
File folder = new File(externalStorageDirectory); //THIS LINE IS CRUCIAL!!
File folder = new File(secondaryStorageDirectory ); //THIS LINE DOES NOTHING?
So my questions are:
- When I create that File object instance, what is happening that makes those directories available?
- Why is having to call mkdir() or mkdirs() seem to unnecessary in this case as the dirs magically appear when the file object was created?
- And why am I able to see my app's secondary(removable) directory only after I set the file to the external(non-removable) path?
Admittedly I am relatively new to Android programming, so I'm not even sure if this approach is correct or just a lucky hack? But it seems to work in my app for now.