You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cordova.apache.org by br...@apache.org on 2013/06/14 20:52:23 UTC
[4/6] android commit: Revert "Revert "Added "DataResource" - allows
many plugins to intercept a single request""
Revert "Revert "Added "DataResource" - allows many plugins to intercept a single request""
This reverts commit 979d8e66f26d27a132d7514386b777fc8fc234f3.
Project: http://git-wip-us.apache.org/repos/asf/cordova-android/repo
Commit: http://git-wip-us.apache.org/repos/asf/cordova-android/commit/df6d1107
Tree: http://git-wip-us.apache.org/repos/asf/cordova-android/tree/df6d1107
Diff: http://git-wip-us.apache.org/repos/asf/cordova-android/diff/df6d1107
Branch: refs/heads/dataresource
Commit: df6d110754db0c491d60f37aa97dd1503ea120a2
Parents: e6c02ca
Author: Braden Shepherdson <br...@gmail.com>
Authored: Fri Jun 7 10:17:39 2013 -0400
Committer: Braden Shepherdson <br...@gmail.com>
Committed: Fri Jun 14 14:52:09 2013 -0400
----------------------------------------------------------------------
.../src/org/apache/cordova/FileHelper.java | 58 ++++++-
.../cordova/IceCreamCordovaWebViewClient.java | 48 ++----
.../org/apache/cordova/api/CordovaPlugin.java | 14 ++
.../org/apache/cordova/api/DataResource.java | 156 +++++++++++++++++++
.../apache/cordova/api/DataResourceContext.java | 56 +++++++
.../org/apache/cordova/api/PluginManager.java | 26 ++++
6 files changed, 323 insertions(+), 35 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/cordova-android/blob/df6d1107/framework/src/org/apache/cordova/FileHelper.java
----------------------------------------------------------------------
diff --git a/framework/src/org/apache/cordova/FileHelper.java b/framework/src/org/apache/cordova/FileHelper.java
index ebbdc8d..e7bf98a 100644
--- a/framework/src/org/apache/cordova/FileHelper.java
+++ b/framework/src/org/apache/cordova/FileHelper.java
@@ -26,10 +26,11 @@ import org.apache.cordova.api.CordovaInterface;
import org.apache.cordova.api.LOG;
import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
-import java.net.URLConnection;
-import java.util.Locale;
+import java.io.OutputStream;
public class FileHelper {
private static final String LOG_TAG = "FileUtils";
@@ -90,7 +91,7 @@ public class FileHelper {
* @throws IOException
*/
public static InputStream getInputStreamFromUriString(String uriString, CordovaInterface cordova) throws IOException {
- if (uriString.startsWith("content")) {
+ if (uriString.startsWith("content:")) {
Uri uri = Uri.parse(uriString);
return cordova.getActivity().getContentResolver().openInputStream(uri);
} else if (uriString.startsWith("file://")) {
@@ -110,6 +111,57 @@ public class FileHelper {
}
}
+ public static OutputStream getOutputStreamFromUriString(String uriString, CordovaInterface cordova) throws FileNotFoundException{
+ if (uriString.startsWith("content:")) {
+ Uri uri = Uri.parse(uriString);
+ return cordova.getActivity().getContentResolver().openOutputStream(uri);
+ } else if (uriString.startsWith("file:") && !uriString.startsWith("file:///android_asset/")) {
+ String realPath = uriString.substring(7);
+ return new FileOutputStream(realPath);
+ } else {
+ return null;
+ }
+ }
+ /**
+ * Returns whether the uri can be written to by openeing a File to that uri
+ *
+ * @param the URI to test
+ * @return boolean indicating whether the uri is writable
+ */
+ public static boolean isUriWritable(String uriString) {
+ String scheme = uriString.split(":")[0];
+ String writableSchemes[] = new String[]{ "content" };
+
+ if(scheme.equals("file")){
+ // special case file
+ if(uriString.startsWith("file:///android_asset/")){
+ return false;
+ } else {
+ return true;
+ }
+ }
+ for(int i = writableSchemes.length - 1; i >= 0 ; i--){
+ if(writableSchemes[i].equals(scheme)){
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Ensures the "file://" prefix exists for the given string
+ * If the given URI string has a "file://" prefix, it is returned unchanged
+ *
+ * @param path - the path string to operate on
+ * @return a String with the "file://" scheme set
+ */
+ public static String insertFileProtocol(String path) {
+ if(!path.matches("^[a-z0-9+.-]+:.*")){
+ path = "file://" + path;
+ }
+ return path;
+ }
+
/**
* Removes the "file://" prefix from the given URI string, if applicable.
* If the given URI string doesn't have a "file://" prefix, it is returned unchanged.
http://git-wip-us.apache.org/repos/asf/cordova-android/blob/df6d1107/framework/src/org/apache/cordova/IceCreamCordovaWebViewClient.java
----------------------------------------------------------------------
diff --git a/framework/src/org/apache/cordova/IceCreamCordovaWebViewClient.java b/framework/src/org/apache/cordova/IceCreamCordovaWebViewClient.java
index 847972e..d71fbe8 100644
--- a/framework/src/org/apache/cordova/IceCreamCordovaWebViewClient.java
+++ b/framework/src/org/apache/cordova/IceCreamCordovaWebViewClient.java
@@ -19,9 +19,10 @@
package org.apache.cordova;
import java.io.IOException;
-import java.io.InputStream;
import org.apache.cordova.api.CordovaInterface;
+import org.apache.cordova.api.DataResource;
+import org.apache.cordova.api.DataResourceContext;
import org.apache.cordova.api.LOG;
import android.annotation.TargetApi;
@@ -43,41 +44,24 @@ public class IceCreamCordovaWebViewClient extends CordovaWebViewClient {
@Override
public WebResourceResponse shouldInterceptRequest(WebView view, String url) {
+ // We need to support the new DataResource intercepts without breaking the shouldInterceptRequest mechanism.
+ DataResource dataResource = DataResource.initiateNewDataRequestForUri(url, this.appView.pluginManager, cordova,
+ new DataResourceContext("WebViewClient.shouldInterceptRequest", true /* this is from a browser request*/));
+ url = dataResource.getUri().toString();
+
//Check if plugins intercept the request
WebResourceResponse ret = super.shouldInterceptRequest(view, url);
- if(ret == null && (url.contains("?") || url.contains("#") || needsIceCreamSpaceInAssetUrlFix(url))){
- ret = generateWebResourceResponse(url);
- }
- return ret;
- }
-
- private WebResourceResponse generateWebResourceResponse(String url) {
- if (url.startsWith("file:///android_asset/")) {
- String mimetype = FileHelper.getMimeType(url, cordova);
-
+// The below bugfix is taken care of by the dataResource mechanism
+// if(ret == null && (url.contains("?") || url.contains("#") || needsIceCreamSpaceInAssetUrlFix(url))){
+// ret = generateWebResourceResponse(url);
+// }
+ if(ret == null) {
try {
- InputStream stream = FileHelper.getInputStreamFromUriString(url, cordova);
- WebResourceResponse response = new WebResourceResponse(mimetype, "UTF-8", stream);
- return response;
- } catch (IOException e) {
- LOG.e("generateWebResourceResponse", e.getMessage(), e);
+ ret = new WebResourceResponse(dataResource.getMimeType(), "UTF-8", dataResource.getInputStream());
+ } catch(IOException e) {
+ LOG.e("IceCreamCordovaWebViewClient", "Error occurred while loading a file.", e);
}
}
- return null;
- }
-
- private static boolean needsIceCreamSpaceInAssetUrlFix(String url) {
- if (!url.contains("%20")){
- return false;
- }
-
- switch(android.os.Build.VERSION.SDK_INT){
- case android.os.Build.VERSION_CODES.ICE_CREAM_SANDWICH:
- case android.os.Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1:
- return true;
- default:
- return false;
- }
+ return ret;
}
-
}
http://git-wip-us.apache.org/repos/asf/cordova-android/blob/df6d1107/framework/src/org/apache/cordova/api/CordovaPlugin.java
----------------------------------------------------------------------
diff --git a/framework/src/org/apache/cordova/api/CordovaPlugin.java b/framework/src/org/apache/cordova/api/CordovaPlugin.java
index 2b225e6..69f8fde 100644
--- a/framework/src/org/apache/cordova/api/CordovaPlugin.java
+++ b/framework/src/org/apache/cordova/api/CordovaPlugin.java
@@ -176,6 +176,20 @@ public class CordovaPlugin {
}
/**
+ * All plugins can now choose if they want to modify any uri requests. This includes all webview requests, opening of files, content uri's etc.
+ * This mechanism allows several plugins to modify the same request
+ * @param requestSource The source of the incoming request
+ *
+ * @param dataResource The resource to be loaded.
+ * @param dataResourceContext Context associated with the resource load
+ * @return Return a new DataResource if the plugin wants o assist in loading the request or null if it doesn't.
+ */
+ @TargetApi(Build.VERSION_CODES.HONEYCOMB)
+ public DataResource shouldInterceptDataResourceRequest(DataResource dataResource, DataResourceContext dataResourceContext) {
+ return null;
+ }
+
+ /**
* Called when the WebView does a top-level navigation or refreshes.
*
* Plugins should stop any long-running processes and clean up internal state.
http://git-wip-us.apache.org/repos/asf/cordova-android/blob/df6d1107/framework/src/org/apache/cordova/api/DataResource.java
----------------------------------------------------------------------
diff --git a/framework/src/org/apache/cordova/api/DataResource.java b/framework/src/org/apache/cordova/api/DataResource.java
new file mode 100644
index 0000000..38bf31b
--- /dev/null
+++ b/framework/src/org/apache/cordova/api/DataResource.java
@@ -0,0 +1,156 @@
+/*
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+*/
+package org.apache.cordova.api;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+import org.apache.cordova.FileHelper;
+
+import android.net.Uri;
+
+/*
+ * All requests to access files, browser network requests etc have to go through this class.
+ */
+public class DataResource {
+ private CordovaInterface cordova;
+
+ // Uri of the request. Always required.
+ private Uri uri;
+ // Remaining fields may or may not be null
+ private InputStream is;
+ private OutputStream os;
+ private String mimeType;
+ private Boolean writable;
+ private File realFile;
+ private boolean retryLoad = true;
+
+ public DataResource(CordovaInterface cordova, Uri uri) {
+ super();
+ this.cordova = cordova;
+ this.uri = uri;
+ }
+ public DataResource(CordovaInterface cordova, Uri uri, InputStream is,
+ OutputStream os, String mimeType, boolean writable, File realFile) {
+ this(cordova, uri);
+ this.is = is;
+ this.mimeType = mimeType;
+ this.writable = Boolean.valueOf(writable);
+ this.realFile = realFile;
+ }
+ public Uri getUri() {
+ // Uri is always provided
+ return uri;
+ }
+ public InputStream getIs() throws IOException {
+ if(is == null && retryLoad) {
+ try {
+ is = FileHelper.getInputStreamFromUriString(uri.toString(), cordova);
+ } finally {
+ // We failed loading once, don't try loading anymore
+ if(is == null) {
+ retryLoad = false;
+ }
+ }
+ }
+ return is;
+ }
+ public OutputStream getOs() throws FileNotFoundException {
+ if(os == null && retryLoad) {
+ try {
+ os = FileHelper.getOutputStreamFromUriString(uri.toString(), cordova);
+ } finally {
+ // We failed loading once, don't try loading anymore
+ if(os == null) {
+ retryLoad = false;
+ }
+ }
+ }
+ return os;
+ }
+ public String getMimeType() {
+ if(mimeType == null && retryLoad) {
+ try {
+ mimeType = FileHelper.getMimeType(uri.toString(), cordova);
+ } finally {
+ // We failed loading once, don't try loading anymore
+ if(mimeType == null) {
+ retryLoad = false;
+ }
+ }
+ }
+ return mimeType;
+ }
+ public boolean isWritable() {
+ if(writable == null && retryLoad) {
+ try {
+ writable = FileHelper.isUriWritable(uri.toString());
+ } finally {
+ // We failed loading once, don't try loading anymore
+ if(writable == null) {
+ retryLoad = false;
+ }
+ }
+ }
+ // default to false
+ return writable != null? writable.booleanValue() : false;
+ }
+ public File getRealFile() {
+ if(realFile == null && retryLoad) {
+ try {
+ String realPath = FileHelper.getRealPath(uri, cordova);
+ if(realPath != null) {
+ realFile = new File(realPath);
+ }
+ } finally {
+ // We failed loading once, don't try loading anymore
+ if(realFile == null) {
+ retryLoad = false;
+ }
+ }
+ }
+ return realFile;
+ }
+
+ // static instantiation methods
+ public static DataResource initiateNewDataRequestForUri(String uriString, PluginManager pluginManager, CordovaInterface cordova, String requestSourceTag){
+ // if no protocol is specified, assume its file:
+ uriString = FileHelper.insertFileProtocol(uriString);
+ return initiateNewDataRequestForUri(Uri.parse(uriString), pluginManager, cordova, requestSourceTag);
+ }
+ public static DataResource initiateNewDataRequestForUri(Uri uri, PluginManager pluginManager, CordovaInterface cordova, String requestSourceTag){
+ return initiateNewDataRequestForUri(uri, pluginManager, cordova, new DataResourceContext(requestSourceTag, false /* Assume, not a browser request by default */ ));
+ }
+ public static DataResource initiateNewDataRequestForUri(String uriString, PluginManager pluginManager, CordovaInterface cordova, DataResourceContext dataResourceContext){
+ // if no protocol is specified, assume its file:
+ uriString = FileHelper.insertFileProtocol(uriString);
+ return initiateNewDataRequestForUri(Uri.parse(uriString), pluginManager, cordova, dataResourceContext);
+ }
+ public static DataResource initiateNewDataRequestForUri(Uri uri, PluginManager pluginManager, CordovaInterface cordova, DataResourceContext dataResourceContext){
+ DataResource dataResource = new DataResource(cordova, uri);
+ if (pluginManager != null) {
+ // get the resource as returned by plugins
+ dataResource = pluginManager.shouldInterceptDataResourceRequest(dataResource, dataResourceContext);
+ }
+ return dataResource;
+ }
+}
http://git-wip-us.apache.org/repos/asf/cordova-android/blob/df6d1107/framework/src/org/apache/cordova/api/DataResourceContext.java
----------------------------------------------------------------------
diff --git a/framework/src/org/apache/cordova/api/DataResourceContext.java b/framework/src/org/apache/cordova/api/DataResourceContext.java
new file mode 100644
index 0000000..55bbc1d
--- /dev/null
+++ b/framework/src/org/apache/cordova/api/DataResourceContext.java
@@ -0,0 +1,56 @@
+/*
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+*/
+package org.apache.cordova.api;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Random;
+/*
+ * Some context information associated with a DataRequest.
+ */
+public class DataResourceContext {
+ // A random id that is unique for a particular request.
+ private int requestId;
+ // A tag associated with the source of this dataResourceContext
+ private String source;
+ // If needed, any data associated with core plugins can be a part of the context object
+ // This field indicates whether the request came from a browser network request
+ private boolean isFromBrowser;
+ // If needed, any data associated with non core plugins should store data in a Map so as to not clutter the context object
+ private Map<String, Object> dataMap;
+ public DataResourceContext(String source, boolean isFromBrowser) {
+ super();
+ this.requestId = new Random().nextInt();
+ this.source = source;
+ this.isFromBrowser = isFromBrowser;
+ this.dataMap = new HashMap<String, Object>();
+ }
+ public int getRequestId() {
+ return requestId;
+ }
+ public String getSource() {
+ return source;
+ }
+ public boolean isFromBrowser() {
+ return isFromBrowser;
+ }
+ public Map<String, Object> getDataMap() {
+ return dataMap;
+ }
+}
http://git-wip-us.apache.org/repos/asf/cordova-android/blob/df6d1107/framework/src/org/apache/cordova/api/PluginManager.java
----------------------------------------------------------------------
diff --git a/framework/src/org/apache/cordova/api/PluginManager.java b/framework/src/org/apache/cordova/api/PluginManager.java
index 71fc258..36ac357 100755
--- a/framework/src/org/apache/cordova/api/PluginManager.java
+++ b/framework/src/org/apache/cordova/api/PluginManager.java
@@ -400,4 +400,30 @@ public class PluginManager {
LOG.e(TAG, "https://git-wip-us.apache.org/repos/asf?p=incubator-cordova-android.git;a=blob;f=framework/res/xml/plugins.xml");
LOG.e(TAG, "=====================================================================================");
}
+
+ /**
+ * Called when the any resource is going to be loaded - either from the webview, files or any other resource
+ *
+ *
+ * @param dataResource The resource request to be loaded.
+ * @param dataResourceContext The context of the dataResource request
+ * @return Return the resource request that will be loaded. The returned request may be modified or unchanged.
+ */
+ public DataResource shouldInterceptDataResourceRequest(DataResource dataResource, DataResourceContext dataResourceContext){
+ boolean requestModified = true;
+ while(requestModified) {
+ requestModified = false;
+ for (PluginEntry entry : this.entries.values()) {
+ if (entry.plugin != null) {
+ DataResource ret = entry.plugin.shouldInterceptDataResourceRequest(dataResource, dataResourceContext);
+ if(ret != null) {
+ dataResource = ret;
+ requestModified = true;
+ break;
+ }
+ }
+ }
+ }
+ return dataResource;
+ }
}