You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cordova.apache.org by fi...@apache.org on 2013/01/22 02:57:58 UTC

[36/52] [partial] support for 2.4.0rc1. "vendored" the platform libs in. added Gord and Braden as contributors. removed dependency on unzip and axed the old download-cordova code.

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/d61deccd/lib/cordova-android/framework/src/org/apache/cordova/api/Plugin.java
----------------------------------------------------------------------
diff --git a/lib/cordova-android/framework/src/org/apache/cordova/api/Plugin.java b/lib/cordova-android/framework/src/org/apache/cordova/api/Plugin.java
new file mode 100755
index 0000000..72171f2
--- /dev/null
+++ b/lib/cordova-android/framework/src/org/apache/cordova/api/Plugin.java
@@ -0,0 +1,177 @@
+/*
+       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.apache.cordova.CordovaWebView;
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+/**
+ * Legacy Plugin class. This acts as a shim to support the old execute() signature.
+ * New plugins should extend CordovaPlugin directly.
+ */
+@Deprecated
+public abstract class Plugin extends CordovaPlugin {
+    public LegacyContext    ctx;			        // LegacyContext object
+
+    public abstract PluginResult execute(String action, JSONArray args, String callbackId);
+
+    public boolean isSynch(String action) {
+        return false;
+    }
+    
+    @Override
+    public void initialize(CordovaInterface cordova, CordovaWebView webView) {
+        super.initialize(cordova, webView);
+        this.setContext(cordova);
+        this.setView(webView);
+    }
+
+    /**
+     * Sets the context of the Plugin. This can then be used to do things like
+     * get file paths associated with the Activity.
+     *
+     * @param ctx The context of the main Activity.
+     */
+    public void setContext(CordovaInterface ctx) {
+        this.cordova = ctx;
+        this.ctx = new LegacyContext(cordova);
+    }
+
+    /**
+     * Sets the main View of the application, this is the WebView within which
+     * a Cordova app runs.
+     *
+     * @param webView The Cordova WebView
+     */
+    public void setView(CordovaWebView webView) {
+        this.webView = webView;
+    }
+    
+    @Override
+    public boolean execute(final String action, final JSONArray args, final CallbackContext callbackContext) throws JSONException {
+        final String callbackId = callbackContext.getCallbackId();
+        boolean runAsync = !isSynch(action);
+        if (runAsync) {
+            // Run this on a different thread so that this one can return back to JS
+            cordova.getThreadPool().execute(new Runnable() {
+                public void run() {
+                    PluginResult cr;
+                    try {
+                        cr = execute(action, args, callbackId);
+                    } catch (Throwable e) {
+                        cr = new PluginResult(PluginResult.Status.ERROR, e.getMessage());
+                    }
+                    sendPluginResult(cr, callbackId);
+                }
+            });
+        } else {
+            PluginResult cr = execute(action, args, callbackId);
+    
+            // Interpret a null response as NO_RESULT, which *does* clear the callbacks on the JS side.
+            if (cr == null) {
+                cr = new PluginResult(PluginResult.Status.NO_RESULT);
+            }
+            
+            callbackContext.sendPluginResult(cr);
+        }
+        return true;
+    }
+
+    /**
+     * Send generic JavaScript statement back to JavaScript.
+     * sendPluginResult() should be used instead where possible.
+     */
+    public void sendJavascript(String statement) {
+        this.webView.sendJavascript(statement);
+    }
+
+    /**
+     * Send generic JavaScript statement back to JavaScript.
+     */
+    public void sendPluginResult(PluginResult pluginResult, String callbackId) {
+        this.webView.sendPluginResult(pluginResult, callbackId);
+    }
+
+    /**
+     * Call the JavaScript success callback for this plugin.
+     *
+     * This can be used if the execute code for the plugin is asynchronous meaning
+     * that execute should return null and the callback from the async operation can
+     * call success(...) or error(...)
+     *
+     * @param pluginResult      The result to return.
+     * @param callbackId        The callback id used when calling back into JavaScript.
+     */
+    public void success(PluginResult pluginResult, String callbackId) {
+        this.webView.sendPluginResult(pluginResult, callbackId);
+    }
+
+    /**
+     * Helper for success callbacks that just returns the Status.OK by default
+     *
+     * @param message           The message to add to the success result.
+     * @param callbackId        The callback id used when calling back into JavaScript.
+     */
+    public void success(JSONObject message, String callbackId) {
+        this.webView.sendPluginResult(new PluginResult(PluginResult.Status.OK, message), callbackId);
+    }
+
+    /**
+     * Helper for success callbacks that just returns the Status.OK by default
+     *
+     * @param message           The message to add to the success result.
+     * @param callbackId        The callback id used when calling back into JavaScript.
+     */
+    public void success(String message, String callbackId) {
+        this.webView.sendPluginResult(new PluginResult(PluginResult.Status.OK, message), callbackId);
+    }
+
+    /**
+     * Call the JavaScript error callback for this plugin.
+     *
+     * @param pluginResult      The result to return.
+     * @param callbackId        The callback id used when calling back into JavaScript.
+     */
+    public void error(PluginResult pluginResult, String callbackId) {
+        this.webView.sendPluginResult(pluginResult, callbackId);
+    }
+
+    /**
+     * Helper for error callbacks that just returns the Status.ERROR by default
+     *
+     * @param message           The message to add to the error result.
+     * @param callbackId        The callback id used when calling back into JavaScript.
+     */
+    public void error(JSONObject message, String callbackId) {
+        this.webView.sendPluginResult(new PluginResult(PluginResult.Status.ERROR, message), callbackId);
+    }
+
+    /**
+     * Helper for error callbacks that just returns the Status.ERROR by default
+     *
+     * @param message           The message to add to the error result.
+     * @param callbackId        The callback id used when calling back into JavaScript.
+     */
+    public void error(String message, String callbackId) {
+        this.webView.sendPluginResult(new PluginResult(PluginResult.Status.ERROR, message), callbackId);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/d61deccd/lib/cordova-android/framework/src/org/apache/cordova/api/PluginEntry.java
----------------------------------------------------------------------
diff --git a/lib/cordova-android/framework/src/org/apache/cordova/api/PluginEntry.java b/lib/cordova-android/framework/src/org/apache/cordova/api/PluginEntry.java
new file mode 100755
index 0000000..9b9af6b
--- /dev/null
+++ b/lib/cordova-android/framework/src/org/apache/cordova/api/PluginEntry.java
@@ -0,0 +1,117 @@
+/*
+       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.apache.cordova.CordovaWebView;
+
+//import android.content.Context;
+//import android.webkit.WebView;
+
+/**
+ * This class represents a service entry object.
+ */
+public class PluginEntry {
+
+    /**
+     * The name of the service that this plugin implements
+     */
+    public String service = "";
+
+    /**
+     * The plugin class name that implements the service.
+     */
+    public String pluginClass = "";
+
+    /**
+     * The plugin object.
+     * Plugin objects are only created when they are called from JavaScript.  (see PluginManager.exec)
+     * The exception is if the onload flag is set, then they are created when PluginManager is initialized.
+     */
+    public CordovaPlugin plugin = null;
+
+    /**
+     * Flag that indicates the plugin object should be created when PluginManager is initialized.
+     */
+    public boolean onload = false;
+
+    /**
+     * Constructor
+     *
+     * @param service               The name of the service
+     * @param pluginClass           The plugin class name
+     * @param onload                Create plugin object when HTML page is loaded
+     */
+    public PluginEntry(String service, String pluginClass, boolean onload) {
+        this.service = service;
+        this.pluginClass = pluginClass;
+        this.onload = onload;
+    }
+
+    /**
+     * Create plugin object.
+     * If plugin is already created, then just return it.
+     *
+     * @return                      The plugin object
+     */
+    public CordovaPlugin createPlugin(CordovaWebView webView, CordovaInterface ctx) {
+        if (this.plugin != null) {
+            return this.plugin;
+        }
+        try {
+            @SuppressWarnings("rawtypes")
+            Class c = getClassByName(this.pluginClass);
+            if (isCordovaPlugin(c)) {
+                this.plugin = (CordovaPlugin) c.newInstance();
+                this.plugin.initialize(ctx, webView);
+                return plugin;
+            }
+        } catch (Exception e) {
+            e.printStackTrace();
+            System.out.println("Error adding plugin " + this.pluginClass + ".");
+        }
+        return null;
+    }
+
+    /**
+     * Get the class.
+     *
+     * @param clazz
+     * @return
+     * @throws ClassNotFoundException
+     */
+    @SuppressWarnings("rawtypes")
+    private Class getClassByName(final String clazz) throws ClassNotFoundException {
+        Class c = null;
+        if (clazz != null) {
+            c = Class.forName(clazz);
+        }
+        return c;
+    }
+
+    /**
+     * Returns whether the given class extends CordovaPlugin.
+     */
+    @SuppressWarnings("rawtypes")
+    private boolean isCordovaPlugin(Class c) {
+        if (c != null) {
+            return org.apache.cordova.api.CordovaPlugin.class.isAssignableFrom(c);
+        }
+        return false;
+    }
+}

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/d61deccd/lib/cordova-android/framework/src/org/apache/cordova/api/PluginManager.java
----------------------------------------------------------------------
diff --git a/lib/cordova-android/framework/src/org/apache/cordova/api/PluginManager.java b/lib/cordova-android/framework/src/org/apache/cordova/api/PluginManager.java
new file mode 100755
index 0000000..98bb157
--- /dev/null
+++ b/lib/cordova-android/framework/src/org/apache/cordova/api/PluginManager.java
@@ -0,0 +1,393 @@
+/*
+       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.IOException;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map.Entry;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
+import org.apache.cordova.CordovaWebView;
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.xmlpull.v1.XmlPullParserException;
+
+import android.content.Intent;
+import android.content.res.XmlResourceParser;
+import android.util.Log;
+
+/**
+ * PluginManager is exposed to JavaScript in the Cordova WebView.
+ *
+ * Calling native plugin code can be done by calling PluginManager.exec(...)
+ * from JavaScript.
+ */
+public class PluginManager {
+    private static String TAG = "PluginManager";
+
+    // List of service entries
+    private final HashMap<String, PluginEntry> entries = new HashMap<String, PluginEntry>();
+
+    private final CordovaInterface ctx;
+    private final CordovaWebView app;
+
+    // Flag to track first time through
+    private boolean firstRun;
+
+    // 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>();
+
+    /**
+     * Constructor.
+     *
+     * @param app
+     * @param ctx
+     */
+    public PluginManager(CordovaWebView app, CordovaInterface ctx) {
+        this.ctx = ctx;
+        this.app = app;
+        this.firstRun = true;
+    }
+
+    /**
+     * Init when loading a new HTML page into webview.
+     */
+    public void init() {
+        LOG.d(TAG, "init()");
+
+        // If first time, then load plugins from plugins.xml file
+        if (this.firstRun) {
+            this.loadPlugins();
+            this.firstRun = false;
+        }
+
+        // Stop plugins on current HTML page and discard plugin objects
+        else {
+            this.onPause(false);
+            this.onDestroy();
+            this.clearPluginObjects();
+        }
+
+        // Start up all plugins that have onload specified
+        this.startupPlugins();
+    }
+
+    /**
+     * Load plugins from res/xml/plugins.xml
+     */
+    public void loadPlugins() {
+        int id = this.ctx.getActivity().getResources().getIdentifier("config", "xml", this.ctx.getActivity().getPackageName());
+        if(id == 0)
+        {
+            id = this.ctx.getActivity().getResources().getIdentifier("plugins", "xml", this.ctx.getActivity().getPackageName());
+            LOG.i(TAG, "Using plugins.xml instead of config.xml.  plugins.xml will eventually be deprecated");
+        }
+        if (id == 0) {
+            this.pluginConfigurationMissing();
+            //We have the error, we need to exit without crashing!
+            return;
+        }
+        XmlResourceParser xml = this.ctx.getActivity().getResources().getXml(id);
+        int eventType = -1;
+        String service = "", pluginClass = "", paramType = "";
+        boolean onload = false;
+        PluginEntry entry = null;
+        boolean insideFeature = false;
+        while (eventType != XmlResourceParser.END_DOCUMENT) {
+            if (eventType == XmlResourceParser.START_TAG) {
+                String strNode = xml.getName();
+                //This is for the old scheme
+                if (strNode.equals("plugin")) {
+                    service = xml.getAttributeValue(null, "name");
+                    pluginClass = xml.getAttributeValue(null, "value");
+                    // System.out.println("Plugin: "+name+" => "+value);
+                    onload = "true".equals(xml.getAttributeValue(null, "onload"));
+                    entry = new PluginEntry(service, pluginClass, onload);
+                    this.addService(entry);                   
+                }
+                //What is this?
+                else if (strNode.equals("url-filter")) {
+                    this.urlMap.put(xml.getAttributeValue(null, "value"), service);
+                }
+                else if (strNode.equals("feature")) {
+                    insideFeature = true;
+                    //Check for supported feature sets (Accelerometer, Geolocation, etc)
+                    //Set the bit for reading params
+                    String uri = xml.getAttributeValue(null,"name");
+                }
+                else if(strNode.equals("param")) {
+                    if(insideFeature)
+                    {
+                        paramType = xml.getAttributeValue(null, "name");
+                        if(paramType.equals("service"))
+                            service = xml.getAttributeValue(null, "value");
+                        else if(paramType.equals("package"))
+                            pluginClass = xml.getAttributeValue(null, "value");
+                        if(service.length() > 0  && pluginClass.length() > 0)
+                        {
+                            onload = "true".equals(xml.getAttributeValue(null, "onload"));
+                            entry = new PluginEntry(service, pluginClass, onload);
+                            this.addService(entry);
+                            service = "";
+                            pluginClass = "";
+                        }
+                    }
+                }
+            }
+            else if (eventType == XmlResourceParser.END_TAG)
+            {
+                String strNode = xml.getName();
+                if(strNode.equals("feature"))
+                {
+                    //Empty the strings to prevent plugin loading bugs
+                    service = "";
+                    pluginClass = "";
+                    insideFeature = false;
+                }
+            }
+            try {
+                eventType = xml.next();
+            } catch (XmlPullParserException e) {
+                e.printStackTrace();
+            } catch (IOException e) {
+                e.printStackTrace();
+            }
+        }
+    }
+
+    /**
+     * Delete all plugin objects.
+     */
+    public void clearPluginObjects() {
+        for (PluginEntry entry : this.entries.values()) {
+            entry.plugin = null;
+        }
+    }
+
+    /**
+     * Create plugins objects that have onload set.
+     */
+    public void startupPlugins() {
+        for (PluginEntry entry : this.entries.values()) {
+            if (entry.onload) {
+                entry.createPlugin(this.app, this.ctx);
+            }
+        }
+    }
+
+    /**
+     * Receives a request for execution and fulfills it by finding the appropriate
+     * Java class and calling it's execute method.
+     *
+     * PluginManager.exec can be used either synchronously or async. In either case, a JSON encoded
+     * string is returned that will indicate if any errors have occurred when trying to find
+     * or execute the class denoted by the clazz argument.
+     *
+     * @param service       String containing the service to run
+     * @param action        String containing the action that the class is supposed to perform. This is
+     *                      passed to the plugin execute method and it is up to the plugin developer
+     *                      how to deal with it.
+     * @param callbackId    String containing the id of the callback that is execute in JavaScript if
+     *                      this is an async plugin call.
+     * @param rawArgs       An Array literal string containing any arguments needed in the
+     *                      plugin execute method.
+     * @return Whether the task completed synchronously.
+     */
+    public boolean exec(String service, String action, String callbackId, String rawArgs) {
+        CordovaPlugin plugin = this.getPlugin(service);
+        if (plugin == null) {
+            PluginResult cr = new PluginResult(PluginResult.Status.CLASS_NOT_FOUND_EXCEPTION);
+            app.sendPluginResult(cr, callbackId);
+            return true;
+        }
+        try {
+            CallbackContext callbackContext = new CallbackContext(callbackId, app);
+            boolean wasValidAction = plugin.execute(action, rawArgs, callbackContext);
+            if (!wasValidAction) {
+                PluginResult cr = new PluginResult(PluginResult.Status.INVALID_ACTION);
+                app.sendPluginResult(cr, callbackId);
+                return true;
+            }
+            return callbackContext.isFinished();
+        } catch (JSONException e) {
+            PluginResult cr = new PluginResult(PluginResult.Status.JSON_EXCEPTION);
+            app.sendPluginResult(cr, callbackId);
+            return true;
+        }
+    }
+
+    @Deprecated
+    public boolean exec(String service, String action, String callbackId, String jsonArgs, boolean async) {
+        return exec(service, action, callbackId, jsonArgs);
+    }
+
+    /**
+     * Get the plugin object that implements the service.
+     * If the plugin object does not already exist, then create it.
+     * If the service doesn't exist, then return null.
+     *
+     * @param service       The name of the service.
+     * @return              CordovaPlugin or null
+     */
+    public CordovaPlugin getPlugin(String service) {
+        PluginEntry entry = this.entries.get(service);
+        if (entry == null) {
+            return null;
+        }
+        CordovaPlugin plugin = entry.plugin;
+        if (plugin == null) {
+            plugin = entry.createPlugin(this.app, this.ctx);
+        }
+        return plugin;
+    }
+
+    /**
+     * Add a plugin class that implements a service to the service entry table.
+     * This does not create the plugin object instance.
+     *
+     * @param service           The service name
+     * @param className         The plugin class name
+     */
+    public void addService(String service, String className) {
+        PluginEntry entry = new PluginEntry(service, className, false);
+        this.addService(entry);
+    }
+
+    /**
+     * Add a plugin class that implements a service to the service entry table.
+     * This does not create the plugin object instance.
+     *
+     * @param entry             The plugin entry
+     */
+    public void addService(PluginEntry entry) {
+        this.entries.put(entry.service, entry);
+    }
+
+    /**
+     * Called when the system is about to start resuming a previous activity.
+     *
+     * @param multitasking      Flag indicating if multitasking is turned on for app
+     */
+    public void onPause(boolean multitasking) {
+        for (PluginEntry entry : this.entries.values()) {
+            if (entry.plugin != null) {
+                entry.plugin.onPause(multitasking);
+            }
+        }
+    }
+
+    /**
+     * Called when the activity will start interacting with the user.
+     *
+     * @param multitasking      Flag indicating if multitasking is turned on for app
+     */
+    public void onResume(boolean multitasking) {
+        for (PluginEntry entry : this.entries.values()) {
+            if (entry.plugin != null) {
+                entry.plugin.onResume(multitasking);
+            }
+        }
+    }
+
+    /**
+     * The final call you receive before your activity is destroyed.
+     */
+    public void onDestroy() {
+        for (PluginEntry entry : this.entries.values()) {
+            if (entry.plugin != null) {
+                entry.plugin.onDestroy();
+            }
+        }
+    }
+
+    /**
+     * Send a message to all plugins.
+     *
+     * @param id                The message id
+     * @param data              The message data
+     * @return
+     */
+    public Object postMessage(String id, Object data) {
+        Object obj = this.ctx.onMessage(id, data);
+        if (obj != null) {
+            return obj;
+        }
+        for (PluginEntry entry : this.entries.values()) {
+            if (entry.plugin != null) {
+                obj = entry.plugin.onMessage(id, data);
+                if (obj != null) {
+                    return obj;
+                }
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Called when the activity receives a new intent.
+     */
+    public void onNewIntent(Intent intent) {
+        for (PluginEntry entry : this.entries.values()) {
+            if (entry.plugin != null) {
+                entry.plugin.onNewIntent(intent);
+            }
+        }
+    }
+
+    /**
+     * Called when the URL of the webview changes.
+     *
+     * @param url               The URL that is being changed to.
+     * @return                  Return false to allow the URL to load, return true to prevent the URL from loading.
+     */
+    public boolean onOverrideUrlLoading(String url) {
+        Iterator<Entry<String, String>> it = this.urlMap.entrySet().iterator();
+        while (it.hasNext()) {
+            HashMap.Entry<String, String> pairs = it.next();
+            if (url.startsWith(pairs.getKey())) {
+                return this.getPlugin(pairs.getValue()).onOverrideUrlLoading(url);
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Called when the app navigates or refreshes.
+     */
+    public void onReset() {
+        Iterator<PluginEntry> it = this.entries.values().iterator();
+        while (it.hasNext()) {
+            CordovaPlugin plugin = it.next().plugin;
+            if (plugin != null) {
+                plugin.onReset();
+            }
+        }
+    }
+
+
+    private void pluginConfigurationMissing() {
+        LOG.e(TAG, "=====================================================================================");
+        LOG.e(TAG, "ERROR: plugin.xml is missing.  Add res/xml/plugins.xml to your project.");
+        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, "=====================================================================================");
+    }
+}

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/d61deccd/lib/cordova-android/framework/src/org/apache/cordova/api/PluginResult.java
----------------------------------------------------------------------
diff --git a/lib/cordova-android/framework/src/org/apache/cordova/api/PluginResult.java b/lib/cordova-android/framework/src/org/apache/cordova/api/PluginResult.java
new file mode 100755
index 0000000..4c1d833
--- /dev/null
+++ b/lib/cordova-android/framework/src/org/apache/cordova/api/PluginResult.java
@@ -0,0 +1,172 @@
+/*
+       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;
+import org.json.JSONObject;
+
+import android.util.Base64;
+
+public class PluginResult {
+    private final int status;
+    private final int messageType;
+    private boolean keepCallback = false;
+    private String strMessage;
+    private String encodedMessage;
+
+    public PluginResult(Status status) {
+        this(status, PluginResult.StatusMessages[status.ordinal()]);
+    }
+
+    public PluginResult(Status status, String message) {
+        this.status = status.ordinal();
+        this.messageType = message == null ? MESSAGE_TYPE_NULL : MESSAGE_TYPE_STRING;
+        this.strMessage = message;
+    }
+
+    public PluginResult(Status status, JSONArray message) {
+        this.status = status.ordinal();
+        this.messageType = MESSAGE_TYPE_JSON;
+        encodedMessage = message.toString();
+    }
+
+    public PluginResult(Status status, JSONObject message) {
+        this.status = status.ordinal();
+        this.messageType = MESSAGE_TYPE_JSON;
+        encodedMessage = message.toString();
+    }
+
+    public PluginResult(Status status, int i) {
+        this.status = status.ordinal();
+        this.messageType = MESSAGE_TYPE_NUMBER;
+        this.encodedMessage = ""+i;
+    }
+
+    public PluginResult(Status status, float f) {
+        this.status = status.ordinal();
+        this.messageType = MESSAGE_TYPE_NUMBER;
+        this.encodedMessage = ""+f;
+    }
+
+    public PluginResult(Status status, boolean b) {
+        this.status = status.ordinal();
+        this.messageType = MESSAGE_TYPE_BOOLEAN;
+        this.encodedMessage = Boolean.toString(b);
+    }
+
+    public PluginResult(Status status, byte[] data) {
+        this.status = status.ordinal();
+        this.messageType = MESSAGE_TYPE_ARRAYBUFFER;
+        this.encodedMessage = Base64.encodeToString(data, Base64.NO_WRAP);
+    }
+
+    public void setKeepCallback(boolean b) {
+        this.keepCallback = b;
+    }
+
+    public int getStatus() {
+        return status;
+    }
+
+    public int getMessageType() {
+        return messageType;
+    }
+
+    public String getMessage() {
+        if (encodedMessage == null) {
+            encodedMessage = JSONObject.quote(strMessage);
+        }
+        return encodedMessage;
+    }
+
+    /**
+     * If messageType == MESSAGE_TYPE_STRING, then returns the message string.
+     * Otherwise, returns null.
+     */
+    public String getStrMessage() {
+        return strMessage;
+    }
+
+    public boolean getKeepCallback() {
+        return this.keepCallback;
+    }
+
+    @Deprecated // Use sendPluginResult instead of sendJavascript.
+    public String getJSONString() {
+        return "{\"status\":" + this.status + ",\"message\":" + this.getMessage() + ",\"keepCallback\":" + this.keepCallback + "}";
+    }
+
+    @Deprecated // Use sendPluginResult instead of sendJavascript.
+    public String toCallbackString(String callbackId) {
+        // If no result to be sent and keeping callback, then no need to sent back to JavaScript
+        if ((status == PluginResult.Status.NO_RESULT.ordinal()) && keepCallback) {
+        	return null;
+        }
+
+        // Check the success (OK, NO_RESULT & !KEEP_CALLBACK)
+        if ((status == PluginResult.Status.OK.ordinal()) || (status == PluginResult.Status.NO_RESULT.ordinal())) {
+            return toSuccessCallbackString(callbackId);
+        }
+
+        return toErrorCallbackString(callbackId);
+    }
+
+    @Deprecated // Use sendPluginResult instead of sendJavascript.
+    public String toSuccessCallbackString(String callbackId) {
+        return "cordova.callbackSuccess('"+callbackId+"',"+this.getJSONString()+");";
+    }
+
+    @Deprecated // Use sendPluginResult instead of sendJavascript.
+    public String toErrorCallbackString(String callbackId) {
+        return "cordova.callbackError('"+callbackId+"', " + this.getJSONString()+ ");";
+    }
+
+    public static final int MESSAGE_TYPE_STRING = 1;
+    public static final int MESSAGE_TYPE_JSON = 2;
+    public static final int MESSAGE_TYPE_NUMBER = 3;
+    public static final int MESSAGE_TYPE_BOOLEAN = 4;
+    public static final int MESSAGE_TYPE_NULL = 5;
+    public static final int MESSAGE_TYPE_ARRAYBUFFER = 6;
+
+    public static String[] StatusMessages = new String[] {
+        "No result",
+        "OK",
+        "Class not found",
+        "Illegal access",
+        "Instantiation error",
+        "Malformed url",
+        "IO error",
+        "Invalid action",
+        "JSON error",
+        "Error"
+    };
+
+    public enum Status {
+        NO_RESULT,
+        OK,
+        CLASS_NOT_FOUND_EXCEPTION,
+        ILLEGAL_ACCESS_EXCEPTION,
+        INSTANTIATION_EXCEPTION,
+        MALFORMED_URL_EXCEPTION,
+        IO_EXCEPTION,
+        INVALID_ACTION,
+        JSON_EXCEPTION,
+        ERROR
+    }
+}

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/d61deccd/lib/cordova-android/framework/src/org/apache/cordova/file/EncodingException.java
----------------------------------------------------------------------
diff --git a/lib/cordova-android/framework/src/org/apache/cordova/file/EncodingException.java b/lib/cordova-android/framework/src/org/apache/cordova/file/EncodingException.java
new file mode 100644
index 0000000..a32e18e
--- /dev/null
+++ b/lib/cordova-android/framework/src/org/apache/cordova/file/EncodingException.java
@@ -0,0 +1,28 @@
+/*
+       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.file;
+
+public class EncodingException extends Exception {
+
+    public EncodingException(String message) {
+        super(message);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/d61deccd/lib/cordova-android/framework/src/org/apache/cordova/file/FileExistsException.java
----------------------------------------------------------------------
diff --git a/lib/cordova-android/framework/src/org/apache/cordova/file/FileExistsException.java b/lib/cordova-android/framework/src/org/apache/cordova/file/FileExistsException.java
new file mode 100644
index 0000000..18aa7ea
--- /dev/null
+++ b/lib/cordova-android/framework/src/org/apache/cordova/file/FileExistsException.java
@@ -0,0 +1,28 @@
+/*
+       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.file;
+
+public class FileExistsException extends Exception {
+
+    public FileExistsException(String msg) {
+        super(msg);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/d61deccd/lib/cordova-android/framework/src/org/apache/cordova/file/InvalidModificationException.java
----------------------------------------------------------------------
diff --git a/lib/cordova-android/framework/src/org/apache/cordova/file/InvalidModificationException.java b/lib/cordova-android/framework/src/org/apache/cordova/file/InvalidModificationException.java
new file mode 100644
index 0000000..aebab4d
--- /dev/null
+++ b/lib/cordova-android/framework/src/org/apache/cordova/file/InvalidModificationException.java
@@ -0,0 +1,29 @@
+/*
+       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.file;
+
+public class InvalidModificationException extends Exception {
+
+    public InvalidModificationException(String message) {
+        super(message);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/d61deccd/lib/cordova-android/framework/src/org/apache/cordova/file/NoModificationAllowedException.java
----------------------------------------------------------------------
diff --git a/lib/cordova-android/framework/src/org/apache/cordova/file/NoModificationAllowedException.java b/lib/cordova-android/framework/src/org/apache/cordova/file/NoModificationAllowedException.java
new file mode 100644
index 0000000..8cae115
--- /dev/null
+++ b/lib/cordova-android/framework/src/org/apache/cordova/file/NoModificationAllowedException.java
@@ -0,0 +1,28 @@
+/*
+       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.file;
+
+public class NoModificationAllowedException extends Exception {
+
+    public NoModificationAllowedException(String message) {
+        super(message);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/d61deccd/lib/cordova-android/framework/src/org/apache/cordova/file/TypeMismatchException.java
----------------------------------------------------------------------
diff --git a/lib/cordova-android/framework/src/org/apache/cordova/file/TypeMismatchException.java b/lib/cordova-android/framework/src/org/apache/cordova/file/TypeMismatchException.java
new file mode 100644
index 0000000..0ea5993
--- /dev/null
+++ b/lib/cordova-android/framework/src/org/apache/cordova/file/TypeMismatchException.java
@@ -0,0 +1,29 @@
+/*
+       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.file;
+
+public class TypeMismatchException extends Exception {
+
+    public TypeMismatchException(String message) {
+        super(message);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/d61deccd/lib/cordova-android/framework/test/org/apache/cordova/PreferenceNodeTest.java
----------------------------------------------------------------------
diff --git a/lib/cordova-android/framework/test/org/apache/cordova/PreferenceNodeTest.java b/lib/cordova-android/framework/test/org/apache/cordova/PreferenceNodeTest.java
new file mode 100644
index 0000000..0dea62a
--- /dev/null
+++ b/lib/cordova-android/framework/test/org/apache/cordova/PreferenceNodeTest.java
@@ -0,0 +1,53 @@
+/*
+       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.
+*/
+import org.junit.*;
+import static org.junit.Assert.*;
+
+import org.apache.cordova.PreferenceNode;
+
+public class PreferenceNodeTest {
+    @Test
+        public void testConstructor() {
+            PreferenceNode foo = new org.apache.cordova.PreferenceNode("fullscreen", "false", false);
+            assertEquals("fullscreen", foo.name);
+            assertEquals("false", foo.value);
+            assertEquals(false, foo.readonly);
+        }
+
+    @Test
+        public void testNameAssignment() {
+            PreferenceNode foo = new org.apache.cordova.PreferenceNode("fullscreen", "false", false);
+            foo.name = "widescreen";
+            assertEquals("widescreen", foo.name);
+        }
+
+    @Test
+        public void testValueAssignment() {
+            PreferenceNode foo = new org.apache.cordova.PreferenceNode("fullscreen", "false", false);
+            foo.value = "maybe";
+            assertEquals("maybe", foo.value);
+        }
+
+    @Test
+        public void testReadonlyAssignment() {
+            PreferenceNode foo = new org.apache.cordova.PreferenceNode("fullscreen", "false", false);
+            foo.readonly = true;
+            assertEquals(true, foo.readonly);
+        }
+}

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/d61deccd/lib/cordova-android/framework/test/org/apache/cordova/PreferenceSetTest.java
----------------------------------------------------------------------
diff --git a/lib/cordova-android/framework/test/org/apache/cordova/PreferenceSetTest.java b/lib/cordova-android/framework/test/org/apache/cordova/PreferenceSetTest.java
new file mode 100644
index 0000000..ea915f9
--- /dev/null
+++ b/lib/cordova-android/framework/test/org/apache/cordova/PreferenceSetTest.java
@@ -0,0 +1,73 @@
+/*
+       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.
+*/
+import org.junit.*;
+import static org.junit.Assert.*;
+
+import org.apache.cordova.PreferenceNode;
+import org.apache.cordova.PreferenceSet;
+
+public class PreferenceSetTest {
+    private PreferenceSet preferences;
+    private PreferenceNode screen;
+
+    @Before
+        public void setUp() {
+            preferences = new PreferenceSet();
+            screen = new PreferenceNode("fullscreen", "true", false);
+        }
+
+    @Test
+        public void testAddition() {
+            preferences.add(screen);
+            assertEquals(1, preferences.size());
+        }
+
+    @Test
+        public void testClear() {
+            preferences.add(screen);
+            preferences.clear();
+            assertEquals(0, preferences.size());
+        }
+
+    @Test
+        public void testPreferenceRetrieval() {
+            preferences.add(screen);
+            assertEquals("true", preferences.pref("fullscreen"));
+        }
+
+    @Test
+        public void testNoPreferenceRetrieval() {
+            // return null if the preference is not defined
+            assertEquals(null, preferences.pref("antigravity"));
+        }
+
+    @Test
+        public void testUnsetPreferenceChecking() {
+            PreferenceSet emptySet = new PreferenceSet();
+            boolean value = emptySet.prefMatches("fullscreen", "true");
+            assertEquals(false, value);
+        }
+
+    @Test
+        public void testSetPreferenceChecking() {
+            preferences.add(screen);
+            boolean value = preferences.prefMatches("fullscreen", "true");
+            assertEquals(true, value);
+        }
+}

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/d61deccd/lib/cordova-android/test/.classpath
----------------------------------------------------------------------
diff --git a/lib/cordova-android/test/.classpath b/lib/cordova-android/test/.classpath
new file mode 100644
index 0000000..a4763d1
--- /dev/null
+++ b/lib/cordova-android/test/.classpath
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+	<classpathentry kind="src" path="src"/>
+	<classpathentry kind="src" path="gen"/>
+	<classpathentry kind="con" path="com.android.ide.eclipse.adt.ANDROID_FRAMEWORK"/>
+	<classpathentry kind="con" path="com.android.ide.eclipse.adt.LIBRARIES"/>
+	<classpathentry kind="output" path="bin/classes"/>
+</classpath>

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/d61deccd/lib/cordova-android/test/.project
----------------------------------------------------------------------
diff --git a/lib/cordova-android/test/.project b/lib/cordova-android/test/.project
new file mode 100644
index 0000000..7bacb6f
--- /dev/null
+++ b/lib/cordova-android/test/.project
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+	<name>CordovaViewTestActivity</name>
+	<comment></comment>
+	<projects>
+	</projects>
+	<buildSpec>
+		<buildCommand>
+			<name>com.android.ide.eclipse.adt.ResourceManagerBuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+		<buildCommand>
+			<name>com.android.ide.eclipse.adt.PreCompilerBuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+		<buildCommand>
+			<name>org.eclipse.jdt.core.javabuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+		<buildCommand>
+			<name>com.android.ide.eclipse.adt.ApkBuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+	</buildSpec>
+	<natures>
+		<nature>com.android.ide.eclipse.adt.AndroidNature</nature>
+		<nature>org.eclipse.jdt.core.javanature</nature>
+	</natures>
+</projectDescription>

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/d61deccd/lib/cordova-android/test/AndroidManifest.xml
----------------------------------------------------------------------
diff --git a/lib/cordova-android/test/AndroidManifest.xml b/lib/cordova-android/test/AndroidManifest.xml
new file mode 100755
index 0000000..aee2e04
--- /dev/null
+++ b/lib/cordova-android/test/AndroidManifest.xml
@@ -0,0 +1,269 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 
+       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.
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:windowSoftInputMode="adjustPan"
+      package="org.apache.cordova.test" android:versionName="1.1" android:versionCode="5">
+    <supports-screens
+        android:largeScreens="true"
+        android:normalScreens="true"
+        android:smallScreens="true"
+        android:xlargeScreens="true"
+        android:resizeable="true"
+        android:anyDensity="true"
+        />
+
+    <uses-permission android:name="android.permission.VIBRATE" />
+    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
+    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
+    <uses-permission android:name="android.permission.ACCESS_LOCATION_EXTRA_COMMANDS" />
+    <uses-permission android:name="android.permission.INTERNET" />
+    <uses-permission android:name="android.permission.READ_PHONE_STATE" />
+    <uses-permission android:name="android.permission.RECEIVE_SMS" />
+    <uses-permission android:name="android.permission.RECORD_AUDIO" />
+    <uses-permission android:name="android.permission.RECORD_VIDEO"/>
+    <uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
+    <uses-permission android:name="android.permission.READ_CONTACTS" />
+    <uses-permission android:name="android.permission.WRITE_CONTACTS" />   
+    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />   
+    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
+    <uses-permission android:name="android.permission.GET_ACCOUNTS" />
+    <uses-permission android:name="android.permission.BROADCAST_STICKY" />
+   
+    <uses-sdk android:minSdkVersion="7" />
+
+    <instrumentation
+        android:name="android.test.InstrumentationTestRunner"
+        android:targetPackage="org.apache.cordova.test" />
+
+    <application
+        android:icon="@drawable/icon"
+        android:label="@string/app_name" >
+        <uses-library android:name="android.test.runner" />
+        <activity
+            android:windowSoftInputMode="adjustPan"
+            android:label="@string/app_name" 
+            android:configChanges="orientation|keyboardHidden"
+            android:name=".actions.CordovaWebViewTestActivity" >
+            <intent-filter >
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+        <activity
+            android:windowSoftInputMode="adjustPan"
+            android:label="@string/app_name" 
+            android:configChanges="orientation|keyboardHidden"
+            android:name=".actions.backbbuttonmultipage" >
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.SAMPLE_CODE" />
+            </intent-filter>
+        </activity>
+        <activity
+            android:windowSoftInputMode="adjustPan"
+            android:label="@string/app_name" 
+            android:configChanges="orientation|keyboardHidden"
+            android:name=".actions.background" >
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.SAMPLE_CODE" />
+            </intent-filter>
+        </activity>
+        <activity
+            android:windowSoftInputMode="adjustPan"
+            android:label="@string/app_name" 
+            android:configChanges="orientation|keyboardHidden"
+            android:name=".actions.basicauth" >
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.SAMPLE_CODE" />
+            </intent-filter>
+        </activity>
+        <activity
+            android:windowSoftInputMode="adjustPan"
+            android:label="@string/app_name" 
+            android:configChanges="orientation|keyboardHidden"
+            android:name=".actions.CordovaActivity" >
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.SAMPLE_CODE" />
+            </intent-filter>
+        </activity>
+        <activity
+            android:windowSoftInputMode="adjustPan"
+            android:label="@string/app_name" 
+            android:configChanges="orientation|keyboardHidden"
+            android:name=".actions.CordovaDriverAction" >
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.SAMPLE_CODE" />
+            </intent-filter>
+        </activity>
+        <activity
+            android:windowSoftInputMode="adjustPan"
+            android:label="@string/app_name" 
+            android:configChanges="orientation|keyboardHidden"
+            android:name=".actions.errorurl" >
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.SAMPLE_CODE" />
+            </intent-filter>
+        </activity>
+        <activity
+            android:windowSoftInputMode="adjustPan"
+            android:label="@string/app_name" 
+            android:configChanges="orientation|keyboardHidden"
+            android:name=".actions.fullscreen" >
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.SAMPLE_CODE" />
+            </intent-filter>
+        </activity>
+        <activity
+            android:windowSoftInputMode="adjustPan"
+            android:label="@string/app_name" 
+            android:configChanges="orientation|keyboardHidden"
+            android:name=".actions.htmlnotfound" >
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.SAMPLE_CODE" />
+            </intent-filter>
+        </activity>
+        <activity
+            android:windowSoftInputMode="adjustPan"
+            android:label="@string/app_name" 
+            android:configChanges="orientation|keyboardHidden"
+            android:name=".actions.iframe" >
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.SAMPLE_CODE" />
+            </intent-filter>
+        </activity>
+        <activity
+            android:windowSoftInputMode="adjustPan"
+            android:label="@string/app_name" 
+            android:configChanges="orientation|keyboardHidden"
+            android:name=".actions.jqmtabbackbutton" >
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.SAMPLE_CODE" />
+            </intent-filter>
+        </activity>
+        <activity
+            android:windowSoftInputMode="adjustPan"
+            android:label="@string/app_name" 
+            android:configChanges="orientation|keyboardHidden"
+            android:name=".actions.lifecycle" >
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.SAMPLE_CODE" />
+            </intent-filter>
+        </activity>
+        <activity
+            android:windowSoftInputMode="adjustPan"
+            android:label="@string/app_name" 
+            android:configChanges="orientation|keyboardHidden"
+            android:name=".actions.loading" >
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.SAMPLE_CODE" />
+            </intent-filter>
+        </activity>
+        <activity
+            android:windowSoftInputMode="adjustPan"
+            android:label="@string/app_name" 
+            android:configChanges="orientation|keyboardHidden"
+            android:name=".actions.menus" >
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.SAMPLE_CODE" />
+            </intent-filter>
+        </activity>
+        <activity
+            android:windowSoftInputMode="adjustPan"
+            android:label="@string/app_name" 
+            android:configChanges="orientation|keyboardHidden"
+            android:name=".actions.splashscreen" >
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.SAMPLE_CODE" />
+            </intent-filter>
+        </activity>
+        <activity
+            android:windowSoftInputMode="adjustPan"
+            android:label="@string/app_name" 
+            android:configChanges="orientation|keyboardHidden"
+            android:name=".actions.tests" >
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.SAMPLE_CODE" />
+            </intent-filter>
+        </activity>
+        <activity
+            android:windowSoftInputMode="adjustPan"
+            android:label="@string/app_name" 
+            android:configChanges="orientation|keyboardHidden"
+            android:name=".actions.timeout" >
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.SAMPLE_CODE" />
+            </intent-filter>
+        </activity>
+        <activity
+            android:windowSoftInputMode="adjustPan"
+            android:label="@string/app_name" 
+            android:configChanges="orientation|keyboardHidden"
+            android:name=".actions.userwebview" >
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.SAMPLE_CODE" />
+            </intent-filter>
+        </activity>
+        <activity
+            android:windowSoftInputMode="adjustPan"
+            android:label="@string/app_name" 
+            android:configChanges="orientation|keyboardHidden"
+            android:name=".actions.whitelist" >
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.SAMPLE_CODE" />
+            </intent-filter>
+        </activity>
+        <activity
+            android:windowSoftInputMode="adjustPan"
+            android:label="@string/app_name" 
+            android:configChanges="orientation|keyboardHidden"
+            android:name=".actions.xhr" >
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.SAMPLE_CODE" />
+            </intent-filter>
+        </activity>
+        <activity
+            android:windowSoftInputMode="adjustPan"
+            android:label="@string/app_name" 
+            android:configChanges="orientation|keyboardHidden"
+            android:name=".actions.backbuttonmultipage" >
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.SAMPLE_CODE" />
+            </intent-filter>
+        </activity>
+        </application>
+</manifest> 

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/d61deccd/lib/cordova-android/test/README.md
----------------------------------------------------------------------
diff --git a/lib/cordova-android/test/README.md b/lib/cordova-android/test/README.md
new file mode 100755
index 0000000..79cf85f
--- /dev/null
+++ b/lib/cordova-android/test/README.md
@@ -0,0 +1,30 @@
+# Android Native Tests #
+
+These tests are designed to verify Android native features and other Android specific features.
+
+## Initial Setup ##
+
+Before running the tests, they need to be set up.
+
+0. Copy cordova-x.y.z.jar into libs directory
+
+To run from command line:
+
+0. Build by entering `ant debug install`
+0. Run tests by clicking on "CordovaTest" icon on device
+
+To run from Eclipse:
+
+0. Import Android project into Eclipse
+0. Ensure Project properties "Java Build Path" includes the lib/cordova-x.y.z.jar
+0. Create run configuration if not already created
+0. Run tests 
+
+## Automatic Runs ##
+
+Once you have installed the test, you can launch and run the tests
+automatically with the below command:
+
+    adb shell am instrument -w org.apache.cordova.test/android.test.InstrumentationTestRunner
+
+(Optionally, you can also run in Eclipse)

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/d61deccd/lib/cordova-android/test/ant.properties
----------------------------------------------------------------------
diff --git a/lib/cordova-android/test/ant.properties b/lib/cordova-android/test/ant.properties
new file mode 100755
index 0000000..ee52d86
--- /dev/null
+++ b/lib/cordova-android/test/ant.properties
@@ -0,0 +1,17 @@
+# This file is used to override default values used by the Ant build system.
+#
+# This file must be checked in Version Control Systems, as it is
+# integral to the build system of your project.
+
+# This file is only used by the Ant script.
+
+# You can use this to override default values such as
+#  'source.dir' for the location of your java source folder and
+#  'out.dir' for the location of your output folder.
+
+# You can also use it define how the release builds are signed by declaring
+# the following properties:
+#  'key.store' for the location of your keystore and
+#  'key.alias' for the name of the key to use.
+# The password will be asked during the build when you use the 'release' target.
+

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/d61deccd/lib/cordova-android/test/assets/www/backbuttonmultipage/index.html
----------------------------------------------------------------------
diff --git a/lib/cordova-android/test/assets/www/backbuttonmultipage/index.html b/lib/cordova-android/test/assets/www/backbuttonmultipage/index.html
new file mode 100755
index 0000000..afab731
--- /dev/null
+++ b/lib/cordova-android/test/assets/www/backbuttonmultipage/index.html
@@ -0,0 +1,41 @@
+<!--
+         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.$
+-->
+<html> 
+<head> 
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> 
+<title>Backbutton</title> 
+<link rel="stylesheet" href="../master.css" type="text/css" media="screen" title="no title">
+<script type="text/javascript" charset="utf-8" src="../cordova.js"></script>
+<script type="text/javascript" charset="utf-8" src="../main.js"></script>
+
+<body onload="init();" id="stage" class="theme">
+    <h1>Cordova Android Tests</h1>
+    <div id="info">
+        <h4>Platform: <span id="platform"> &nbsp;</span>,   Version: <span id="version">&nbsp;</span></h4>
+        <h4>UUID: <span id="uuid"> &nbsp;</span>,   Name: <span id="name">&nbsp;</span></h4>
+        <h4>Width: <span id="width"> &nbsp;</span>,   Height: <span id="height">&nbsp;</span>, Color Depth: <span id="colorDepth"></span></h4>
+    </div>
+    <div id="info">
+        <h4>Page 1</h4>
+        Go to next page.<br>
+        If returning from previous page, press "backbutton".  You should exit this app.
+    </div>
+    <a href="sample2.html" class="btn large">Next page</a>
+</body> 
+</html>

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/d61deccd/lib/cordova-android/test/assets/www/backbuttonmultipage/sample2.html
----------------------------------------------------------------------
diff --git a/lib/cordova-android/test/assets/www/backbuttonmultipage/sample2.html b/lib/cordova-android/test/assets/www/backbuttonmultipage/sample2.html
new file mode 100755
index 0000000..397ac70
--- /dev/null
+++ b/lib/cordova-android/test/assets/www/backbuttonmultipage/sample2.html
@@ -0,0 +1,41 @@
+<!--
+         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.$
+-->
+<html> 
+<head> 
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> 
+<title>Backbutton</title> 
+<link rel="stylesheet" href="../master.css" type="text/css" media="screen" title="no title">
+<script type="text/javascript" charset="utf-8" src="../cordova.js"></script>
+<script type="text/javascript" charset="utf-8" src="../main.js"></script>
+
+<body onload="init();" id="stage" class="theme">
+    <h1>Cordova Android Tests</h1>
+    <div id="info">
+        <h4>Platform: <span id="platform"> &nbsp;</span>,   Version: <span id="version">&nbsp;</span></h4>
+        <h4>UUID: <span id="uuid"> &nbsp;</span>,   Name: <span id="name">&nbsp;</span></h4>
+        <h4>Width: <span id="width"> &nbsp;</span>,   Height: <span id="height">&nbsp;</span>, Color Depth: <span id="colorDepth"></span></h4>
+    </div>
+    <div id="info">
+        <h4>Page 2</h4>
+        Go to next page.<br>
+        If returning from previous page, press "backbutton".  You should go to Page 1.
+    </div>
+    <a href="sample3.html" class="btn large">Next page</a>
+</body> 
+</html>

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/d61deccd/lib/cordova-android/test/assets/www/backbuttonmultipage/sample3.html
----------------------------------------------------------------------
diff --git a/lib/cordova-android/test/assets/www/backbuttonmultipage/sample3.html b/lib/cordova-android/test/assets/www/backbuttonmultipage/sample3.html
new file mode 100755
index 0000000..f4b1f8a
--- /dev/null
+++ b/lib/cordova-android/test/assets/www/backbuttonmultipage/sample3.html
@@ -0,0 +1,43 @@
+<!--
+         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.$
+-->
+<html> 
+<head> 
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> 
+<title>Backbutton</title> 
+<link rel="stylesheet" href="../master.css" type="text/css" media="screen" title="no title">
+<script type="text/javascript" charset="utf-8" src="../cordova.js"></script>
+<script type="text/javascript" charset="utf-8" src="../main.js"></script>
+
+<body onload="init();" id="stage" class="theme">
+    <h1>Cordova Android Tests</h1>
+    <div id="info">
+        <h4>Platform: <span id="platform"> &nbsp;</span>,   Version: <span id="version">&nbsp;</span></h4>
+        <h4>UUID: <span id="uuid"> &nbsp;</span>,   Name: <span id="name">&nbsp;</span></h4>
+        <h4>Width: <span id="width"> &nbsp;</span>,   Height: <span id="height">&nbsp;</span>, Color Depth: <span id="colorDepth"></span></h4>
+    </div>
+    <div id="info">
+        <h4>Page 3</h4>
+        Press the 3 buttons below.  You should stay on same page.<br>
+        Press "backbutton" 4 times.  This will go back to #test3, #test2, #test1, then return to previous Page 2.<br>
+    </div>
+    <a href="sample3.html#test1" class="btn large">page3#test1</a>
+    <a href="sample3.html#test2" class="btn large">page3#test2</a>
+    <a href="sample3.html#test3" class="btn large">page3#test3</a>
+</body> 
+</html>

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/d61deccd/lib/cordova-android/test/assets/www/background/index.html
----------------------------------------------------------------------
diff --git a/lib/cordova-android/test/assets/www/background/index.html b/lib/cordova-android/test/assets/www/background/index.html
new file mode 100755
index 0000000..2a073a6
--- /dev/null
+++ b/lib/cordova-android/test/assets/www/background/index.html
@@ -0,0 +1,117 @@
+<!DOCTYPE HTML>
+<!--
+         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.$
+-->
+<html>
+<head>
+<head>
+<meta http-equiv="Content-type" content="text/html; charset=utf-8">
+<title>Background Page 1</title>
+<link rel="stylesheet" href="../master.css" type="text/css" media="screen" title="no title">
+<script type="text/javascript" charset="utf-8" src="../cordova.js"></script>
+
+<script type="text/javascript" charset="utf-8">
+
+	function onLoad() {
+		console.log("Page1: onload");
+		log("Page1: onload @ " + new Date().toLocaleTimeString());
+		document.addEventListener("deviceready", onDeviceReady, false);
+	}
+
+	function onUnLoaded() {
+		console.log("Page1: onunload");
+		log("Page1: onunload @ " + new Date().toLocaleTimeString());
+	}
+
+	function onDeviceReady() {
+		// Register the event listener
+		document.getElementById("platform").innerHTML = device.platform;
+		document.getElementById("version").innerHTML = device.version;
+		document.getElementById("uuid").innerHTML = device.uuid;
+		document.getElementById("name").innerHTML = device.name;
+		document.getElementById("width").innerHTML = screen.width;
+		document.getElementById("height").innerHTML = screen.height;
+		document.getElementById("colorDepth").innerHTML = screen.colorDepth;
+
+		document.addEventListener("pause", onPause, false);
+		document.addEventListener("resume", onResume, false);
+		
+		window.setInterval(function() {
+			log("Page1: Running");
+		}, 2000);
+	}
+
+	function onPause() {
+		console.log("Page1: onpause");
+		log("Page1: onpause @ " + new Date().toLocaleTimeString());
+	}
+
+	function onResume() {
+		console.log("Page1: onresume");
+		log("Page1: onresume @ " + new Date().toLocaleTimeString());
+	}
+
+	function log(s) {
+		var el = document.getElementById('status');
+		var status = el.innerHTML + s + "<br>";
+		el.innerHTML = status;
+		localStorage.backgroundstatus = status;
+	}
+	
+	function clearStatus() {
+		console.log("clear()");
+		localStorage.backgroundstatus = "";
+		document.getElementById('status').innerHTML = "";
+	}
+		
+</script>
+</head>
+<body onload="onLoad()" onunload="onUnLoaded()"  id="stage" class="theme">
+	<h1>Events</h1>
+	<div id="info">
+		<h4>
+			Platform: <span id="platform"> &nbsp;</span>, Version: <span
+				id="version">&nbsp;</span>
+		</h4>
+		<h4>
+			UUID: <span id="uuid"> &nbsp;</span>, Name: <span id="name">&nbsp;</span>
+		</h4>
+		<h4>
+			Width: <span id="width"> &nbsp;</span>, Height: <span id="height">&nbsp;
+			</span>, Color Depth: <span id="colorDepth"></span>
+		</h4>
+	</div>
+	<div id="info">
+	   Press "Home" button, then return to this app to see pause/resume.<br>
+	   There shouldn't be any "Running" entries between pause and resume.<br>
+	</div>
+	<div id="info">
+	   <h4>Info for event testing:</h4>
+	   <div id="status"></div>
+	</div>
+    
+    <!--  a href="index2.html" class="btn large" >Load new page</a -->
+    <a href="javascript:" class="btn large" onclick="clearStatus();">Clear status</a>
+    
+    <script>
+    document.getElementById('status').innerHTML = localStorage.backgroundstatus;
+    </script>
+</body>
+</html>
+
+

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/d61deccd/lib/cordova-android/test/assets/www/background/index2.html
----------------------------------------------------------------------
diff --git a/lib/cordova-android/test/assets/www/background/index2.html b/lib/cordova-android/test/assets/www/background/index2.html
new file mode 100755
index 0000000..addf6eb
--- /dev/null
+++ b/lib/cordova-android/test/assets/www/background/index2.html
@@ -0,0 +1,116 @@
+<!DOCTYPE HTML>
+<!--
+         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.$
+-->
+<html>
+<head>
+<head>
+<meta http-equiv="Content-type" content="text/html; charset=utf-8">
+<title>Background Page 2</title>
+<link rel="stylesheet" href="../master.css" type="text/css" media="screen" title="no title">
+<script type="text/javascript" charset="utf-8" src="../cordova.js"></script>
+
+<script type="text/javascript" charset="utf-8">
+
+    function onLoad() {
+        console.log("Page2: onload");
+        log("Page2: onload @ " + new Date().toLocaleTimeString());
+        document.addEventListener("deviceready", onDeviceReady, false);
+    }
+
+    function onUnLoaded() {
+        console.log("Page2: onunload");
+        log("Page2: onunload @ " + new Date().toLocaleTimeString());
+    }
+
+    function onDeviceReady() {
+        // Register the event listener
+        document.getElementById("platform").innerHTML = device.platform;
+        document.getElementById("version").innerHTML = device.version;
+        document.getElementById("uuid").innerHTML = device.uuid;
+        document.getElementById("name").innerHTML = device.name;
+        document.getElementById("width").innerHTML = screen.width;
+        document.getElementById("height").innerHTML = screen.height;
+        document.getElementById("colorDepth").innerHTML = screen.colorDepth;
+
+        document.addEventListener("pause", onPause, false);
+        document.addEventListener("resume", onResume, false);
+
+        window.setInterval(function() {
+            log("Page2: Running");
+        }, 2000);
+}
+
+    function onPause() {
+        console.log("Page2: onpause");
+        log("Page2: onpause @ " + new Date().toLocaleTimeString());
+    }
+
+    function onResume() {
+        console.log("Page2: onresume");
+        log("Page2: onresume @ " + new Date().toLocaleTimeString());
+    }
+
+    function log(s) {
+        var el = document.getElementById('status');
+        var status = el.innerHTML + s + "<br>";
+        el.innerHTML = status;
+        localStorage.backgroundstatus = status;
+    }
+    
+    function clearStatus() {
+        console.log("clear()");
+        localStorage.backgroundstatus = "";
+        document.getElementById('status').innerHTML = "";
+    }
+        
+</script>
+</head>
+<body onload="onLoad()" onunload="onUnLoaded()"  id="stage" class="theme">
+    <h1>Events</h1>
+    <div id="info">
+        <h4>
+            Platform: <span id="platform"> &nbsp;</span>, Version: <span
+                id="version">&nbsp;</span>
+        </h4>
+        <h4>
+            UUID: <span id="uuid"> &nbsp;</span>, Name: <span id="name">&nbsp;</span>
+        </h4>
+        <h4>
+            Width: <span id="width"> &nbsp;</span>, Height: <span id="height">&nbsp;
+            </span>, Color Depth: <span id="colorDepth"></span>
+        </h4>
+    </div>
+    <div id="info">
+       <h4>Press "Back" button to return to Page 1.</h4>
+    </div>
+    <div id="info">
+       <h4>Info for event testing:</h4>
+       <div id="status"></div>
+    </div>
+    
+    <a href="index.html" class="btn large" >Load new page</a>
+    <a href="javascript:" class="btn large" onclick="clearStatus();">Clear status</a>
+    
+    <script>
+    document.getElementById('status').innerHTML = localStorage.backgroundstatus;
+    </script>
+</body>
+</html>
+
+

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/d61deccd/lib/cordova-android/test/assets/www/backgroundcolor/index.html
----------------------------------------------------------------------
diff --git a/lib/cordova-android/test/assets/www/backgroundcolor/index.html b/lib/cordova-android/test/assets/www/backgroundcolor/index.html
new file mode 100755
index 0000000..0746dcf
--- /dev/null
+++ b/lib/cordova-android/test/assets/www/backgroundcolor/index.html
@@ -0,0 +1,41 @@
+<!--
+         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.
+-->
+<!DOCTYPE HTML>
+<html>
+  <head>
+    <meta name="viewport" content="width=320; user-scalable=no" />
+    <meta http-equiv="Content-type" content="text/html; charset=utf-8">
+    <title>Cordova Tests</title>
+      <link rel="stylesheet" href="../master.css" type="text/css" media="screen" title="no title">
+      <script type="text/javascript" charset="utf-8" src="../cordova.js"></script>
+      <script type="text/javascript" charset="utf-8" src="../main.js"></script>
+  </head>
+  <body onload="init();" id="stage" class="theme">
+    <h1>Background Color Test</h1>
+    <div id="info">
+        <h4>Platform: <span id="platform"> &nbsp;</span>,   Version: <span id="version">&nbsp;</span></h4>
+        <h4>UUID: <span id="uuid"> &nbsp;</span>,   Name: <span id="name">&nbsp;</span></h4>
+        <h4>Width: <span id="width"> &nbsp;</span>,   Height: <span id="height">&nbsp;
+                   </span>, Color Depth: <span id="colorDepth"></span></h4>
+     </div>
+     <div id="info">
+     Before this page was show, you should have seen the background flash green.</br>
+     </div>
+  </body>
+</html>

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/d61deccd/lib/cordova-android/test/assets/www/basicauth/index.html
----------------------------------------------------------------------
diff --git a/lib/cordova-android/test/assets/www/basicauth/index.html b/lib/cordova-android/test/assets/www/basicauth/index.html
new file mode 100755
index 0000000..02ff0b2
--- /dev/null
+++ b/lib/cordova-android/test/assets/www/basicauth/index.html
@@ -0,0 +1,42 @@
+<!--
+         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.
+-->
+<!DOCTYPE HTML>
+<html>
+  <head>
+    <meta name="viewport" content="width=320; user-scalable=no" />
+    <meta http-equiv="Content-type" content="text/html; charset=utf-8">
+    <title>Cordova Tests</title>
+      <link rel="stylesheet" href="../master.css" type="text/css" media="screen" title="no title">
+      <script type="text/javascript" charset="utf-8" src="../cordova.js"></script>
+      <script type="text/javascript" charset="utf-8" src="../main.js"></script>
+  </head>
+  <body onload="init();" id="stage" class="theme">
+    <h1>Basic Auth</h1>
+    <div id="info">
+        <h4>Platform: <span id="platform"> &nbsp;</span>,   Version: <span id="version">&nbsp;</span></h4>
+        <h4>UUID: <span id="uuid"> &nbsp;</span>,   Name: <span id="name">&nbsp;</span></h4>
+        <h4>Width: <span id="width"> &nbsp;</span>,   Height: <span id="height">&nbsp;
+                   </span>, Color Depth: <span id="colorDepth"></span></h4>
+     </div>
+     <div id="info">
+     Loading link below should be successful and show page indicating username=test & password=test. <br>
+     </div>
+    <a href="http://browserspy.dk/password-ok.php" class="btn large">Test password</a>
+  </body>
+</html>