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;
+    }
 }