diff --git a/README.md b/README.md
index 434ac7812..ae47fa3a9 100644
--- a/README.md
+++ b/README.md
@@ -420,7 +420,22 @@ This method will now return filesystem URLs of the form
which can be used to identify the file uniquely.
+In v7.0.0 the return value of `toURL()` for Android was updated to return the absolute `file://` URL when app content is served from the `file://` scheme.
+
+If app content is served from the `http(s)://` scheme, a `cdvfile` formatted URL will be returned instead. The `cdvfile` formatted URL is created from the internal method `toInternalURL()`.
+
+An example `toInternalURL()` return filesystem URL:
+
+ https://localhost/persistent/path/to/file
+
+[![toURL flow](https://sketchviz.com/@erisu/7b05499842275be93a0581e8e3576798/6dc71d8302cafd05b443d874a592d10fa415b8e3.sketchy.png)](//sketchviz.com/@erisu/7b05499842275be93a0581e8e3576798)
+
+It is recommended to always use the `toURL()` to ensure that the correct URL is returned.
+
## cdvfile protocol
+
+- Not Supported on Android
+
**Purpose**
`cdvfile://localhost/persistent|temporary|another-fs-root*/path/to/file` can be used for platform-independent file paths.
diff --git a/package.json b/package.json
index 74dddddc0..ebdc5e1f7 100644
--- a/package.json
+++ b/package.json
@@ -37,7 +37,7 @@
"cordova-android": ">=6.3.0"
},
"7.0.0": {
- "cordova-android": ">=9.0.0"
+ "cordova-android": ">=10.0.0"
},
"8.0.0": {
"cordova": ">100"
diff --git a/plugin.xml b/plugin.xml
index 64331dc59..2eec461cf 100644
--- a/plugin.xml
+++ b/plugin.xml
@@ -30,7 +30,7 @@ xmlns:android="http://schemas.android.com/apk/res/android"
https://github.com/apache/cordova-plugin-file/issues
-
+
@@ -153,6 +153,9 @@ to config.xml in order for the application to find previously stored files.
+
+
+
diff --git a/src/android/AssetFilesystem.java b/src/android/AssetFilesystem.java
index b035c40e6..6d766a4ad 100644
--- a/src/android/AssetFilesystem.java
+++ b/src/android/AssetFilesystem.java
@@ -21,6 +21,7 @@ Licensed to the Apache Software Foundation (ASF) under one
import android.content.res.AssetManager;
import android.net.Uri;
+import org.apache.cordova.CordovaPreferences;
import org.apache.cordova.CordovaResourceApi;
import org.apache.cordova.LOG;
import org.json.JSONArray;
@@ -133,8 +134,8 @@ private long getAssetSize(String assetPath) throws FileNotFoundException {
}
}
- public AssetFilesystem(AssetManager assetManager, CordovaResourceApi resourceApi) {
- super(Uri.parse("file:///android_asset/"), "assets", resourceApi);
+ public AssetFilesystem(AssetManager assetManager, CordovaResourceApi resourceApi, CordovaPreferences preferences) {
+ super(Uri.parse("file:///android_asset/"), "assets", resourceApi, preferences);
this.assetManager = assetManager;
}
@@ -161,10 +162,9 @@ public LocalFilesystemURL toLocalUri(Uri inputURL) {
if (!subPath.isEmpty()) {
subPath = subPath.substring(1);
}
- Uri.Builder b = new Uri.Builder()
- .scheme(LocalFilesystemURL.FILESYSTEM_PROTOCOL)
- .authority("localhost")
- .path(name);
+
+ Uri.Builder b = createLocalUriBuilder();
+
if (!subPath.isEmpty()) {
b.appendEncodedPath(subPath);
}
diff --git a/src/android/ContentFilesystem.java b/src/android/ContentFilesystem.java
index 6b983c089..f4df440de 100644
--- a/src/android/ContentFilesystem.java
+++ b/src/android/ContentFilesystem.java
@@ -28,6 +28,8 @@ Licensed to the Apache Software Foundation (ASF) under one
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
+
+import org.apache.cordova.CordovaPreferences;
import org.apache.cordova.CordovaResourceApi;
import org.json.JSONException;
import org.json.JSONObject;
@@ -36,8 +38,8 @@ public class ContentFilesystem extends Filesystem {
private final Context context;
- public ContentFilesystem(Context context, CordovaResourceApi resourceApi) {
- super(Uri.parse("content://"), "content", resourceApi);
+ public ContentFilesystem(Context context, CordovaResourceApi resourceApi, CordovaPreferences preferences) {
+ super(Uri.parse("content://"), "content", resourceApi, preferences);
this.context = context;
}
@@ -68,11 +70,9 @@ public LocalFilesystemURL toLocalUri(Uri inputURL) {
if (subPath.length() > 0) {
subPath = subPath.substring(1);
}
- Uri.Builder b = new Uri.Builder()
- .scheme(LocalFilesystemURL.FILESYSTEM_PROTOCOL)
- .authority("localhost")
- .path(name)
- .appendPath(inputURL.getAuthority());
+
+ Uri.Builder b = createLocalUriBuilder().appendPath(inputURL.getAuthority());
+
if (subPath.length() > 0) {
b.appendEncodedPath(subPath);
}
diff --git a/src/android/FileUtils.java b/src/android/FileUtils.java
index 695af7a59..cd2a3383b 100644
--- a/src/android/FileUtils.java
+++ b/src/android/FileUtils.java
@@ -20,16 +20,23 @@ Licensed to the Apache Software Foundation (ASF) under one
import android.Manifest;
import android.app.Activity;
+import android.content.ContentResolver;
import android.content.Context;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Build;
import android.os.Environment;
import android.util.Base64;
+import android.util.Log;
+import android.webkit.MimeTypeMap;
+import android.webkit.WebResourceResponse;
+
+import androidx.webkit.WebViewAssetLoader;
import org.apache.cordova.CallbackContext;
import org.apache.cordova.CordovaInterface;
import org.apache.cordova.CordovaPlugin;
+import org.apache.cordova.CordovaPluginPathHandler;
import org.apache.cordova.CordovaWebView;
import org.apache.cordova.LOG;
import org.apache.cordova.PermissionHelper;
@@ -39,12 +46,16 @@ Licensed to the Apache Software Foundation (ASF) under one
import org.json.JSONException;
import org.json.JSONObject;
+import java.io.BufferedInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
+import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
+import java.net.HttpURLConnection;
import java.net.MalformedURLException;
+import java.net.URL;
import java.security.Permission;
import java.util.ArrayList;
import java.util.HashMap;
@@ -87,8 +98,6 @@ public class FileUtils extends CordovaPlugin {
private PendingRequests pendingRequests;
-
-
/*
* We need both read and write when accessing the storage, I think.
*/
@@ -136,10 +145,10 @@ protected void registerExtraFileSystems(String[] filesystems, HashMap {
+ String targetFileSystem = null;
+
+ if (path.startsWith(LocalFilesystemURL.fsNameToCdvKeyword("persistent"))) {
+ targetFileSystem = "persistent";
+ } else if (path.startsWith(LocalFilesystemURL.fsNameToCdvKeyword("temporary"))) {
+ targetFileSystem = "temporary";
+ } else if (path.startsWith(LocalFilesystemURL.fsNameToCdvKeyword("files"))) {
+ targetFileSystem = "files";
+ } else if (path.startsWith(LocalFilesystemURL.fsNameToCdvKeyword("documents"))) {
+ targetFileSystem = "documents";
+ } else if (path.startsWith(LocalFilesystemURL.fsNameToCdvKeyword("cache"))) {
+ targetFileSystem = "cache";
+ } else if (path.startsWith(LocalFilesystemURL.fsNameToCdvKeyword("root"))) {
+ targetFileSystem = "root";
+ } else if (path.startsWith(LocalFilesystemURL.fsNameToCdvKeyword("files-external"))) {
+ targetFileSystem = "files-external";
+ } else if (path.startsWith(LocalFilesystemURL.fsNameToCdvKeyword("sdcard"))) {
+ targetFileSystem = "sdcard";
+ } else if (path.startsWith(LocalFilesystemURL.fsNameToCdvKeyword("cache-external"))) {
+ targetFileSystem = "cache-external";
+ }
+
+ if (targetFileSystem != null) {
+ // Loop the registered file systems to find the target.
+ for (Filesystem fileSystem : filesystems) {
+
+ /*
+ * When target is discovered:
+ * 1. Transform the url path to the native path
+ * 2. Load the file contents
+ * 3. Get the file mime type
+ * 4. Return the file & mime information back we Web Resources
+ */
+ if (fileSystem.name.equals(targetFileSystem)) {
+ // E.g. replace __cdvfile_persistent__ with native path "/data/user/0/com.example.file/files/files/"
+ String fileSystemNativeUri = fileSystem.rootUri.toString().replace("file://", "");
+ String fileTarget = path.replace(LocalFilesystemURL.fsNameToCdvKeyword(targetFileSystem) + "/", fileSystemNativeUri);
+
+ File file = new File(fileTarget);
+
+ try {
+ InputStream in = new FileInputStream(file);
+ String mimeType = getMimeType(Uri.parse(file.toString()));
+ return new WebResourceResponse(mimeType, null, in);
+ } catch (FileNotFoundException e) {
+ Log.e(LOG_TAG, e.getMessage());
+ }
+ }
+ }
+ }
+
+ return null;
+ };
+
+ return new CordovaPluginPathHandler(pathHandler);
+ }
}
diff --git a/src/android/Filesystem.java b/src/android/Filesystem.java
index c69d3bdd0..54532211b 100644
--- a/src/android/Filesystem.java
+++ b/src/android/Filesystem.java
@@ -29,6 +29,7 @@ Licensed to the Apache Software Foundation (ASF) under one
import java.util.ArrayList;
import java.util.Arrays;
+import org.apache.cordova.CordovaPreferences;
import org.apache.cordova.CordovaResourceApi;
import org.json.JSONArray;
import org.json.JSONException;
@@ -38,13 +39,18 @@ public abstract class Filesystem {
protected final Uri rootUri;
protected final CordovaResourceApi resourceApi;
+ protected final CordovaPreferences preferences;
public final String name;
private JSONObject rootEntry;
- public Filesystem(Uri rootUri, String name, CordovaResourceApi resourceApi) {
+ static String SCHEME_HTTPS = "https";
+ static String DEFAULT_HOSTNAME = "localhost";
+
+ public Filesystem(Uri rootUri, String name, CordovaResourceApi resourceApi, CordovaPreferences preferences) {
this.rootUri = rootUri;
this.name = name;
this.resourceApi = resourceApi;
+ this.preferences = preferences;
}
public interface ReadFileCallback {
@@ -328,4 +334,15 @@ public int read(byte[] buffer, int byteOffset, int byteCount) throws IOException
return numBytesRead;
}
}
+
+ protected Uri.Builder createLocalUriBuilder() {
+ String scheme = preferences.getString("scheme", SCHEME_HTTPS).toLowerCase();
+ String hostname = preferences.getString("hostname", DEFAULT_HOSTNAME).toLowerCase();
+ String path = LocalFilesystemURL.fsNameToCdvKeyword(name);
+
+ return new Uri.Builder()
+ .scheme(scheme)
+ .authority(hostname)
+ .path(path);
+ }
}
diff --git a/src/android/LocalFilesystem.java b/src/android/LocalFilesystem.java
index 393344f4f..97e488824 100644
--- a/src/android/LocalFilesystem.java
+++ b/src/android/LocalFilesystem.java
@@ -28,6 +28,8 @@ Licensed to the Apache Software Foundation (ASF) under one
import java.io.OutputStream;
import java.io.RandomAccessFile;
import java.nio.channels.FileChannel;
+
+import org.apache.cordova.CordovaPreferences;
import org.apache.cordova.CordovaResourceApi;
import org.json.JSONException;
import org.json.JSONObject;
@@ -44,8 +46,8 @@ Licensed to the Apache Software Foundation (ASF) under one
public class LocalFilesystem extends Filesystem {
private final Context context;
- public LocalFilesystem(String name, Context context, CordovaResourceApi resourceApi, File fsRoot) {
- super(Uri.fromFile(fsRoot).buildUpon().appendEncodedPath("").build(), name, resourceApi);
+ public LocalFilesystem(String name, Context context, CordovaResourceApi resourceApi, File fsRoot, CordovaPreferences preferences) {
+ super(Uri.fromFile(fsRoot).buildUpon().appendEncodedPath("").build(), name, resourceApi, preferences);
this.context = context;
}
@@ -88,10 +90,9 @@ public LocalFilesystemURL toLocalUri(Uri inputURL) {
if (!subPath.isEmpty()) {
subPath = subPath.substring(1);
}
- Uri.Builder b = new Uri.Builder()
- .scheme(LocalFilesystemURL.FILESYSTEM_PROTOCOL)
- .authority("localhost")
- .path(name);
+
+ Uri.Builder b = createLocalUriBuilder();
+
if (!subPath.isEmpty()) {
b.appendEncodedPath(subPath);
}
diff --git a/src/android/LocalFilesystemURL.java b/src/android/LocalFilesystemURL.java
index b96b6ee49..d3f41d07a 100644
--- a/src/android/LocalFilesystemURL.java
+++ b/src/android/LocalFilesystemURL.java
@@ -23,6 +23,7 @@ Licensed to the Apache Software Foundation (ASF) under one
public class LocalFilesystemURL {
public static final String FILESYSTEM_PROTOCOL = "cdvfile";
+ public static final String CDVFILE_KEYWORD = "__cdvfile_";
public final Uri uri;
public final String fsName;
@@ -37,19 +38,26 @@ private LocalFilesystemURL(Uri uri, String fsName, String fsPath, boolean isDire
}
public static LocalFilesystemURL parse(Uri uri) {
- if (!FILESYSTEM_PROTOCOL.equals(uri.getScheme())) {
+ if(!uri.toString().contains(CDVFILE_KEYWORD)) {
return null;
}
+
String path = uri.getPath();
if (path.length() < 1) {
return null;
}
+
int firstSlashIdx = path.indexOf('/', 1);
if (firstSlashIdx < 0) {
return null;
}
+
String fsName = path.substring(1, firstSlashIdx);
+ fsName = fsName.substring(CDVFILE_KEYWORD.length());
+ fsName = fsName.substring(0, fsName.length() - 2);
+
path = path.substring(firstSlashIdx);
+
boolean isDirectory = path.charAt(path.length() - 1) == '/';
return new LocalFilesystemURL(uri, fsName, path, isDirectory);
}
@@ -58,6 +66,8 @@ public static LocalFilesystemURL parse(String uri) {
return parse(Uri.parse(uri));
}
+ public static String fsNameToCdvKeyword(String fsName) { return CDVFILE_KEYWORD + fsName + "__"; }
+
public String toString() {
return uri.toString();
}
diff --git a/www/Entry.js b/www/Entry.js
index a67be96fa..c90353765 100644
--- a/www/Entry.js
+++ b/www/Entry.js
@@ -187,15 +187,12 @@ Entry.prototype.toInternalURL = function () {
/**
* Return a URL that can be used to identify this entry.
* Use a URL that can be used to as the src attribute of a