How to manage external storage independently of the phone - android

I have phone (B15 CAT) with a sd card slot. When i insert a sdcard in this phone and asking for the external storage directory with :
Environment.getExternalStorageDirectory()
it always return an space on sdcard0 which is the internal memory. This memory is too small for my need.
By listing /mnt i found a mount point named /sdcard2 which is the "real" scard.
Unfortunately sdcard2 doesn't seems to be a standard and some other brand will use some other name...
Knowing that getExternalStorageDirectory() seems working as expected on phone with no sdcard slot , like nexus 4, how should i handle external storage to be sure to write on the sdcard (big space available) and not on internal memory ?
I have tried something like this :
File mnt = new File("/mnt");
File[] liste = mnt.listFiles();
boolean hassd2 = false;
for(File mount : liste) {
if(folder.getName().equals("sdcard2") {
hassd2 = true;
break;
}
}
String path = "";
if(hassd2) {
path = "/sdcard2/my/folder/"
} else {
File p = new File(Environment.getExternalStorageDirectory().getAbsolutePath()+"/my/folder/");
path = p.toString();
}
It's working but only with this specific phone and others one with no sdcard slot ...

I also had the problem with the build in functions of Android in case of multiple 'external' storages mounted. I parsed the mounted directories directly from the f_stab file.
This link should give you the code you needed.
After having the mount points you could try to calculate the available space in order to decide if it is enough for your operation.

Related

Use Googles FUSE to determine if SD CARD is mounted

We have looked at numerous SO post that deal with the SD CARD also the SO post which seems to be the Gold Standard Gold Standard But it deals with permissions we are not asking about permission. The question deals with finding another way to determine if the SD CARD is mounted. This question only wants to deal with SDK 23+ The article that discuss FUSE is at this link FUSE
We have tried this code that when the emulator has the SD CARD ejected returns or evaluates to TRUE. Other similar configuration from SO have also been tested.
My question is not only how to detect if the SD CARD is mounted but why is this code failing? We are not sure if this code can be tested on an emulator or if a real device is needed. We feel this code failure is because of the concept of the term EXTERNAL storage not meaning an actual SD CARD but making reference to the secondary EXTERNAL storage that is internal.
public boolean chkFORSDCARD() {
String state = Environment.getExternalStorageState();
if (Environment.MEDIA_MOUNTED.equals(state)) {
System.out.println("#################### IS ####### TRUE "+state);
return true;
}
System.out.println("##################### IS ###### Not Available "+state);
return false;
}
Here is where #james_duh are getting into trouble this line of code as mentioned in your comment `THE_PATH = THE_PATH + "/Documents/"; will not work when the SD CARD is unmounted with this line of code set to [1]
File removable = ContextCompat.getExternalFilesDirs(this, null)[1];
The solution is simple remove the THE_PATH = THE_PATH + "/Documents/";
As for testing if the SD CARD is mounted I am still working on that stay tuned
This code is not real neat but it works. Why you want it to work might be a 64K question ? ? I have tested the code and it works. What might be or concern is the words used to evaluate the path not sure they are or will remain consistent
Here is the code It seems point less to check the state so you can remove that test and construct a new more suitable one I did not get that far
public void onAvail() {
String state = Environment.getExternalStorageState();
if (state.equals(Environment.MEDIA_MOUNTED) || (!state.equals(Environment.MEDIA_MOUNTED_READ_ONLY))) {
File removable = ContextCompat.getExternalFilesDirs(this, null)[1];
THE_PATH = String.valueOf(removable);
if(THE_PATH.contains("Android")){
System.out.println("################################### EXTERNAL Storage "+THE_PATH);
THE_PATH = THE_PATH + "/Documents/";
}else {
File dir = new File(Environment.getExternalStorageDirectory().getAbsolutePath());
String INTERNAL_PATH = String.valueOf(dir);
if(INTERNAL_PATH.contains("emulated")){
System.out.println("######################## $$$$$$$ #### Internal Storage "+INTERNAL_PATH);
}
}
}
}

Can an Android app obtain write permission to /mnt/asec or /mnt/obb?

I have an application that performs a write operation on an external SD Card (external as in not the "external" in the device's flash memory), but as it has been thoroughly discussed here and in quite a few other questions, it appears there is no generic way / supported API to retrieve an SD Card mounting point that works on every phone, from different manufacturers.
With that in mind, something like the code below needs to be done to find out if an external storage device is mounted and where:
final String state = Environment.getExternalStorageState();
if ( Environment.MEDIA_MOUNTED.equals(state) || Environment.MEDIA_MOUNTED_READ_ONLY.equals(state) ) { // we can read the External Storage...
//Retrieve the primary External Storage:
final File primaryExternalStorage = Environment.getExternalStorageDirectory();
//Retrieve the External Storages' root directory:
final String externalStorageRootDir;
if ( (externalStorageRootDir = primaryExternalStorage.getParent()) == null ) { // no parent...
Log.i("SD Card Path", "External Storage: " + primaryExternalStorage + "\n");
}
else {
final File externalStorageRoot = new File( externalStorageRootDir );
final File[] files = externalStorageRoot.listFiles();
for ( final File file : files ) {
if ( file.isDirectory() && file.canRead() && (file.listFiles().length > 0) ) { // it is a real directory (not a USB drive)...
Log.i("SD Card Path", "External Storage: " + file.getAbsolutePath() + "\n");
}
}
}
}
I could be wrong, but I understand the SD Card could be completely empty and that last condition (file.listFiles().length > 0) could return false. So I thought, instead of checking that length, I would add file.canWrite(). That worked as I expected on the phone I am currently testing on (Motorola Atrix MB860 - it's old, I know, but one of my requirements is make it compatible with versions starting from Gingerbread, API 9), but I am unsure whether or not that condition could return true to /mnt/asec/ or /mnt/obb on other phones , recent or not, since their both readable directories, thus also satisfying the first two conditions (and could even return true to (file.listFiles().length > 0) if it had something written on it (like a 50MB+ size file on /mnt/obb).
So, in short, what I need to know is whether an app will ever have write permission to these two locations or if only the system has such permission. If yes, what else can I check for in order to exclude them from the final path I wish to obtain: the external SD Card installed on the device?
Thank you in advance for any help!
This is the output on a Nexus 5
shell#hammerhead:/ $ ls -la /mnt/
drwxr-xr-x root system 1970-09-18 01:42 asec
drwx------ media_rw media_rw 1970-09-18 01:42 media_rw
drwxr-xr-x root system 1970-09-18 01:42 obb
as you can see only root has write permission on the directory. So the only way an app can write that directory is to be executed as root

Environment.getExternalStorageState() returns MEDIA_REMOVED on an HTC that has available memory

In my app, im trying to check whether the user has connected their phone to the pc as a drive, so I can warn them to disconnect it because I need access to the storage.
It works fine on all 4-5 devices that I've tested on, except this HTC Desire X phone, Android 4.0.4 . It has no SD card, but there is about 2 gb of some storage available for writing.
Here is the code that I use
private void checkStorage() {
// Get the external storage's state
String state = Environment.getExternalStorageState();
Log.d("STATE", state);
if (state.equals(Environment.MEDIA_MOUNTED)) {
// Storage is available and writeable
externalStorageAvailable = externalStorageWriteable = true;
} else if (state.equals(Environment.MEDIA_MOUNTED_READ_ONLY)) {
// Storage is only readable
externalStorageAvailable = true;
externalStorageWriteable = false;
} else {
// Storage is neither readable nor writeable
externalStorageAvailable = externalStorageWriteable = false;
}
}
So when I run it, the logcat debug tag returns:
removed
According to documentation:
http://developer.android.com/reference/android/os/Environment.html#MEDIA_REMOVED
"Storage state if the media is not present."
How can I modify this code so it detects the presence of a memory that is available for use?
Now, there is another thing. On this phone, when I try downloading an image from the browser, Im not allowed to, and I get the following message:
No SD card
An SD card is required to download <filename>
OK
Is there something wrong with the phone or is this normal? How do I make my app work on this phone?
EDIT: Furthermore, if I am able to get past this, how do I write files to the storage? Here's my code that does that and works on other devices:
File directory = null;
File file = null;
try {
directory = new File(Environment.getExternalStorageDirectory().getAbsolutePath(),
Utils.getWritableDiectory(Locale.ENGLISH));
if (!directory.exists()) {
directory.mkdirs();
}
file = new File(directory, "data.json");

Removable storage (external) sdcard path by manufacturers

I've been Googling around but it is sooooo hard to find what manufacturers/models use which path for sdcard/external storage.
I am NOT talking about the internal storage path which can be found by:
Environment.getExternalStorageDirectory()
I know that getExternalStorageDirectory() sometimes points to external sdcard on some devices.
Here's what I've found some common path for external path (Not sure which manufacturer uses which path):
/emmc
/mnt/sdcard/external_sd
/mnt/external_sd
/sdcard/sd
/mnt/sdcard/bpemmctest
/mnt/sdcard/_ExternalSD
/mnt/sdcard-ext
/mnt/Removable/MicroSD
/Removable/MicroSD
/mnt/external1
/mnt/extSdCard
/mnt/extsd
/mnt/usb_storage <-- usb flash mount
/mnt/extSdCard <-- usb flash mount
/mnt/UsbDriveA <-- usb flash mount
/mnt/UsbDriveB <-- usb flash mount
These are what I found by Googling around.
I need to scan entire internal + external storage + USB flash drive to look for a certain file. If I am missing any path, please add to the above list. If someone knows paths used by each manufacturers, please share with us.
Good news! In KitKat there's now a public API for interacting with these secondary shared storage devices.
The new Context.getExternalFilesDirs() and Context.getExternalCacheDirs() methods can return multiple paths, including both primary and secondary devices. You can then iterate over them and check Environment.getStorageState() and File.getFreeSpace() to determine the best place to store your files. These methods are also available on ContextCompat in the support-v4 library.
Also note that if you're only interested in using the directories returned by Context, you no longer need the READ_ or WRITE_EXTERNAL_STORAGE permissions. Going forward, you'll always have read/write access to these directories with no additional permissions required.
Apps can also continue working on older devices by end-of-lifing their permission request like this:
<uses-permission
android:name="android.permission.WRITE_EXTERNAL_STORAGE"
android:maxSdkVersion="18" />
I also started with a list like yours from somewhere (sorry couldn't find the original source) and continue to add some paths collected from user feedback. There are duplicate to the above list.
/storage/sdcard1 //!< Motorola Xoom
/storage/extsdcard //!< Samsung SGS3
/storage/sdcard0/external_sdcard // user request
/mnt/extsdcard
/mnt/sdcard/external_sd //!< Samsung galaxy family
/mnt/external_sd
/mnt/media_rw/sdcard1 //!< 4.4.2 on CyanogenMod S3
/removable/microsd //!< Asus transformer prime
/mnt/emmc
/storage/external_SD //!< LG
/storage/ext_sd //!< HTC One Max
/storage/removable/sdcard1 //!< Sony Xperia Z1
/data/sdext
/data/sdext2
/data/sdext3
/data/sdext4
/sdcard1 //Sony Xperia Z
/sdcard2 //HTC One M8s
/storage/microsd //ASUS ZenFone 2
For what you want to achieve (scanning all possible paths for certain files), I think it's quite impossible to do it this way (using a list of known paths). Some paths on same device even changed between android upgrade.
Based on the paths oceanuz provided, I wrote a method, which locates the external storage path (ignoring the case) and returns it as a String.
Note: I used StreamSupport library inside my method, so in order for it to work, you'll need to download the jar file and add that to libs folder in your project and that's it.
public static String getExternalSdPath(Context context) {
List<String> listOfFoldersToSearch = Arrays.asList("/storage/", "/mnt/", "/removable/", "/data/");
List<String> listOf2DepthFolders = Arrays.asList("sdcard0", "media_rw", "removable");
List<String> listOfExtFolders = Arrays.asList("sdcard1", "extsdcard", "external_sd", "microsd", "emmc", "ext_sd", "sdext",
"sdext1", "sdext2", "sdext3", "sdext4");
final String[] thePath = {""};
Optional<File> optional = StreamSupport.stream(listOfFoldersToSearch)
.filter(s -> {
File folder = new File(s);
return folder.exists() && folder.isDirectory();
}) //I got the ones that exist and are directories
.flatMap(s -> {
try {
List<File> files = Arrays.asList(new File(s).listFiles());
return StreamSupport.stream(files);
} catch (NullPointerException e) {
return StreamSupport.stream(Collections.emptyList());
}
}) //I got all sub-dirs of the main folders
.flatMap(file1 -> {
if (listOf2DepthFolders.contains(file1.getName().toLowerCase())) {
try {
List<File> files = Arrays.asList(file1.listFiles());
return StreamSupport.stream(files);
} catch (NullPointerException e) {
return StreamSupport.stream(Collections.singletonList(file1));
}
} else
return StreamSupport.stream(Collections.singletonList(file1));
}) //Here I got all the 2 depth and 3 depth folders
.filter(o -> listOfExtFolders.contains(o.getName().toLowerCase()))
.findFirst();
optional.ifPresent(file -> thePath[0] = file.getAbsolutePath());
Log.i("Path", thePath[0]);
return thePath[0];
}
This method will return an empty string if there is no Sd Card path found.
I was trying to find path to my external SD card on Sony Xperia XA. I then installed ES File explorer and that clearly showed path to any folder, internal or external, in it's characteristics window. I found the path to external SD as /storage/C4EE-F3A3. I was trying to save output file from Python3 to external SD but it did not have permission to save anywhere other than Qpython folder in the internal SD.

Saving to device with no SD card

My friend and I are attempting to create an app that saves files to a device. We used this code to write to an external SD card, and it works great on his Droid X and Samsung Galaxy Tab.
Get the path to the SD card:
private static final File ROOT = Environment.getExternalStorageDirectory();
Create the folder path and files:
FileWriter fw = new FileWriter(ROOT + "/test/" + "time_frames.txt");
we are using document factory to create the documents
so you can see that we create the path then try to save to that path that was just created
File file = new File(ROOT + "/test/" + "time_frames.txt");
When I run it on my Nexus S (which does NOT have a SD card) is having trouble with the exact same code.
private static final File ROOTtest = Environment.getExternalStorageDirectory();
this returns /data
private static final File ROOT = Environment.getRootDirectory();
this returns /mnt/sdcard
private static final File intData = Environment.getDataDirectory();
this returns /system
my question is which one of these will work for devices that have SD cards and no SD cards? I have tried a lot, but trying all this stuff has really confused me. Thanks in advance
Environment.getExternalStorageDirectory() returns the path to external storage, it should work on all devices. Whether they have an actual SD card doesn't matter, and your code shouldn't care either. You need to make sure that external storage is available before you try to use it though, because it could be unmounted at any time.

Categories

Resources