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 68A9B11C56 for ; Tue, 8 Jul 2014 18:46:27 +0000 (UTC) Received: (qmail 91211 invoked by uid 500); 8 Jul 2014 18:46:27 -0000 Delivered-To: apmail-cordova-commits-archive@cordova.apache.org Received: (qmail 91099 invoked by uid 500); 8 Jul 2014 18:46:27 -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 91054 invoked by uid 99); 8 Jul 2014 18:46:27 -0000 Received: from tyr.zones.apache.org (HELO tyr.zones.apache.org) (140.211.11.114) by apache.org (qpsmtpd/0.29) with ESMTP; Tue, 08 Jul 2014 18:46:27 +0000 Received: by tyr.zones.apache.org (Postfix, from userid 65534) id D2645946E42; Tue, 8 Jul 2014 18:46:26 +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: Tue, 08 Jul 2014 18:46:32 -0000 Message-Id: <47d849abf7d7419d844098f5d0d44dcc@git.apache.org> In-Reply-To: References: X-Mailer: ASF-Git Admin Mailer Subject: [7/9] Merge branch 'master' into 4.0.x (remove Config.* references) http://git-wip-us.apache.org/repos/asf/cordova-android/blob/ac194cd3/framework/src/org/apache/cordova/CordovaWebView.java ---------------------------------------------------------------------- diff --cc framework/src/org/apache/cordova/CordovaWebView.java index dc871ac,7c1974c..977ad4d mode 100644,100755..100644 --- a/framework/src/org/apache/cordova/CordovaWebView.java +++ b/framework/src/org/apache/cordova/CordovaWebView.java @@@ -1,76 -1,467 +1,77 @@@ -/* - 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 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; +import android.webkit.WebChromeClient.CustomViewCallback; +import android.widget.LinearLayout.LayoutParams; -/* - * This class is our web view. - * - * @see WebView guide - * @see WebView - */ -public class CordovaWebView extends WebView { +public interface CordovaWebView { + public static final String CORDOVA_VERSION = "4.0.0-dev"; - void init(CordovaInterface cordova, CordovaWebViewClient webViewClient, CordovaChromeClient webChromeClient, List pluginEntries); - public static final String TAG = "CordovaWebView"; - public static final String CORDOVA_VERSION = "3.6.0-dev"; ++ void init(CordovaInterface cordova, CordovaWebViewClient webViewClient, CordovaChromeClient webChromeClient, ++ List pluginEntries, Whitelist whitelist, CordovaPreferences preferences); - private HashSet boundKeyCodes = new HashSet(); + View getView(); - public PluginManager pluginManager; - private boolean paused; + CordovaWebViewClient makeWebViewClient(CordovaInterface cordova); - private BroadcastReceiver receiver; + CordovaChromeClient makeWebChromeClient(CordovaInterface cordova); + + void setWebViewClient(CordovaWebViewClient webViewClient); + void setWebChromeClient(CordovaChromeClient webChromeClient); - /** Activities and other important classes **/ - private CordovaInterface cordova; - CordovaWebViewClient viewClient; - private CordovaChromeClient chromeClient; + void setId(int i); - // Flag to track that a loadUrl timeout occurred - int loadUrlTimeout = 0; + void setLayoutParams(LayoutParams layoutParams); - private long lastMenuEventTime = 0; + void setVisibility(int invisible); - NativeToJsMessageQueue jsMessageQueue; - ExposedJsApi exposedJsApi; + Object getParent(); - /** custom view created by the browser (a video player for example) */ - private View mCustomView; - private WebChromeClient.CustomViewCallback mCustomViewCallback; + void loadUrl(String url); - private CordovaResourceApi resourceApi; - private Whitelist whitelist; - // The URL passed to loadUrl(), not necessarily the URL of the current page. - String loadedUrl; - private CordovaPreferences preferences; + void loadUrl(String url, int splashscreenTime); - class ActivityResult { - - int request; - int result; - Intent incoming; - - public ActivityResult(int req, int res, Intent intent) { - request = req; - result = res; - incoming = intent; - } + void loadUrlNow(String url); - - } - - static final FrameLayout.LayoutParams COVER_SCREEN_GRAVITY_CENTER = - new FrameLayout.LayoutParams( - ViewGroup.LayoutParams.MATCH_PARENT, - ViewGroup.LayoutParams.MATCH_PARENT, - Gravity.CENTER); - - public CordovaWebView(Context context) { - this(context, null); - } - - public CordovaWebView(Context context, AttributeSet attrs) { - super(context, attrs); - } + void loadUrlIntoView(final String url); - @Deprecated - public CordovaWebView(Context context, AttributeSet attrs, int defStyle) { - super(context, attrs, defStyle); - } + void loadUrlIntoView(final String url, boolean recreatePlugins); - @TargetApi(11) - @Deprecated - public CordovaWebView(Context context, AttributeSet attrs, int defStyle, boolean privateBrowsing) { - super(context, attrs, defStyle, privateBrowsing); - } - - // Use two-phase init so that the control will work with XML layouts. - public void init(CordovaInterface cordova, CordovaWebViewClient webViewClient, CordovaChromeClient webChromeClient, - List pluginEntries, Whitelist whitelist, CordovaPreferences preferences) { - if (this.cordova != null) { - throw new IllegalStateException(); - } - this.cordova = cordova; - this.viewClient = webViewClient; - this.chromeClient = webChromeClient; - this.whitelist = whitelist; - this.preferences = preferences; - super.setWebChromeClient(webChromeClient); - super.setWebViewClient(webViewClient); - - pluginManager = new PluginManager(this, this.cordova, pluginEntries); - jsMessageQueue = new NativeToJsMessageQueue(this, cordova); - exposedJsApi = new ExposedJsApi(pluginManager, jsMessageQueue); - resourceApi = new CordovaResourceApi(this.getContext(), pluginManager); - - pluginManager.addService("App", "org.apache.cordova.App"); - 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(); - } - // Enable JavaScript - 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); + void loadUrlIntoView(final String url, final int splashscreenTime); - // Enable DOM storage - settings.setDomStorageEnabled(true); + void stopLoading(); - // 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) { - getSettings().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(); - } - } - - public CordovaChromeClient makeWebChromeClient(CordovaInterface cordova) { - return new CordovaChromeClient(cordova, this); - } - - public CordovaWebViewClient makeWebViewClient(CordovaInterface cordova) { - if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.ICE_CREAM_SANDWICH) { - return new CordovaWebViewClient(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); - } - - public CordovaChromeClient getWebChromeClient() { - return this.chromeClient; - } + boolean canGoBack(); - - public Whitelist getWhitelist() { - return this.whitelist; - } - - /** - * 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); - } - } + void clearCache(boolean b); - /** - * 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); - } + void clearHistory(); - /** - * Load the url into the webview. - * - * @param url - */ - public void loadUrlIntoView(final String url, boolean recreatePlugins) { - LOG.d(TAG, ">>> loadUrl(" + url + ")"); - - if (recreatePlugins) { - this.loadedUrl = url; - this.pluginManager.init(); - } - - // Create a timeout timer for loadUrl - final CordovaWebView 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(me, -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); - } - }); - } + boolean backHistory(); - /** - * Load URL in webview. - * - * @param url - */ - void loadUrlNow(String url) { - if (LOG.isLoggable(LOG.DEBUG) && !url.startsWith("javascript:")) { - LOG.d(TAG, ">>> loadUrlNow()"); - } - if (url.startsWith("file://") || url.startsWith("javascript:") || whitelist.isUrlWhiteListed(url)) { - super.loadUrl(url); - } - } + void handlePause(boolean keepRunning); - /** - * 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) { + void onNewIntent(Intent intent); - // If not first page of app, then load immediately - // Add support for browser history if we use it. - if ((url.startsWith("javascript:")) || this.canGoBack()) { - } + void handleResume(boolean keepRunning, boolean activityResultKeepRunning); - // If first page, then show splashscreen - else { + void handleDestroy(); - LOG.d(TAG, "loadUrlIntoView(%s, %d)", url, time); + void postMessage(String id, Object data); - // Send message to show splashscreen now if desired - this.postMessage("splashscreen", "show"); - } + void addJavascript(String statement); - // 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 * Deprecated (https://issues.apache.org/jira/browse/CB-6851) * Instead of executing snippets of JS, you should use the exec bridge * to create a Java->JS communication channel. @@@ -90,47 -481,432 +91,49 @@@ * savedCallbackContext.sendPluginResult(dataResult); */ @Deprecated - public void sendJavascript(String statement) { - this.jsMessageQueue.addJavaScript(statement); - } + void sendJavascript(String statememt); - /** - * 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); - } + CordovaChromeClient getWebChromeClient(); - /** - * 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); - } - } + CordovaPlugin getPlugin(String initCallbackClass); + void showWebPage(String errorUrl, boolean b, boolean c, HashMap params); - /** - * 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() { + Object getFocusedChild(); - // 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; - } + boolean isCustomViewShowing(); + void showCustomView(View view, CustomViewCallback callback); - /** - * 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://") || whitelist.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); - } - } + void hideCustomView(); - /** - * 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(); - } - - @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; - } - // If volumeup key - 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); - } - - 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); - } - } - - @Deprecated // Use setButtonPlumbedToJs() instead. - public void bindButton(boolean override) - { - setButtonPlumbedToJs(KeyEvent.KEYCODE_BACK, override); - } - - @Deprecated // Use setButtonPlumbedToJs() instead. - public void bindButton(String button, boolean override) { - if (button.compareTo("volumeup")==0) { - setButtonPlumbedToJs(KeyEvent.KEYCODE_VOLUME_UP, override); - } - else if (button.compareTo("volumedown")==0) { - setButtonPlumbedToJs(KeyEvent.KEYCODE_VOLUME_DOWN, override); - } - } - - @Deprecated // Use setButtonPlumbedToJs() instead. - public void bindButton(int keyCode, boolean keyDown, boolean override) { - setButtonPlumbedToJs(keyCode, override); - } - - @Deprecated // Use isButtonPlumbedToJs - public boolean isBackButtonBound() - { - return isButtonPlumbedToJs(KeyEvent.KEYCODE_BACK); - } - - 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) - { + Context getContext(); - 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; - } - - @Deprecated // This never did anything. - public boolean hadKeyEvent() { - return false; - } - - // 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; - } + boolean onOverrideUrlLoading(String url); + + int getVisibility(); + + void incUrlTimeout(); + + void setOverScrollMode(int overScrollNever); + + void setNetworkAvailable(boolean online); + + CordovaResourceApi getResourceApi(); + + void setButtonPlumbedToJs(int keyCode, boolean override); + boolean isButtonPlumbedToJs(int keyCode); + + void sendPluginResult(PluginResult cr, String callbackId); + + PluginManager getPluginManager(); + + void setLayoutParams(android.widget.FrameLayout.LayoutParams layoutParams); - 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; - } - - @Deprecated // This never did anything - public void storeResult(int requestCode, int resultCode, Intent intent) { - } + // Required for test + String getUrl(); + boolean isPaused(); - + - public CordovaResourceApi getResourceApi() { - return resourceApi; - } - - public CordovaPreferences getPreferences() { - return preferences; - } ++ Whitelist getWhitelist(); ++ CordovaPreferences getPreferences(); } http://git-wip-us.apache.org/repos/asf/cordova-android/blob/ac194cd3/framework/src/org/apache/cordova/CoreAndroid.java ---------------------------------------------------------------------- diff --cc framework/src/org/apache/cordova/CoreAndroid.java index 56c7e64,0000000..6403673 mode 100755,000000..100755 --- a/framework/src/org/apache/cordova/CoreAndroid.java +++ b/framework/src/org/apache/cordova/CoreAndroid.java @@@ -1,305 -1,0 +1,301 @@@ +/* + 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 org.apache.cordova.CallbackContext; +import org.apache.cordova.CordovaPlugin; +import org.apache.cordova.LOG; +import org.apache.cordova.PluginResult; +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.telephony.TelephonyManager; +import android.view.KeyEvent; + +import java.util.HashMap; + +/** + * This class exposes methods in Cordova that can be called from JavaScript. + */ +public class CoreAndroid extends CordovaPlugin { + + protected static final String TAG = "CordovaApp"; + private BroadcastReceiver telephonyReceiver; + + /** + * Sets the context of the Command. This can then be used to do things like + * get file paths associated with the Activity. - * - * @param cordova The context of the main Activity. - * @param webView The CordovaWebView Cordova is running in. + */ - public void initialize(CordovaInterface cordova, CordovaWebView webView) { - super.initialize(cordova, webView); ++ @Override ++ public void initialize() { + this.initTelephonyReceiver(); + } + - + /** + * Executes the request and returns PluginResult. + * + * @param action The action to execute. + * @param args JSONArry of arguments for the plugin. + * @param callbackContext The callback context from which we were invoked. + * @return A PluginResult object with a status and message. + */ + public boolean execute(String action, JSONArray args, CallbackContext callbackContext) throws JSONException { + PluginResult.Status status = PluginResult.Status.OK; + String result = ""; + + try { + if (action.equals("clearCache")) { + this.clearCache(); + } + else if (action.equals("show")) { + // This gets called from JavaScript onCordovaReady to show the webview. + // I recommend we change the name of the Message as spinner/stop is not + // indicative of what this actually does (shows the webview). + cordova.getActivity().runOnUiThread(new Runnable() { + public void run() { + webView.postMessage("spinner", "stop"); + } + }); + } + else if (action.equals("loadUrl")) { + this.loadUrl(args.getString(0), args.optJSONObject(1)); + } + else if (action.equals("cancelLoadUrl")) { + //this.cancelLoadUrl(); + } + else if (action.equals("clearHistory")) { + this.clearHistory(); + } + else if (action.equals("backHistory")) { + this.backHistory(); + } + else if (action.equals("overrideButton")) { + this.overrideButton(args.getString(0), args.getBoolean(1)); + } + else if (action.equals("overrideBackbutton")) { + this.overrideBackbutton(args.getBoolean(0)); + } + else if (action.equals("exitApp")) { + this.exitApp(); + } + callbackContext.sendPluginResult(new PluginResult(status, result)); + return true; + } catch (JSONException e) { + callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.JSON_EXCEPTION)); + return false; + } + } + + //-------------------------------------------------------------------------- + // LOCAL METHODS + //-------------------------------------------------------------------------- + + /** + * Clear the resource cache. + */ + public void clearCache() { + cordova.getActivity().runOnUiThread(new Runnable() { + public void run() { + webView.clearCache(true); + } + }); + } + + /** + * Load the url into the webview. + * + * @param url + * @param props Properties that can be passed in to the Cordova activity (i.e. loadingDialog, wait, ...) + * @throws JSONException + */ + public void loadUrl(String url, JSONObject props) throws JSONException { + LOG.d("App", "App.loadUrl("+url+","+props+")"); + int wait = 0; + boolean openExternal = false; + boolean clearHistory = false; + + // If there are properties, then set them on the Activity + HashMap params = new HashMap(); + if (props != null) { + JSONArray keys = props.names(); + for (int i = 0; i < keys.length(); i++) { + String key = keys.getString(i); + if (key.equals("wait")) { + wait = props.getInt(key); + } + else if (key.equalsIgnoreCase("openexternal")) { + openExternal = props.getBoolean(key); + } + else if (key.equalsIgnoreCase("clearhistory")) { + clearHistory = props.getBoolean(key); + } + else { + Object value = props.get(key); + if (value == null) { + + } + else if (value.getClass().equals(String.class)) { + params.put(key, (String)value); + } + else if (value.getClass().equals(Boolean.class)) { + params.put(key, (Boolean)value); + } + else if (value.getClass().equals(Integer.class)) { + params.put(key, (Integer)value); + } + } + } + } + + // If wait property, then delay loading + + if (wait > 0) { + try { + synchronized(this) { + this.wait(wait); + } + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + this.webView.showWebPage(url, openExternal, clearHistory, params); + } + + /** + * Clear page history for the app. + */ + public void clearHistory() { + cordova.getActivity().runOnUiThread(new Runnable() { + public void run() { + webView.clearHistory(); + } + }); + } + + /** + * Go to previous page displayed. + * This is the same as pressing the backbutton on Android device. + */ + public void backHistory() { + cordova.getActivity().runOnUiThread(new Runnable() { + public void run() { + webView.backHistory(); + } + }); + } + + /** + * Override the default behavior of the Android back button. + * If overridden, when the back button is pressed, the "backKeyDown" JavaScript event will be fired. + * + * @param override T=override, F=cancel override + */ + public void overrideBackbutton(boolean override) { + LOG.i("App", "WARNING: Back Button Default Behavior will be overridden. The backbutton event will be fired!"); + webView.setButtonPlumbedToJs(KeyEvent.KEYCODE_BACK, override); + } + + /** + * Override the default behavior of the Android volume buttons. + * If overridden, when the volume button is pressed, the "volume[up|down]button" JavaScript event will be fired. + * + * @param button volumeup, volumedown + * @param override T=override, F=cancel override + */ + public void overrideButton(String button, boolean override) { + LOG.i("App", "WARNING: Volume Button Default Behavior will be overridden. The volume event will be fired!"); + if (button.equals("volumeup")) { + webView.setButtonPlumbedToJs(KeyEvent.KEYCODE_VOLUME_UP, override); + } + else if (button.equals("volumedown")) { + webView.setButtonPlumbedToJs(KeyEvent.KEYCODE_VOLUME_DOWN, override); + } + } + + /** + * Return whether the Android back button is overridden by the user. + * + * @return boolean + */ + public boolean isBackbuttonOverridden() { + return webView.isButtonPlumbedToJs(KeyEvent.KEYCODE_BACK); + } + + /** + * Exit the Android application. + */ + public void exitApp() { + this.webView.postMessage("exit", null); + } + + + /** + * Listen for telephony events: RINGING, OFFHOOK and IDLE + * Send these events to all plugins using + * CordovaActivity.onMessage("telephone", "ringing" | "offhook" | "idle") + */ + private void initTelephonyReceiver() { + IntentFilter intentFilter = new IntentFilter(); + intentFilter.addAction(TelephonyManager.ACTION_PHONE_STATE_CHANGED); + //final CordovaInterface mycordova = this.cordova; + this.telephonyReceiver = new BroadcastReceiver() { + + @Override + public void onReceive(Context context, Intent intent) { + + // If state has changed + if ((intent != null) && intent.getAction().equals(TelephonyManager.ACTION_PHONE_STATE_CHANGED)) { + if (intent.hasExtra(TelephonyManager.EXTRA_STATE)) { + String extraData = intent.getStringExtra(TelephonyManager.EXTRA_STATE); + if (extraData.equals(TelephonyManager.EXTRA_STATE_RINGING)) { + LOG.i(TAG, "Telephone RINGING"); + webView.postMessage("telephone", "ringing"); + } + else if (extraData.equals(TelephonyManager.EXTRA_STATE_OFFHOOK)) { + LOG.i(TAG, "Telephone OFFHOOK"); + webView.postMessage("telephone", "offhook"); + } + else if (extraData.equals(TelephonyManager.EXTRA_STATE_IDLE)) { + LOG.i(TAG, "Telephone IDLE"); + webView.postMessage("telephone", "idle"); + } + } + } + } + }; + + // Register the receiver + this.cordova.getActivity().registerReceiver(this.telephonyReceiver, intentFilter); + } + + /* + * Unregister the receiver + * + */ + public void onDestroy() + { + this.cordova.getActivity().unregisterReceiver(this.telephonyReceiver); + } +} http://git-wip-us.apache.org/repos/asf/cordova-android/blob/ac194cd3/framework/src/org/apache/cordova/IceCreamCordovaWebViewClient.java ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/cordova-android/blob/ac194cd3/framework/src/org/apache/cordova/PluginEntry.java ----------------------------------------------------------------------