Return-Path: X-Original-To: apmail-cordova-commits-archive@www.apache.org Delivered-To: apmail-cordova-commits-archive@www.apache.org Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by minotaur.apache.org (Postfix) with SMTP id 903B611E1F for ; Mon, 7 Jul 2014 20:39:13 +0000 (UTC) Received: (qmail 90633 invoked by uid 500); 7 Jul 2014 20:39:13 -0000 Delivered-To: apmail-cordova-commits-archive@cordova.apache.org Received: (qmail 90522 invoked by uid 500); 7 Jul 2014 20:39:13 -0000 Mailing-List: contact commits-help@cordova.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@cordova.apache.org Delivered-To: mailing list commits@cordova.apache.org Received: (qmail 90500 invoked by uid 99); 7 Jul 2014 20:39:13 -0000 Received: from tyr.zones.apache.org (HELO tyr.zones.apache.org) (140.211.11.114) by apache.org (qpsmtpd/0.29) with ESMTP; Mon, 07 Jul 2014 20:39:13 +0000 Received: by tyr.zones.apache.org (Postfix, from userid 65534) id 292768ADF19; Mon, 7 Jul 2014 20:39:13 +0000 (UTC) Content-Type: text/plain; charset="us-ascii" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit From: agrieve@apache.org To: commits@cordova.apache.org Date: Mon, 07 Jul 2014 20:39:15 -0000 Message-Id: In-Reply-To: <7e13a2dd7a39451eaf09dcc7f31b228f@git.apache.org> References: <7e13a2dd7a39451eaf09dcc7f31b228f@git.apache.org> X-Mailer: ASF-Git Admin Mailer Subject: [3/3] android commit: Merge branch 'master' into 4.0.x (tweaks to setWebViewClient) Merge branch 'master' into 4.0.x (tweaks to setWebViewClient) Conflicts: framework/src/org/apache/cordova/CordovaWebView.java framework/src/org/apache/cordova/CordovaWebViewClient.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/67006add Tree: http://git-wip-us.apache.org/repos/asf/cordova-android/tree/67006add Diff: http://git-wip-us.apache.org/repos/asf/cordova-android/diff/67006add Branch: refs/heads/4.0.x Commit: 67006add53ff6470e8c00716edf32a63b6cecedf Parents: 1571b26 caeb868 Author: Andrew Grieve Authored: Mon Jul 7 16:38:50 2014 -0400 Committer: Andrew Grieve Committed: Mon Jul 7 16:38:50 2014 -0400 ---------------------------------------------------------------------- .../src/org/apache/cordova/AndroidWebView.java | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/cordova-android/blob/67006add/framework/src/org/apache/cordova/AndroidWebView.java ---------------------------------------------------------------------- diff --cc framework/src/org/apache/cordova/AndroidWebView.java index 3604082,0000000..417fcb3 mode 100755,000000..100755 --- a/framework/src/org/apache/cordova/AndroidWebView.java +++ b/framework/src/org/apache/cordova/AndroidWebView.java @@@ -1,919 -1,0 +1,929 @@@ +/* + 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 java.util.Locale; + +import org.apache.cordova.Config; +import org.apache.cordova.CordovaInterface; +import org.apache.cordova.LOG; +import org.apache.cordova.PluginManager; +import org.apache.cordova.PluginResult; + +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 WebView guide + * @see WebView + */ +public class AndroidWebView extends WebView implements CordovaWebView { + + public static final String TAG = "AndroidWebView"; + + private HashSet boundKeyCodes = new HashSet(); + + PluginManager pluginManager; + private boolean paused; + + private BroadcastReceiver receiver; + + + /** Activities and other important classes **/ + private CordovaInterface cordova; + CordovaWebViewClient viewClient; + @SuppressWarnings("unused") + private CordovaChromeClient chromeClient; + + private String url; + + // Flag to track that a loadUrl timeout occurred + int loadUrlTimeout = 0; + + private long lastMenuEventTime = 0; + + NativeToJsMessageQueue jsMessageQueue; + AndroidExposedJsApi exposedJsApi; + + /** custom view created by the browser (a video player for example) */ + private View mCustomView; + private WebChromeClient.CustomViewCallback mCustomViewCallback; + + private ActivityResult mResult = null; + + private CordovaResourceApi resourceApi; + + class ActivityResult { + + int request; + int result; + Intent incoming; + + public ActivityResult(int req, int res, Intent intent) { + request = req; + result = res; + incoming = intent; + } + + + } + + 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, CordovaWebViewClient webViewClient, CordovaChromeClient webChromeClient, List pluginEntries) { + if (this.cordova != null) { + throw new IllegalStateException(); + } + this.cordova = cordova; + setWebChromeClient(webChromeClient); + setWebViewClient(webViewClient); + + pluginManager = new PluginManager(this, this.cordova, pluginEntries); + jsMessageQueue = new NativeToJsMessageQueue(this, cordova); + exposedJsApi = new AndroidExposedJsApi(pluginManager, jsMessageQueue); + resourceApi = new CordovaResourceApi(this.getContext(), pluginManager); + + initWebViewSettings(); + 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.ICE_CREAM_SANDWICH_MR1) + Level16Apis.enableUniversalAccess(settings); + // 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 + public CordovaChromeClient makeWebChromeClient(CordovaInterface cordova) { + return new AndroidChromeClient(cordova, this); + } + + @Override + public CordovaWebViewClient makeWebViewClient(CordovaInterface cordova) { + if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.ICE_CREAM_SANDWICH) { + return new AndroidWebViewClient(cordova, this); + } + return new IceCreamCordovaWebViewClient(cordova, this); + } + + /** + * 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; + } + this.addJavascriptInterface(exposedJsApi, "_cordovaNative"); + } + + @Override ++ public void setWebViewClient(WebViewClient client) { ++ this.viewClient = (CordovaWebViewClient)client; ++ super.setWebViewClient(client); ++ } ++ ++ @Override ++ public void setWebChromeClient(WebChromeClient client) { ++ this.chromeClient = (CordovaChromeClient)client; ++ super.setWebChromeClient(client); ++ } ++ ++ @Override + public void setWebViewClient(CordovaWebViewClient client) { - this.viewClient = client; - super.setWebViewClient((WebViewClient) client); ++ setWebViewClient((WebViewClient) client); + } + + @Override + public void setWebChromeClient(CordovaChromeClient client) { - this.chromeClient = client; - super.setWebChromeClient((WebChromeClient) client); ++ setWebChromeClient((WebChromeClient) client); + } + + public CordovaChromeClient getWebChromeClient() { + return this.chromeClient; + } + + /** + * Load the url into the webview. + * + * @param url + */ + @Override + public void loadUrl(String url) { + if (url.equals("about:blank") || url.startsWith("javascript:")) { + this.loadUrlNow(url); + } + else { + this.loadUrlIntoView(url); + } + } + + /** + * Load the url into the webview after waiting for period of time. + * This is used to display the splashscreen for certain amount of time. + * + * @param url + * @param time The number of ms to wait before loading webview + */ + @Deprecated + public void loadUrl(final String url, int time) { + if(url == null) + { + this.loadUrlIntoView(Config.getStartUrl()); + } + else + { + this.loadUrlIntoView(url); + } + } + + public void loadUrlIntoView(final String url) { + loadUrlIntoView(url, true); + } + + /** + * Load the url into the webview. + * + * @param url + */ + public void loadUrlIntoView(final String url, boolean recreatePlugins) { + LOG.d(TAG, ">>> loadUrl(" + url + ")"); + + if (recreatePlugins) { + this.url = url; + this.pluginManager.init(); + } + + // Create a timeout timer for loadUrl + final AndroidWebView me = this; + final int currentLoadUrlTimeout = me.loadUrlTimeout; + final int loadUrlTimeoutValue = Integer.parseInt(this.getProperty("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(-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. + * + * @param url + */ + public void loadUrlNow(String url) { + if (LOG.isLoggable(LOG.DEBUG) && !url.startsWith("javascript:")) { + LOG.d(TAG, ">>> loadUrlNow()"); + } + if (url.startsWith("file://") || url.startsWith("javascript:") || Config.isUrlWhiteListed(url)) { + super.loadUrl(url); + } + } + + /** + * Load the url into the webview after waiting for period of time. + * This is used to display the splashscreen for certain amount of time. + * + * @param url + * @param time The number of ms to wait before loading webview + */ + public void loadUrlIntoView(final String url, final int time) { + + // If not first page of app, then load immediately + // Add support for browser history if we use it. + if ((url.startsWith("javascript:")) || this.canGoBack()) { + } + + // If first page, then show splashscreen + else { + + LOG.d(TAG, "loadUrlIntoView(%s, %d)", url, time); + + // Send message to show splashscreen now if desired + this.postMessage("splashscreen", "show"); + } + + // Load url + this.loadUrlIntoView(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); + this.postMessage("onScrollChanged", myEvent); + } + + /** + * Send JavaScript statement back to JavaScript. + * (This is a convenience method) + * + * @param statement + */ + public void sendJavascript(String statement) { + this.jsMessageQueue.addJavaScript(statement); + } + + /** + * Send a plugin result back to JavaScript. + * (This is a convenience method) + * + * @param result + * @param callbackId + */ + public void sendPluginResult(PluginResult result, String callbackId) { + this.jsMessageQueue.addPluginResult(result, callbackId); + } + + /** + * Send a message to all plugins. + * + * @param id The message id + * @param data The message data + */ + public void postMessage(String id, Object data) { + if (this.pluginManager != null) { + this.pluginManager.postMessage(id, data); + } + } + + + /** + * 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 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://") || Config.isUrlWhiteListed(url)) { + // TODO: What about params? + // Load new URL + this.loadUrl(url); + 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); + } + } + + /** + * Get string property for activity. + * + * @param name + * @param defaultValue + * @return the String value for the named property + */ + public String getProperty(String name, String defaultValue) { + Bundle bundle = this.cordova.getActivity().getIntent().getExtras(); + if (bundle == null) { + return defaultValue; + } + name = name.toLowerCase(Locale.getDefault()); + Object p = bundle.get(name); + if (p == null) { + return defaultValue; + } + return p.toString(); + } + + /* + * 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 value) { + 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? + boundKeyCodes.add(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(); + } + paused = true; + + } + + 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(); + paused = false; + } + + public void handleDestroy() + { + // 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"); + + // 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); + } + } + + public boolean isPaused() + { + return paused; + } + + // 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); + } + } + + 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 void storeResult(int requestCode, int resultCode, Intent intent) { + mResult = new ActivityResult(requestCode, resultCode, intent); + } + + public CordovaResourceApi getResourceApi() { + return resourceApi; + } + + @Override + public void setLayoutParams( + android.widget.LinearLayout.LayoutParams layoutParams) { + super.setLayoutParams(layoutParams); + } + + @Override + public void setOverScrollMode(int mode) { + super.setOverScrollMode(mode); + } + + @Override + public void addJavascript(String statement) { + this.jsMessageQueue.addJavaScript(statement); + } + + @Override + public CordovaPlugin getPlugin(String initCallbackClass) { + // TODO Auto-generated method stub + return this.pluginManager.getPlugin(initCallbackClass); + } + + @Override + public boolean onOverrideUrlLoading(String url) { + return this.pluginManager.onOverrideUrlLoading(url); + } + + void onPageReset() { + boundKeyCodes.clear(); + pluginManager.onReset(); + jsMessageQueue.reset(); + exposedJsApi.clearBridgeSecret(); + } + + @Override + public void incUrlTimeout() { + this.loadUrlTimeout++; + } + + @Override + public PluginManager getPluginManager() { + // TODO Auto-generated method stub + return this.pluginManager; + } + + @Override + public void setLayoutParams( + android.widget.FrameLayout.LayoutParams layoutParams) { + super.setLayoutParams(layoutParams); + } + + @Override + public View getView() { + return this; + } + + +}