I want to give the user the option to save images on their sd card and I want to be able to calculate the sizes of folders of such images. I find that Environment.getExternalStorageDirectory() returns internal storage. The only way I can find the sd card is by using an ACTION_OPEN_DOCUMENT_TREE request. But this means I have to use DocumentFiles rather than regular Files. I can only get a content uri, not a direct path to the sd card itself.
So, File.length() works well with internal storage but DocumentFile.length() is extremely slow with external storage. Is there any way to get the size of a folder based on its content uri without it taking forever? Or can I somehow find an actual path to the sd card and use Files instead? I found a folder like /storage/3857-3732 but I don't seem to have permission to write to it (yes I have WRITE_EXTERNAL_STORAGE permission).
Related
I want to create directory in SD card same as in the internal storage.
My internal storage path is "sdcard/<my_directory_name>/"
I want to create the same directory in root of SD card.
I have try the following ways to find the path of directory.
sdcard1/<my_directory_name>/
Environment.getExternalStorageDirectory() +"/<my_directory_name>/"
Please suggest the way to find SD card path.
You do not have arbitrary access to removable storage on Android 4.4+. Hence, there is no useful path, from a filesystem standpoint. You are welcome to use getExternalFilesDirs() and kin -- if they return 2+ locations, the second and subsequent ones are on removable storage, and you can read and write to those locations.
Although as CommonsWare answered, you cannot read or write in Secondary External Storage, here's a way to generate it's path.
System.getenv("SECONDARY_STORAGE"); //returns /storage/extSdCard
You can also use String path = "/storage/extSdCard"; but again you cannot write files there.
Edit: As #CommonsWare commented, there is no guarantee of this method. Though when I tested it, it worked, but again, if he says there's no guarantee, you can take his word.
Background
Android had a lot of changes as to how to handle the SD-card and storage in general:
API 3 - you get all access, no permission needed
API 4-15 - you need to use WRITE_EXTERNAL_STORAGE, and you get all access.
API 16-18 - if you wish only to read, use READ_EXTERNAL_STORAGE
API 19-20 - you can't read or write to secondary external storage (SD-card), unless your app is a system app, or you have root.
API 21-22 - in order to access the SD-card, you need to ask the user for permission, and use the DocumentFile API instead of the File API. This raied a lot of questions, as I've written about here, here and here.
Starting with API 23 (Android 6), things seem to change yet again...
The problem
For API 23, there are at least 2 things that are new and are storage-related :
"Adoptable Storage Devices" - The user can optionally make the SD-card as something that's like the primary external storage.
As part of the new permissions mechanism (requesting permissions at runtime), it seems that storage is also a permission the user needs to confirm. This is for both READ_EXTERNAL_STORAGE and WRITE_EXTERNAL_STORAGE
Since there is no Android 6 device out there that has SD-card, and because the emulator itself doesn't really have the ability to use an SD-card, it's still impossible to know what's going on.
The questions
Will the SD-card get the access using the File-API instead of DocumentFile?
If I want access to all external storage paths (including SD-card), does this mean I need to request this permissions twice: one for the primary external storage and one for the SD-card?
Are files on the SD-card accessible in any way before the manual granting of the permission?
Suppose the user has chosen to use "Adoptable Storage Devices", what does it mean for the various functions that retrieve the paths of the app's files? For example : getFilesDir, getExternalFilesDir,... ? Would the oder of getExternalFilesDirs change because of it?
What happens to the files of the app when the user moves the app from/to the SD-card (using the "Adoptable Storage Devices") ? What about the app's files on the SD-card? Would they stay? Or would they move somewhere?
For example, if the app has "file1.txt" on the SD-card, on path "/storage/extSdCard/Android/data/appPackageName", and it has a file "file2.txt" (or even the same name) on the primary external storage on path "/storage/emulated/0/Android/data/appPackageName". After switching, what would happen for those files? How would they merge into a single folder, if at all?
When moving the app to the SD-card (using "Adoptable Storage Devices"), does it mean no internal storage will be used?
Let me answer Adoptable Storage Devices related questions:
Suppose the user has chosen to use "Adoptable Storage Devices", what does it mean for the various functions that retrieve the paths of
the app's files? For example : getFilesDir, getExternalFilesDir,... ?
Would the oder of getExternalFilesDirs change because of it?
When user choose to use SD card as "Adoptable Storage Device" (Format as internal), it means now that SD card is available only as Internal Storage i.e. no SD card available to store downloaded files. There will be no order change in paths returned by the related methods. For example: getExternalFilesDir() will list only external storage path if user formatted his SD card as "Adoptable Storage Devices". SD card path will not be available.
What happens to the files of the app when the user moves the app from/to the SD-card (using the "Adoptable Storage Devices") ? What
about the app's files on the SD-card? Would they stay? Or would they
move somewhere? For example, if the app has "file1.txt" on the
SD-card, on path "/storage/extSdCard/Android/data/appPackageName", and
it has a file "file2.txt" (or even the same name) on the primary
external storage on path
"/storage/emulated/0/Android/data/appPackageName". After switching,
what would happen for those files? How would they merge into a single
folder, if at all?
When ever user will choose his SD card as "Adoptable Storage Devices" then user need to format his SD card as internal storage using "Format as internal" option. Format means all the data/files stored on SD card will be erased. Similarly when user want to remove his SD card from "Adoptable Storage Devices" then user again have to format his SD card as portable storage using "Format as portable" option.
When moving the app to the SD-card (using "Adoptable Storage Devices"), does it mean no internal storage will be used?
Yes, original internal storage will not be used. Only SD card storage will be used because after choosing SD card as "Adoptable Storage Devices". All the data/cache will be stored to SD card
To answer your question 5: under Android 7, all files in the "public data" area, /storage/emulated/0/ (or a manufacturer-specific location; it's the directory containing DCIM Downloads, etc.), are stored either entirely in the internal memory or entirely on the adopted SD card. After the initial format of an adopted SD card the user will be presented with an opportunity to "Migrate data" which will move all of /storage/emulated/0 to the SD card. At any time the user can also go to Settings / Storage, pick the storage area that does not currently hold the public data area, and the and clicking the ⋮ (three vertical dots) icon at the upper right to bring up a menu with the "Migrate data" option.
It appears that there's no way for a user or application to force a particular public file to the SD card if an adopted card is in use; it's all or nothing. (The app's private storage will be in the internal storage or on the SD card based on whether or not the app has been moved to the SD card; there too it appears that the app will only ever be using one or the other, not both.)
My app can write and delete file from the android sd card by adding the
android.permission.WRITE_EXTERNAL_STORAGE permission.
But this does not work with the external SD card some devices support. Writing to the default SD card (/mnt/sdcard0 in most cases) has no problem, but this does not work with the extra sd cards (/mnt/sdcard1 ).
I have googled about this and also gone through some threads in stackoverfolw itself, but still i am not able to find a proper solution.
Is it really a bug in android? Is there any workaround for this?
I will really appreciate any clarification on this.
You can get the path of sdcard from this code:
String path = Environment.getExternalStorageDirectory().getAbsolutePath();
Then specify the foldername and file name.
if you are using an emulator, double-check to make sure that you have filled in a value for the SD Card size.
how can I get the path for the folder where my app can save large JPG files?. getExternalStorageDirectory() works fine only when SD Card is present, but what happens when SD is removed or the harware don't have SD Card slot.
thanks
You can use the getFilesDir() method of a Context. From a context you can also use methods to get the cache directory, external cache directory, and the external files directory. An Activity is also a context, so you can use these methods from inside one.
The getFilesDir() method gives you the folder where your files will be accessible only from your application and will be always available. However, you should use the cache directory instead, when possible. This way you will avoid making the system run out of space.
EDIT:
My answer: Almost always a device will either have an SD card or built-in external storage. When it's built-in, it's still called external storage. To check whether the external storage is removable (SD card) or built-in you can use isExternalStorageRemovable() in Environment.
Basically, you shouldn't place large files on the internal memory. There is no public folder in the internal memory. If a device doesn't have external storage, it's simply not capable of doing certain things. Simple as that. So one option you have when there is no external storage is to inform the user about it and ask them to insert a card. You don't have to handle this case, let the user handle it.
The answer you asked for: Try using getDir(String name, int mode) and/or openFileOutput(String name, int mode) of a Context object, and for mode use MODE_WORLD_READABLE or MODE_WORLD_WRITEABLE. Also check Using the Internal Storage.
You are facing intended limitations of the platform that are there for the good of everyone.
You could either require external directory to exist, or you can store to the internal directory. If you choose to permit both, I suggest you store a flag in internal space to indicate that you've stored something externally, so that if external storage is not present you can take appropriate action.
As you are Saving Large JPG files, its is better to save it in external storage because Phone has very small internal memory and its all effect the performance of phone.
According to my application, I'm downloading the pictures and some data and I need to save them somewhere in the phone memory or sd card. Since the pictures can be quite big, I decided to save it into sd card. I'm using the code below to determine whether the sd card is available or not. If it is I'm using SD card if not I'm using phone memory. But the Issue is, If I eject the SD card, my application crashes on start up and I get File Not Found exception. As far as I understand, since the phone has internal SD card,
android.os.Environment.getExternalStorageState().equals(android.os.Environment.MEDIA_MOUNTED)
is always returning "mounted". How can I understand the external SD card is really ejected ?
EDIT: I tried to check whether the SD card is available or not , since I'm using the code below and since I have internal SD card in my phone
android.os.Environment.getExternalStorageState().equals(android.os.Environment.MEDIA_MOUNTED)
always returns true and It creates the directory without having and SD card. So checking directory for null is not working to understand whether the file is exist or not. Here is my code below.
if (android.os.Environment.getExternalStorageState().equals(android.os.Environment.MEDIA_MOUNTED)) {
cacheDir=new File(context.getExternalFilesDir(null), applicationDirectory + folderName);
}
else {
cacheDir = new File(context.getCacheDir(), applicationDirectory + folderName);
}
if(!cacheDir.isDirectory())
cacheDir.mkdirs();
So according to my solution, I try to read the required file and If I get FileNotFoundException I copy all the content from assets folder to the directory(SD card or phone memory) when application starts up and I control it with a boolean value in shared preferences. In that way if the user ejects the SD card, the content will be copied to phone memory and will be working from that directory.
I need around 10MB of cache space to save some of my files, and with this solution (I'm not sure if it's that effective) I will have my content both in SD card and phone memory if the user runs the application when there is no SD card. Any other solution to find out where to keep files, external or internal memory ? Thanks for the answers. Any Idea will be appreciated.
You could using these methods to check if your Folder on the external sd exists:
if(myFolder != null && myFolder.exists()) { /* work here */ }
myFolder would be a file targeting at the external sd (you should check the vendor mappings in the internal memory because they differ vor each vendor).
Sadly two sds is not the real Android way and this results in Android thinking the internal sd is the external one.
There is a discussion and a code example for finding the external sd here.
Check this thread:
How android application detects two SD CARDS in a device
Maybe you could then check if the folders for the sdcards exist and handle the cases where the card does not exist differently?
So something like:
sdcardloc = /mnt/sdcard/sdcard1;
if(sdcardloc != null && myFolder.exists()) { /*case where card is mounted*/ }
else { /*handle case where no card is mounted*/}
External SD card, the concept is specific for device vendor. In android only External Storage concept. And also Environment.getExternalStorageDirectory() refers to whatever the device manufacturer considered to be "external storage".