diff --git a/README.md b/README.md index b2fa76ba..909e2dd2 100644 --- a/README.md +++ b/README.md @@ -103,6 +103,8 @@ Each URL is in the form _file:///path/to/spot/_, and can be converted to a * `cordova.file.externalCacheDirectory` - Application cache on external storage. (_Android_). See [Quirks](#androids-external-storage-quirks). +* `cordova.file.externalMediaDirectory` - Application media directory on external storage. (_Android_) + * `cordova.file.externalRootDirectory` - External storage (SD card) root. (_Android_, _BlackBerry 10_). See [Quirks](#androids-external-storage-quirks). * `cordova.file.tempDirectory` - Temp directory that the OS can clear at will. Do not @@ -117,6 +119,14 @@ Each URL is in the form _file:///path/to/spot/_, and can be converted to a * `cordova.file.sharedDirectory` - Files globally available to all applications (_BlackBerry 10_) +* `cordova.file.removableExternalApplicationStorageDirectories` - Application space on removable external storages. (_Android_) + +* `cordova.file.removableExternalDataDirectories` - Where to put app-specific data files on removable external storages. (_Android_) + +* `cordova.file.removableExternalCacheDirectories` - Application cache on removable external storages. (_Android_) + +* `cordova.file.removableExternalMediaDirectories` - Application media directory on removable external storages. (_Android_) + ## File System Layouts Although technically an implementation detail, it can be very useful to know how @@ -156,14 +166,20 @@ the `cordova.file.*` properties map to physical paths on a real device. | Device Path | `cordova.file.*` | `AndroidExtraFileSystems` | r/w? | persistent? | OS clears | private | |:------------------------------------------------|:----------------------------|:--------------------------|:----:|:-----------:|:---------:|:-------:| | `file:///android_asset/` | applicationDirectory | assets | r | N/A | N/A | Yes | -| `/data/data//` | applicationStorageDirectory | - | r/w | N/A | N/A | Yes | +| `//` | applicationStorageDirectory | - | r/w | N/A | N/A | Yes | |    `cache` | cacheDirectory | cache | r/w | Yes | Yes\* | Yes | |    `files` | dataDirectory | files | r/w | Yes | No | Yes | |       `Documents` | | documents | r/w | Yes | No | Yes | | `/` | externalRootDirectory | sdcard | r/w\*\*\* | Yes | No | No | |    `Android/data//` | externalApplicationStorageDirectory | - | r/w | Yes | No | No | -|       `cache` | externalCacheDirectory | cache-external | r/w | Yes | No\*\*| No | +|       `cache` | externalCacheDirectory | cache-external | r/w | Yes | No\*\*| No | |       `files` | externalDataDirectory | files-external | r/w | Yes | No | No | +|    `Android/media//` | externalMediaDirectory | - | r/w | Yes | No | No | +| `/` (0 or more) | | - | r/w\*\*\* | Yes | No | No | +|    `Android/data//` | removableExternalApplicationStorageDirectories | - | r/w | Yes | No | No | +|       `cache` | removableExternalCacheDirectories | - | r/w | Yes | No\*\*| No | +|       `files` | removableExternalDataDirectories | - | r/w | Yes | No | No | +|    `Android/media//` | removableExternalMediaDirectories | - | r/w | Yes | No | No | \* The OS may periodically clear this directory, but do not rely on this behavior. Clear the contents of this directory as appropriate for your application. Should a user diff --git a/src/android/FileUtils.java b/src/android/FileUtils.java index 0edf90a0..35281f7b 100644 --- a/src/android/FileUtils.java +++ b/src/android/FileUtils.java @@ -583,6 +583,21 @@ private boolean needPermission(String nativeURL, int permissionType) throws JSON if (j.has("externalApplicationStorageDirectory")) { allowedStorageDirectories.add(j.getString("externalApplicationStorageDirectory")); } + if (j.has("removableExternalApplicationStorageDirectories")) { + JSONArray array = j.getJSONArray("removableExternalApplicationStorageDirectories"); + for (int i = 0; i < array.length(); i++) { + allowedStorageDirectories.add(array.getString(i)); + } + } + if (j.has("removableExternalMediaDirectories")) { + JSONArray array = j.getJSONArray("removableExternalMediaDirectories"); + for (int i = 0; i < array.length(); i++) { + allowedStorageDirectories.add(array.getString(i)); + } + } + if (j.has("externalMediaDirectory")) { + allowedStorageDirectories.add(j.getString("externalMediaDirectory")); + } if (permissionType == READ && hasReadPermission()) { return false; @@ -998,16 +1013,56 @@ private JSONObject requestAllPaths() throws JSONException { ret.put("applicationStorageDirectory", toDirUrl(context.getFilesDir().getParentFile())); ret.put("dataDirectory", toDirUrl(context.getFilesDir())); ret.put("cacheDirectory", toDirUrl(context.getCacheDir())); - if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) { - try { + try { + if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) { ret.put("externalApplicationStorageDirectory", toDirUrl(context.getExternalFilesDir(null).getParentFile())); ret.put("externalDataDirectory", toDirUrl(context.getExternalFilesDir(null))); ret.put("externalCacheDirectory", toDirUrl(context.getExternalCacheDir())); ret.put("externalRootDirectory", toDirUrl(Environment.getExternalStorageDirectory())); - } catch (NullPointerException e) { - /* If external storage is unavailable, context.getExternal* returns null */ - LOG.d(LOG_TAG, "Unable to access these paths, most liklely due to USB storage"); } + + JSONArray removableExternalApplicationStorageDirs = new JSONArray(); + JSONArray removableExternalDataDirs = new JSONArray(); + JSONArray removableExternalCacheDirs = new JSONArray(); + JSONArray removableExternalMediaDirs = new JSONArray(); + String externalMediaDir = null; + for (File filesDir : context.getExternalFilesDirs(null)) { + if (filesDir != null) { + if (Environment.isExternalStorageRemovable(filesDir)) { + removableExternalApplicationStorageDirs.put(toDirUrl(filesDir.getParentFile())); + removableExternalDataDirs.put(toDirUrl(filesDir)); + } + } + } + for (File cacheDir : context.getExternalCacheDirs()) { + if (cacheDir != null) { + if (Environment.isExternalStorageRemovable(cacheDir)) { + removableExternalCacheDirs.put(toDirUrl(cacheDir)); + } + } + } + for (File mediaDir : context.getExternalMediaDirs()) { + if (mediaDir != null) { + String dirUrl = toDirUrl(mediaDir); + if (Environment.isExternalStorageRemovable(mediaDir)) { + removableExternalMediaDirs.put(dirUrl); + } else { + if (externalMediaDir != null) { + LOG.w(LOG_TAG, "External media directory already found ; skip other value " + dirUrl); + continue; + } + externalMediaDir = dirUrl; + } + } + } + ret.put("removableExternalApplicationStorageDirectories", removableExternalApplicationStorageDirs); + ret.put("removableExternalDataDirectories", removableExternalDataDirs); + ret.put("removableExternalCacheDirectories", removableExternalCacheDirs); + ret.put("removableExternalMediaDirectories", removableExternalMediaDirs); + ret.put("externalMediaDirectory", externalMediaDir); + } catch (NullPointerException e) { + /* If external storage is unavailable, context.getExternal* returns null */ + LOG.d(LOG_TAG, "Unable to access these paths, most likely due to USB storage"); } return ret; }