rv = new ArrayList<>();
// Primary physical SD-CARD (not emulated)
final String rawExternalStorage = System.getenv("EXTERNAL_STORAGE");
// All Secondary SD-CARDs (all exclude primary) separated by ":"
final String rawSecondaryStoragesStr = System.getenv("SECONDARY_STORAGE");
// Primary emulated SD-CARD
final String rawEmulatedStorageTarget = System.getenv("EMULATED_STORAGE_TARGET");
if (TextUtils.isEmpty(rawEmulatedStorageTarget)) {
// Device has physical external storage; use plain paths.
if (TextUtils.isEmpty(rawExternalStorage)) {
// EXTERNAL_STORAGE undefined; falling back to default.
// Check for actual existence of the directory before adding to list
if (new File(DEFAULT_FALLBACK_STORAGE_PATH).exists()) {
rv.add(DEFAULT_FALLBACK_STORAGE_PATH);
} else {
//We know nothing else, use Environment's fallback
rv.add(Environment.getExternalStorageDirectory().getAbsolutePath());
}
} else {
rv.add(rawExternalStorage);
}
} else {
// Device has emulated storage; external storage paths should have
// userId burned into them.
final String rawUserId;
final String path = Environment.getExternalStorageDirectory().getAbsolutePath();
final String[] folders = OCFile.PATH_SEPARATOR.split(path);
final String lastFolder = folders[folders.length - 1];
boolean isDigit = false;
try {
Integer.valueOf(lastFolder);
isDigit = true;
} catch (NumberFormatException ignored) {
}
rawUserId = isDigit ? lastFolder : "";
// /storage/emulated/0[1,2,...]
if (TextUtils.isEmpty(rawUserId)) {
rv.add(rawEmulatedStorageTarget);
} else {
rv.add(rawEmulatedStorageTarget + File.separator + rawUserId);
}
}
// Add all secondary storages
if (!TextUtils.isEmpty(rawSecondaryStoragesStr)) {
// All Secondary SD-CARDs splited into array
final String[] rawSecondaryStorages = rawSecondaryStoragesStr.split(File.pathSeparator);
Collections.addAll(rv, rawSecondaryStorages);
}
if (SDK_INT >= Build.VERSION_CODES.M && checkStoragePermission(context)) {
rv.clear();
}
String[] extSdCardPaths = getExtSdCardPathsForActivity(context);
File f;
for (String extSdCardPath : extSdCardPaths) {
f = new File(extSdCardPath);
if (!rv.contains(extSdCardPath) && canListFiles(f)) {
rv.add(extSdCardPath);
}
}
return rv;
}
/**
* Update the local path summary display. If a special directory is recognized, it is replaced by its name.
*
* Example: /storage/emulated/0/Movies -> Internal Storage / Movies Example: /storage/ABC/non/standard/directory ->
* ABC /non/standard/directory
*
* @param path the path to display
* @return a user friendly path as defined in examples, or {@param path} if the storage device isn't recognized.
*/
public static String pathToUserFriendlyDisplay(String path, Context context, Resources resources) {
// Determine storage device (external, sdcard...)
String storageDevice = null;
for (String storageDirectory : FileStorageUtils.getStorageDirectories(context)) {
if (path.startsWith(storageDirectory)) {
storageDevice = storageDirectory;
break;
}
}
// If storage device was not found, display full path
if (storageDevice == null) {
return path;
}
// Default to full path without storage device path
String storageFolder;
try {
storageFolder = path.substring(storageDevice.length() + 1);
} catch (StringIndexOutOfBoundsException e) {
storageFolder = "";
}
FileStorageUtils.StandardDirectory standardDirectory = FileStorageUtils.StandardDirectory.fromPath(storageFolder);
if (standardDirectory != null) { // Friendly name of standard directory
storageFolder = " " + resources.getString(standardDirectory.getDisplayName());
}
// Shorten the storage device to a friendlier display name
if (storageDevice.startsWith(Environment.getExternalStorageDirectory().getAbsolutePath())) {
storageDevice = resources.getString(R.string.storage_internal_storage);
} else {
storageDevice = new File(storageDevice).getName();
}
return resources.getString(R.string.local_folder_friendly_path, storageDevice, storageFolder);
}
/**
* Taken from https://github.com/TeamAmaze/AmazeFileManager/blob/d11e0d2874c6067910e58e059859431a31ad6aee/app/src
* /main/java/com/amaze/filemanager/activities/superclasses/PermissionsActivity.java#L47 on 14.02.2019
*/
private static boolean checkStoragePermission(Context context) {
// Verify that all required contact permissions have been granted.
return ActivityCompat.checkSelfPermission(context, Manifest.permission.WRITE_EXTERNAL_STORAGE)
== PackageManager.PERMISSION_GRANTED;
}
/**
* Taken from https://github.com/TeamAmaze/AmazeFileManager/blob/616f2a696823ab0e64ea7a017602dc08e783162e/app/src
* /main/java/com/amaze/filemanager/filesystem/FileUtil.java#L764 on 14.02.2019
*/
private static String[] getExtSdCardPathsForActivity(Context context) {
List paths = new ArrayList<>();
for (File file : context.getExternalFilesDirs("external")) {
if (file != null) {
int index = file.getAbsolutePath().lastIndexOf("/Android/data");
if (index < 0) {
Log_OC.w(TAG, "Unexpected external file dir: " + file.getAbsolutePath());
} else {
String path = file.getAbsolutePath().substring(0, index);
try {
path = new File(path).getCanonicalPath();
} catch (IOException e) {
// Keep non-canonical path.
}
paths.add(path);
}
}
}
if (paths.isEmpty()) {
paths.add("/storage/sdcard1");
}
return paths.toArray(new String[0]);
}
/**
* Taken from https://github.com/TeamAmaze/AmazeFileManager/blob/9cf1fd5ff1653c692cb54cf6bc71b572c19a11cd/app/src
* /main/java/com/amaze/filemanager/utils/files/FileUtils.java#L754 on 14.02.2019
*/
private static boolean canListFiles(File f) {
return f.canRead() && f.isDirectory();
}
/**
* Should be converted to an enum when we only support min SDK version for Environment.DIRECTORY_DOCUMENTS
*/
public static class StandardDirectory {
public static final StandardDirectory PICTURES = new StandardDirectory(
Environment.DIRECTORY_PICTURES,
R.string.storage_pictures,
R.drawable.ic_image_grey600
);
public static final StandardDirectory CAMERA = new StandardDirectory(
Environment.DIRECTORY_DCIM,
R.string.storage_camera,
R.drawable.ic_camera
);
public static final StandardDirectory DOCUMENTS;
static {
DOCUMENTS = new StandardDirectory(
Environment.DIRECTORY_DOCUMENTS,
R.string.storage_documents,
R.drawable.ic_document_grey600
);
}
public static final StandardDirectory DOWNLOADS = new StandardDirectory(
Environment.DIRECTORY_DOWNLOADS,
R.string.storage_downloads,
R.drawable.ic_download_grey600
);
public static final StandardDirectory MOVIES = new StandardDirectory(
Environment.DIRECTORY_MOVIES,
R.string.storage_movies,
R.drawable.ic_movie_grey600
);
public static final StandardDirectory MUSIC = new StandardDirectory(
Environment.DIRECTORY_MUSIC,
R.string.storage_music,
R.drawable.ic_music_grey600
);
private final String name;
private final int displayNameResource;
private final int iconResource;
private StandardDirectory(String name, int displayNameResource, int iconResource) {
this.name = name;
this.displayNameResource = displayNameResource;
this.iconResource = iconResource;
}
public String getName() {
return this.name;
}
public int getDisplayName() {
return this.displayNameResource;
}
public int getIcon() {
return this.iconResource;
}
public static Collection getStandardDirectories() {
Collection standardDirectories = new HashSet<>();
standardDirectories.add(PICTURES);
standardDirectories.add(CAMERA);
if (DOCUMENTS != null) {
standardDirectories.add(DOCUMENTS);
}
standardDirectories.add(DOWNLOADS);
standardDirectories.add(MOVIES);
standardDirectories.add(MUSIC);
return standardDirectories;
}
@Nullable
public static StandardDirectory fromPath(String path) {
for (StandardDirectory directory : getStandardDirectories()) {
if (directory.getName().equals(path)) {
return directory;
}
}
return null;
}
}
}