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 F37AD11D78 for ; Fri, 4 Jul 2014 03:02:45 +0000 (UTC) Received: (qmail 3670 invoked by uid 500); 4 Jul 2014 03:02:45 -0000 Delivered-To: apmail-cordova-commits-archive@cordova.apache.org Received: (qmail 3562 invoked by uid 500); 4 Jul 2014 03:02:45 -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 3536 invoked by uid 99); 4 Jul 2014 03:02:45 -0000 Received: from tyr.zones.apache.org (HELO tyr.zones.apache.org) (140.211.11.114) by apache.org (qpsmtpd/0.29) with ESMTP; Fri, 04 Jul 2014 03:02:45 +0000 Received: by tyr.zones.apache.org (Postfix, from userid 65534) id 70736999E49; Fri, 4 Jul 2014 03:02:45 +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: Fri, 04 Jul 2014 03:02:49 -0000 Message-Id: <19b3770801314c0aae6f594faa9a1c91@git.apache.org> In-Reply-To: <0af1897f56fb4a878380a28bd8fb54bb@git.apache.org> References: <0af1897f56fb4a878380a28bd8fb54bb@git.apache.org> X-Mailer: ASF-Git Admin Mailer Subject: [8/8] android commit: Merge branch 'master' into 4.0.x (Bridge fixes) Merge branch 'master' into 4.0.x (Bridge fixes) Conflicts: framework/src/org/apache/cordova/CordovaChromeClient.java framework/src/org/apache/cordova/CordovaUriHelper.java framework/src/org/apache/cordova/CordovaWebView.java framework/src/org/apache/cordova/CordovaWebViewClient.java framework/src/org/apache/cordova/ExposedJsApi.java framework/src/org/apache/cordova/NativeToJsMessageQueue.java framework/src/org/apache/cordova/PluginManager.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/4ca23056 Tree: http://git-wip-us.apache.org/repos/asf/cordova-android/tree/4ca23056 Diff: http://git-wip-us.apache.org/repos/asf/cordova-android/diff/4ca23056 Branch: refs/heads/4.0.x Commit: 4ca2305693918233d150702d82729cb1ab46ebbd Parents: 428e1bc f577af0 Author: Andrew Grieve Authored: Thu Jul 3 23:02:02 2014 -0400 Committer: Andrew Grieve Committed: Thu Jul 3 23:02:02 2014 -0400 ---------------------------------------------------------------------- .../org/apache/cordova/AndroidChromeClient.java | 149 ++++++++++--------- .../org/apache/cordova/AndroidExposedJsApi.java | 39 +++-- .../src/org/apache/cordova/AndroidWebView.java | 28 ++-- .../apache/cordova/AndroidWebViewClient.java | 143 +----------------- framework/src/org/apache/cordova/Config.java | 15 +- .../src/org/apache/cordova/CordovaActivity.java | 2 +- .../org/apache/cordova/CordovaChromeClient.java | 5 - .../org/apache/cordova/CordovaUriHelper.java | 43 +----- .../src/org/apache/cordova/ExposedJsApi.java | 11 +- .../cordova/IceCreamCordovaWebViewClient.java | 11 +- .../apache/cordova/NativeToJsMessageQueue.java | 82 +++++----- .../src/org/apache/cordova/PluginManager.java | 45 ------ 12 files changed, 193 insertions(+), 380 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/cordova-android/blob/4ca23056/framework/src/org/apache/cordova/AndroidChromeClient.java ---------------------------------------------------------------------- diff --cc framework/src/org/apache/cordova/AndroidChromeClient.java index e0e9dfa,0000000..31d9b68 mode 100755,000000..100755 --- a/framework/src/org/apache/cordova/AndroidChromeClient.java +++ b/framework/src/org/apache/cordova/AndroidChromeClient.java @@@ -1,400 -1,0 +1,409 @@@ +/* + 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.CordovaInterface; +import org.apache.cordova.LOG; +import org.json.JSONArray; +import org.json.JSONException; + +import android.annotation.TargetApi; +import android.app.AlertDialog; +import android.content.DialogInterface; +import android.content.Intent; +import android.net.Uri; +import android.view.Gravity; +import android.view.KeyEvent; +import android.view.View; +import android.view.ViewGroup.LayoutParams; +import android.webkit.ConsoleMessage; +import android.webkit.JsPromptResult; +import android.webkit.JsResult; +import android.webkit.ValueCallback; +import android.webkit.WebChromeClient; +import android.webkit.WebStorage; +import android.webkit.WebView; +import android.webkit.GeolocationPermissions.Callback; +import android.widget.EditText; +import android.widget.LinearLayout; +import android.widget.ProgressBar; +import android.widget.RelativeLayout; ++import android.util.Log; ++ ++ + +/** + * This class is the WebChromeClient that implements callbacks for our web view. + * The kind of callbacks that happen here are on the chrome outside the document, + * such as onCreateWindow(), onConsoleMessage(), onProgressChanged(), etc. Related + * to but different than CordovaWebViewClient. + * + * @see WebChromeClient + * @see WebView guide + * @see CordovaWebViewClient + * @see CordovaWebView + */ +public class AndroidChromeClient extends WebChromeClient implements CordovaChromeClient { + + public static final int FILECHOOSER_RESULTCODE = 5173; + private static final String LOG_TAG = "CordovaChromeClient"; - private String TAG = "CordovaLog"; + private long MAX_QUOTA = 100 * 1024 * 1024; + protected CordovaInterface cordova; + protected CordovaWebView appView; + + // the video progress view + private View mVideoProgressView; + + // File Chooser + public ValueCallback mUploadMessage; + + /** + * Constructor. + * + * @param cordova + */ + public AndroidChromeClient(CordovaInterface cordova) { + this.cordova = cordova; + } + + /** + * Constructor. + * + * @param ctx + * @param app + */ + public AndroidChromeClient(CordovaInterface ctx, CordovaWebView app) { + this.cordova = ctx; + this.appView = app; + } + + /** + * Constructor. + * + * @param view + */ + public void setWebView(CordovaWebView view) { + this.appView = view; + } + + /** + * Tell the client to display a javascript alert dialog. + * + * @param view + * @param url + * @param message + * @param result + * @see Other implementation in the Dialogs plugin. + */ + @Override + public boolean onJsAlert(WebView view, String url, String message, final JsResult result) { + AlertDialog.Builder dlg = new AlertDialog.Builder(this.cordova.getActivity()); + dlg.setMessage(message); + dlg.setTitle("Alert"); + //Don't let alerts break the back button + dlg.setCancelable(true); + dlg.setPositiveButton(android.R.string.ok, + new AlertDialog.OnClickListener() { + public void onClick(DialogInterface dialog, int which) { + result.confirm(); + } + }); + dlg.setOnCancelListener( + new DialogInterface.OnCancelListener() { + public void onCancel(DialogInterface dialog) { + result.cancel(); + } + }); + dlg.setOnKeyListener(new DialogInterface.OnKeyListener() { + //DO NOTHING + public boolean onKey(DialogInterface dialog, int keyCode, KeyEvent event) { + if (keyCode == KeyEvent.KEYCODE_BACK) + { + result.confirm(); + return false; + } + else + return true; + } + }); + dlg.show(); + return true; + } + + /** + * Tell the client to display a confirm dialog to the user. + * + * @param view + * @param url + * @param message + * @param result + * @see Other implementation in the Dialogs plugin. + */ + @Override + public boolean onJsConfirm(WebView view, String url, String message, final JsResult result) { + AlertDialog.Builder dlg = new AlertDialog.Builder(this.cordova.getActivity()); + dlg.setMessage(message); + dlg.setTitle("Confirm"); + dlg.setCancelable(true); + dlg.setPositiveButton(android.R.string.ok, + new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int which) { + result.confirm(); + } + }); + dlg.setNegativeButton(android.R.string.cancel, + new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int which) { + result.cancel(); + } + }); + dlg.setOnCancelListener( + new DialogInterface.OnCancelListener() { + public void onCancel(DialogInterface dialog) { + result.cancel(); + } + }); + dlg.setOnKeyListener(new DialogInterface.OnKeyListener() { + //DO NOTHING + public boolean onKey(DialogInterface dialog, int keyCode, KeyEvent event) { + if (keyCode == KeyEvent.KEYCODE_BACK) + { + result.cancel(); + return false; + } + else + return true; + } + }); + dlg.show(); + return true; + } + + /** + * Tell the client to display a prompt dialog to the user. + * If the client returns true, WebView will assume that the client will + * handle the prompt dialog and call the appropriate JsPromptResult method. + * + * Since we are hacking prompts for our own purposes, we should not be using them for + * this purpose, perhaps we should hack console.log to do this instead! + * - * @param view - * @param url - * @param message - * @param defaultValue - * @param result + * @see Other implementation in the Dialogs plugin. + */ + @Override - public boolean onJsPrompt(WebView view, String url, String message, String defaultValue, JsPromptResult result) { - - // Security check to make sure any requests are coming from the page initially - // loaded in webview and not another loaded in an iframe. - boolean reqOk = false; - if (url.startsWith("file://") || Config.isUrlWhiteListed(url)) { - reqOk = true; - } - - // Calling PluginManager.exec() to call a native service using - // prompt(this.stringify(args), "gap:"+this.stringify([service, action, callbackId, true])); - if (reqOk && defaultValue != null && defaultValue.length() > 3 && defaultValue.substring(0, 4).equals("gap:")) { ++ public boolean onJsPrompt(WebView view, String origin, String message, String defaultValue, JsPromptResult result) { ++ // Unlike the @JavascriptInterface bridge, this method is always called on the UI thread. ++ if (defaultValue != null && defaultValue.length() > 3 && defaultValue.startsWith("gap:")) { + JSONArray array; + try { + array = new JSONArray(defaultValue.substring(4)); - String service = array.getString(0); - String action = array.getString(1); - String callbackId = array.getString(2); - - //String r = this.appView.exposedJsApi.exec(service, action, callbackId, message); - String r = this.appView.exec(service, action, callbackId, message); ++ int bridgeSecret = array.getInt(0); ++ String service = array.getString(1); ++ String action = array.getString(2); ++ String callbackId = array.getString(3); ++ String r = appView.exposedJsApi.exec(bridgeSecret, service, action, callbackId, message); + result.confirm(r == null ? "" : r); + } catch (JSONException e) { + e.printStackTrace(); - return false; ++ result.cancel(); ++ } catch (IllegalAccessException e) { ++ e.printStackTrace(); ++ result.cancel(); + } + } + + // Sets the native->JS bridge mode. - else if (reqOk && defaultValue != null && defaultValue.equals("gap_bridge_mode:")) { - try { - //this.appView.exposedJsApi.setNativeToJsBridgeMode(Integer.parseInt(message)); - this.appView.setNativeToJsBridgeMode(Integer.parseInt(message)); - result.confirm(""); - } catch (NumberFormatException e){ - result.confirm(""); ++ else if (defaultValue != null && defaultValue.startsWith("gap_bridge_mode:")) { ++ try { ++ int bridgeSecret = Integer.parseInt(defaultValue.substring(16)); ++ appView.exposedJsApi.setNativeToJsBridgeMode(bridgeSecret, Integer.parseInt(message)); ++ result.cancel(); ++ } catch (NumberFormatException e){ ++ e.printStackTrace(); ++ result.cancel(); ++ } catch (IllegalAccessException e) { + e.printStackTrace(); - } ++ result.cancel(); ++ } + } + + // Polling for JavaScript messages - else if (reqOk && defaultValue != null && defaultValue.equals("gap_poll:")) { - //String r = this.appView.exposedJsApi.retrieveJsMessages("1".equals(message)); - String r = this.appView.retrieveJsMessages("1".equals(message)); - result.confirm(r == null ? "" : r); - } - - // Do NO-OP so older code doesn't display dialog - else if (defaultValue != null && defaultValue.equals("gap_init:")) { - result.confirm("OK"); ++ else if (defaultValue != null && defaultValue.startsWith("gap_poll:")) { ++ int bridgeSecret = Integer.parseInt(defaultValue.substring(9)); ++ try { ++ String r = appView.exposedJsApi.retrieveJsMessages(bridgeSecret, "1".equals(message)); ++ result.confirm(r == null ? "" : r); ++ } catch (IllegalAccessException e) { ++ e.printStackTrace(); ++ result.cancel(); ++ } + } + - // Show dialog - else { ++ else if (defaultValue != null && defaultValue.startsWith("gap_init:")) { ++ String startUrl = Config.getStartUrl(); ++ // Protect against random iframes being able to talk through the bridge. ++ // Trust only file URLs and the start URL's domain. ++ // The extra origin.startsWith("http") is to protect against iframes with data: having "" as origin. ++ if (origin.startsWith("file:") || (origin.startsWith("http") && startUrl.startsWith(origin))) { ++ // Enable the bridge ++ int bridgeMode = Integer.parseInt(defaultValue.substring(9)); ++ appView.jsMessageQueue.setBridgeMode(bridgeMode); ++ // Tell JS the bridge secret. ++ int secret = appView.exposedJsApi.generateBridgeSecret(); ++ result.confirm(""+secret); ++ } else { ++ Log.e(LOG_TAG, "gap_init called from restricted origin: " + origin); ++ result.cancel(); ++ } ++ } else { ++ // Returning false would also show a dialog, but the default one shows the origin (ugly). + final JsPromptResult res = result; + AlertDialog.Builder dlg = new AlertDialog.Builder(this.cordova.getActivity()); + dlg.setMessage(message); + final EditText input = new EditText(this.cordova.getActivity()); + if (defaultValue != null) { + input.setText(defaultValue); + } + dlg.setView(input); + dlg.setCancelable(false); + dlg.setPositiveButton(android.R.string.ok, + new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int which) { + String usertext = input.getText().toString(); + res.confirm(usertext); + } + }); + dlg.setNegativeButton(android.R.string.cancel, + new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int which) { + res.cancel(); + } + }); + dlg.show(); + } + return true; + } + + /** + * Handle database quota exceeded notification. + */ + @Override + public void onExceededDatabaseQuota(String url, String databaseIdentifier, long currentQuota, long estimatedSize, + long totalUsedQuota, WebStorage.QuotaUpdater quotaUpdater) + { - LOG.d(TAG, "onExceededDatabaseQuota estimatedSize: %d currentQuota: %d totalUsedQuota: %d", estimatedSize, currentQuota, totalUsedQuota); ++ LOG.d(LOG_TAG, "onExceededDatabaseQuota estimatedSize: %d currentQuota: %d totalUsedQuota: %d", estimatedSize, currentQuota, totalUsedQuota); + quotaUpdater.updateQuota(MAX_QUOTA); + } + + // console.log in api level 7: http://developer.android.com/guide/developing/debug-tasks.html + // Expect this to not compile in a future Android release! + @SuppressWarnings("deprecation") + @Override + public void onConsoleMessage(String message, int lineNumber, String sourceID) + { + //This is only for Android 2.1 + if(android.os.Build.VERSION.SDK_INT == android.os.Build.VERSION_CODES.ECLAIR_MR1) + { - LOG.d(TAG, "%s: Line %d : %s", sourceID, lineNumber, message); ++ LOG.d(LOG_TAG, "%s: Line %d : %s", sourceID, lineNumber, message); + super.onConsoleMessage(message, lineNumber, sourceID); + } + } + + @TargetApi(8) + @Override + public boolean onConsoleMessage(ConsoleMessage consoleMessage) + { + if (consoleMessage.message() != null) - LOG.d(TAG, "%s: Line %d : %s" , consoleMessage.sourceId() , consoleMessage.lineNumber(), consoleMessage.message()); ++ LOG.d(LOG_TAG, "%s: Line %d : %s" , consoleMessage.sourceId() , consoleMessage.lineNumber(), consoleMessage.message()); + return super.onConsoleMessage(consoleMessage); + } + + @Override + /** + * Instructs the client to show a prompt to ask the user to set the Geolocation permission state for the specified origin. + * + * @param origin + * @param callback + */ + public void onGeolocationPermissionsShowPrompt(String origin, Callback callback) { + super.onGeolocationPermissionsShowPrompt(origin, callback); + callback.invoke(origin, true, false); + } + + // API level 7 is required for this, see if we could lower this using something else + @Override + public void onShowCustomView(View view, WebChromeClient.CustomViewCallback callback) { + this.appView.showCustomView(view, callback); + } + - @Override - public void onHideCustomView() { - this.appView.hideCustomView(); - } ++ @Override ++ public void onHideCustomView() { ++ this.appView.hideCustomView(); ++ } + + @Override + /** + * Ask the host application for a custom progress view to show while + * a