From 19f76d34dba248e4549d3abe4e6e033b915d3e21 Mon Sep 17 00:00:00 2001 From: Joe Bowser Date: Wed, 5 Mar 2014 15:50:02 -0800 Subject: [PATCH] Hurray! It runs! Now that we have the default WebView working, it's time to make things a little more pluggable. --- .../apache/cordova/AndroidChromeClient.java | 397 ++++++ .../org/apache/cordova/AndroidWebView.java | 1074 +++++++++++++++++ .../apache/cordova/AndroidWebViewClient.java | 496 ++++++++ .../org/apache/cordova/CordovaActivity.java | 114 +- .../apache/cordova/CordovaChromeClient.java | 369 +----- .../org/apache/cordova/CordovaWebView.java | 1021 +--------------- .../apache/cordova/CordovaWebViewClient.java | 480 +------- .../cordova/IceCreamCordovaWebViewClient.java | 8 +- .../LinearLayoutSoftKeyboardDetect.java | 4 +- 9 files changed, 2058 insertions(+), 1905 deletions(-) create mode 100755 framework/src/org/apache/cordova/AndroidChromeClient.java create mode 100755 framework/src/org/apache/cordova/AndroidWebView.java create mode 100755 framework/src/org/apache/cordova/AndroidWebViewClient.java mode change 100755 => 100644 framework/src/org/apache/cordova/CordovaChromeClient.java mode change 100755 => 100644 framework/src/org/apache/cordova/CordovaWebView.java mode change 100755 => 100644 framework/src/org/apache/cordova/CordovaWebViewClient.java diff --git a/framework/src/org/apache/cordova/AndroidChromeClient.java b/framework/src/org/apache/cordova/AndroidChromeClient.java new file mode 100755 index 00000000..1297a6c7 --- /dev/null +++ b/framework/src/org/apache/cordova/AndroidChromeClient.java @@ -0,0 +1,397 @@ +/* + 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; + +/** + * 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 + */ + @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 + */ + @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 + */ + @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:")) { + 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); + result.confirm(r == null ? "" : r); + } catch (JSONException e) { + e.printStackTrace(); + return false; + } + } + + // 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(""); + e.printStackTrace(); + } + } + + // 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"); + } + + // Show dialog + else { + 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); + 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); + 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()); + 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 + /** + * Ask the host application for a custom progress view to show while + * a