mkdir() works while inside internal flash storage, but not SD card?
First, you should note that file.mkdir()
and file.mkdirs()
returns false
if the directory already existed. If you want to know whether the directory exists on return, either use (file.mkdir() || file.isDirectory())
or simply ignore the return value and call file.isDirectory()
(see the documentation).
That said, your real problem is that you need permission to create the directory on removable storage on Android 5.0+. Working with removable SD cards on Android is horrendous.
On Android 4.4 (KitKat), Google restricted access to SD cards (see here, here, and here). See this StackOverflow answer which leads to this XDA post if you need to create a directory on a removable SD card on Android 4.4 (KitKat).
On Android 5.0 (Lollipop), Google introduced new SD card access APIs. For sample usage please refer to this stackoverflow answer.
Basically, you need to use DocumentFile#createDirectory(String displayName)
to create your directory. You will need to ask the user to grant permissions to your app before creating this directory.
NOTE: This is for removable storage. Using File#mkdirs()
will work on internal storage (which is often confused with external storage on Android) if you have the permission android.permission.WRITE_EXTERNAL_STORAGE
.
I will post some example code below:
Check if you need to ask for permission:
File sdcard = ... // the removable SD card
List<UriPermission> permissions = context.getContentResolver().getPersistedUriPermissions();
DocumentFile documentFile = null;
boolean needPermissions = true;
for (UriPermission permission : permissions) {
if (permission.isWritePermission()) {
documentFile = DocumentFile.fromTreeUri(context, permission.getUri());
if (documentFile != null) {
if (documentFile.lastModified() == sdcard.lastModified()) {
needPermissions = false;
break;
}
}
}
}
Next (if needPermissions
is true
), you can display a dialog to explain to the user that they need to select the "SD Card" to give your app permissions to create files/directories and then start the following activity:
if (needPermissions) {
// show a dialog explaining that you need permission to create the directory
// here, we will just launch to chooser (what you need to do after showing the dialog)
startActivityForResult(new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE), STORAGE_REQUEST_CODE);
} else {
// we already have permission to write to the removable SD card
// use DocumentFile#createDirectory
}
You will now need to check the resultCode
and requestCode
in onActivityResult
:
@Override protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == STORAGE_REQUEST_CODE && resultCode == RESULT_OK) {
File sdcard = ... // get the removable SD card
boolean needPermissions = true;
DocumentFile documentFile = DocumentFile.fromTreeUri(MainActivity.this, data.getData());
if (documentFile != null) {
if (documentFile.lastModified() == sdcard.lastModified()) {
needPermissions = false;
}
}
if (needPermissions) {
// The user didn't select the "SD Card".
// You should try the process over again or do something else.
} else {
// remember this permission grant so we don't need to ask again.
getContentResolver().takePersistableUriPermission(data.getData(),
Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
// Now we can work with DocumentFile and create our directory
DocumentFile doc = DocumentFile.fromTreeUri(this, data.getData());
// do stuff...
}
return;
}
super.onActivityResult(requestCode, resultCode, data);
}
That should give you a good start on working with DocumentFile
and removable SD cards on Android 5.0+. It can be a PITA.
Also, there is no public API to get the path to a removable SD card (if one even exists). You should not rely on hardcoding "/storage/sdcard1"
! There are quite a few posts about it on StackOverflow. Many of the solutions use the environment variable SECONDARY_STORAGE
. Below is two methods you can use to find removable storage devices:
public static List<File> getRemovabeStorages(Context context) throws Exception {
List<File> storages = new ArrayList<>();
Method getService = Class.forName("android.os.ServiceManager")
.getDeclaredMethod("getService", String.class);
if (!getService.isAccessible()) getService.setAccessible(true);
IBinder service = (IBinder) getService.invoke(null, "mount");
Method asInterface = Class.forName("android.os.storage.IMountService$Stub")
.getDeclaredMethod("asInterface", IBinder.class);
if (!asInterface.isAccessible()) asInterface.setAccessible(true);
Object mountService = asInterface.invoke(null, service);
Object[] storageVolumes;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
String packageName = context.getPackageName();
int uid = context.getPackageManager().getPackageInfo(packageName, 0).applicationInfo.uid;
Method getVolumeList = mountService.getClass().getDeclaredMethod(
"getVolumeList", int.class, String.class, int.class);
if (!getVolumeList.isAccessible()) getVolumeList.setAccessible(true);
storageVolumes = (Object[]) getVolumeList.invoke(mountService, uid, packageName, 0);
} else {
Method getVolumeList = mountService.getClass().getDeclaredMethod("getVolumeList");
if (!getVolumeList.isAccessible()) getVolumeList.setAccessible(true);
storageVolumes = (Object[]) getVolumeList.invoke(mountService, (Object[]) null);
}
for (Object storageVolume : storageVolumes) {
Class<?> cls = storageVolume.getClass();
Method isRemovable = cls.getDeclaredMethod("isRemovable");
if (!isRemovable.isAccessible()) isRemovable.setAccessible(true);
if ((boolean) isRemovable.invoke(storageVolume, (Object[]) null)) {
Method getState = cls.getDeclaredMethod("getState");
if (!getState.isAccessible()) getState.setAccessible(true);
String state = (String) getState.invoke(storageVolume, (Object[]) null);
if (state.equals("mounted")) {
Method getPath = cls.getDeclaredMethod("getPath");
if (!getPath.isAccessible()) getPath.setAccessible(true);
String path = (String) getPath.invoke(storageVolume, (Object[]) null);
storages.add(new File(path));
}
}
}
return storages;
}
public static File getRemovabeStorageDir(Context context) {
try {
List<File> storages = getRemovabeStorages(context);
if (!storages.isEmpty()) {
return storages.get(0);
}
} catch (Exception ignored) {
}
final String SECONDARY_STORAGE = System.getenv("SECONDARY_STORAGE");
if (SECONDARY_STORAGE != null) {
return new File(SECONDARY_STORAGE.split(":")[0]);
}
return null;
}
Get directory of both internal storage and sd card if exist
There are thousands of links for tutorials, Android developer page is one of the many, below is the link ;
http://developer.android.com/guide/topics/data/data-storage.html#filesExternal
As far as I know, you can have access to your app internal dir and query whether sd card is mounted etc. But to get full access to device internal Dir? I doubt it because it would be breach of user's privacy. You even require user permission to query external storage
simple write file code works in java but not on android device
This how you get a file from external storage:
File file = new File(Environment.getExternalStorageDirectory().getAbsolutePath(), filename);
Note:
may be if your device is rooted. mnt could work. I have no idea. but,
it is a wrong approach.
Properly detect if SD card is available
see this method is available since API level 11, which let you know if Internal Memory working as External storage. in some devices getExternalStorageDirectory()
returns true even if SD-Card is not available. read below docs for details.
public static boolean isExternalStorageEmulated ()
Added in API level 11
Returns whether the device has an external storage device which is emulated. If true, the device does not have real external storage, and the directory returned by getExternalStorageDirectory() will be allocated using a portion of the internal storage system.
Certain system services, such as the package manager, use this to determine where to install an application.
Emulated external storage may also be encrypted - see setStorageEncryption(android.content.ComponentName, boolean) for additional details.
Android docs
Android create a new folder on internal Device Storage
But I'd like to bundle all of the pictures into an easy-to-get-to folder on the main "device storage" directory.
I am going to assume, from context, that you are referring to what the Android SDK calls external storage.
Note that while you may think that cluttering up the user's external storage with yet another top-level directory is a good idea, please do not be shocked if users disagree with your assessment.
Eventually, I'll allow the app user to decide if they want internal or external memory
My guess is that you are referring to external storage and removable storage, respectively.
I was trying to find the folder like this:
You do not have read/write access to /mnt
, and the user has no access to that either.
What you appear to be aiming for is Environment.getExternalStorageDirectory()
. However, you should strongly consider either Environment.getExternalStoragePublicDirectory()
(for common locations, like photos and stuff) or getExternalFilesDir()
on Context
(for an app-specific location on external storage).
Do I need any type of permissions in the android manifest?
All locations on external storage require WRITE_EXTERNAL_STORAGE
on Android 4.3 and older. Also, the locations on external storage provided by Environment
require WRITE_EXTERNAL_STORAGE
on Android 4.4+ as well.
However, if you use getExternalFilesDir()
, you do not need WRITE_EXTERNAL_STORAGE
on Android 4.4+. This is another reason to consider using that location.
Also note that WRITE_EXTERNAL_STORAGE
is a dangerous
permission in Android 6.0+, meaning that if your targetSdkVersion
is 23+, you have to ask for it at runtime. Plus, regardless of targetSdkVersion
, the user can decide to not grant you that permission. Hence, avoiding WRITE_EXTERNAL_STORAGE
is generally a good idea.
Related Topics
How to Extract the Text from the Selected Item on the Listview
Bitmap Is Returning Null from Bitmapfactory.Decodefile(Filename)
Start a Service in a Separate Process Android
Android: 2 or More Expandablelistview Inside Navigation Drawer
How to Set the Output Image Use Com.Android.Camera.Action.Crop
Error:Execution Failed for Task ':App:Transformclasseswithdexfordebug' in Android Studio
Using Gson with Proguard Enabled
Android - Inner Element Must Either Be a Resource Reference or Empty
How to Include Data Files with the App's APK
Preferencefragmentcompat Has Padding on Preferencecategory That I Can't Get Rid Of
How to Set Different Applicationid for Each Flavor Combination Using Flavordimensions
Android: Change Default Home Application
Use Recyclerview Inside Scrollview with Flexible Recycler Item Height
How to Block Virtual Keyboard While Clicking on Edittext in Android
Migration from Gcm to Fcm Needed
Mylib.So Has Text Relocations. This Is Wasting Memory and Is a Security Risk. Please Fix