From 09958368e21fd7f9d3ef210b48c621ef93dc81fe Mon Sep 17 00:00:00 2001 From: Andrew n marshall Date: Wed, 10 Oct 2018 14:08:32 -0700 Subject: [PATCH] Adding hooks for Android dialogs upon console.prompt() and similar. Adapting code from Android's JsDialogHelper, a private class in the Android source code. --- .../webview/BlocklyWebViewFragment.java | 3 + .../android/webview/JsDialogHelper.java | 163 ++++++++++++++++++ .../android/webview/WebChromeClient.java | 32 ++++ .../app/src/main/res/layout/js_prompt.xml | 37 ++++ .../src/main/res/values/js_dialog_helper.xml | 7 + demos/mobile/html/index.html | 2 +- 6 files changed, 243 insertions(+), 1 deletion(-) create mode 100644 demos/mobile/android/app/src/main/java/com/google/blockly/android/webview/JsDialogHelper.java create mode 100644 demos/mobile/android/app/src/main/java/com/google/blockly/android/webview/WebChromeClient.java create mode 100644 demos/mobile/android/app/src/main/res/layout/js_prompt.xml create mode 100644 demos/mobile/android/app/src/main/res/values/js_dialog_helper.xml diff --git a/demos/mobile/android/app/src/main/java/com/google/blockly/android/webview/BlocklyWebViewFragment.java b/demos/mobile/android/app/src/main/java/com/google/blockly/android/webview/BlocklyWebViewFragment.java index c9629d2f3..e4f6f763f 100644 --- a/demos/mobile/android/app/src/main/java/com/google/blockly/android/webview/BlocklyWebViewFragment.java +++ b/demos/mobile/android/app/src/main/java/com/google/blockly/android/webview/BlocklyWebViewFragment.java @@ -1,5 +1,6 @@ package com.google.blockly.android.webview; +import android.annotation.SuppressLint; import android.os.Bundle; import android.support.annotation.NonNull; import android.support.annotation.Nullable; @@ -16,11 +17,13 @@ import android.webkit.WebView; public class BlocklyWebViewFragment extends Fragment { protected @Nullable WebView mWebView = null; + @SuppressLint("SetJavaScriptEnabled") @Nullable @Override public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { mWebView = new WebView(inflater.getContext()); + mWebView.setWebChromeClient(new WebChromeClient()); WebSettings webSettings = mWebView.getSettings(); webSettings.setJavaScriptEnabled(true); mWebView.loadUrl("file:///android_asset/blockly/webview.html"); diff --git a/demos/mobile/android/app/src/main/java/com/google/blockly/android/webview/JsDialogHelper.java b/demos/mobile/android/app/src/main/java/com/google/blockly/android/webview/JsDialogHelper.java new file mode 100644 index 000000000..c03f62146 --- /dev/null +++ b/demos/mobile/android/app/src/main/java/com/google/blockly/android/webview/JsDialogHelper.java @@ -0,0 +1,163 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed 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 com.google.blockly.android.webview; + +import android.app.Activity; +import android.content.Context; +import android.content.DialogInterface; +import android.os.Message; +import android.support.annotation.Nullable; +import android.support.v7.app.AlertDialog; +import android.util.Log; +import android.view.KeyEvent; +import android.view.LayoutInflater; +import android.view.View; +import android.view.inputmethod.EditorInfo; +import android.webkit.JsPromptResult; +import android.webkit.JsResult; +import android.webkit.WebView; +import android.widget.EditText; +import android.widget.TextView; + +import com.example.blocklywebview.R; + +/** + * Helper class to create JavaScript dialogs. + * Adapted from android-9.0.0_r10/core/java/android/webkit/JsDialogHelper.java. + * Removes dialog title (page domain) and uses a larger prompt message area than original. + */ +public class JsDialogHelper { + private static final String TAG = "JsDialogHelper"; + // Dialog types + /** An alert dialog, for console.alert(..). */ + public static final int ALERT = 1; + /** An alert dialog, for console.confirm(..). */ + public static final int CONFIRM = 2; + /** An alert dialog, for console.prompt(..). */ + public static final int PROMPT = 3; + + private final @Nullable String mDefaultValue; + private final JsResult mResult; + private final String mMessage; + private final int mType; + private final String mUrl; + + public JsDialogHelper(JsResult result, int type, @Nullable String defaultValue, + String message, String url) { + if (type == PROMPT && !(result instanceof JsPromptResult)) { + throw new IllegalArgumentException("JsDialogHelper PROMPT requires JsPromptResult"); + } + mResult = result; + mDefaultValue = defaultValue; + mMessage = message; + mType = type; + mUrl = url; + } + + public JsDialogHelper(JsResult result, Message msg) { + mResult = result; + mDefaultValue = msg.getData().getString("default"); + mMessage = msg.getData().getString("message"); + mType = msg.getData().getInt("type"); + mUrl = msg.getData().getString("url"); + } + + public boolean invokeCallback(WebChromeClient client, WebView webView) { + switch (mType) { + case ALERT: + return client.onJsAlert(webView, mUrl, mMessage, mResult); + case CONFIRM: + return client.onJsConfirm(webView, mUrl, mMessage, mResult); + case PROMPT: + return client.onJsPrompt(webView, mUrl, mMessage, mDefaultValue, (JsPromptResult) mResult); + default: + throw new IllegalArgumentException("Unexpected type: " + mType); + } + } + + public void showDialog(Context context) { + if (!canShowAlertDialog(context)) { + Log.w(TAG, "Cannot create a dialog, the WebView context is not an Activity"); + mResult.cancel(); + return; + } + final EditText edit; + AlertDialog.Builder builder = new AlertDialog.Builder(context); + builder.setOnCancelListener(new CancelListener()); + if (mType != PROMPT) { + edit = null; + builder.setMessage(mMessage); + builder.setPositiveButton(android.R.string.ok, new PositiveListener(null)); + } else { + final View view = LayoutInflater.from(context).inflate(R.layout.js_prompt, null); + edit = view.findViewById(R.id.js_prompt_value); + edit.setText(mDefaultValue); + builder.setPositiveButton(android.R.string.ok, new PositiveListener(edit)); + ((TextView) view.findViewById(R.id.js_prompt_message)).setText(mMessage); + builder.setView(view); + + // TODO: Open keyboard and place text cursor. + } + if (mType != ALERT) { + builder.setNegativeButton(android.R.string.cancel, new CancelListener()); + } + final AlertDialog dialog = builder.show(); + + if (edit != null) { // Is it a prompt dialog? + edit.setOnEditorActionListener(new TextView.OnEditorActionListener() { + @Override + public boolean onEditorAction(TextView v, int actionId, KeyEvent event) { + if (actionId == EditorInfo.IME_ACTION_DONE) { + // TODO: Accept input, close keyboard, close dialog + return true; + } + return false; + } + }); + } + } + + private class CancelListener implements DialogInterface.OnCancelListener, + DialogInterface.OnClickListener { + @Override + public void onCancel(DialogInterface dialog) { + mResult.cancel(); + } + @Override + public void onClick(DialogInterface dialog, int which) { + mResult.cancel(); + } + } + + private class PositiveListener implements DialogInterface.OnClickListener { + private final EditText mEdit; + public PositiveListener(EditText edit) { + mEdit = edit; + } + @Override + public void onClick(DialogInterface dialog, int which) { + if (mEdit == null) { + mResult.confirm(); + } else { + ((JsPromptResult) mResult).confirm(mEdit.getText().toString()); + } + } + } + + private static boolean canShowAlertDialog(Context context) { + return context instanceof Activity; + } +} diff --git a/demos/mobile/android/app/src/main/java/com/google/blockly/android/webview/WebChromeClient.java b/demos/mobile/android/app/src/main/java/com/google/blockly/android/webview/WebChromeClient.java new file mode 100644 index 000000000..7cc0f226e --- /dev/null +++ b/demos/mobile/android/app/src/main/java/com/google/blockly/android/webview/WebChromeClient.java @@ -0,0 +1,32 @@ +package com.google.blockly.android.webview; + +import android.webkit.JsPromptResult; +import android.webkit.JsResult; +import android.webkit.WebView; + +/** + * Provides native hooks for JavaScript console dialog functions. + */ +public class WebChromeClient extends android.webkit.WebChromeClient { + @Override + public boolean onJsAlert(WebView view, String url, String message, JsResult result) { + new JsDialogHelper(result, JsDialogHelper.ALERT, null, message, url) + .showDialog(view.getContext()); + return true; + } + + @Override + public boolean onJsConfirm(WebView view, String url, String message, JsResult result) { + new JsDialogHelper(result, JsDialogHelper.CONFIRM, null, message, url) + .showDialog(view.getContext()); + return true; + } + + @Override + public boolean onJsPrompt(WebView view, String url, String message, String defaultValue, + JsPromptResult result) { + new JsDialogHelper(result, JsDialogHelper.PROMPT, defaultValue, message, url) + .showDialog(view.getContext()); + return true; + } +} diff --git a/demos/mobile/android/app/src/main/res/layout/js_prompt.xml b/demos/mobile/android/app/src/main/res/layout/js_prompt.xml new file mode 100644 index 000000000..759295bcc --- /dev/null +++ b/demos/mobile/android/app/src/main/res/layout/js_prompt.xml @@ -0,0 +1,37 @@ + + + + + + + \ No newline at end of file diff --git a/demos/mobile/android/app/src/main/res/values/js_dialog_helper.xml b/demos/mobile/android/app/src/main/res/values/js_dialog_helper.xml new file mode 100644 index 000000000..1d401fc63 --- /dev/null +++ b/demos/mobile/android/app/src/main/res/values/js_dialog_helper.xml @@ -0,0 +1,7 @@ + + + + The page at \"%s\" says: + + JavaScript + \ No newline at end of file diff --git a/demos/mobile/html/index.html b/demos/mobile/html/index.html index 55906503d..e527aa3b9 100644 --- a/demos/mobile/html/index.html +++ b/demos/mobile/html/index.html @@ -2,7 +2,7 @@ - +