You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cordova.apache.org by ag...@apache.org on 2014/12/10 22:10:56 UTC

[1/6] android commit: CB-8119 Restart adb when we detect it's hung

Repository: cordova-android
Updated Branches:
  refs/heads/4.0.x 3206c2100 -> 87cdc5ad1


CB-8119 Restart adb when we detect it's hung


Project: http://git-wip-us.apache.org/repos/asf/cordova-android/repo
Commit: http://git-wip-us.apache.org/repos/asf/cordova-android/commit/66fa12a0
Tree: http://git-wip-us.apache.org/repos/asf/cordova-android/tree/66fa12a0
Diff: http://git-wip-us.apache.org/repos/asf/cordova-android/diff/66fa12a0

Branch: refs/heads/4.0.x
Commit: 66fa12a091c44d062ce4423e9e413e647895c466
Parents: 132650d
Author: Andrew Grieve <ag...@chromium.org>
Authored: Thu Dec 4 09:58:00 2014 -0500
Committer: Andrew Grieve <ag...@chromium.org>
Committed: Thu Dec 4 10:00:26 2014 -0500

----------------------------------------------------------------------
 bin/templates/cordova/lib/build.js  | 42 ++++++++++++++++++++++++++++----
 bin/templates/cordova/lib/device.js | 40 ++++++++++++++++++++++--------
 2 files changed, 67 insertions(+), 15 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cordova-android/blob/66fa12a0/bin/templates/cordova/lib/build.js
----------------------------------------------------------------------
diff --git a/bin/templates/cordova/lib/build.js b/bin/templates/cordova/lib/build.js
index 85a6e7c..0d4d004 100644
--- a/bin/templates/cordova/lib/build.js
+++ b/bin/templates/cordova/lib/build.js
@@ -398,12 +398,44 @@ module.exports.run = function(options, optResolvedTarget) {
  * Returns "arm" or "x86".
  */
 module.exports.detectArchitecture = function(target) {
-    return exec('adb -s ' + target + ' shell cat /proc/cpuinfo')
-    .then(function(output) {
-        if (/intel/i.exec(output)) {
-            return 'x86';
+    function helper() {
+        return exec('adb -s ' + target + ' shell cat /proc/cpuinfo')
+        .then(function(output) {
+            if (/intel/i.exec(output)) {
+                return 'x86';
+            }
+            return 'arm';
+        });
+    }
+    // It sometimes happens (at least on OS X), that this command will hang forever.
+    // To fix it, either unplug & replug device, or restart adb server.
+    return helper().timeout(1000, 'Device communication timed out. Try unplugging & replugging the device.')
+    .then(null, function(err) {
+        if (/timed out/.exec('' + err)) {
+            // adb kill-server doesn't seem to do the trick.
+            // Could probably find a x-platform version of killall, but I'm not actually
+            // sure that this scenario even happens on non-OSX machines.
+            return exec('killall adb')
+            .then(function() {
+                console.log('adb seems hung. retrying.');
+                return helper()
+                .then(null, function() {
+                    // The double kill is sadly often necessary, at least on mac.
+                    console.log('Now device not found... restarting adb again.');
+                    return exec('killall adb')
+                    .then(function() {
+                        return helper()
+                        .then(null, function() {
+                            return Q.reject('USB is flakey. Try unplugging & replugging the device.');
+                        });
+                    });
+                });
+            }, function() {
+                // For non-killall OS's.
+                return Q.reject(err);
+            })
         }
-        return 'arm';
+        throw err;
     });
 };
 

http://git-wip-us.apache.org/repos/asf/cordova-android/blob/66fa12a0/bin/templates/cordova/lib/device.js
----------------------------------------------------------------------
diff --git a/bin/templates/cordova/lib/device.js b/bin/templates/cordova/lib/device.js
index 6be55e3..6430d73 100644
--- a/bin/templates/cordova/lib/device.js
+++ b/bin/templates/cordova/lib/device.js
@@ -28,23 +28,43 @@ var exec  = require('./exec'),
 
 /**
  * Returns a promise for the list of the device ID's found
+ * @param lookHarder When true, try restarting adb if no devices are found.
  */
-module.exports.list = function() {
-    return exec('adb devices')
-    .then(function(output) {
-        var response = output.split('\n');
-        var device_list = [];
-        for (var i = 1; i < response.length; i++) {
-            if (response[i].match(/\w+\tdevice/) && !response[i].match(/emulator/)) {
-                device_list.push(response[i].replace(/\tdevice/, '').replace('\r', ''));
+module.exports.list = function(lookHarder) {
+    function helper() {
+        return exec('adb devices')
+        .then(function(output) {
+            var response = output.split('\n');
+            var device_list = [];
+            for (var i = 1; i < response.length; i++) {
+                if (response[i].match(/\w+\tdevice/) && !response[i].match(/emulator/)) {
+                    device_list.push(response[i].replace(/\tdevice/, '').replace('\r', ''));
+                }
             }
+            return device_list;
+        });
+    }
+    return helper()
+    .then(function(list) {
+        if (list.length === 0 && lookHarder) {
+            // adb kill-server doesn't seem to do the trick.
+            // Could probably find a x-platform version of killall, but I'm not actually
+            // sure that this scenario even happens on non-OSX machines.
+            return exec('killall adb')
+            .then(function() {
+                console.log('Restarting adb to see if more devices are detected.');
+                return helper();
+            }, function() {
+                // For non-killall OS's.
+                return list;
+            });
         }
-        return device_list;
+        return list;
     });
 }
 
 module.exports.resolveTarget = function(target) {
-    return this.list()
+    return this.list(true)
     .then(function(device_list) {
         if (!device_list || !device_list.length) {
             return Q.reject('ERROR: Failed to deploy to device, no devices found.');


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@cordova.apache.org
For additional commands, e-mail: commits-help@cordova.apache.org


[6/6] android commit: Merge branch 'master' into 4.0.x (SplashScreen breakout)

Posted by ag...@apache.org.
Merge branch 'master' into 4.0.x (SplashScreen breakout)

Conflicts:
	framework/src/org/apache/cordova/CordovaActivity.java
	framework/src/org/apache/cordova/CordovaWebView.java


Project: http://git-wip-us.apache.org/repos/asf/cordova-android/repo
Commit: http://git-wip-us.apache.org/repos/asf/cordova-android/commit/87cdc5ad
Tree: http://git-wip-us.apache.org/repos/asf/cordova-android/tree/87cdc5ad
Diff: http://git-wip-us.apache.org/repos/asf/cordova-android/diff/87cdc5ad

Branch: refs/heads/4.0.x
Commit: 87cdc5ad1c00cef6ec98ef419b71ba1f48bca008
Parents: 3206c21 ba140a8
Author: Andrew Grieve <ag...@chromium.org>
Authored: Wed Dec 10 16:09:22 2014 -0500
Committer: Andrew Grieve <ag...@chromium.org>
Committed: Wed Dec 10 16:09:22 2014 -0500

----------------------------------------------------------------------
 bin/templates/cordova/lib/build.js              |  45 +++-
 bin/templates/cordova/lib/device.js             |  40 +++-
 bin/templates/project/build.gradle              |  14 +-
 .../src/org/apache/cordova/AndroidWebView.java  |   8 +-
 .../src/org/apache/cordova/CordovaActivity.java | 208 +------------------
 5 files changed, 93 insertions(+), 222 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cordova-android/blob/87cdc5ad/bin/templates/cordova/lib/build.js
----------------------------------------------------------------------

http://git-wip-us.apache.org/repos/asf/cordova-android/blob/87cdc5ad/bin/templates/project/build.gradle
----------------------------------------------------------------------

http://git-wip-us.apache.org/repos/asf/cordova-android/blob/87cdc5ad/framework/src/org/apache/cordova/AndroidWebView.java
----------------------------------------------------------------------
diff --cc framework/src/org/apache/cordova/AndroidWebView.java
index c3c9e5b,0000000..f63a6fb
mode 100755,000000..100755
--- a/framework/src/org/apache/cordova/AndroidWebView.java
+++ b/framework/src/org/apache/cordova/AndroidWebView.java
@@@ -1,791 -1,0 +1,795 @@@
 +/*
 +       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.lang.reflect.InvocationTargetException;
 +import java.lang.reflect.Method;
 +import java.util.HashMap;
 +import java.util.HashSet;
 +import java.util.List;
 +
 +import android.annotation.SuppressLint;
 +import android.annotation.TargetApi;
 +import android.content.BroadcastReceiver;
 +import android.content.Context;
 +import android.content.Intent;
 +import android.content.IntentFilter;
 +import android.content.pm.ApplicationInfo;
 +import android.net.Uri;
 +import android.os.Build;
 +import android.os.Bundle;
 +import android.util.AttributeSet;
 +import android.util.Log;
 +import android.view.Gravity;
 +import android.view.KeyEvent;
 +import android.view.View;
 +import android.view.ViewGroup;
 +import android.view.inputmethod.InputMethodManager;
 +import android.webkit.WebBackForwardList;
 +import android.webkit.WebHistoryItem;
 +import android.webkit.WebChromeClient;
 +import android.webkit.WebSettings;
 +import android.webkit.WebView;
 +import android.webkit.WebSettings.LayoutAlgorithm;
 +import android.webkit.WebViewClient;
 +import android.widget.FrameLayout;
 +
 +
 +/*
 + * This class is our web view.
 + *
 + * @see <a href="http://developer.android.com/guide/webapps/webview.html">WebView guide</a>
 + * @see <a href="http://developer.android.com/reference/android/webkit/WebView.html">WebView</a>
 + */
 +public class AndroidWebView extends WebView implements CordovaWebView {
 +
 +    public static final String TAG = "AndroidWebView";
 +
 +    private HashSet<Integer> boundKeyCodes = new HashSet<Integer>();
 +
 +    PluginManager pluginManager;
 +
 +    private BroadcastReceiver receiver;
 +
 +
 +    /** Activities and other important classes **/
 +    private CordovaInterface cordova;
 +    AndroidWebViewClient viewClient;
 +    private AndroidChromeClient chromeClient;
 +
 +    // Flag to track that a loadUrl timeout occurred
 +    int loadUrlTimeout = 0;
 +
 +    private long lastMenuEventTime = 0;
 +
 +    CordovaBridge bridge;
 +
 +    /** custom view created by the browser (a video player for example) */
 +    private View mCustomView;
 +    private WebChromeClient.CustomViewCallback mCustomViewCallback;
 +
 +    private CordovaResourceApi resourceApi;
 +    private Whitelist internalWhitelist;
 +    private Whitelist externalWhitelist;
 +    private CordovaPreferences preferences;
 +    // The URL passed to loadUrl(), not necessarily the URL of the current page.
 +    String loadedUrl;
 +    
 +    static final FrameLayout.LayoutParams COVER_SCREEN_GRAVITY_CENTER =
 +            new FrameLayout.LayoutParams(
 +            ViewGroup.LayoutParams.MATCH_PARENT,
 +            ViewGroup.LayoutParams.MATCH_PARENT,
 +            Gravity.CENTER);
 +    
 +    /** Used when created via reflection. */
 +    public AndroidWebView(Context context) {
 +        this(context, null);
 +    }
 +
 +    /** Required to allow view to be used within XML layouts. */
 +    public AndroidWebView(Context context, AttributeSet attrs) {
 +        super(context, attrs);
 +    }
 +
 +    // Use two-phase init so that the control will work with XML layouts.
 +    @Override
 +    public void init(CordovaInterface cordova, List<PluginEntry> pluginEntries,
 +            Whitelist internalWhitelist, Whitelist externalWhitelist,
 +            CordovaPreferences preferences) {
 +        if (this.cordova != null) {
 +            throw new IllegalStateException();
 +        }
 +        this.cordova = cordova;
 +        this.internalWhitelist = internalWhitelist;
 +        this.externalWhitelist = externalWhitelist;
 +        this.preferences = preferences;
 +
 +        pluginManager = new PluginManager(this, this.cordova, pluginEntries);
 +        resourceApi = new CordovaResourceApi(this.getContext(), pluginManager);
 +        bridge = new CordovaBridge(pluginManager, new NativeToJsMessageQueue(this, cordova), this.cordova.getActivity().getPackageName());
-         pluginManager.addService("App", "org.apache.cordova.CoreAndroid");
 +        initWebViewSettings();
++        pluginManager.addService("App", "org.apache.cordova.CoreAndroid");
++        pluginManager.init();
 +        
 +        if (this.viewClient == null) {
 +            setWebViewClient(Build.VERSION.SDK_INT < Build.VERSION_CODES.ICE_CREAM_SANDWICH ?
 +                new AndroidWebViewClient(cordova, this) :
 +                new IceCreamCordovaWebViewClient(cordova, this));
 +        }
 +        if (this.chromeClient == null) {
 +            setWebChromeClient(new AndroidChromeClient(cordova, this));
 +        }
 +
 +        exposeJsInterface();
 +    }
 +
 +    @SuppressLint("SetJavaScriptEnabled")
 +    @SuppressWarnings("deprecation")
 +    private void initWebViewSettings() {
 +        this.setInitialScale(0);
 +        this.setVerticalScrollBarEnabled(false);
 +        // TODO: The Activity is the one that should call requestFocus().
 +        if (shouldRequestFocusOnInit()) {
 +            this.requestFocusFromTouch();
 +        }
 +        this.setInitialScale(0);
 +        this.setVerticalScrollBarEnabled(false);
 +        if (shouldRequestFocusOnInit()) {
 +            this.requestFocusFromTouch();
 +        }
 +        // Enable JavaScript
 +        final WebSettings settings = this.getSettings();
 +        settings.setJavaScriptEnabled(true);
 +        settings.setJavaScriptCanOpenWindowsAutomatically(true);
 +        settings.setLayoutAlgorithm(LayoutAlgorithm.NORMAL);
 +        
 +        // Set the nav dump for HTC 2.x devices (disabling for ICS, deprecated entirely for Jellybean 4.2)
 +        try {
 +            Method gingerbread_getMethod =  WebSettings.class.getMethod("setNavDump", new Class[] { boolean.class });
 +            
 +            String manufacturer = android.os.Build.MANUFACTURER;
 +            Log.d(TAG, "CordovaWebView is running on device made by: " + manufacturer);
 +            if(android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.HONEYCOMB &&
 +                    android.os.Build.MANUFACTURER.contains("HTC"))
 +            {
 +                gingerbread_getMethod.invoke(settings, true);
 +            }
 +        } catch (NoSuchMethodException e) {
 +            Log.d(TAG, "We are on a modern version of Android, we will deprecate HTC 2.3 devices in 2.8");
 +        } catch (IllegalArgumentException e) {
 +            Log.d(TAG, "Doing the NavDump failed with bad arguments");
 +        } catch (IllegalAccessException e) {
 +            Log.d(TAG, "This should never happen: IllegalAccessException means this isn't Android anymore");
 +        } catch (InvocationTargetException e) {
 +            Log.d(TAG, "This should never happen: InvocationTargetException means this isn't Android anymore.");
 +        }
 +
 +        //We don't save any form data in the application
 +        settings.setSaveFormData(false);
 +        settings.setSavePassword(false);
 +        
 +        // Jellybean rightfully tried to lock this down. Too bad they didn't give us a whitelist
 +        // while we do this
 +        if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN) {
 +            Level16Apis.enableUniversalAccess(settings);
 +        }
 +        if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN_MR1) {
 +            Level17Apis.setMediaPlaybackRequiresUserGesture(settings, false);
 +        }
 +        // Enable database
 +        // We keep this disabled because we use or shim to get around DOM_EXCEPTION_ERROR_16
 +        String databasePath = getContext().getApplicationContext().getDir("database", Context.MODE_PRIVATE).getPath();
 +        settings.setDatabaseEnabled(true);
 +        settings.setDatabasePath(databasePath);
 +        
 +        
 +        //Determine whether we're in debug or release mode, and turn on Debugging!
 +        ApplicationInfo appInfo = getContext().getApplicationContext().getApplicationInfo();
 +        if ((appInfo.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0 &&
 +            android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.KITKAT) {
 +            enableRemoteDebugging();
 +        }
 +        
 +        settings.setGeolocationDatabasePath(databasePath);
 +
 +        // Enable DOM storage
 +        settings.setDomStorageEnabled(true);
 +
 +        // Enable built-in geolocation
 +        settings.setGeolocationEnabled(true);
 +        
 +        // Enable AppCache
 +        // Fix for CB-2282
 +        settings.setAppCacheMaxSize(5 * 1048576);
 +        settings.setAppCachePath(databasePath);
 +        settings.setAppCacheEnabled(true);
 +        
 +        // Fix for CB-1405
 +        // Google issue 4641
 +        settings.getUserAgentString();
 +        
 +        IntentFilter intentFilter = new IntentFilter();
 +        intentFilter.addAction(Intent.ACTION_CONFIGURATION_CHANGED);
 +        if (this.receiver == null) {
 +            this.receiver = new BroadcastReceiver() {
 +                @Override
 +                public void onReceive(Context context, Intent intent) {
 +                    settings.getUserAgentString();
 +                }
 +            };
 +            getContext().registerReceiver(this.receiver, intentFilter);
 +        }
 +        // end CB-1405
 +    }
 +
 +    @TargetApi(Build.VERSION_CODES.KITKAT)
 +    private void enableRemoteDebugging() {
 +        try {
 +            WebView.setWebContentsDebuggingEnabled(true);
 +        } catch (IllegalArgumentException e) {
 +            Log.d(TAG, "You have one job! To turn on Remote Web Debugging! YOU HAVE FAILED! ");
 +            e.printStackTrace();
 +        }
 +    }
 +
 +	/**
 +	 * Override this method to decide whether or not you need to request the
 +	 * focus when your application start
 +	 * 
 +	 * @return true unless this method is overriden to return a different value
 +	 */
 +    protected boolean shouldRequestFocusOnInit() {
 +		return true;
 +	}
 +
 +    private void exposeJsInterface() {
 +        if ((Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR1)) {
 +            Log.i(TAG, "Disabled addJavascriptInterface() bridge since Android version is old.");
 +            // Bug being that Java Strings do not get converted to JS strings automatically.
 +            // This isn't hard to work-around on the JS side, but it's easier to just
 +            // use the prompt bridge instead.
 +            return;            
 +        } 
 +        AndroidExposedJsApi exposedJsApi = new AndroidExposedJsApi(bridge);
 +        this.addJavascriptInterface(exposedJsApi, "_cordovaNative");
 +    }
 +
 +    @Override
 +    public void setWebViewClient(WebViewClient client) {
 +        this.viewClient = (AndroidWebViewClient)client;
 +        super.setWebViewClient(client);
 +    }
 +
 +    @Override
 +    public void setWebChromeClient(WebChromeClient client) {
 +        this.chromeClient = (AndroidChromeClient)client;
 +        super.setWebChromeClient(client);
 +    }
 +
 +    /**
 +     * Load the url into the webview.
 +     */
 +    @Override
 +    public void loadUrl(String url) {
 +        this.loadUrlIntoView(url, true);
 +    }
 +
 +    /**
 +     * Load the url into the webview.
 +     */
 +    @Override
 +    public void loadUrlIntoView(final String url, boolean recreatePlugins) {
 +        if (url.equals("about:blank") || url.startsWith("javascript:")) {
 +            this.loadUrlNow(url);
 +            return;
 +        }
 +
 +        LOG.d(TAG, ">>> loadUrl(" + url + ")");
 +        recreatePlugins = recreatePlugins || (loadedUrl == null);
 +
 +        if (recreatePlugins) {
++            // Don't re-initialize on first load.
++            if (loadedUrl != null) {
++                this.pluginManager.init();
++            }
 +            this.loadedUrl = url;
-             this.pluginManager.init();
 +        }
 +
 +        // Create a timeout timer for loadUrl
 +        final AndroidWebView me = this;
 +        final int currentLoadUrlTimeout = me.loadUrlTimeout;
 +        final int loadUrlTimeoutValue = preferences.getInteger("LoadUrlTimeoutValue", 20000);
 +
 +        // Timeout error method
 +        final Runnable loadError = new Runnable() {
 +            public void run() {
 +                me.stopLoading();
 +                LOG.e(TAG, "CordovaWebView: TIMEOUT ERROR!");
 +                if (viewClient != null) {
 +                    viewClient.onReceivedError(AndroidWebView.this, -6, "The connection to the server was unsuccessful.", url);
 +                }
 +            }
 +        };
 +
 +        // Timeout timer method
 +        final Runnable timeoutCheck = new Runnable() {
 +            public void run() {
 +                try {
 +                    synchronized (this) {
 +                        wait(loadUrlTimeoutValue);
 +                    }
 +                } catch (InterruptedException e) {
 +                    e.printStackTrace();
 +                }
 +
 +                // If timeout, then stop loading and handle error
 +                if (me.loadUrlTimeout == currentLoadUrlTimeout) {
 +                    me.cordova.getActivity().runOnUiThread(loadError);
 +                }
 +            }
 +        };
 +
 +        // Load url
 +        this.cordova.getActivity().runOnUiThread(new Runnable() {
 +            public void run() {
 +                cordova.getThreadPool().execute(timeoutCheck);
 +                me.loadUrlNow(url);
 +            }
 +        });
 +    }
 +
 +    /**
 +     * Load URL in webview.
 +     */
 +    private void loadUrlNow(String url) {
 +        if (LOG.isLoggable(LOG.DEBUG) && !url.startsWith("javascript:")) {
 +            LOG.d(TAG, ">>> loadUrlNow()");
 +        }
 +        if (url.startsWith("file://") || url.startsWith("javascript:") || internalWhitelist.isUrlWhiteListed(url)) {
 +            super.loadUrl(url);
 +        }
 +    }
 +
 +    @Override
 +    public void stopLoading() {
 +        //viewClient.isCurrentlyLoading = false;
 +        super.stopLoading();
 +    }
 +    
 +    public void onScrollChanged(int l, int t, int oldl, int oldt)
 +    {
 +        super.onScrollChanged(l, t, oldl, oldt);
 +        //We should post a message that the scroll changed
 +        ScrollEvent myEvent = new ScrollEvent(l, t, oldl, oldt, this);
 +        pluginManager.postMessage("onScrollChanged", myEvent);
 +    }
 +    
 +    /**
 +     * Send JavaScript statement back to JavaScript.
 +     * (This is a convenience method)
 +     */
 +    public void sendJavascript(String statement) {
 +        bridge.getMessageQueue().addJavaScript(statement);
 +    }
 +
 +    /**
 +     * Send a plugin result back to JavaScript.
 +     */
 +    public void sendPluginResult(PluginResult result, String callbackId) {
 +        bridge.getMessageQueue().addPluginResult(result, callbackId);
 +    }
 +
 +    /**
 +     * Go to previous page in history.  (We manage our own history)
 +     *
 +     * @return true if we went back, false if we are already at top
 +     */
 +    public boolean backHistory() {
 +
 +        // Check webview first to see if there is a history
 +        // This is needed to support curPage#diffLink, since they are added to appView's history, but not our history url array (JQMobile behavior)
 +        if (super.canGoBack()) {
 +            printBackForwardList();
 +            super.goBack();
 +            
 +            return true;
 +        }
 +        return false;
 +    }
 +
 +
 +    /**
 +     * Load the specified URL in the Cordova webview or a new browser instance.
 +     *
 +     * NOTE: If openExternal is false, only URLs listed in whitelist can be loaded.
 +     *
 +     * @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        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);
 +
 +        // If clearing history
 +        if (clearHistory) {
 +            this.clearHistory();
 +        }
 +
 +        // If loading into our webview
 +        if (!openExternal) {
 +
 +            // Make sure url is in whitelist
 +            if (url.startsWith("file://") || internalWhitelist.isUrlWhiteListed(url)) {
 +                // TODO: What about params?
 +                // Load new URL
 +                loadUrlIntoView(url, true);
 +                return;
 +            }
 +            // Load in default viewer if not
 +            LOG.w(TAG, "showWebPage: Cannot load URL into webview since it is not in white list.  Loading into browser instead. (URL=" + url + ")");
 +        }
 +        try {
 +            // Omitting the MIME type for file: URLs causes "No Activity found to handle Intent".
 +            // Adding the MIME type to http: URLs causes them to not be handled by the downloader.
 +            Intent intent = new Intent(Intent.ACTION_VIEW);
 +            Uri uri = Uri.parse(url);
 +            if ("file".equals(uri.getScheme())) {
 +                intent.setDataAndType(uri, resourceApi.getMimeType(uri));
 +            } else {
 +                intent.setData(uri);
 +            }
 +            cordova.getActivity().startActivity(intent);
 +        } catch (android.content.ActivityNotFoundException e) {
 +            LOG.e(TAG, "Error loading url " + url, e);
 +        }
 +    }
 +
 +    /*
 +     * onKeyDown
 +     */
 +    @Override
 +    public boolean onKeyDown(int keyCode, KeyEvent event)
 +    {
 +        if(boundKeyCodes.contains(keyCode))
 +        {
 +            if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN) {
 +                    this.loadUrl("javascript:cordova.fireDocumentEvent('volumedownbutton');");
 +                    return true;
 +            }
 +            else if (keyCode == KeyEvent.KEYCODE_VOLUME_UP) {
 +                    this.loadUrl("javascript:cordova.fireDocumentEvent('volumeupbutton');");
 +                    return true;
 +            }
 +            else
 +            {
 +                return super.onKeyDown(keyCode, event);
 +            }
 +        }
 +        else if(keyCode == KeyEvent.KEYCODE_BACK)
 +        {
 +            return !(this.startOfHistory()) || isButtonPlumbedToJs(KeyEvent.KEYCODE_BACK);
 +
 +        }
 +        else if(keyCode == KeyEvent.KEYCODE_MENU)
 +        {
 +            //How did we get here?  Is there a childView?
 +            View childView = this.getFocusedChild();
 +            if(childView != null)
 +            {
 +                //Make sure we close the keyboard if it's present
 +                InputMethodManager imm = (InputMethodManager) cordova.getActivity().getSystemService(Context.INPUT_METHOD_SERVICE);
 +                imm.hideSoftInputFromWindow(childView.getWindowToken(), 0);
 +                cordova.getActivity().openOptionsMenu();
 +                return true;
 +            } else {
 +                return super.onKeyDown(keyCode, event);
 +            }
 +        }
 +        return super.onKeyDown(keyCode, event);
 +    }
 +
 +    @Override
 +    public boolean onKeyUp(int keyCode, KeyEvent event)
 +    {
 +        // If back key
 +        if (keyCode == KeyEvent.KEYCODE_BACK) {
 +            // A custom view is currently displayed  (e.g. playing a video)
 +            if(mCustomView != null) {
 +                this.hideCustomView();
 +                return true;
 +            } else {
 +                // The webview is currently displayed
 +                // If back key is bound, then send event to JavaScript
 +                if (isButtonPlumbedToJs(KeyEvent.KEYCODE_BACK)) {
 +                    this.loadUrl("javascript:cordova.fireDocumentEvent('backbutton');");
 +                    return true;
 +                } else {
 +                    // If not bound
 +                    // Go to previous page in webview if it is possible to go back
 +                    if (this.backHistory()) {
 +                        return true;
 +                    }
 +                    // If not, then invoke default behavior
 +                }
 +            }
 +        }
 +        // Legacy
 +        else if (keyCode == KeyEvent.KEYCODE_MENU) {
 +            if (this.lastMenuEventTime < event.getEventTime()) {
 +                this.loadUrl("javascript:cordova.fireDocumentEvent('menubutton');");
 +            }
 +            this.lastMenuEventTime = event.getEventTime();
 +            return super.onKeyUp(keyCode, event);
 +        }
 +        // If search key
 +        else if (keyCode == KeyEvent.KEYCODE_SEARCH) {
 +            this.loadUrl("javascript:cordova.fireDocumentEvent('searchbutton');");
 +            return true;
 +        }
 +
 +        //Does webkit change this behavior?
 +        return super.onKeyUp(keyCode, event);
 +    }
 +
 +    @Override
 +    public void setButtonPlumbedToJs(int keyCode, boolean override) {
 +        switch (keyCode) {
 +            case KeyEvent.KEYCODE_VOLUME_DOWN:
 +            case KeyEvent.KEYCODE_VOLUME_UP:
 +            case KeyEvent.KEYCODE_BACK:
 +                // TODO: Why are search and menu buttons handled separately?
 +                if (override) {
 +                    boundKeyCodes.add(keyCode);
 +                } else {
 +                    boundKeyCodes.remove(keyCode);
 +                }
 +                return;
 +            default:
 +                throw new IllegalArgumentException("Unsupported keycode: " + keyCode);
 +        }
 +    }
 +
 +    @Override
 +    public boolean isButtonPlumbedToJs(int keyCode)
 +    {
 +        return boundKeyCodes.contains(keyCode);
 +    }
 +
 +    public void handlePause(boolean keepRunning)
 +    {
 +        LOG.d(TAG, "Handle the pause");
 +        // Send pause event to JavaScript
 +        this.loadUrl("javascript:try{cordova.fireDocumentEvent('pause');}catch(e){console.log('exception firing pause event from native');};");
 +
 +        // Forward to plugins
 +        if (this.pluginManager != null) {
 +            this.pluginManager.onPause(keepRunning);
 +        }
 +
 +        // If app doesn't want to run in background
 +        if (!keepRunning) {
 +            // Pause JavaScript timers (including setInterval)
 +            this.pauseTimers();
 +        }
 +    }
 +    
 +    public void handleResume(boolean keepRunning, boolean activityResultKeepRunning)
 +    {
 +        this.loadUrl("javascript:try{cordova.fireDocumentEvent('resume');}catch(e){console.log('exception firing resume event from native');};");
 +        
 +        // Forward to plugins
 +        if (this.pluginManager != null) {
 +            this.pluginManager.onResume(keepRunning);
 +        }
 +
 +        // Resume JavaScript timers (including setInterval)
 +        this.resumeTimers();
 +    }
 +    
 +    public void handleDestroy()
 +    {
 +        // Cancel pending timeout timer.
 +        loadUrlTimeout++;
 +
 +        // Send destroy event to JavaScript
 +        this.loadUrl("javascript:try{cordova.require('cordova/channel').onDestroy.fire();}catch(e){console.log('exception firing destroy event from native');};");
 +
 +        // Load blank page so that JavaScript onunload is called
 +        this.loadUrl("about:blank");
 +        
 +        //Remove last AlertDialog
 +        this.chromeClient.destroyLastDialog();
 +
 +        // Forward to plugins
 +        if (this.pluginManager != null) {
 +            this.pluginManager.onDestroy();
 +        }
 +        
 +        // unregister the receiver
 +        if (this.receiver != null) {
 +            try {
 +                getContext().unregisterReceiver(this.receiver);
 +            } catch (Exception e) {
 +                Log.e(TAG, "Error unregistering configuration receiver: " + e.getMessage(), e);
 +            }
 +        }
 +    }
 +    
 +    public void onNewIntent(Intent intent)
 +    {
 +        //Forward to plugins
 +        if (this.pluginManager != null) {
 +            this.pluginManager.onNewIntent(intent);
 +        }
 +    }
 +    
 +    // Wrapping these functions in their own class prevents warnings in adb like:
 +    // VFY: unable to resolve virtual method 285: Landroid/webkit/WebSettings;.setAllowUniversalAccessFromFileURLs
 +    @TargetApi(16)
 +    private static class Level16Apis {
 +        static void enableUniversalAccess(WebSettings settings) {
 +            settings.setAllowUniversalAccessFromFileURLs(true);
 +        }
 +    }
 +
 +    @TargetApi(17)
 +    private static final class Level17Apis {
 +        static void setMediaPlaybackRequiresUserGesture(WebSettings settings, boolean value) {
 +            settings.setMediaPlaybackRequiresUserGesture(value);
 +        }
 +    }
 +    public void printBackForwardList() {
 +        WebBackForwardList currentList = this.copyBackForwardList();
 +        int currentSize = currentList.getSize();
 +        for(int i = 0; i < currentSize; ++i)
 +        {
 +            WebHistoryItem item = currentList.getItemAtIndex(i);
 +            String url = item.getUrl();
 +            LOG.d(TAG, "The URL at index: " + Integer.toString(i) + " is " + url );
 +        }
 +    }
 +    
 +    
 +    //Can Go Back is BROKEN!
 +    public boolean startOfHistory()
 +    {
 +        WebBackForwardList currentList = this.copyBackForwardList();
 +        WebHistoryItem item = currentList.getItemAtIndex(0);
 +        if( item!=null){	// Null-fence in case they haven't called loadUrl yet (CB-2458)
 +	        String url = item.getUrl();
 +	        String currentUrl = this.getUrl();
 +	        LOG.d(TAG, "The current URL is: " + currentUrl);
 +	        LOG.d(TAG, "The URL at item 0 is: " + url);
 +	        return currentUrl.equals(url);
 +        }
 +        return false;
 +    }
 +
 +    public void showCustomView(View view, WebChromeClient.CustomViewCallback callback) {
 +        // This code is adapted from the original Android Browser code, licensed under the Apache License, Version 2.0
 +        Log.d(TAG, "showing Custom View");
 +        // if a view already exists then immediately terminate the new one
 +        if (mCustomView != null) {
 +            callback.onCustomViewHidden();
 +            return;
 +        }
 +        
 +        // Store the view and its callback for later (to kill it properly)
 +        mCustomView = view;
 +        mCustomViewCallback = callback;
 +        
 +        // Add the custom view to its container.
 +        ViewGroup parent = (ViewGroup) this.getParent();
 +        parent.addView(view, COVER_SCREEN_GRAVITY_CENTER);
 +        
 +        // Hide the content view.
 +        this.setVisibility(View.GONE);
 +        
 +        // Finally show the custom view container.
 +        parent.setVisibility(View.VISIBLE);
 +        parent.bringToFront();
 +    }
 +
 +    public void hideCustomView() {
 +        // This code is adapted from the original Android Browser code, licensed under the Apache License, Version 2.0
 +        Log.d(TAG, "Hiding Custom View");
 +        if (mCustomView == null) return;
 +
 +        // Hide the custom view.
 +        mCustomView.setVisibility(View.GONE);
 +        
 +        // Remove the custom view from its container.
 +        ViewGroup parent = (ViewGroup) this.getParent();
 +        parent.removeView(mCustomView);
 +        mCustomView = null;
 +        mCustomViewCallback.onCustomViewHidden();
 +        
 +        // Show the content view.
 +        this.setVisibility(View.VISIBLE);
 +    }
 +    
 +    /**
 +     * if the video overlay is showing then we need to know 
 +     * as it effects back button handling
 +     * 
 +     * @return true if custom view is showing
 +     */
 +    public boolean isCustomViewShowing() {
 +        return mCustomView != null;
 +    }
 +    
 +    public WebBackForwardList restoreState(Bundle savedInstanceState)
 +    {
 +        WebBackForwardList myList = super.restoreState(savedInstanceState);
 +        Log.d(TAG, "WebView restoration crew now restoring!");
 +        //Initialize the plugin manager once more
 +        this.pluginManager.init();
 +        return myList;
 +    }
 +
 +    public CordovaResourceApi getResourceApi() {
 +        return resourceApi;
 +    }
 +
 +    void onPageReset() {
 +        boundKeyCodes.clear();
 +        pluginManager.onReset();
 +        bridge.reset(loadedUrl);
 +    }
 +
 +    @Override
 +    public PluginManager getPluginManager() {
 +        return this.pluginManager;
 +    }
 +
 +    @Override
 +    public View getView() {
 +        return this;
 +    }
 +
 +    @Override
 +    public Whitelist getWhitelist() {
 +        return this.internalWhitelist;
 +    }
 +
 +    @Override
 +    public Whitelist getExternalWhitelist() {
 +        return this.externalWhitelist;
 +    }
 +
 +    @Override
 +    public CordovaPreferences getPreferences() {
 +        return preferences;
 +    }
 +
 +    @Override
 +    public void onFilePickerResult(Uri uri) {
 +        if (null == chromeClient.mUploadMessage)
 +            return;
 +        chromeClient.mUploadMessage.onReceiveValue(uri);
 +        chromeClient.mUploadMessage = null;
 +    }
 +    
 +    @Override
 +    public Object postMessage(String id, Object data) {
 +        return pluginManager.postMessage(id, data);
 +    }
 +}

http://git-wip-us.apache.org/repos/asf/cordova-android/blob/87cdc5ad/framework/src/org/apache/cordova/CordovaActivity.java
----------------------------------------------------------------------
diff --cc framework/src/org/apache/cordova/CordovaActivity.java
index b993f37,b61fa98..8dec805
--- a/framework/src/org/apache/cordova/CordovaActivity.java
+++ b/framework/src/org/apache/cordova/CordovaActivity.java
@@@ -31,10 -28,9 +29,8 @@@ import org.json.JSONArray
  import org.json.JSONException;
  import org.json.JSONObject;
  
 -import android.annotation.SuppressLint;
  import android.app.Activity;
  import android.app.AlertDialog;
- import android.app.Dialog;
- import android.app.ProgressDialog;
  import android.content.Context;
  import android.content.DialogInterface;
  import android.content.Intent;
@@@ -42,9 -38,9 +38,8 @@@ import android.graphics.Color
  import android.media.AudioManager;
  import android.net.Uri;
  import android.os.Bundle;
- import android.os.Handler;
  import android.util.Log;
  import android.view.Display;
 -import android.view.KeyEvent;
  import android.view.Menu;
  import android.view.MenuItem;
  import android.view.View;
@@@ -92,7 -89,12 +87,6 @@@ public class CordovaActivity extends Ac
      // The webview for our app
      protected CordovaWebView appView;
  
-     protected ProgressDialog spinnerDialog = null;
 -    @Deprecated // unused.
 -    protected CordovaWebViewClient webViewClient;
 -
 -    @Deprecated // Will be removed. Use findViewById() to retrieve views.
 -    protected LinearLayout root;
 -
      private final ExecutorService threadPool = Executors.newCachedThreadPool();
  
      private static int ACTIVITY_STARTING = 0;
@@@ -108,10 -110,12 +102,6 @@@
       * The variables below are used to cache some of the activity properties.
       */
  
--    // Draw a splash screen using an image located in the drawable resource directory.
-     // This is not the same as calling super.loadSplashscreen(url)
-     protected int splashscreen = 0;
- 
 -    @Deprecated // Use "SplashScreen" preference instead.
 -    protected int splashscreen = 0;
 -    @Deprecated // Use "SplashScreenDelay" preference instead.
 -    protected int splashscreenTime = -1;
 -
      // LoadUrl timeout value in msec (default of 20 sec)
      protected int loadUrlTimeoutValue = 20000;
  
@@@ -213,10 -262,8 +203,8 @@@
                  ViewGroup.LayoutParams.MATCH_PARENT,
                  1.0F));
  
-         // Add web view but make it invisible while loading URL
-         appView.getView().setVisibility(View.INVISIBLE);
          // need to remove appView from any existing parent before invoking root.addView(appView)
 -        ViewParent parent = appView.getParent();
 +        ViewParent parent = appView.getView().getParent();
          if ((parent != null) && (parent != root)) {
              LOG.d(TAG, "removing appView from existing parent");
              ViewGroup parentGroup = (ViewGroup) parent;
@@@ -243,34 -290,67 +231,34 @@@
       * require a more specialized web view.
       */
      protected CordovaWebView makeWebView() {
 -        return new CordovaWebView(CordovaActivity.this);
 -    }
 -
 -    /**
 -     * Construct the client for the default web view object.
 -     *
 -     * This is intended to be overridable by subclasses of CordovaIntent which
 -     * require a more specialized web view.
 -     *
 -     * @param webView the default constructed web view object
 -     */
 -    protected CordovaWebViewClient makeWebViewClient(CordovaWebView webView) {
 -        return webView.makeWebViewClient(this);
 -    }
 -
 -    /**
 -     * Construct the chrome client for the default web view object.
 -     *
 -     * This is intended to be overridable by subclasses of CordovaIntent which
 -     * require a more specialized web view.
 -     *
 -     * @param webView the default constructed web view object
 -     */
 -    protected CordovaChromeClient makeChromeClient(CordovaWebView webView) {
 -        return webView.makeWebChromeClient(this);
 -    }
 -
 -    public void init() {
 -        this.init(appView, null, null);
 -    }
 -
 -    @SuppressLint("NewApi")
 -    @Deprecated // Call init() instead and override makeWebView() to customize.
 -    public void init(CordovaWebView webView, CordovaWebViewClient webViewClient, CordovaChromeClient webChromeClient) {
 -        LOG.d(TAG, "CordovaActivity.init()");
 -
 -        if (splashscreenTime >= 0) {
 -            preferences.set("SplashScreenDelay", splashscreenTime);
 -        }
 -        if (splashscreen != 0) {
 -            preferences.set("SplashDrawableId", splashscreen);
 -        }
 -
 -        appView = webView != null ? webView : makeWebView();
 -        if (appView.pluginManager == null) {
 -            appView.init(this, webViewClient != null ? webViewClient : makeWebViewClient(appView),
 -                    webChromeClient != null ? webChromeClient : makeChromeClient(appView),
 -                    pluginEntries, internalWhitelist, externalWhitelist, preferences);
 -        }
 -
 -        // TODO: Have the views set this themselves.
 -        if (preferences.getBoolean("DisallowOverscroll", false)) {
 -            appView.setOverScrollMode(View.OVER_SCROLL_NEVER);
 +        String r = preferences.getString("webView", null);
 +        CordovaWebView ret = null;
 +        if (r != null) {
 +            try {
 +                Class<?> webViewClass = Class.forName(r);
 +                Constructor<?> constructor = webViewClass.getConstructor(Context.class);
 +                ret = (CordovaWebView) constructor.newInstance((Context)this);
 +            } catch (ClassNotFoundException e) {
 +                e.printStackTrace();
 +            } catch (InstantiationException e) {
 +                e.printStackTrace();
 +            } catch (IllegalAccessException e) {
 +                e.printStackTrace();
 +            } catch (IllegalArgumentException e) {
 +                e.printStackTrace();
 +            } catch (InvocationTargetException e) {
 +                e.printStackTrace();
 +            } catch (NoSuchMethodException e) {
 +                e.printStackTrace();
 +            }
          }
-             
 -        createViews();
+ 
 -        // Wire the hardware volume controls to control media if desired.
 -        String volumePref = preferences.getString("DefaultVolumeStream", "");
 -        if ("media".equals(volumePref.toLowerCase(Locale.ENGLISH))) {
 -            setVolumeControlStream(AudioManager.STREAM_MUSIC);
 +        if (ret == null) {
 +            // If all else fails, return a default WebView
 +            ret = new AndroidWebView(this);
          }
 +        ret.init(this, pluginEntries, internalWhitelist, externalWhitelist, preferences);
 +        return ret;
      }
  
      /**
@@@ -280,16 -360,6 +268,7 @@@
          if (appView == null) {
              init();
          }
-         String splash = preferences.getString("SplashScreen", null);
-         if(splashscreenTime > 0 && splash != null)
-         {
-             this.splashscreen = getResources().getIdentifier(splash, "drawable", getClass().getPackage().getName());;
-             if(this.splashscreen != 0)
-             {
-                 this.showSplashScreen(splashscreenTime);
-             }
-         }
-         
++
          // If keepRunning
          this.keepRunning = preferences.getBoolean("KeepRunning", true);
  
@@@ -444,36 -592,63 +359,6 @@@
      }
  
      /**
-      * Show the spinner.  Must be called from the UI thread.
-      *
-      * @param title         Title of the dialog
-      * @param message       The message of the dialog
-      */
-     public void spinnerStart(final String title, final String message) {
-         if (this.spinnerDialog != null) {
-             this.spinnerDialog.dismiss();
-             this.spinnerDialog = null;
-         }
-         final CordovaActivity me = this;
-         this.spinnerDialog = ProgressDialog.show(CordovaActivity.this, title, message, true, true,
-                 new DialogInterface.OnCancelListener() {
-                     public void onCancel(DialogInterface dialog) {
-                         me.spinnerDialog = null;
-                     }
-                 });
-     }
- 
-     /**
-      * Stop spinner - Must be called from UI thread
-      */
-     public void spinnerStop() {
-         if (this.spinnerDialog != null && this.spinnerDialog.isShowing()) {
-             this.spinnerDialog.dismiss();
-             this.spinnerDialog = null;
-         }
-     }
- 
-     /**
 -     * Send a message to all plugins.
 -     */
 -    public void postMessage(String id, Object data) {
 -        if (this.appView != null) {
 -            this.appView.postMessage(id, data);
 -        }
 -    }
 -
 -    /**
 -     * @deprecated
 -     * Add services to res/xml/plugins.xml instead.
 -     *
 -     * Add a class that implements a service.
 -     */
 -    @Deprecated
 -    public void addService(String serviceType, String className) {
 -        if (this.appView != null && this.appView.pluginManager != null) {
 -            this.appView.pluginManager.addService(serviceType, className);
 -        }
 -    }
 -
 -    /**
 -     * Send JavaScript statement back to JavaScript.
 -     * (This is a convenience method)
 -     *
 -     * @param statement
 -     */
 -    @Deprecated // Call method on appView directly.
 -    public void sendJavascript(String statement) {
 -        if (this.appView != null) {
 -            this.appView.bridge.getMessageQueue().addJavaScript(statement);
 -        }
 -    }
 -
 -    /**
 -     * Show the spinner.  Must be called from the UI thread.
 -     *
 -     * @param title         Title of the dialog
 -     * @param message       The message of the dialog
 -     */
 -    @Deprecated // Call this directly on SplashScreen plugin instead.
 -    public void spinnerStart(final String title, final String message) {
 -        JSONArray args = new JSONArray();
 -        args.put(title);
 -        args.put(message);
 -        doSplashScreenAction("spinnerStart", args);
 -    }
 -
 -    /**
 -     * Stop spinner - Must be called from UI thread
 -     */
 -    @Deprecated // Call this directly on SplashScreen plugin instead.
 -    public void spinnerStop() {
 -        doSplashScreenAction("spinnerStop", null);
 -    }
 -
 -    /**
       * End this activity by calling finish for activity
       */
      public void endActivity() {
@@@ -632,71 -810,97 +515,13 @@@
  
      @Override
      public boolean onOptionsItemSelected(MenuItem item) {
 -        this.postMessage("onOptionsItemSelected", item);
 -        return true;
 -    }
 -
 -    /**
 -     * Get Activity context.
 -     */
 -    @Deprecated
 -    public Context getContext() {
 -        LOG.d(TAG, "This will be deprecated December 2012");
 -        return this;
 -    }
 -
 -    /**
 -     * Load the specified URL in the Cordova webview or a new browser instance.
 -     *
 -     * NOTE: If openExternal is false, only URLs listed in whitelist can be loaded.
 -     *
 -     * @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        Parameters for new app
 -     */
 -    @Deprecated // Call method on appView directly.
 -    public void showWebPage(String url, boolean openExternal, boolean clearHistory, HashMap<String, Object> params) {
 -        if (this.appView != null) {
 -            appView.showWebPage(url, openExternal, clearHistory, params);
 -        }
 -    }
 -
 -    private void doSplashScreenAction(String action, JSONArray args) {
 -        CordovaPlugin p = appView.pluginManager.getPlugin("org.apache.cordova.splashscreeninternal");
 -        if (p != null) {
 -            args = args == null ? new JSONArray() : args;
 -            try {
 -                p.execute(action, args, null);
 -            } catch (JSONException e) {
 -                e.printStackTrace();
 -            }
 +        if (appView != null) {
 +            appView.getPluginManager().postMessage("onOptionsItemSelected", item);
          }
 +        return true;
      }
  
-     protected Dialog splashDialog;
- 
-     /**
-      * Removes the Dialog that displays the splash screen
-      */
-     public void removeSplashScreen() {
-         if (splashDialog != null && splashDialog.isShowing()) {
-             splashDialog.dismiss();
-             splashDialog = null;
-         }
-     }
- 
-     /**
-      * Shows the splash screen over the full Activity
-      */
-     @SuppressWarnings("deprecation")
-     protected void showSplashScreen(final int time) {
-         final CordovaActivity that = this;
- 
-         Runnable runnable = new Runnable() {
-             public void run() {
-                 // Get reference to display
-                 Display display = getWindowManager().getDefaultDisplay();
- 
-                 // Create the layout for the dialog
-                 LinearLayout root = new LinearLayout(that.getActivity());
-                 root.setMinimumHeight(display.getHeight());
-                 root.setMinimumWidth(display.getWidth());
-                 root.setOrientation(LinearLayout.VERTICAL);
-                 root.setBackgroundColor(preferences.getInteger("backgroundColor", Color.BLACK));
-                 root.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
-                         ViewGroup.LayoutParams.MATCH_PARENT, 0.0F));
-                 root.setBackgroundResource(that.splashscreen);
-                 
-                 // Create and show the dialog
-                 splashDialog = new Dialog(that, android.R.style.Theme_Translucent_NoTitleBar);
-                 // check to see if the splash screen should be full screen
-                 if ((getWindow().getAttributes().flags & WindowManager.LayoutParams.FLAG_FULLSCREEN)
-                         == WindowManager.LayoutParams.FLAG_FULLSCREEN) {
-                     splashDialog.getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
-                             WindowManager.LayoutParams.FLAG_FULLSCREEN);
-                 }
-                 splashDialog.setContentView(root);
-                 splashDialog.setCancelable(false);
-                 splashDialog.show();
- 
-                 // Set Runnable to remove splash screen just in case
-                 final Handler handler = new Handler();
-                 handler.postDelayed(new Runnable() {
-                     public void run() {
-                         removeSplashScreen();
-                     }
-                 }, time);
-             }
-         };
-         this.runOnUiThread(runnable);
-     }
- 
      /**
 -     * Removes the Dialog that displays the splash screen
 -     */
 -    @Deprecated
 -    public void removeSplashScreen() {
 -        doSplashScreenAction("hide", null);
 -    }
 -
 -    /**
 -     * Shows the splash screen over the full Activity
 -     */
 -    @SuppressWarnings("deprecation")
 -    @Deprecated
 -    protected void showSplashScreen(final int time) {
 -        preferences.set("SplashScreenDelay", time);
 -        doSplashScreenAction("show", null);
 -    }
 -
 -    @Override
 -    public boolean onKeyUp(int keyCode, KeyEvent event)
 -    {
 -        if (appView != null && (appView.isCustomViewShowing() || appView.getFocusedChild() != null ) &&
 -                (keyCode == KeyEvent.KEYCODE_BACK || keyCode == KeyEvent.KEYCODE_MENU)) {
 -            return appView.onKeyUp(keyCode, event);
 -        } else {
 -            return super.onKeyUp(keyCode, event);
 -    	}
 -    }
 -    
 -    /*
 -     * Android 2.x needs to be able to check where the cursor is.  Android 4.x does not
 -     * 
 -     * (non-Javadoc)
 -     * @see android.app.Activity#onKeyDown(int, android.view.KeyEvent)
 -     */
 -    
 -    @Override
 -    public boolean onKeyDown(int keyCode, KeyEvent event)
 -    {
 -        //Determine if the focus is on the current view or not
 -        if (appView != null && appView.getFocusedChild() != null && (keyCode == KeyEvent.KEYCODE_BACK || keyCode == KeyEvent.KEYCODE_MENU)) {
 -                    return appView.onKeyDown(keyCode, event);
 -        }
 -        else
 -            return super.onKeyDown(keyCode, event);
 -    }
 -    
 -    
 -    /**
       * Called when a message is sent to plugin.
       *
       * @param id            The message id


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@cordova.apache.org
For additional commands, e-mail: commits-help@cordova.apache.org


[4/6] android commit: CB-3679 Move splashscreen logic into splashscreen plugin

Posted by ag...@apache.org.
CB-3679 Move splashscreen logic into splashscreen plugin

Tried as hard as possible for this not to be a breaking change (all
symbols were preserved). Planning to remove delegating symbols in 4.0.x
though.

Also for backwards compatability - a copy of the plugin is bundled. It
will likewise be removed in 4.0.x


Project: http://git-wip-us.apache.org/repos/asf/cordova-android/repo
Commit: http://git-wip-us.apache.org/repos/asf/cordova-android/commit/27f1181d
Tree: http://git-wip-us.apache.org/repos/asf/cordova-android/tree/27f1181d
Diff: http://git-wip-us.apache.org/repos/asf/cordova-android/diff/27f1181d

Branch: refs/heads/4.0.x
Commit: 27f1181d53a6feca77f5bb645892f7d357a6f35a
Parents: f953e6a
Author: Andrew Grieve <ag...@chromium.org>
Authored: Mon Nov 17 23:46:39 2014 -0800
Committer: Andrew Grieve <ag...@chromium.org>
Committed: Wed Dec 10 15:40:03 2014 -0500

----------------------------------------------------------------------
 .../src/org/apache/cordova/CordovaActivity.java | 198 +++------------
 .../src/org/apache/cordova/CordovaWebView.java  |  11 +-
 .../apache/cordova/SplashScreenInternal.java    | 252 +++++++++++++++++++
 3 files changed, 296 insertions(+), 165 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cordova-android/blob/27f1181d/framework/src/org/apache/cordova/CordovaActivity.java
----------------------------------------------------------------------
diff --git a/framework/src/org/apache/cordova/CordovaActivity.java b/framework/src/org/apache/cordova/CordovaActivity.java
index 816b12b..b61fa98 100755
--- a/framework/src/org/apache/cordova/CordovaActivity.java
+++ b/framework/src/org/apache/cordova/CordovaActivity.java
@@ -24,17 +24,13 @@ import java.util.Locale;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
 
-import org.apache.cordova.CordovaInterface;
-import org.apache.cordova.CordovaPlugin;
-import org.apache.cordova.LOG;
+import org.json.JSONArray;
 import org.json.JSONException;
 import org.json.JSONObject;
 
 import android.annotation.SuppressLint;
 import android.app.Activity;
 import android.app.AlertDialog;
-import android.app.Dialog;
-import android.app.ProgressDialog;
 import android.content.Context;
 import android.content.DialogInterface;
 import android.content.Intent;
@@ -42,7 +38,6 @@ import android.graphics.Color;
 import android.media.AudioManager;
 import android.net.Uri;
 import android.os.Bundle;
-import android.os.Handler;
 import android.util.Log;
 import android.view.Display;
 import android.view.KeyEvent;
@@ -100,7 +95,6 @@ public class CordovaActivity extends Activity implements CordovaInterface {
     @Deprecated // Will be removed. Use findViewById() to retrieve views.
     protected LinearLayout root;
 
-    protected ProgressDialog spinnerDialog = null;
     private final ExecutorService threadPool = Executors.newCachedThreadPool();
 
     private static int ACTIVITY_STARTING = 0;
@@ -117,9 +111,10 @@ public class CordovaActivity extends Activity implements CordovaInterface {
      */
 
     // Draw a splash screen using an image located in the drawable resource directory.
-    // This is not the same as calling super.loadSplashscreen(url)
+    @Deprecated // Use "SplashScreen" preference instead.
     protected int splashscreen = 0;
-    protected int splashscreenTime = 3000;
+    @Deprecated // Use "SplashScreenDelay" preference instead.
+    protected int splashscreenTime = -1;
 
     // LoadUrl timeout value in msec (default of 20 sec)
     protected int loadUrlTimeoutValue = 20000;
@@ -267,9 +262,6 @@ public class CordovaActivity extends Activity implements CordovaInterface {
                 ViewGroup.LayoutParams.MATCH_PARENT,
                 1.0F));
 
-        // Add web view but make it invisible while loading URL
-        appView.setVisibility(View.INVISIBLE);
-        
         // need to remove appView from any existing parent before invoking root.addView(appView)
         ViewParent parent = appView.getParent();
         if ((parent != null) && (parent != root)) {
@@ -334,6 +326,13 @@ public class CordovaActivity extends Activity implements CordovaInterface {
     public void init(CordovaWebView webView, CordovaWebViewClient webViewClient, CordovaChromeClient webChromeClient) {
         LOG.d(TAG, "CordovaActivity.init()");
 
+        if (splashscreenTime >= 0) {
+            preferences.set("SplashScreenDelay", splashscreenTime);
+        }
+        if (splashscreen != 0) {
+            preferences.set("SplashDrawableId", splashscreen);
+        }
+
         appView = webView != null ? webView : makeWebView();
         if (appView.pluginManager == null) {
             appView.init(this, webViewClient != null ? webViewClient : makeWebViewClient(appView),
@@ -361,36 +360,10 @@ public class CordovaActivity extends Activity implements CordovaInterface {
         if (appView == null) {
             init();
         }
-        this.splashscreenTime = preferences.getInteger("SplashScreenDelay", this.splashscreenTime);
-        String splash = preferences.getString("SplashScreen", null);
-        if(this.splashscreenTime > 0 && splash != null)
-        {
-            this.splashscreen = getResources().getIdentifier(splash, "drawable", getClass().getPackage().getName());;
-            if(this.splashscreen != 0)
-            {
-                this.showSplashScreen(this.splashscreenTime);
-            }
-        }
-        
         // If keepRunning
         this.keepRunning = preferences.getBoolean("KeepRunning", true);
 
-        //Check if the view is attached to anything
-        if(appView.getParent() != null)
-        {
-            // Then load the spinner
-            this.loadSpinner();
-        }
-        //Load the correct splashscreen
-        
-        if(this.splashscreen != 0)
-        {
-            this.appView.loadUrl(url, this.splashscreenTime);
-        }
-        else
-        {
-            this.appView.loadUrl(url);
-        }
+        appView.loadUrlIntoView(url, true);
     }
 
     /**
@@ -400,44 +373,12 @@ public class CordovaActivity extends Activity implements CordovaInterface {
      * @param url
      * @param time              The number of ms to wait before loading webview
      */
+    @Deprecated // Use loadUrl(String url) instead.
     public void loadUrl(final String url, int time) {
 
         this.splashscreenTime = time;
         this.loadUrl(url);
     }
-    
-    /*
-     * Load the spinner
-     */
-    void loadSpinner() {
-
-        // If loadingDialog property, then show the App loading dialog for first page of app
-        String loading = null;
-        if ((this.appView == null) || !this.appView.canGoBack()) {
-            loading = preferences.getString("LoadingDialog", null);
-        }
-        else {
-            loading = preferences.getString("LoadingPageDialog", null);
-        }
-        if (loading != null) {
-
-            String title = "";
-            String message = "Loading Application...";
-
-            if (loading.length() > 0) {
-                int comma = loading.indexOf(',');
-                if (comma > 0) {
-                    title = loading.substring(0, comma);
-                    message = loading.substring(comma + 1);
-                }
-                else {
-                    title = "";
-                    message = loading;
-                }
-            }
-            this.spinnerStart(title, message);
-        }
-    }
 
     @Deprecated
     public void cancelLoadUrl() {
@@ -588,9 +529,6 @@ public class CordovaActivity extends Activity implements CordovaInterface {
         {
             this.appView.handlePause(this.keepRunning);
         }
-
-        // hide the splash screen to avoid leaking a window
-        this.removeSplashScreen();
     }
 
     /**
@@ -645,9 +583,6 @@ public class CordovaActivity extends Activity implements CordovaInterface {
         LOG.d(TAG, "CordovaActivity.onDestroy()");
         super.onDestroy();
 
-        // hide the splash screen to avoid leaking a window
-        this.removeSplashScreen();
-
         if (this.appView != null) {
             appView.handleDestroy();
         }
@@ -697,28 +632,20 @@ public class CordovaActivity extends Activity implements CordovaInterface {
      * @param title         Title of the dialog
      * @param message       The message of the dialog
      */
+    @Deprecated // Call this directly on SplashScreen plugin instead.
     public void spinnerStart(final String title, final String message) {
-        if (this.spinnerDialog != null) {
-            this.spinnerDialog.dismiss();
-            this.spinnerDialog = null;
-        }
-        final CordovaActivity me = this;
-        this.spinnerDialog = ProgressDialog.show(CordovaActivity.this, title, message, true, true,
-                new DialogInterface.OnCancelListener() {
-                    public void onCancel(DialogInterface dialog) {
-                        me.spinnerDialog = null;
-                    }
-                });
+        JSONArray args = new JSONArray();
+        args.put(title);
+        args.put(message);
+        doSplashScreenAction("spinnerStart", args);
     }
 
     /**
      * Stop spinner - Must be called from UI thread
      */
+    @Deprecated // Call this directly on SplashScreen plugin instead.
     public void spinnerStop() {
-        if (this.spinnerDialog != null && this.spinnerDialog.isShowing()) {
-            this.spinnerDialog.dismiss();
-            this.spinnerDialog = null;
-        }
+        doSplashScreenAction("spinnerStop", null);
     }
 
     /**
@@ -810,8 +737,6 @@ public class CordovaActivity extends Activity implements CordovaInterface {
             // Load URL on UI thread
             me.runOnUiThread(new Runnable() {
                 public void run() {
-                    // Stop "app loading" spinner if showing
-                    me.spinnerStop();
                     me.appView.showWebPage(errorUrl, false, true, null);
                 }
             });
@@ -915,62 +840,34 @@ public class CordovaActivity extends Activity implements CordovaInterface {
         }
     }
 
-    protected Dialog splashDialog;
+    private void doSplashScreenAction(String action, JSONArray args) {
+        CordovaPlugin p = appView.pluginManager.getPlugin("org.apache.cordova.splashscreeninternal");
+        if (p != null) {
+            args = args == null ? new JSONArray() : args;
+            try {
+                p.execute(action, args, null);
+            } catch (JSONException e) {
+                e.printStackTrace();
+            }
+        }
+    }
 
     /**
      * Removes the Dialog that displays the splash screen
      */
+    @Deprecated
     public void removeSplashScreen() {
-        if (splashDialog != null && splashDialog.isShowing()) {
-            splashDialog.dismiss();
-            splashDialog = null;
-        }
+        doSplashScreenAction("hide", null);
     }
 
     /**
      * Shows the splash screen over the full Activity
      */
     @SuppressWarnings("deprecation")
+    @Deprecated
     protected void showSplashScreen(final int time) {
-        final CordovaActivity that = this;
-
-        Runnable runnable = new Runnable() {
-            public void run() {
-                // Get reference to display
-                Display display = getWindowManager().getDefaultDisplay();
-
-                // Create the layout for the dialog
-                LinearLayout root = new LinearLayout(that.getActivity());
-                root.setMinimumHeight(display.getHeight());
-                root.setMinimumWidth(display.getWidth());
-                root.setOrientation(LinearLayout.VERTICAL);
-                root.setBackgroundColor(preferences.getInteger("backgroundColor", Color.BLACK));
-                root.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
-                        ViewGroup.LayoutParams.MATCH_PARENT, 0.0F));
-                root.setBackgroundResource(that.splashscreen);
-                
-                // Create and show the dialog
-                splashDialog = new Dialog(that, android.R.style.Theme_Translucent_NoTitleBar);
-                // check to see if the splash screen should be full screen
-                if ((getWindow().getAttributes().flags & WindowManager.LayoutParams.FLAG_FULLSCREEN)
-                        == WindowManager.LayoutParams.FLAG_FULLSCREEN) {
-                    splashDialog.getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
-                            WindowManager.LayoutParams.FLAG_FULLSCREEN);
-                }
-                splashDialog.setContentView(root);
-                splashDialog.setCancelable(false);
-                splashDialog.show();
-
-                // Set Runnable to remove splash screen just in case
-                final Handler handler = new Handler();
-                handler.postDelayed(new Runnable() {
-                    public void run() {
-                        removeSplashScreen();
-                    }
-                }, time);
-            }
-        };
-        this.runOnUiThread(runnable);
+        preferences.set("SplashScreenDelay", time);
+        doSplashScreenAction("show", null);
     }
 
     @Override
@@ -1015,28 +912,7 @@ public class CordovaActivity extends Activity implements CordovaInterface {
             LOG.d(TAG, "onMessage(" + id + "," + data + ")");
         }
 
-        if ("splashscreen".equals(id)) {
-            if ("hide".equals(data.toString())) {
-                this.removeSplashScreen();
-            }
-            else {
-                // If the splash dialog is showing don't try to show it again
-                if (this.splashDialog == null || !this.splashDialog.isShowing()) {
-                    String splashResource = preferences.getString("SplashScreen", null);
-                    if (splashResource != null) {
-                        splashscreen = getResources().getIdentifier(splashResource, "drawable", getClass().getPackage().getName());
-                    }
-                    this.showSplashScreen(this.splashscreenTime);
-                }
-            }
-        }
-        else if ("spinner".equals(id)) {
-            if ("stop".equals(data.toString())) {
-                this.spinnerStop();
-                this.appView.setVisibility(View.VISIBLE);
-            }
-        }
-        else if ("onReceivedError".equals(id)) {
+        if ("onReceivedError".equals(id)) {
             JSONObject d = (JSONObject) data;
             try {
                 this.onReceivedError(d.getInt("errorCode"), d.getString("description"), d.getString("url"));

http://git-wip-us.apache.org/repos/asf/cordova-android/blob/27f1181d/framework/src/org/apache/cordova/CordovaWebView.java
----------------------------------------------------------------------
diff --git a/framework/src/org/apache/cordova/CordovaWebView.java b/framework/src/org/apache/cordova/CordovaWebView.java
index 62a641b..feec5a9 100755
--- a/framework/src/org/apache/cordova/CordovaWebView.java
+++ b/framework/src/org/apache/cordova/CordovaWebView.java
@@ -156,6 +156,9 @@ public class CordovaWebView extends WebView {
         resourceApi = new CordovaResourceApi(this.getContext(), pluginManager);
 
         pluginManager.addService("App", "org.apache.cordova.App");
+        // This will be removed in 4.0.x in favour of the plugin not being bundled.
+        pluginManager.addService(new PluginEntry("SplashScreenInternal", "org.apache.cordova.SplashScreenInternal", true));
+        pluginManager.init();
         initWebViewSettings();
         exposeJsInterface();
     }
@@ -383,8 +386,11 @@ public class CordovaWebView extends WebView {
         initIfNecessary();
 
         if (recreatePlugins) {
+            // Don't re-initialize on first load.
+            if (loadedUrl != null) {
+                this.pluginManager.init();
+            }
             this.loadedUrl = url;
-            this.pluginManager.init();
         }
 
         // Create a timeout timer for loadUrl
@@ -462,9 +468,6 @@ public class CordovaWebView extends WebView {
         else {
 
             LOG.d(TAG, "loadUrlIntoView(%s, %d)", url, time);
-
-            // Send message to show splashscreen now if desired
-            this.postMessage("splashscreen", "show");
         }
 
         // Load url

http://git-wip-us.apache.org/repos/asf/cordova-android/blob/27f1181d/framework/src/org/apache/cordova/SplashScreenInternal.java
----------------------------------------------------------------------
diff --git a/framework/src/org/apache/cordova/SplashScreenInternal.java b/framework/src/org/apache/cordova/SplashScreenInternal.java
new file mode 100644
index 0000000..605fce7
--- /dev/null
+++ b/framework/src/org/apache/cordova/SplashScreenInternal.java
@@ -0,0 +1,252 @@
+/*
+       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 android.app.Dialog;
+import android.app.ProgressDialog;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.graphics.Color;
+import android.os.Handler;
+import android.view.Display;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.WindowManager;
+import android.widget.LinearLayout;
+
+import org.json.JSONArray;
+import org.json.JSONException;
+
+// This file is a copy of SplashScreen.java from cordova-plugin-splashscreen, and is required only
+// for pre-4.0 Cordova as a transition path to it being extracted into the plugin.
+public class SplashScreenInternal extends CordovaPlugin {
+    private static final String LOG_TAG = "SplashScreenInternal";
+    private static Dialog splashDialog;
+    private static ProgressDialog spinnerDialog;
+    private static boolean firstShow = true;
+
+    @Override
+    protected void pluginInitialize() {
+        if (!firstShow) {
+            return;
+        }
+        // Make WebView invisible while loading URL
+        webView.setVisibility(View.INVISIBLE);
+        int drawableId = preferences.getInteger("SplashDrawableId", 0);
+        if (drawableId == 0) {
+            String splashResource = preferences.getString("SplashScreen", null);
+            if (splashResource != null) {
+                drawableId = cordova.getActivity().getResources().getIdentifier(splashResource, "drawable", cordova.getActivity().getClass().getPackage().getName());
+                preferences.set("SplashDrawableId", drawableId);
+            }
+        }
+
+        firstShow = false;
+        loadSpinner();
+        showSplashScreen();
+    }
+
+    @Override
+    public void onPause(boolean multitasking) {
+        // hide the splash screen to avoid leaking a window
+        this.removeSplashScreen();
+    }
+
+    @Override
+    public void onDestroy() {
+        // hide the splash screen to avoid leaking a window
+        this.removeSplashScreen();
+        firstShow = true;
+    }
+
+    @Override
+    public boolean execute(String action, JSONArray args, CallbackContext callbackContext) throws JSONException {
+        if (action.equals("hide")) {
+            cordova.getActivity().runOnUiThread(new Runnable() {
+                public void run() {
+                    webView.postMessage("splashscreen", "hide");
+                }
+            });
+        } else if (action.equals("show")) {
+            cordova.getActivity().runOnUiThread(new Runnable() {
+                public void run() {
+                    webView.postMessage("splashscreen", "show");
+                }
+            });
+        } else if (action.equals("spinnerStart")) {
+            final String title = args.getString(0);
+            final String message = args.getString(1);
+            cordova.getActivity().runOnUiThread(new Runnable() {
+                public void run() {
+                    spinnerStart(title, message);
+                }
+            });
+        } else {
+            return false;
+        }
+
+        callbackContext.success();
+        return true;
+    }
+
+    @Override
+    public Object onMessage(String id, Object data) {
+        if ("splashscreen".equals(id)) {
+            if ("hide".equals(data.toString())) {
+                this.removeSplashScreen();
+            } else {
+                this.showSplashScreen();
+            }
+        } else if ("spinner".equals(id)) {
+            if ("stop".equals(data.toString())) {
+                this.spinnerStop();
+                webView.setVisibility(View.VISIBLE);
+            }
+        } else if ("onReceivedError".equals(id)) {
+            spinnerStop();
+        }
+        return null;
+    }
+
+    private void removeSplashScreen() {
+        cordova.getActivity().runOnUiThread(new Runnable() {
+            public void run() {
+                if (splashDialog != null && splashDialog.isShowing()) {
+                    splashDialog.dismiss();
+                    splashDialog = null;
+                }
+            }
+        });
+    }
+
+    /**
+     * Shows the splash screen over the full Activity
+     */
+    @SuppressWarnings("deprecation")
+    private void showSplashScreen() {
+        final int splashscreenTime = preferences.getInteger("SplashScreenDelay", 3000);
+        final int drawableId = preferences.getInteger("SplashDrawableId", 0);
+
+        // If the splash dialog is showing don't try to show it again
+        if (this.splashDialog != null && splashDialog.isShowing()) {
+            return;
+        }
+        if (drawableId == 0 || splashscreenTime <= 0) {
+            return;
+        }
+
+        cordova.getActivity().runOnUiThread(new Runnable() {
+            public void run() {
+                // Get reference to display
+                Display display = cordova.getActivity().getWindowManager().getDefaultDisplay();
+                Context context = webView.getContext();
+
+                // Create the layout for the dialog
+                LinearLayout root = new LinearLayout(context);
+                root.setMinimumHeight(display.getHeight());
+                root.setMinimumWidth(display.getWidth());
+                root.setOrientation(LinearLayout.VERTICAL);
+
+                // TODO: Use the background color of the webview's parent instead of using the
+                // preference.
+                root.setBackgroundColor(preferences.getInteger("backgroundColor", Color.BLACK));
+                root.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
+                        ViewGroup.LayoutParams.MATCH_PARENT, 0.0F));
+                root.setBackgroundResource(drawableId);
+
+                // Create and show the dialog
+                splashDialog = new Dialog(context, android.R.style.Theme_Translucent_NoTitleBar);
+                // check to see if the splash screen should be full screen
+                if ((cordova.getActivity().getWindow().getAttributes().flags & WindowManager.LayoutParams.FLAG_FULLSCREEN)
+                        == WindowManager.LayoutParams.FLAG_FULLSCREEN) {
+                    splashDialog.getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
+                            WindowManager.LayoutParams.FLAG_FULLSCREEN);
+                }
+                splashDialog.setContentView(root);
+                splashDialog.setCancelable(false);
+                splashDialog.show();
+
+                // Set Runnable to remove splash screen just in case
+                final Handler handler = new Handler();
+                handler.postDelayed(new Runnable() {
+                    public void run() {
+                        removeSplashScreen();
+                    }
+                }, splashscreenTime);
+            }
+        });
+    }
+
+    /*
+     * Load the spinner
+     */
+    private void loadSpinner() {
+        // If loadingDialog property, then show the App loading dialog for first page of app
+        String loading = null;
+        if (webView.canGoBack()) {
+            loading = preferences.getString("LoadingDialog", null);
+        }
+        else {
+            loading = preferences.getString("LoadingPageDialog", null);
+        }
+        if (loading != null) {
+            String title = "";
+            String message = "Loading Application...";
+
+            if (loading.length() > 0) {
+                int comma = loading.indexOf(',');
+                if (comma > 0) {
+                    title = loading.substring(0, comma);
+                    message = loading.substring(comma + 1);
+                }
+                else {
+                    title = "";
+                    message = loading;
+                }
+            }
+            spinnerStart(title, message);
+        }
+    }
+
+    private void spinnerStart(final String title, final String message) {
+        cordova.getActivity().runOnUiThread(new Runnable() {
+            public void run() {
+                spinnerStop();
+                spinnerDialog = ProgressDialog.show(webView.getContext(), title, message, true, true,
+                        new DialogInterface.OnCancelListener() {
+                            public void onCancel(DialogInterface dialog) {
+                                spinnerDialog = null;
+                            }
+                        });
+            }
+        });
+    }
+
+    private void spinnerStop() {
+        cordova.getActivity().runOnUiThread(new Runnable() {
+            public void run() {
+                if (spinnerDialog != null && spinnerDialog.isShowing()) {
+                    spinnerDialog.dismiss();
+                    spinnerDialog = null;
+                }
+            }
+        });
+    }
+}


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@cordova.apache.org
For additional commands, e-mail: commits-help@cordova.apache.org


[2/6] android commit: Revert Gradle distributionUrlRegex cleanup.

Posted by ag...@apache.org.
Revert Gradle distributionUrlRegex cleanup.

This reverts commit 75a0a6752a77e2e0f491ae4de7137f5163c7a4bd.


Project: http://git-wip-us.apache.org/repos/asf/cordova-android/repo
Commit: http://git-wip-us.apache.org/repos/asf/cordova-android/commit/ffd14fe7
Tree: http://git-wip-us.apache.org/repos/asf/cordova-android/tree/ffd14fe7
Diff: http://git-wip-us.apache.org/repos/asf/cordova-android/diff/ffd14fe7

Branch: refs/heads/4.0.x
Commit: ffd14fe7d95161f98ce88dc9f8b58046a51bbd79
Parents: 66fa12a
Author: Brian Geppert <br...@briangeppert.com>
Authored: Tue Dec 9 11:57:29 2014 -0600
Committer: Max Woghiren <ma...@gmail.com>
Committed: Tue Dec 9 14:23:38 2014 -0500

----------------------------------------------------------------------
 bin/templates/cordova/lib/build.js | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cordova-android/blob/ffd14fe7/bin/templates/cordova/lib/build.js
----------------------------------------------------------------------
diff --git a/bin/templates/cordova/lib/build.js b/bin/templates/cordova/lib/build.js
index 0d4d004..fead206 100644
--- a/bin/templates/cordova/lib/build.js
+++ b/bin/templates/cordova/lib/build.js
@@ -239,7 +239,8 @@ var builders = {
 
                 // If the gradle distribution URL is set, make sure it points to version 1.12.
                 // If it's not set, do nothing, assuming that we're using a future version of gradle that we don't want to mess with.
-                var distributionUrlRegex = '/^distributionUrl=.*$/';
+                // For some reason, using ^ and $ don't work.  This does the job, though.
+                var distributionUrlRegex = /distributionUrl.*zip/;
                 var distributionUrl = 'distributionUrl=http\\://services.gradle.org/distributions/gradle-1.12-all.zip';
                 var gradleWrapperPropertiesPath = path.join(projectPath, 'gradle', 'wrapper', 'gradle-wrapper.properties');
                 shell.sed('-i', distributionUrlRegex, distributionUrl, gradleWrapperPropertiesPath);


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@cordova.apache.org
For additional commands, e-mail: commits-help@cordova.apache.org


[5/6] android commit: Add a section for plugin extensions

Posted by ag...@apache.org.
Add a section for plugin extensions

The build.gradle will apply gradle srcipte from plugin extension
When install the plugin with "gradleReference" framework.
The gradle can set ext.multiarch=true to support multiple APKs by
default, so add this section in here.


Project: http://git-wip-us.apache.org/repos/asf/cordova-android/repo
Commit: http://git-wip-us.apache.org/repos/asf/cordova-android/commit/ba140a8a
Tree: http://git-wip-us.apache.org/repos/asf/cordova-android/tree/ba140a8a
Diff: http://git-wip-us.apache.org/repos/asf/cordova-android/diff/ba140a8a

Branch: refs/heads/4.0.x
Commit: ba140a8a84ea770e9d9cff48cec606122ed0de04
Parents: 27f1181
Author: fujunwei <ju...@intel.com>
Authored: Mon Nov 17 15:52:40 2014 +0800
Committer: Andrew Grieve <ag...@chromium.org>
Committed: Wed Dec 10 15:44:39 2014 -0500

----------------------------------------------------------------------
 bin/templates/project/build.gradle | 3 +++
 1 file changed, 3 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cordova-android/blob/ba140a8a/bin/templates/project/build.gradle
----------------------------------------------------------------------
diff --git a/bin/templates/project/build.gradle b/bin/templates/project/build.gradle
index 5a997e6..f427561 100644
--- a/bin/templates/project/build.gradle
+++ b/bin/templates/project/build.gradle
@@ -50,6 +50,9 @@ task wrapper(type: Wrapper) {
     gradleVersion = '1.12'
 }
 
+// PLUGIN GRADLE EXTENSIONS START
+// PLUGIN GRADLE EXTENSIONS END
+
 ext.multiarch=false
 
 android {


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@cordova.apache.org
For additional commands, e-mail: commits-help@cordova.apache.org


[3/6] android commit: CB-8143: Use the correct Android Gradle plugin for the installed Gradle version

Posted by ag...@apache.org.
CB-8143: Use the correct Android Gradle plugin for the installed Gradle version


Project: http://git-wip-us.apache.org/repos/asf/cordova-android/repo
Commit: http://git-wip-us.apache.org/repos/asf/cordova-android/commit/f953e6ad
Tree: http://git-wip-us.apache.org/repos/asf/cordova-android/tree/f953e6ad
Diff: http://git-wip-us.apache.org/repos/asf/cordova-android/diff/f953e6ad

Branch: refs/heads/4.0.x
Commit: f953e6adb8478edee1d68419cba35d35a400b641
Parents: ffd14fe
Author: Ian Clelland <ic...@chromium.org>
Authored: Wed Dec 10 10:07:05 2014 -0500
Committer: Ian Clelland <ic...@chromium.org>
Committed: Wed Dec 10 10:07:05 2014 -0500

----------------------------------------------------------------------
 bin/templates/project/build.gradle | 14 ++++++++++++--
 1 file changed, 12 insertions(+), 2 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cordova-android/blob/f953e6ad/bin/templates/project/build.gradle
----------------------------------------------------------------------
diff --git a/bin/templates/project/build.gradle b/bin/templates/project/build.gradle
index 63ecd81..5a997e6 100644
--- a/bin/templates/project/build.gradle
+++ b/bin/templates/project/build.gradle
@@ -31,8 +31,18 @@ buildscript {
         mavenCentral()
     }
 
-    dependencies {
-        classpath 'com.android.tools.build:gradle:0.12.0+'
+    // Switch the Android Gradle plugin version requirement depending on the
+    // installed version of Gradle. This dependency is documented at
+    // http://tools.android.com/tech-docs/new-build-system/version-compatibility
+    // and https://issues.apache.org/jira/browse/CB-8143
+    if (gradle.gradleVersion >= "2.1") {
+        dependencies {
+            classpath 'com.android.tools.build:gradle:0.14.0+'
+        }
+    } else {
+        dependencies {
+            classpath 'com.android.tools.build:gradle:0.12.0+'
+        }
     }
 }
 


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@cordova.apache.org
For additional commands, e-mail: commits-help@cordova.apache.org