diff --git a/framework/src/org/apache/cordova/CordovaPlugin.java b/framework/src/org/apache/cordova/CordovaPlugin.java index ee111bc2..95a39c79 100644 --- a/framework/src/org/apache/cordova/CordovaPlugin.java +++ b/framework/src/org/apache/cordova/CordovaPlugin.java @@ -28,6 +28,9 @@ import org.json.JSONException; import android.content.Intent; import android.net.Uri; +import java.io.FileNotFoundException; +import java.io.IOException; + /** * Plugins must extend this class and override one of the execute methods. */ @@ -249,11 +252,53 @@ public class CordovaPlugin { /** * Hook for redirecting requests. Applies to WebView requests as well as requests made by plugins. + * To handle the request directly, return a URI in the form: + * + * cdvplugin://pluginId/... + * + * And implement handleOpenForRead(). + * To make this easier, use the toPluginUri() and fromPluginUri() helpers: + * + * public Uri remapUri(Uri uri) { return toPluginUri(uri); } + * + * public CordovaResourceApi.OpenForReadResult handleOpenForRead(Uri uri) throws IOException { + * Uri origUri = fromPluginUri(uri); + * ... + * } */ public Uri remapUri(Uri uri) { return null; } - + + /** + * Called to handle CordovaResourceApi.openForRead() calls for a cdvplugin://pluginId/ URL. + * Should never return null. + * Added in cordova-android@4.0.0 + */ + public CordovaResourceApi.OpenForReadResult handleOpenForRead(Uri uri) throws IOException { + throw new FileNotFoundException("Plugin can't handle uri: " + uri); + } + + /** + * Refer to remapUri() + * Added in cordova-android@4.0.0 + */ + protected Uri toPluginUri(Uri origUri) { + return new Uri.Builder() + .scheme(CordovaResourceApi.PLUGIN_URI_SCHEME) + .authority(serviceName) + .appendQueryParameter("origUri", origUri.toString()) + .build(); + } + + /** + * Refer to remapUri() + * Added in cordova-android@4.0.0 + */ + protected Uri fromPluginUri(Uri pluginUri) { + return Uri.parse(pluginUri.getQueryParameter("origUri")); + } + /** * Called when the WebView does a top-level navigation or refreshes. * diff --git a/framework/src/org/apache/cordova/CordovaResourceApi.java b/framework/src/org/apache/cordova/CordovaResourceApi.java index 47f936e8..274e9a15 100644 --- a/framework/src/org/apache/cordova/CordovaResourceApi.java +++ b/framework/src/org/apache/cordova/CordovaResourceApi.java @@ -72,8 +72,11 @@ public class CordovaResourceApi { public static final int URI_TYPE_DATA = 4; public static final int URI_TYPE_HTTP = 5; public static final int URI_TYPE_HTTPS = 6; + public static final int URI_TYPE_PLUGIN = 7; public static final int URI_TYPE_UNKNOWN = -1; - + + public static final String PLUGIN_URI_SCHEME = "cdvplugin"; + private static final String[] LOCAL_FILE_PROJECTION = { "_data" }; public static Thread jsThread; @@ -123,6 +126,9 @@ public class CordovaResourceApi { if ("https".equals(scheme)) { return URI_TYPE_HTTPS; } + if (PLUGIN_URI_SCHEME.equals(scheme)) { + return URI_TYPE_PLUGIN; + } return URI_TYPE_UNKNOWN; } @@ -282,6 +288,14 @@ public class CordovaResourceApi { InputStream inputStream = conn.getInputStream(); return new OpenForReadResult(uri, inputStream, mimeType, length, null); } + case URI_TYPE_PLUGIN: { + String pluginId = uri.getHost(); + CordovaPlugin plugin = pluginManager.getPlugin(pluginId); + if (plugin == null) { + throw new FileNotFoundException("Invalid plugin ID in URI: " + uri); + } + return plugin.handleOpenForRead(uri); + } } throw new FileNotFoundException("URI not supported by CordovaResourceApi: " + uri); } @@ -431,7 +445,7 @@ public class CordovaResourceApi { public final long length; public final AssetFileDescriptor assetFd; - OpenForReadResult(Uri uri, InputStream inputStream, String mimeType, long length, AssetFileDescriptor assetFd) { + public OpenForReadResult(Uri uri, InputStream inputStream, String mimeType, long length, AssetFileDescriptor assetFd) { this.uri = uri; this.inputStream = inputStream; this.mimeType = mimeType; diff --git a/test/androidTest/src/org/apache/cordova/test/CordovaResourceApiTest.java b/test/androidTest/src/org/apache/cordova/test/CordovaResourceApiTest.java index 73aa066f..7295987b 100644 --- a/test/androidTest/src/org/apache/cordova/test/CordovaResourceApiTest.java +++ b/test/androidTest/src/org/apache/cordova/test/CordovaResourceApiTest.java @@ -36,9 +36,11 @@ import org.apache.cordova.PluginEntry; import org.json.JSONArray; import org.json.JSONException; +import java.io.ByteArrayInputStream; import java.io.File; import java.io.IOException; import java.io.OutputStream; +import java.nio.charset.StandardCharsets; import java.util.Scanner; public class CordovaResourceApiTest extends BaseCordovaIntegrationTest { @@ -58,8 +60,18 @@ public class CordovaResourceApiTest extends BaseCordovaIntegrationTest { return cordovaWebView.getResourceApi().remapUri( Uri.parse("data:text/plain;charset=utf-8,pass")); } + if (uri.getQuery() != null && uri.getQuery().contains("pluginUri")) { + return toPluginUri(uri); + } return null; } + @Override + public OpenForReadResult handleOpenForRead(Uri uri) throws IOException { + Uri orig = fromPluginUri(uri); + ByteArrayInputStream retStream = new ByteArrayInputStream(orig.toString().getBytes(StandardCharsets.UTF_8)); + return new OpenForReadResult(uri, retStream, "text/plain", retStream.available(), null); + } + @Override public boolean execute(String action, JSONArray args, CallbackContext callbackContext) throws JSONException { synchronized (CordovaResourceApiTest.this) { execPayload = args.getString(0); @@ -214,6 +226,16 @@ public class CordovaResourceApiTest extends BaseCordovaIntegrationTest { String data = new Scanner(readResult.inputStream, "UTF-8").useDelimiter("\\A").next(); assertEquals("pass", data); } + // testPluginUris + { + String origUri = "http://orig/foo?pluginUri"; + Uri uri = resourceApi.remapUri(Uri.parse(origUri)); + OpenForReadResult readResult = resourceApi.openForRead(uri); + assertEquals("openForRead mime-type", "text/plain", readResult.mimeType); + String data = new Scanner(readResult.inputStream, "UTF-8").useDelimiter("\\A").next(); + assertEquals(origUri, data); + assertEquals(origUri.length(), readResult.length); + } } public void testWebViewRequestIntercept() throws Throwable