updated from master

This commit is contained in:
Steven Gill
2013-06-10 17:15:05 -07:00
39 changed files with 1903 additions and 496 deletions
+2 -3
View File
@@ -50,9 +50,8 @@ public class Config {
private static Config self = null;
public static void init(Activity action) {
if (self == null) {
self = new Config(action);
}
//Just re-initialize this! Seriously, we lose this all the time
self = new Config(action);
}
// Intended to be used for testing only; creates an empty configuration.
@@ -64,11 +64,10 @@ import android.widget.LinearLayout;
* As an example:
*
* package org.apache.cordova.examples;
* import android.app.Activity;
* import android.os.Bundle;
* import org.apache.cordova.*;
*
* public class Examples extends DroidGap {
* public class Example extends CordovaActivity {
* @Override
* public void onCreate(Bundle savedInstanceState) {
* super.onCreate(savedInstanceState);
@@ -77,9 +76,6 @@ import android.widget.LinearLayout;
* super.setStringProperty("loadingDialog", "Title,Message"); // show loading dialog
* super.setStringProperty("errorUrl", "file:///android_asset/www/error.html"); // if error loading file in super.loadUrl().
*
* // Initialize activity
* super.init();
*
* // Clear cache if you want
* super.appView.clearCache(true);
*
@@ -121,7 +117,7 @@ import android.widget.LinearLayout;
* Cordova.xml configuration:
* Cordova uses a configuration file at res/xml/cordova.xml to specify the following settings.
*
* Approved list of URLs that can be loaded into DroidGap
* Approved list of URLs that can be loaded into Cordova
* <access origin="http://server regexp" subdomains="true" />
* Log level: ERROR, WARN, INFO, DEBUG, VERBOSE (default=ERROR)
* <log level="DEBUG" />
@@ -138,7 +134,7 @@ import android.widget.LinearLayout;
* </plugins>
*/
public class CordovaActivity extends Activity implements CordovaInterface {
public static String TAG = "DroidGap";
public static String TAG = "CordovaActivity";
// The webview for our app
protected CordovaWebView appView;
@@ -263,7 +259,7 @@ public class CordovaActivity extends Activity implements CordovaInterface {
@Override
public void onCreate(Bundle savedInstanceState) {
Config.init(this);
LOG.d(TAG, "DroidGap.onCreate()");
LOG.d(TAG, "CordovaActivity.onCreate()");
super.onCreate(savedInstanceState);
if(savedInstanceState != null)
@@ -336,7 +332,7 @@ public class CordovaActivity extends Activity implements CordovaInterface {
*/
@SuppressLint("NewApi")
public void init(CordovaWebView webView, CordovaWebViewClient webViewClient, CordovaChromeClient webChromeClient) {
LOG.d(TAG, "DroidGap.init()");
LOG.d(TAG, "CordovaActivity.init()");
// Set up web container
this.appView = webView;
@@ -599,7 +595,7 @@ public class CordovaActivity extends Activity implements CordovaInterface {
* @param value
*/
public void setBooleanProperty(String name, boolean value) {
Log.d(TAG, "Setting boolean properties in DroidGap will be deprecated in 3.0 on July 2013, please use config.xml");
Log.d(TAG, "Setting boolean properties in CordovaActivity will be deprecated in 3.0 on July 2013, please use config.xml");
this.getIntent().putExtra(name, value);
}
@@ -610,7 +606,7 @@ public class CordovaActivity extends Activity implements CordovaInterface {
* @param value
*/
public void setIntegerProperty(String name, int value) {
Log.d(TAG, "Setting integer properties in DroidGap will be deprecated in 3.1 on August 2013, please use config.xml");
Log.d(TAG, "Setting integer properties in CordovaActivity will be deprecated in 3.1 on August 2013, please use config.xml");
this.getIntent().putExtra(name, value);
}
@@ -621,7 +617,7 @@ public class CordovaActivity extends Activity implements CordovaInterface {
* @param value
*/
public void setStringProperty(String name, String value) {
Log.d(TAG, "Setting string properties in DroidGap will be deprecated in 3.0 on July 2013, please use config.xml");
Log.d(TAG, "Setting string properties in CordovaActivity will be deprecated in 3.0 on July 2013, please use config.xml");
this.getIntent().putExtra(name, value);
}
@@ -632,7 +628,7 @@ public class CordovaActivity extends Activity implements CordovaInterface {
* @param value
*/
public void setDoubleProperty(String name, double value) {
Log.d(TAG, "Setting double properties in DroidGap will be deprecated in 3.0 on July 2013, please use config.xml");
Log.d(TAG, "Setting double properties in CordovaActivity will be deprecated in 3.0 on July 2013, please use config.xml");
this.getIntent().putExtra(name, value);
}
@@ -716,7 +712,7 @@ public class CordovaActivity extends Activity implements CordovaInterface {
* The final call you receive before your activity is destroyed.
*/
public void onDestroy() {
LOG.d(TAG, "onDestroy()");
LOG.d(TAG, "CordovaActivity.onDestroy()");
super.onDestroy();
// hide the splash screen to avoid leaking a window
@@ -856,18 +852,13 @@ public class CordovaActivity extends Activity implements CordovaInterface {
mUploadMessage = null;
}
CordovaPlugin callback = this.activityResultCallback;
if(callback == null)
{
if(initCallbackClass != null)
{
this.activityResultCallback = appView.pluginManager.getPlugin(initCallbackClass);
callback = activityResultCallback;
LOG.d(TAG, "We have a callback to send this result to");
callback.onActivityResult(requestCode, resultCode, intent);
}
if(callback == null && initCallbackClass != null) {
// The application was restarted, but had defined an initial callback
// before being shut down.
this.activityResultCallback = appView.pluginManager.getPlugin(initCallbackClass);
callback = this.activityResultCallback;
}
else
{
if(callback != null) {
LOG.d(TAG, "We have a callback to send this result to");
callback.onActivityResult(requestCode, resultCode, intent);
}
@@ -961,7 +952,7 @@ public class CordovaActivity extends Activity implements CordovaInterface {
}
/*
* Hook in DroidGap for menu plugins
* Hook in Cordova for menu plugins
*
*/
@Override
@@ -1000,7 +991,7 @@ public class CordovaActivity extends Activity implements CordovaInterface {
* @param url The url to load.
* @param openExternal Load url in browser instead of Cordova webview.
* @param clearHistory Clear the history stack, so new page becomes top of history
* @param params DroidGap parameters for new app
* @param params Parameters for new app
*/
public void showWebPage(String url, boolean openExternal, boolean clearHistory, HashMap<String, Object> params) {
if (this.appView != null) {
@@ -222,6 +222,7 @@ public class CordovaChromeClient extends WebChromeClient {
result.confirm(r == null ? "" : r);
} catch (JSONException e) {
e.printStackTrace();
return false;
}
}
@@ -286,7 +287,7 @@ public class CordovaChromeClient extends WebChromeClient {
public void onExceededDatabaseQuota(String url, String databaseIdentifier, long currentQuota, long estimatedSize,
long totalUsedQuota, WebStorage.QuotaUpdater quotaUpdater)
{
LOG.d(TAG, "DroidGap: onExceededDatabaseQuota estimatedSize: %d currentQuota: %d totalUsedQuota: %d", estimatedSize, currentQuota, totalUsedQuota);
LOG.d(TAG, "onExceededDatabaseQuota estimatedSize: %d currentQuota: %d totalUsedQuota: %d", estimatedSize, currentQuota, totalUsedQuota);
if (estimatedSize < MAX_QUOTA)
{
@@ -485,7 +485,7 @@ public class CordovaWebView extends WebView {
// If first page, then show splashscreen
else {
LOG.d(TAG, "DroidGap.loadUrl(%s, %d)", url, time);
LOG.d(TAG, "loadUrlIntoView(%s, %d)", url, time);
// Send message to show splashscreen now if desired
this.postMessage("splashscreen", "show");
@@ -555,7 +555,7 @@ public class CordovaWebView extends WebView {
* @param url The url to load.
* @param openExternal Load url in browser instead of Cordova webview.
* @param clearHistory Clear the history stack, so new page becomes top of history
* @param params DroidGap parameters for new app
* @param params Parameters for new app
*/
public void showWebPage(String url, boolean openExternal, boolean clearHistory, HashMap<String, Object> params) {
LOG.d(TAG, "showWebPage(%s, %b, %b, HashMap", url, openExternal, clearHistory);
@@ -601,7 +601,7 @@ public class CordovaWebView extends WebView {
/**
* Check configuration parameters from Config.
* Approved list of URLs that can be loaded into DroidGap
* Approved list of URLs that can be loaded into Cordova
* <access origin="http://server regexp" subdomains="true" />
* Log level: ERROR, WARN, INFO, DEBUG, VERBOSE (default=ERROR)
* <log level="DEBUG" />
+1 -1
View File
@@ -101,7 +101,7 @@ public class Device extends CordovaPlugin {
/**
* Listen for telephony events: RINGING, OFFHOOK and IDLE
* Send these events to all plugins using
* DroidGap.onMessage("telephone", "ringing" | "offhook" | "idle")
* CordovaActivity.onMessage("telephone", "ringing" | "offhook" | "idle")
*/
private void initTelephonyReceiver() {
IntentFilter intentFilter = new IntentFilter();
@@ -26,11 +26,10 @@ 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.io.OutputStream;
import java.net.URLConnection;
import java.util.Locale;
public class FileHelper {
private static final String LOG_TAG = "FileUtils";
@@ -91,63 +90,24 @@ 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:///android_asset/")) {
Uri uri = Uri.parse(uriString);
String relativePath = uri.getPath().substring(15);
return cordova.getActivity().getAssets().open(relativePath);
} else if (uriString.startsWith("file://")) {
return new FileInputStream(getRealPath(uriString, cordova));
} else {
return null;
}
}
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];
if(scheme.equals("file")){
// special case file
return !uriString.startsWith("file:///android_asset/");
}
return "content".equals(scheme);
}
/**
* Ensures the "file://" prefix exists for the given string
* If the given URI string already has a scheme, 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+.-]+:.*")){
//Ensure it is not a relative path
if(!path.startsWith("/")){
throw new IllegalArgumentException("Relative paths" + path + "are not allowed.");
int question = uriString.indexOf("?");
if (question > -1) {
uriString = uriString.substring(0,question);
}
path = "file://" + path;
if (uriString.startsWith("file:///android_asset/")) {
Uri uri = Uri.parse(uriString);
String relativePath = uri.getPath().substring(15);
return cordova.getActivity().getAssets().open(relativePath);
} else {
return new FileInputStream(getRealPath(uriString, cordova));
}
} else {
return new FileInputStream(getRealPath(uriString, cordova));
}
return path;
}
/**
@@ -22,8 +22,6 @@ 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;
@@ -45,27 +43,41 @@ 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,
"WebViewClient.shouldInterceptRequest");
url = dataResource.getUri().toString();
// This mechanism is no longer needed due to the dataResource mechanism. It would be awesome to just get rid of it.
//Check if plugins intercept the request
WebResourceResponse ret = super.shouldInterceptRequest(view, url);
if(ret == null) {
try {
InputStream is;
String mimeType;
if((is = dataResource.getInputStream()) != null && (mimeType = dataResource.getMimeType()) != null) {
// If we don't know how to open this file, let the browser continue loading
ret = new WebResourceResponse(mimeType, "UTF-8", is);
}
} catch(IOException e) {
LOG.e("IceCreamCordovaWebViewClient", "Error occurred while loading a file.", e);
}
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);
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);
}
}
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;
}
}
}
@@ -1,3 +1,21 @@
/*
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;
import java.util.ArrayList;
@@ -1,3 +1,21 @@
/*
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 org.json.JSONArray;
@@ -24,7 +24,7 @@ import android.content.Intent;
import java.util.concurrent.ExecutorService;
/**
* The Cordova activity abstract class that is extended by DroidGap.
* The Activity interface that is implemented by CordovaActivity.
* It is used to isolate plugin development, and remove dependency on entire Cordova library.
*/
public interface CordovaInterface {
@@ -175,20 +175,6 @@ public class CordovaPlugin {
return null;
}
/**
* 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 to assist in loading the request or null if it doesn't.
*/
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
public DataResource handleDataResourceRequest(DataResource dataResource, DataResourceContext dataResourceContext) {
return null;
}
/**
* Called when the WebView does a top-level navigation or refreshes.
*
@@ -1,141 +0,0 @@
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 retryIsLoad = true;
private boolean retryOsLoad = true;
private boolean retryMimeTypeLoad = true;
private boolean retryWritableLoad = true;
private boolean retryRealFileLoad = true;
public DataResource(CordovaInterface cordova, Uri uri) {
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 getInputStream() throws IOException {
if(is == null && retryIsLoad) {
try {
is = FileHelper.getInputStreamFromUriString(uri.toString(), cordova);
} finally {
// We failed loading once, don't try loading anymore
if(is == null) {
retryIsLoad = false;
}
}
}
return is;
}
public OutputStream getOutputStream() throws FileNotFoundException {
if(os == null && retryOsLoad) {
try {
os = FileHelper.getOutputStreamFromUriString(uri.toString(), cordova);
} finally {
// We failed loading once, don't try loading anymore
if(os == null) {
retryOsLoad = false;
}
}
}
return os;
}
public String getMimeType() {
if(mimeType == null && retryMimeTypeLoad) {
try {
mimeType = FileHelper.getMimeType(uri.toString(), cordova);
} finally {
// We failed loading once, don't try loading anymore
if(mimeType == null) {
retryMimeTypeLoad = false;
}
}
}
return mimeType;
}
public boolean isWritable() {
if(writable == null && retryWritableLoad) {
try {
writable = FileHelper.isUriWritable(uri.toString());
} finally {
// We failed loading once, don't try loading anymore
if(writable == null) {
retryWritableLoad = false;
}
}
}
// default to false
return writable != null && writable.booleanValue();
}
public File getRealFile() {
if(realFile == null && retryRealFileLoad) {
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) {
retryRealFileLoad = 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));
}
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.handleDataResourceRequestWithPlugins(dataResource, dataResourceContext);
}
return dataResource;
}
}
@@ -1,31 +0,0 @@
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
// 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) {
this.requestId = new Random().nextInt();
this.source = source;
this.dataMap = new HashMap<String, Object>();
}
public int getRequestId() {
return requestId;
}
public String getSource() {
return source;
}
public Map<String, Object> getDataMap() {
return dataMap;
}
}
@@ -54,7 +54,6 @@ public class PluginManager {
// Map URL schemes like foo: to plugins that want to handle those schemes
// This would allow how all URLs are handled to be offloaded to a plugin
protected HashMap<String, String> urlMap = new HashMap<String, String>();
private int MAX_REPITIONS = 1000;
/**
* Constructor.
@@ -401,32 +400,4 @@ 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 handleDataResourceRequestWithPlugins(DataResource dataResource, DataResourceContext dataResourceContext){
int repetitions = 0;
boolean requestModified = true;
while(requestModified && repetitions < MAX_REPITIONS) {
requestModified = false;
repetitions ++;
for (PluginEntry entry : this.entries.values()) {
if (entry.plugin != null) {
DataResource ret = entry.plugin.handleDataResourceRequest(dataResource, dataResourceContext);
if(ret != null) {
dataResource = ret;
requestModified = true;
break;
}
}
}
}
return dataResource;
}
}