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);
}
}
}
}
Related
I am unable to write file to external SD Card . I get error message EAcess denied. I have searched a lot on internet and found that from Android 4.4 + android's Storage Access Framwork (SAF) is required to write file.
But I am using some android applications which are able to write(Create/Delete/Rename) file on SD Cards. They are not using SAF.
So please help me as to how can I do this without using SAF framwork.
Thanks
There are a lot of confusions talking about External Memory of Android. It doesn't point to Removable SD MICRO CARD actually. So, what Google thinks "external memory" means
Refer to Android API Document
Every Android-compatible device supports a shared "external storage"
that you can use to save files. This can be a removable storage media
(such as an SD card) or an internal (non-removable) storage. Files
saved to the external storage are world-readable and can be modified
by the user when they enable USB mass storage to transfer files on a
computer.
The fact is Environment.getExternalStorageDirectory() and Context.getExternalFilesDirs() could return an emulated External Memory located inside the Internal storage. Thus, these functions themselves don't give an expected results. The SECONDARY_STORAGE environment variable can help to get a real path of removable memory but writing on root of this isn't allowed because of OEM implementation. In this case, we should try to get app's data folder by Context.getExternalFilesDirs() or ContextCompat.getExternalFilesDirs() on which app's data file is allowed to be read and written.
I solve my problem by using below method, please check it and hope it helps you overcome your issues.
#TargetApi(Build.VERSION_CODES.KITKAT)
private String getRemovablePath(){
String secondaryStore = System.getenv("SECONDARY_STORAGE");
if (secondaryStore != null){
secondaryStore = secondaryStore.split(":")[0];
secondaryStore += File.separator + "Backups/";
File file = new File(secondaryStore);
if((file.mkdir() || file.isDirectory()) && isFileWritable(secondaryStore)){
return secondaryStore;
} else {
secondaryStore = null;
}
}
// try again by fix address
if(secondaryStore == null){
if (new File("/Removable/MicroSD/").exists()){
secondaryStore = "/Removable/MicroSD/";
} else if( new File("/storage/extSdCard/").exists()){
secondaryStore = "/storage/extSdCard/";
} else if( new File("/storage/sdcard1/").exists()){
secondaryStore = "/storage/sdcard1/";
} else if( new File("/storage/usbcard1/").exists()){
secondaryStore = "/storage/usbcard1/";
} else if( new File("/storage/external_SD/").exists()){
secondaryStore = "/storage/external_SD/";
}
/** add more fix addresses you know */
secondaryStore += "Backups/";
File file = new File(secondaryStore);
if((file.mkdir() || file.isDirectory()) && isFileWritable(secondaryStore)){
return secondaryStore;
} else {
secondaryStore = null;
}
}
/** Try data folder*/
if(secondaryStore == null){
int ver = Build.VERSION.SDK_INT;
File[] externalRoots = null;
if(ver <= Build.VERSION_CODES.JELLY_BEAN_MR2){
externalRoots = ContextCompat.getExternalFilesDirs(getBaseContext(), null);
} else {
externalRoots = getExternalFilesDirs(null);
}
if(externalRoots.length > 1){
secondaryStore = externalRoots[1].getAbsolutePath() + File.separator;
return secondaryStore;
} else {
secondaryStore = null;
}
}
return secondaryStore;
}
please check the link, where present issue:
issue
for access to external memory in previous android versions there is no problem. current possess improvements
Android API < 23
Your Android Manifest must declare the specific user permission:
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
You also have to declare the reading permission if you also intend to read files:
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
User permissions must be placed before the application section, like this:
<manifest>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
...
<application>
Source of my explanation and examples on how to save files can be found in the official documentation.
Android 6 (API 23)
Things get a bit different starting with Android API 23 because user permissions have to be asked to the user in runtime when needed. A valid answer to this was already given here.
SAF is only needed if you have to write to any location on the SD Card. To write to your app-specific directory on the SD Card, you can use context.getExternalFilesDirs. One of the paths returned will be the path of the your app specific folder on the SD Card.
Again, this is manufacturer dependent as well. If the manufacturer has not set the SECONDARY_STORAGE environment variable, the paths returned by getExternalFilesDirs will not contain the SD Card path.
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");
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.
I have a Samsung Nexus S device with android 4.0 loaded on it. I am trying to create a file in an existing folder on sdcard and get a "permission denied". In the following code, exists() returns true but canWrite() returns false. Why?
File exst = Environment.getExternalStorageDirectory();
String exstPath = exst.getPath();
File d = new File(exstPath+"/TestDir/");
if (!d.exists())
{
int b = 1;
}
if (!d.canWrite())
{
int a = 1;
}
By the way, I've added <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> to the manifest but that did not help.
Is your phone plugged into your computer? If so, the computer will take control of the SD card and not allow it to be written to. Try changing the connection mode to 'Charge Only' if this is the case.
Append getAbsolutePath() to your first line, then it should work:
File exst = Environment.getExternalStorageDirectory().getAbsolutePath();
Update:
Reviewing my own code and other SO answers, I believe you do not use canWrite to check if a path is writable on a SD card. Instead you use Environment.MEDIA_MOUNTED:
String state = Environment.getExternalStorageState();
if (Environment.MEDIA_MOUNTED.equals(state)) {
Log.d("Test", "sdcard mounted and writable");
}
else if (Environment.MEDIA_MOUNTED_READ_ONLY.equals(state)) {
Log.d("Test", "sdcard mounted readonly");
}
else {
Log.d("Test", "sdcard state: " + state);
}
Although all the answers give partial suggestions, the problem is very likely Samsungs non-standard implementation of the API
String myPath = Environment.getExternalStorageDirectory().getAbsolutePath ;
// or .getName() or .getPath() <-- these don't return everything you need.
// same is true for the file version you're trying.
// you have to append
"/external_sd/"
to the path before the file name.
Here's Samsungs explanation for their "breaking" of the API
http://developer.samsung.com/forum/board/thread/view.do?boardName=GeneralB&messageId=162934&messageNumber=1381&startId=zzzzz~&searchType=TITLE&searchText=sdcard
it's also mentioned in several SO posts, but I don't have the links handy...
H
[Edit Mid May, 2013] Here's the pathology of this problem: you can get the path() by the various normal methods mentioned. Then, just write a simple file and watch it show up in the DDMS file explorer. Try hardcoding that exact path and file name OR use the /external_sd/ thing - in both cases, you will not be able to read your own file back in ! (Or you might, but it will contain garbage.) We've verified this on three different S3 phones. Will test more on Samsungs "real" phones via their RemoteTestingLab site and report back.
I have an Android app which by preference uses external storage if available to store various files but will use internal storage if external storage is unavailable.
I've extended Application and maintain a static File for the app's working directory as follows...
public class MyApp extends Application {
protected static File myFilesDir = null;
protected static Helper myHelper = null;
#Override
public void onCreate() {
super.onCreate();
myHelper = new Helper(this);
if (myHelper.CanWriteExtStorage()) {
Log.d(TAG, "onCreate() - myHelper.CanWriteExtStorage() returned TRUE");
myFilesDir = new File(Environment.getExternalStorageDirectory().getAbsolutePath() + "/Android/data/" + packageName + "/files");
}
else {
Log.d(TAG, "onCreate() - myHelper.CanWriteExtStorage() returned FALSE");
myFilesDir = new File(getFilesDir().getAbsolutePath());
}
myFilesDir.mkdirs();
if (!myFilesDir.equals(null)) {
Log.d(TAG, "onCreate() - myFilesDir: " + myFilesDir.getAbsolutePath());
}
The app is currently being beta-tested by some people I'm in direct contact with and one guy commented that he thought I was supposed to be using the SD card if available but he could see from logcat the app was using /nand/Android/data... I asked what /nand referenced and he said it is internal memory on his pad/tablet device.
The code the Helper class uses to check for external storage is as follows...
protected boolean CanWriteExtStorage() {
boolean mExternalStorageWriteable = false;
String state = Environment.getExternalStorageState();
if (Environment.MEDIA_MOUNTED.equals(state))
mExternalStorageWriteable = true;
return mExternalStorageWriteable;
}
...but the relevant log output is as follows...
D/com.mycompany.myapp(3844): onCreate() - myHelper.CanWriteExtStorage() returned TRUE
D/com.mycompant.myapp(3844): onCreate() - myFilesDir: /nand/Android/data/com.company.myapp/files
So, the question is why, when I'm able to test that the external storage state shows it is mounted, would Environment.getExternalStorageDirectory() return a path to what appears to be internal storage?
Should I be testing for something other than Environment.MEDIA_MOUNTED or is there just something strange about this device or Android version (he is running v2.1).
In the long run it possible doesn't matter but I'm concerned my logic is incorrect for certain devices.
Read the documentation for Environment.getExternalStorageDirectory()
Note: don't be confused by the word
"external" here. This directory can
better be thought as media/shared
storage. It is a filesystem that can
hold a relatively large amount of data
and that is shared across all
applications (does not enforce
permissions). Traditionally this is an
SD card, but it may also be
implemented as built-in storage in a
device that is distinct from the
protected internal storage and can be
mounted as a filesystem on a computer.
In devices with multiple "external"
storage directories (such as both
secure app storage and mountable
shared storage), this directory
represents the "primary" external
storage that the user will interact
with.
I have not personally seen a case but there is no requirement that getExternalStorageDirectory returns the memory card. You are doing the correct thing by treating the returned directory as shared storage as it was an intentional decision by the device manufacturer.