I've already figured out that on some devices the externalStorage may be set as the device Storage, like on my Samsung Galaxy 4 10.1 with 5.0.2. Android OS.
The Question now is how can I still say to save it on the SD-Card ?
For Example:
private File makeFolder(String folder) {
File dir = new File(Environment.getExternalStorageDirectory() + "/" + folder + "/");
if (!(dir.exists() && dir.isDirectory())) {
dir.mkdirs();
}
return dir;
}
On my older Devices (like my Sony Ericson Pro) this works just fine. So how to solve this?
You are into a slightly complicated area of Android programming :)
I can not give you complete answer here, but I can give some pointers:
On almost all devices I've seen, Environment.getExternalStorageDirectory() will give you the path to the root directory of an emulated SDCARD - which is not a physical SDCARD. Manufacturers are free to map the physical SDCARD to this path - but usually, they emulate an SDCARD using internal memory)
And prior to Kitkat, there are no API available to even to determine the presence of a physical SDCARD.
From Kitkat onwards, you can see if a physical SDCARD is present by checking the result of appContext.getExternalFilesDirs() which will give you your app's private data directory path on all available storage media.
If a physical SDCARD is present and mounted, appContext.getExternalFilesDirs() will give you two paths are result. Then the first one is for the emulated SDCARD. You may notice that this path will contain same subpath that returned by Environment.getExternalStorageDirectory().
The following example will make this clearer. I am assuming a Samsung S4 with a real SDCARD inserted and mounted in it
The first path returned will be:
/storage/emulated/0/Android/data/com.example.yourapp/files.
The second path is interesting - as this path is your app's accessible area on the the real physical SDCARD. This path will be of the form:
/storage/extSdCard/Android/data/com.example.yourapp/files
This is the only path that you have write access in Kitkat.
So, from Kitkat onwards, from the presence of these two paths, (and added checks for mounted SDCARD), you can easily find out whether your app is running on a phone with real SDCARD or not. You can even parse the second path to get the root directory of the real SDCARD (just remove /Android/... substring from this path)
And yes, now you know why you are so restricted with real SDCARDs in Kitkat. As said in italics above, you have write access to only that directory on a physical SDCARd in Kitkat.
Now comes Marshmallow and things changes again. In Marshmallow you have a storage framework, using which you can present the user with a permission dialog box and you can tell him to grant your app complete access to whatever device or directory therein. Please search for "Intent.ACTION_OPEN_DOCUMENT_TREE" for nice tutorials.
So, in short, as of now, there is no nice way in Android to deal with SDCARD which works uniformly in all versions. Before Kitkat, it is a grey area, In Kitkat, you are severely restricted, and in Lollipop, you are stuck with a system Activity with an absurd looking GUI for permission and not so easy to use framework.
Related
I get a warning from android lint:
Do not hardcode "/sdcard/"; use Environment.getExternalStorageDirectory().getPath() instead
I will fix this, but still would need to know:
On which (example) devices is this a problem, what other (example) paths can you get from this call? If this is not related to specific devices, to what is it specific or when would it happen?
Under what circumstances is there no /sdcard/ directory that my app could write to?
The app has the rights
<uses-permission android:name="android.permission.WRITE_INTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
what other (example) paths can you get from this call?
Partly, it is whatever the manufacturer wants.
Partly, it will vary based on the user account that is running your app. Please bear in mind that for a few years (since the release of Android 4.2), Android devices can have multiple user accounts (tablets starting with 4.2, phones starting with 5.0). Each user gets a distinct location for internal and external storage, and there is no guarantee as to what actual filesystem paths those will point to.
NEVER HARDCODE ROOT PATHS to internal or external storage. Always use an appropriate method for getting a root location, then use the appropriate File constructor to point to whatever you want within there.
The app has the rights
Since there is no WRITE_INTERNAL_STORAGE permission in Android, please remove it.
The SD card path is different for different Android manufacturers. So I make my own research with my friends' sdcard on their phone. And the result:
Sony XPERIA X10i (my phone)
Android 2.3.3 (Gingerbread)
path for phone internal memory: not available
path for sdcard: /mnt/sdcard/
Samsung Galaxy S3 Mini & Samsung Galaxy Young (given same result)
Android 4.2.2 (JellyBean)
path for phone internal memory: /storage/sdcard0/
path for sdcard: /storage/extSdCard/
OPPO (I don't remember what her phone type is)
Android 4.2.2 (JellyBean)
path for phone internal memory: /storage/sdcard0/external_sd/
path for sdcard: /sdcard0/
I wrote the result in a book. So my suggestion, never use hardcode for sdcard's path. Check here to know your sdcard's path.
The external storage may point other places,
you may define and mount other place as device external storage,
it a configure option, that why you have a Lint warning.
For example if the device support External SD card, than the external sdcard (getExteranlStorage()) will point to him (the exteranl sdcard will mount at /nmt/sdcard#/), top keep the internal sdcard free.
To make it clear
The /sdcard/ == /mnt/sdcard[0] -> internal sdcard
other sdcard will mount at /mnt/sdcard[1..]/ -> external sdcard
And also the manufacture of the device can call it as he wish (/sdcard/ is just a convention not a must have)
I am having very Strange problem with my Samsung Nexus with Android 4.0.2. I wrote a simple program which basically create file each time user presses a button. My program is working great on other devices where as on Galaxy Nexus Windows/Mac can not see those files on SD card, but when i do adb shell i see all files on SD Card.
I retest this on another Nexus which has Android 4.0.4 but still same result.
What could be the reason?
You probably need to index your files via MediaScannerConnection. Quoting myself from a blog post from last year:
...the MTP contents are not based on the literal contents of external storage. Instead, MTP contents are based on what files have been scanned by MediaScannerConnection. If you write a file to external storage, until and unless that file is scanned by MediaScannerConnection, it will not be visible to users over MTP.
External storage is scanned on a reboot and possibly on a periodic basis. Users can manually force a scan via utilities like SDRescan. However, the best answer is for you to use scanFile() on MediaScannerConnection to update the media database after you close your file. This will make your file immediately available to the user.
I have an app which saves backup files on SD-Card. It works fine on HTC Nexus One, and other android phones, but with some phones it doesn't work (reading or writing).
In the manifest I have set this:
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
For example (when I set the path for recording file) :
OutputStream output = new FileOutputStream(Environment.getExternalStorageDirectory()
+ "/mydata.dat");
//Writing file...(It doesn't work)
How I can get the right path of the SD-Card for manipulating files rightly?
When you say "it doesn't work".... what exactly happens? Do you mean the program runs without complaint but the file doesn't get written to the sdcard, or do you mean something else?
If the file is simply not getting there, it's not your fault, it's the phone manufacturer's fault. I've seen several devices that return "/mnt/sdcard" from getExternalStorageDirectory(), however this is not truly the path to the sdcard! On at least one Motorola device and on the Samsung Galaxy Tab (7"), for example, /mnt/sdcard is returned even though this points to internal storage, and the external storage on each of these devices is /mnt/sdcard-ext.
There's nothing legitimately you can do about this -- the OEM is lying to you. If you want a hack of a work-around, you can read /proc/mounts and try to find the actual path to the sdcard, but /mnt/sdcard will also show up there, and there's no guaranteed way to distinguish the truth for all devices.
Generally, Android wouldn't allow you to write a file directly to the SDCard's root. You must create a folder in the SDCard root, and then write your file inside the newly created folder.
Try this, and it should work.
I have Sony Xperia tipo dual and this:
FILE* pFile = fopen("/mnt/sdcard/mydata.dat","w+");
and this:
FILE* pFile = fopen("/sdcard/mydata.dat","w+");
Both work ok. (I did it in Native)
Of course in my code I use Environment.getExternalStorageDirectory() instead of hardwiring /mnt/sdcard/. But I just realized that when I export data from my application via a database dump in an exchange format, file paths are /mnt/sdcard/... This may explain some strange errors that I have seen in the logs from users.
I will make the appropriate changes in the import modules. But I am curious - does any Android devices > 1.6 have Environment.getExternalStorageDirectory() != /mnt/sdcard/ ?
Actually, some for some devices the getExternalStorageDirectory() will return localization for internal memory (internal SD card). The removable SD card is located as mount point below, usually getExternalStorageDirectory()+"/sd" or getExternalStorageDirectory()+"/external_sd". Unfortunately, there is no standard for this and you won't know what kind of storage you are pointing at.
No. I haven't seen any devices with sdcard mounted to different location. And I've played with more then 10 different devices from most popular vendors.
Having said that, you shouldn't rely on this fact. Especially if you have such an easy way to get path to External storage.
I had a user of one of my apps report this, he was using Andy 2.2 IIRC but I can't remember what device he was using. The path can be found using
Environment.getExternalStorageDirectory().getPath()
I think the path my user got was /sdcard & not /mnt/sdcard like my HTC phone does.
My app provides a file browser. I set the root of the file browser to: Environment.getExternalStorageDirectory(); which, on my Acer Iconia Tab, returns "/mnt/sdcard". However, many 3.0 devices support usb storage. Is there a safe/proper way to get the path to "/mnt"? (to the directory that contains the sdcard and usb storage)
If Environment.getExternalStorageDirectory(); gives /mnt/sdcard/, i was hoping there would be a safe method to get /mnt/usb_storage. There does not appear to be. However, Environment.getExternalStorageDirectory().getParentFile() appears to reliable give the parent directory of /mnt/usb_storage.
If you are talking about devices like the Motorola Xoom that do not have sdcard support then it is safe to use Environment.getExternalStorageDirectory(). The Xoom actually has the sdcard folder structure on the internal storage.