I have a device with an SD card. Now I want to check that device has mounted an external SD card and can read files from the public DCIM folder. I know that I can use Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM);, but this method returns only files, that is on primary external memory, not on the mounted SD card (Secondary external storage).
I found that Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM); returns an element with index 1, for the secondary external storage, but this method gets files only for application sandbox (Android/data/packagename). So my question is how get path to secondary external path for public directory like DCIM?
I found solution, here is code snippet:
String strSDCardPath = System.getenv("SECONDARY_STORAGE");
if ((strSDCardPath == null) || (strSDCardPath.length() == 0)) {
strSDCardPath = System.getenv("EXTERNAL_SDCARD_STORAGE");
}
//If may get a full path that is not the right one, even if we don't have the SD Card there.
//We just need the "/mnt/extSdCard/" i.e and check if it's writable
if(strSDCardPath != null) {
if (strSDCardPath.contains(":")) {
strSDCardPath = strSDCardPath.substring(0,strSDCardPath.indexOf(":"));
}
File externalFilePath = new File(strSDCardPath);
if (externalFilePath.exists() && externalFilePath.canWrite()) {
//do what you need here
}
}
For more details, read here: Finding the SDCard Path on Android devices
Have you tried this?
private boolean canWriteToFlash() {
String state = Environment.getExternalStorageState();
if (Environment.MEDIA_MOUNTED.equals(state)) {
return true;
} else if (Environment.MEDIA_MOUNTED_READ_ONLY.equals(state)) {
// Read only isn't good enough
return false;
} else {
return false;
} }
For accessing multiple external storage, you could use the api
ContextCompat.getExternalCacheDirs(Context context);
ContextCompat.getExternalFilesDirs(Context context, String type);
This will return a path like /storage/sdcard1/Android/data/com.example.foo/, then you can replace Android/data/com.example.foo/ with DCIM to get the path you want.
This method is not reliable, because the path Android/data/com.example.foo/ will be different or changed in the future, but it worths to have a try.
You can see more information from the official android documents.
Related
I am currently working on an app, that goes through your phone and lists all available MP3 files. I managed to get this done and search for everything on the internal storage, but didnt manage to find a way using the envoirment to get to the sd card, when one is installed. This is my code - u will see a missing part when SD card is TRUE. Can you complete it?
public List<string> ReturnPlayableMp3(bool sdCard)
{
List<string> res = new List<string>();
string phyle;
if(sdCard)
{
// missing
}
else
{
try
{
var path1 = Android.OS.Environment.ExternalStorageDirectory.ToString();
var mp3Files = Directory.EnumerateFiles(path1, "*.mp3", SearchOption.AllDirectories);
foreach (string currentFile in mp3Files)
{
phyle = currentFile;
res.Add(phyle);
}
}
catch (Exception e9)
{
Toast.MakeText(ApplicationContext, "ut oh\n" + e9.Message, ToastLength.Long).Show();
}
}
return res;
}
}
It would need to return the exact same thing as it does for the internal storage only this time for the sd card. Right now, what is beeing returned is:
""/storage/emulated/0""
I hope you can help me. Thank you!
SO I found the place it is: /storage/05B6-2226/
But the digits refer to only MY sd card. How do I get this path programatically?
Take a look at these methods:
Context.GetExternalFilesDir
Returns the absolute path to the directory on the primary external
filesystem (that is somewhere on Environment.ExternalStorageDirectory)
where the application can place persistent files it owns. These files
are internal to the applications, and not typically visible to the
user as media.
Context.GetExternalFilesDirs
Returns absolute paths to application-specific directories on all
external storage devices where the application can place persistent
files it owns. These files are internal to the application, and not
typically visible to the user as media.
I've been searching for a couple of days with a lot of solutions that just ended up giving you the 'external' built in storage. Finally found this solution for the 'removable' SD Card and wanted to post it here in case someone else is still looking.
How to write on external storage sd card in mashmallow in xamarin.android
//Get the list of External Storage Volumes (E.g. SD Card)
Context context = Android.App.Application.Context;
var storageManager = (Android.OS.Storage.StorageManager)context.GetSystemService(Context.StorageService);
var volumeList = (Java.Lang.Object[])storageManager.Class.GetDeclaredMethod("getVolumeList").Invoke(storageManager);
List<Java.IO.File> ExtFolders = new List<Java.IO.File>();
//Select the Directories that are not Emulated
foreach (var storage in volumeList)
{
Java.IO.File info = (Java.IO.File)storage.Class.GetDeclaredMethod("getDirectory").Invoke(storage);
if ((bool)storage.Class.GetDeclaredMethod("isEmulated").Invoke(storage) == false && info.TotalSpace > 0)
{
//Get Directory Path
Console.WriteLine(info.Path);
}
}
Just wanna share my answer, where I have get the extStorages Path and I use this method in my simple file browser app.
public static string[] GetRemovableStorages()
{
List<string> extStorage = new List<string>();
//If this throws exception
string storageDir = (string)Environment.StorageDirectory;
//Try this
string storageDir = Directory.GetParent (Environment.ExternalStoragePublicDirectory).Parent.FullName;
string[] directories = Directory.GetDirectories(storageDir);
foreach(string dir in directories)
{
try
{
var extStoragePath = new Java.IO.File(dir);
bool isRemovable = Environment.InvokeIsExternalStorageRemovable(extStoragePath);
if(isRemovable) extStorage.Add(extStoragePath.AbsolutePath);
else return null;
}
catch
{
}
}
return extStorage.ToArray();
}
Elikill58's answer throws exception no such method "getDirectory" in my case but I recommend Elikill58's answer
Most of the new android devices have an internal sdcard and an external sdcard. I want to make a file explorer app but I can't find out how to get the path to use in my app because
File file = Environment.getExternalStorageDirectory();
just returns in most device /mnt/sdcard
but there is another path for the other external sdcard like /storage1 or /storage2
. Any help appreciated.
How to get the internal and external sdcard path in android
Methods to store in Internal Storage:
File getDir (String name, int mode)
File getFilesDir ()
Above methods are present in Context class
Methods to store in phone's internal memory:
File getExternalStorageDirectory ()
File getExternalFilesDir (String type)
File getExternalStoragePublicDirectory (String type)
In the beginning, everyone used Environment.getExternalStorageDirectory() , which pointed to the root of phone's internal memory. As a result, root directory was filled with random content.
Later, these two methods were added:
In Context class they added getExternalFilesDir(), pointing to an app-specific directory on phone's internal memory. This directory and its contents will be deleted when the app is uninstalled.
Environment.getExternalStoragePublicDirectory() for centralized places to store well-known file types, like photos and movies. This directory and its contents will NOT be deleted when the app is uninstalled.
Methods to store in Removable Storage i.e. micro SD card
Before API level 19, there was no official way to store in SD card. But many could do it using unofficial APIs.
Officially, one method was introduced in Context class in API level 19 (Android version 4.4 - Kitkat).
File[] getExternalFilesDirs (String type)
It returns absolute paths to application-specific directories on all
shared/external storage devices where the application can place
persistent files it owns. These files are internal to the application,
and not typically visible to the user as media.
That means, it will return paths to both Micro SD card and Internal memory. Generally, second returned path would be storage path of micro SD card.
The Internal and External Storage terminology according to Google/official Android docs is quite different from what we think.
Yes. Different manufacturer use different SDcard name like in Samsung Tab 3 its extsd, and other samsung devices use sdcard like this different manufacturer use different names.
I had the same requirement as you. so i have created a sample example for you from my project goto this link Android Directory chooser example which uses the androi-dirchooser library. This example detect the SDcard and list all the subfolders and it also detects if the device has morethan one SDcard.
Part of the code looks like this For full example goto the link Android Directory Chooser
/**
* Returns the path to internal storage ex:- /storage/emulated/0
*
* #return
*/
private String getInternalDirectoryPath() {
return Environment.getExternalStorageDirectory().getAbsolutePath();
}
/**
* Returns the SDcard storage path for samsung ex:- /storage/extSdCard
*
* #return
*/
private String getSDcardDirectoryPath() {
return System.getenv("SECONDARY_STORAGE");
}
mSdcardLayout.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View view) {
String sdCardPath;
/***
* Null check because user may click on already selected buton before selecting the folder
* And mSelectedDir may contain some wrong path like when user confirm dialog and swith back again
*/
if (mSelectedDir != null && !mSelectedDir.getAbsolutePath().contains(System.getenv("SECONDARY_STORAGE"))) {
mCurrentInternalPath = mSelectedDir.getAbsolutePath();
} else {
mCurrentInternalPath = getInternalDirectoryPath();
}
if (mCurrentSDcardPath != null) {
sdCardPath = mCurrentSDcardPath;
} else {
sdCardPath = getSDcardDirectoryPath();
}
//When there is only one SDcard
if (sdCardPath != null) {
if (!sdCardPath.contains(":")) {
updateButtonColor(STORAGE_EXTERNAL);
File dir = new File(sdCardPath);
changeDirectory(dir);
} else if (sdCardPath.contains(":")) {
//Multiple Sdcards show root folder and remove the Internal storage from that.
updateButtonColor(STORAGE_EXTERNAL);
File dir = new File("/storage");
changeDirectory(dir);
}
} else {
//In some unknown scenario at least we can list the root folder
updateButtonColor(STORAGE_EXTERNAL);
File dir = new File("/storage");
changeDirectory(dir);
}
}
});
For all Android versions,
Permissions:
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" android:maxSdkVersion="29" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" android:maxSdkVersion="29" />
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" tools:ignore="ScopedStorage" />
Use requestLegacyExternalStorage for Android 10 (add to AndroidManifest > application tag):
android:requestLegacyExternalStorage="true"
Get internal directory path:
#Nullable
public static String getInternalStorageDirectoryPath(Context context) {
String storageDirectoryPath;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
StorageManager storageManager = (StorageManager) context.getSystemService(Context.STORAGE_SERVICE);
if(storageManager == null) {
storageDirectoryPath = null; //you can replace it with the Environment.getExternalStorageDirectory().getAbsolutePath()
} else {
storageDirectoryPath = storageManager.getPrimaryStorageVolume().getDirectory().getAbsolutePath();
}
} else {
storageDirectoryPath = Environment.getExternalStorageDirectory().getAbsolutePath();
}
return storageDirectoryPath;
}
Get external directories:
#NonNull
public static List<String> getExternalStorageDirectoryPaths(Context context) {
List<String> externalPaths = new ArrayList<>();
String internalStoragePath = getInternalStorageDirectoryPath(context);
File[] allExternalFilesDirs = ContextCompat.getExternalFilesDirs(context, null);
for(File filesDir : allExternalFilesDirs) {
if(filesDir != null) {
int nameSubPos = filesDir.getAbsolutePath().lastIndexOf("/Android/data");
if(nameSubPos > 0) {
String filesDirName = filesDir.getAbsolutePath().substring(0, nameSubPos);
if(!filesDirName.equals(internalStoragePath)) {
externalPaths.add(filesDirName);
}
}
}
}
return externalPaths;
}
Since there is no direct meathod to get the paths the solution may be
Scan the /system/etc/vold.fstab file and look for lines like this:
dev_mount sdcard /mnt/sdcard 1
/devices/platform/s3c-sdhci.0/mmc_host/mmc0
When one is found, split it into its elements and then pull out the
path to the that mount point and add it to the arraylist
emphasized textsome devices are missing the vold file entirely so we add a path here
to make sure the list always includes the path to the first sdcard,
whether real or emulated.
sVold.add("/mnt/sdcard");
try {
Scanner scanner = new Scanner(new File("/system/etc/vold.fstab"));
while (scanner.hasNext()) {
String line = scanner.nextLine();
if (line.startsWith("dev_mount")) {
String[] lineElements = line.split(" ");
String element = lineElements[2];
if (element.contains(":"))
element = element.substring(0, element.indexOf(":"));
if (element.contains("usb"))
continue;
// don't add the default vold path
// it's already in the list.
if (!sVold.contains(element))
sVold.add(element);
}
}
} catch (Exception e) {
// swallow - don't care
e.printStackTrace();
}
}
Now that we have a cleaned list of mount paths, test each one to make
sure it's a valid and available path. If it is not, remove it from
the list.
private static void testAndCleanList()
{
for (int i = 0; i < sVold.size(); i++) {
String voldPath = sVold.get(i);
File path = new File(voldPath);
if (!path.exists() || !path.isDirectory() || !path.canWrite())
sVold.remove(i--);
}
}
I'm not sure how general an answer this but I tested it on a motorola XT830C with Android 4.4 and on a Nexus 7 android 6.0.1. and on a Samsung SM-T530NU Android 5.0.2.
I used System.getenv("SECONDARY_STORAGE") and Environment.getExternalStorageDirectory().getAbsolutePath().
The Nexus which has no second SD card, System.getenv returns null and Envirnoment.getExterna... gives /storage/emulated/0.
The motorola device which has an external SD card gives /storage/sdcard1 for System.getenv("SECONDARY_STORAGE") and Envirnoment.getExterna... gives /storage/emulated/0.
The samsumg returns /storage/extSdCard for the external SD.
In my case I am making a subdirectory on the external location and am using
appDirectory = (System.getenv("SECONDARY_STORAGE") == null)
? Environment.getExternalStorageDirectory().getAbsolutePath()
: System.getenv("SECONDARY_STORAGE");
to find the sdcard. Making a subdirectory in this directory is working.Of course I had to set permission in the manifest file to access the external memory.
I also have a Nook 8" color tablet. When I get a chance to test on them, I'll post if I have any problems with this approach.
but there is another path for the other external sdcard like /storage1 or /storage2
There is nothing in the Android SDK -- at least through Android 4.1 -- that gives you access to those paths. They may not be readable or writable by your app, anyway. The behavior of such storage locations, and what they are used for, is up to device manufacturers.
File main=new File(String.valueOf(Environment.getExternalStorageDirectory().getAbsolutePath()));
File[]t=main.getParentFile().listFiles();
for(File dir:t)
{
Log.e("Main",dir.getAbsolutePath());
}
Output:
E/Main: /storage/sdcard1
E/Main: /storage/sdcard0
I have one SD card and inbuilt memory.
There is no public api for get internal/external sdcard path.
But there is platform api called StorageManager in android.os.storage package. see http://goo.gl/QJj1eu .
There are some features such as list storage, mount/unmount storage, get mount state, get storage path, etc.
But it is hidden api and it should be deprecated or broken in next android release.
And some methods need special permission, and most are not Documented.
Try this code it will help
Map<String, File> externalLocations = ExternalStorage.getAllStorageLocations();
File sdCard = externalLocations.get(ExternalStorage.SD_CARD);
File externalSdCard = externalLocations.get(ExternalStorage.EXTERNAL_SD_CARD);
I'm faced with the well-known problem of obtaining the path of an external SD card mounted on some Android devices. (see this question for understanding what I mean)
I've thought to solve the problem by reading the content of /etc/vold.fstab, then taking just lines representing partitions, but I don't have a device for doing tests.
What I want to do is to read that file, ignore the row which refers to the address returned by Environment.getExternalStorageDirectory(), and take the other row (if present).
What I don't know (and I don't have the possibility to test it) is: are there cases in which I can have other lines which are not the external SD card? The SD card, if present, appears on the file vold.fstab?
edit:
The answer is: YES. Read the accepted answer.
What is wrong with this?
Environment.getExternalStoreDirectory()
Why are you ignoring this when it's the SD Card?
OK - In the case of devices with /sdcard (Internal) and an external SD card (??) you could always scan the fstab file and look for "sdhci" which is the SD Host Controller bridge driver.
Something like:
dev_mount sdcard /mnt/external_sdcard auto /devices/platform/sdhci.2/mmc_host/mmc2
Then just parse as necessary.
Why the "necessity" to find the actual SD card though when it's not actually treated as such by the OS? (Won't be mounted as mass storage)
Is your application only available for devices where this is the case? What is wrong with using whatever Android believes is the SD storage space?
I use the following code to first detect wether the sdCard exists and then run the relevent code:
Detecting whether SD card exists:
Boolean isSDPresent = android.os.Environment.getExternalStorageState().equals(android.os.Environment.MEDIA_MOUNTED);
if(isSDPresent)
{
// file path = "/mnt/sdcard/Android/data/PACKAGE_NAME/..."
}
else
{
// file path = "/data/data/PACKAGE_NAME/..."
}
Think this is what you are after?
This could be the right solution. Read it from /etc/vold.fstab, which lists all the partitions currently mounted on a Linux system (Android included)
String getExternalSdcardDirectory() {
FileInputStream fis = null;
try {
fis = new FileInputStream(new File("/etc/vold.fstab"));
} catch (FileNotFoundException e) {
return null; // should never be reached
}
try {
byte[] buffer = new byte[4096];
int n=0;
String file = "";
while ((n=fis.read(buffer, 0, 4096))>0) {
file += new String(buffer, 0, n);
}
fis.close();
String[] rows = file.split("\n");
for (String row: rows) {
String trimmedRow = row.trim();
if (trimmedRow.startsWith("#") || trimmedRow.equals(""))
continue;
else if (trimmedRow.equals(Environment.getExternalStorageDirectory().getAbsolutePath()))
continue;
else
return trimmedRow.split(" ")[2];
}
} catch (IOException e) {
// nothing
}
return null;
}
I am pretty new to Android programming and writing my very first app for my Diploma Thesis.
Now I am all but finished but have one thing left that I couldn´t get an answer to anywhere on the net. So maybe someone here can help.
Part of what my app must do is write results from previous operations to a freely accessible file for later analysis.
So what I got so far is that I am able to write a file to SD card with the following code:
String packageName = this.getClass().getPackage().getName();
String path = Environment.getExternalStorageDirectory().getAbsolutePath()+ "/Android/data/" + packageName + "/files/";
if (isExternalStorageAvailable() && isExternalStorageWritable())
{
String filename = "_results.dat";
boolean exists = (new File(path)).exists();
if (!exists)
{
new File(path).mkdirs();
}
// Open output stream
FileOutputStream fOut = new FileOutputStream(path + filename,true);
fOut.write("lalala".getBytes());
etc.
}
public static boolean isExternalStorageAvailable()
{
// Retrieving the external storage state
String state = Environment.getExternalStorageState();
// Check if available
if (Environment.MEDIA_MOUNTED.equals(state) || Environment.MEDIA_MOUNTED_READ_ONLY.equals(state))
{
return true;
}
return false;
}
public static boolean isExternalStorageWritable()
{
// Retrieving the external storage state
String state = Environment.getExternalStorageState();
// Check if writable
if (Environment.MEDIA_MOUNTED.equals(state))
{
return true;
}
return false;
}
That file I can later access, copy, whatever. Like I want it to be. Now the actual question is:
Is there a way to achieve this without an SD card in the phone? Or can I only write into 'app-memory' (which wouldn´t suffice my needs)?
I only ask because as of yet there are no SD cards in the phones where I am going to deploy the app. And we have to decide if we have to 'upgrade' the devices :).
Well if you want to avoid having them upgrade the phone with an SD card, check out this link. It SAYS that files written to internal storage are not accessible by other apps, but if you read on, you can set other flags:
MODE_PRIVATE will create the file (or replace a file of the same name)
and make it private to your application. Other modes available are:
MODE_APPEND, MODE_WORLD_READABLE, and MODE_WORLD_WRITEABLE.
My app will require external storage (SD card) in order to save the user's data. I've searched high and low, but I can't find any best practices for this.
Do I...
Simply assume external storage will be present and skip adding checks and alerts? (Assumption here would be that external storage only goes missing in rare cases, and it's not worth spending a ton of time coding a solution for this. Typical users don't ever remove their SD cards, etc.)
Check that external storage is mounted only when reading from or writing to it? (Users may remove their SD card at any time, but the app will still work until it comes time to access their data.)
Add a listener at the application level that waits for external storage notifications and displays a modal alert app-wide? (This sounds ideal, but could also be overkill for all I know.)
Any insight will be greatly appreciated.
Yes u have to test for the presence of the sdcard the following function would help you.
private boolean checkForDirectory()
{
boolean cardstate = true;
String state = Environment.getExternalStorageState();
if (Environment.MEDIA_BAD_REMOVAL.equals(state)) {
cardstate = false;
runDialog("Memory Card was removed before it was unmounted");
}
else if (Environment.MEDIA_CHECKING.equals(state)) {
runDialog("Memory Card is present and being disk-checked");
}
else if (Environment.MEDIA_MOUNTED.equals(state)) {
cardstate = true;
//runDialog("Memory Card is present and mounted with read/write access");
}
else if (Environment.MEDIA_MOUNTED_READ_ONLY.equals(state)) {
runDialog("Memory Card is present and mounted with readonly access");
}
else if (Environment.MEDIA_NOFS.equals(state)) {
cardstate = false;
runDialog("Memory Card is present but is blank or using unsupported file system");
}
else if (Environment.MEDIA_REMOVED.equals(state)) {
cardstate = false;
runDialog("Memory Card is not present");
}
else if (Environment.MEDIA_SHARED.equals(state)) {
cardstate = false;
runDialog("Memory Card is present but shared via USB mass storage");
}
else if (Environment.MEDIA_UNMOUNTABLE.equals(state)) {
cardstate = false;
runDialog("Memory Card is present but cannot be mounted");
}
else if (Environment.MEDIA_UNMOUNTED.equals(state)) {
cardstate = false;
runDialog("Memory Card is present but not mounted");
}
File dir = new File(Environment.getExternalStorageDirectory() + "/gwc");
if(dir.exists() && dir.isDirectory())
{
System.out.println("Folder exists");
}
else
{
String extStorageDirectory = Environment.getExternalStorageDirectory().toString();
File myNewFolder = new File(extStorageDirectory + "/gwc");
myNewFolder.mkdir();
System.out.println("Folder gwc created ");
}
return cardstate;
}
You should definitely check for it before using it. I would suggest #2; you're right, #3 does seem like overkill (I don't even know if there is a listener for that). The Google docs has this to say:
Before you do any work with the external storage, you should always call getExternalStorageState() to check whether the media is available. The media might be mounted to a computer, missing, read-only, or in some other state.
Absolutely agree with Shawn Lauzon's answer, and here is a post on developer.android.com, that has some code for checking if external storage is available and if it's writable. Hope this helps you.
Data Storage