367 lines
14 KiB
Java
367 lines
14 KiB
Java
/*
|
|
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 android.annotation.TargetApi;
|
|
import android.app.Activity;
|
|
import android.app.AlertDialog;
|
|
import android.content.ActivityNotFoundException;
|
|
import android.content.DialogInterface;
|
|
import android.content.Intent;
|
|
import android.net.Uri;
|
|
import android.os.Build;
|
|
import android.util.Log;
|
|
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 <a href="http://developer.android.com/reference/android/webkit/WebChromeClient.html">WebChromeClient</a>
|
|
* @see <a href="http://developer.android.com/guide/webapps/webview.html">WebView guide</a>
|
|
* @see CordovaWebViewClient
|
|
* @see CordovaWebView
|
|
*/
|
|
public class CordovaChromeClient extends WebChromeClient {
|
|
|
|
public static final int FILECHOOSER_RESULTCODE = 5173;
|
|
private String TAG = "CordovaLog";
|
|
private long MAX_QUOTA = 100 * 1024 * 1024;
|
|
protected CordovaInterface cordova;
|
|
protected CordovaWebView appView;
|
|
|
|
// the video progress view
|
|
private View mVideoProgressView;
|
|
|
|
//Keep track of last AlertDialog showed
|
|
private AlertDialog lastHandledDialog;
|
|
|
|
@Deprecated
|
|
public CordovaChromeClient(CordovaInterface cordova) {
|
|
this.cordova = cordova;
|
|
}
|
|
|
|
public CordovaChromeClient(CordovaInterface ctx, CordovaWebView app) {
|
|
this.cordova = ctx;
|
|
this.appView = app;
|
|
}
|
|
|
|
@Deprecated
|
|
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;
|
|
}
|
|
});
|
|
lastHandledDialog = 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;
|
|
}
|
|
});
|
|
lastHandledDialog = 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!
|
|
*
|
|
* @see Other implementation in the Dialogs plugin.
|
|
*/
|
|
@Override
|
|
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.
|
|
String handledRet = appView.bridge.promptOnJsPrompt(origin, message, defaultValue);
|
|
if (handledRet != null) {
|
|
result.confirm(handledRet);
|
|
} 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();
|
|
}
|
|
});
|
|
lastHandledDialog = 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 <video> is loading.
|
|
* @return View The progress view.
|
|
*/
|
|
public View getVideoLoadingProgressView() {
|
|
|
|
if (mVideoProgressView == null) {
|
|
// Create a new Loading view programmatically.
|
|
|
|
// create the linear layout
|
|
LinearLayout layout = new LinearLayout(this.appView.getContext());
|
|
layout.setOrientation(LinearLayout.VERTICAL);
|
|
RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
|
|
layoutParams.addRule(RelativeLayout.CENTER_IN_PARENT);
|
|
layout.setLayoutParams(layoutParams);
|
|
// the proress bar
|
|
ProgressBar bar = new ProgressBar(this.appView.getContext());
|
|
LinearLayout.LayoutParams barLayoutParams = new LinearLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
|
|
barLayoutParams.gravity = Gravity.CENTER;
|
|
bar.setLayoutParams(barLayoutParams);
|
|
layout.addView(bar);
|
|
|
|
mVideoProgressView = layout;
|
|
}
|
|
return mVideoProgressView;
|
|
}
|
|
|
|
// <input type=file> support:
|
|
// openFileChooser() is for pre KitKat and in KitKat mr1 (it's known broken in KitKat).
|
|
// For Lollipop, we use onShowFileChooser().
|
|
public void openFileChooser(ValueCallback<Uri> uploadMsg) {
|
|
this.openFileChooser(uploadMsg, "*/*");
|
|
}
|
|
|
|
public void openFileChooser( ValueCallback<Uri> uploadMsg, String acceptType ) {
|
|
this.openFileChooser(uploadMsg, acceptType, null);
|
|
}
|
|
|
|
public void openFileChooser(final ValueCallback<Uri> uploadMsg, String acceptType, String capture)
|
|
{
|
|
Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
|
|
intent.addCategory(Intent.CATEGORY_OPENABLE);
|
|
intent.setType("*/*");
|
|
cordova.startActivityForResult(new CordovaPlugin() {
|
|
@Override
|
|
public void onActivityResult(int requestCode, int resultCode, Intent intent) {
|
|
Uri result = intent == null || resultCode != Activity.RESULT_OK ? null : intent.getData();
|
|
Log.d(TAG, "Receive file chooser URL: " + result);
|
|
uploadMsg.onReceiveValue(result);
|
|
}
|
|
}, intent, FILECHOOSER_RESULTCODE);
|
|
}
|
|
|
|
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
|
|
@Override
|
|
public boolean onShowFileChooser(WebView webView, final ValueCallback<Uri[]> filePathsCallback, final WebChromeClient.FileChooserParams fileChooserParams) {
|
|
Intent intent = fileChooserParams.createIntent();
|
|
try {
|
|
cordova.startActivityForResult(new CordovaPlugin() {
|
|
@Override
|
|
public void onActivityResult(int requestCode, int resultCode, Intent intent) {
|
|
Uri[] result = WebChromeClient.FileChooserParams.parseResult(resultCode, intent);
|
|
Log.d(TAG, "Receive file chooser URL: " + result);
|
|
filePathsCallback.onReceiveValue(result);
|
|
}
|
|
}, intent, FILECHOOSER_RESULTCODE);
|
|
} catch (ActivityNotFoundException e) {
|
|
Log.w("No activity found to handle file chooser intent.", e);
|
|
filePathsCallback.onReceiveValue(null);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
public void destroyLastDialog(){
|
|
if(lastHandledDialog != null){
|
|
lastHandledDialog.cancel();
|
|
}
|
|
}
|
|
|
|
}
|