198dedf7ccc9841c19d2213d25e09e962014a02ecKristian Monsen/*
298dedf7ccc9841c19d2213d25e09e962014a02ecKristian Monsen * Copyright (C) 2013 The Android Open Source Project
398dedf7ccc9841c19d2213d25e09e962014a02ecKristian Monsen *
498dedf7ccc9841c19d2213d25e09e962014a02ecKristian Monsen * Licensed under the Apache License, Version 2.0 (the "License");
598dedf7ccc9841c19d2213d25e09e962014a02ecKristian Monsen * you may not use this file except in compliance with the License.
698dedf7ccc9841c19d2213d25e09e962014a02ecKristian Monsen * You may obtain a copy of the License at
798dedf7ccc9841c19d2213d25e09e962014a02ecKristian Monsen *
898dedf7ccc9841c19d2213d25e09e962014a02ecKristian Monsen *      http://www.apache.org/licenses/LICENSE-2.0
998dedf7ccc9841c19d2213d25e09e962014a02ecKristian Monsen *
1098dedf7ccc9841c19d2213d25e09e962014a02ecKristian Monsen * Unless required by applicable law or agreed to in writing, software
1198dedf7ccc9841c19d2213d25e09e962014a02ecKristian Monsen * distributed under the License is distributed on an "AS IS" BASIS,
1298dedf7ccc9841c19d2213d25e09e962014a02ecKristian Monsen * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1398dedf7ccc9841c19d2213d25e09e962014a02ecKristian Monsen * See the License for the specific language governing permissions and
1498dedf7ccc9841c19d2213d25e09e962014a02ecKristian Monsen * limitations under the License.
1598dedf7ccc9841c19d2213d25e09e962014a02ecKristian Monsen */
1698dedf7ccc9841c19d2213d25e09e962014a02ecKristian Monsen
1798dedf7ccc9841c19d2213d25e09e962014a02ecKristian Monsenpackage android.webkit;
1898dedf7ccc9841c19d2213d25e09e962014a02ecKristian Monsen
19451e338c51e8c45efc0d21536dfae6f78f6d5e06Ignacio Sollaimport android.annotation.SystemApi;
2098dedf7ccc9841c19d2213d25e09e962014a02ecKristian Monsenimport android.app.Activity;
2198dedf7ccc9841c19d2213d25e09e962014a02ecKristian Monsenimport android.app.AlertDialog;
2298dedf7ccc9841c19d2213d25e09e962014a02ecKristian Monsenimport android.content.Context;
2398dedf7ccc9841c19d2213d25e09e962014a02ecKristian Monsenimport android.content.DialogInterface;
2498dedf7ccc9841c19d2213d25e09e962014a02ecKristian Monsenimport android.os.Message;
2598dedf7ccc9841c19d2213d25e09e962014a02ecKristian Monsenimport android.util.Log;
2698dedf7ccc9841c19d2213d25e09e962014a02ecKristian Monsenimport android.view.LayoutInflater;
2798dedf7ccc9841c19d2213d25e09e962014a02ecKristian Monsenimport android.view.View;
2898dedf7ccc9841c19d2213d25e09e962014a02ecKristian Monsenimport android.widget.EditText;
2998dedf7ccc9841c19d2213d25e09e962014a02ecKristian Monsenimport android.widget.TextView;
3098dedf7ccc9841c19d2213d25e09e962014a02ecKristian Monsen
3198dedf7ccc9841c19d2213d25e09e962014a02ecKristian Monsenimport java.net.MalformedURLException;
3298dedf7ccc9841c19d2213d25e09e962014a02ecKristian Monsenimport java.net.URL;
3398dedf7ccc9841c19d2213d25e09e962014a02ecKristian Monsen
3498dedf7ccc9841c19d2213d25e09e962014a02ecKristian Monsen/**
3598dedf7ccc9841c19d2213d25e09e962014a02ecKristian Monsen * Helper class to create JavaScript dialogs. It is used by
3698dedf7ccc9841c19d2213d25e09e962014a02ecKristian Monsen * different WebView implementations.
3798dedf7ccc9841c19d2213d25e09e962014a02ecKristian Monsen *
38451e338c51e8c45efc0d21536dfae6f78f6d5e06Ignacio Solla * @hide
3998dedf7ccc9841c19d2213d25e09e962014a02ecKristian Monsen */
40451e338c51e8c45efc0d21536dfae6f78f6d5e06Ignacio Solla@SystemApi
4198dedf7ccc9841c19d2213d25e09e962014a02ecKristian Monsenpublic class JsDialogHelper {
4298dedf7ccc9841c19d2213d25e09e962014a02ecKristian Monsen
4398dedf7ccc9841c19d2213d25e09e962014a02ecKristian Monsen    private static final String TAG = "JsDialogHelper";
4498dedf7ccc9841c19d2213d25e09e962014a02ecKristian Monsen
4598dedf7ccc9841c19d2213d25e09e962014a02ecKristian Monsen    // Dialog types
4698dedf7ccc9841c19d2213d25e09e962014a02ecKristian Monsen    public static final int ALERT   = 1;
4798dedf7ccc9841c19d2213d25e09e962014a02ecKristian Monsen    public static final int CONFIRM = 2;
4898dedf7ccc9841c19d2213d25e09e962014a02ecKristian Monsen    public static final int PROMPT  = 3;
4998dedf7ccc9841c19d2213d25e09e962014a02ecKristian Monsen    public static final int UNLOAD  = 4;
5098dedf7ccc9841c19d2213d25e09e962014a02ecKristian Monsen
5198dedf7ccc9841c19d2213d25e09e962014a02ecKristian Monsen    private final String mDefaultValue;
5298dedf7ccc9841c19d2213d25e09e962014a02ecKristian Monsen    private final JsPromptResult mResult;
5398dedf7ccc9841c19d2213d25e09e962014a02ecKristian Monsen    private final String mMessage;
5498dedf7ccc9841c19d2213d25e09e962014a02ecKristian Monsen    private final int mType;
5598dedf7ccc9841c19d2213d25e09e962014a02ecKristian Monsen    private final String mUrl;
5698dedf7ccc9841c19d2213d25e09e962014a02ecKristian Monsen
5798dedf7ccc9841c19d2213d25e09e962014a02ecKristian Monsen    public JsDialogHelper(JsPromptResult result, int type, String defaultValue, String message,
5898dedf7ccc9841c19d2213d25e09e962014a02ecKristian Monsen            String url) {
5998dedf7ccc9841c19d2213d25e09e962014a02ecKristian Monsen        mResult = result;
6098dedf7ccc9841c19d2213d25e09e962014a02ecKristian Monsen        mDefaultValue = defaultValue;
6198dedf7ccc9841c19d2213d25e09e962014a02ecKristian Monsen        mMessage = message;
6298dedf7ccc9841c19d2213d25e09e962014a02ecKristian Monsen        mType = type;
6398dedf7ccc9841c19d2213d25e09e962014a02ecKristian Monsen        mUrl = url;
6498dedf7ccc9841c19d2213d25e09e962014a02ecKristian Monsen    }
6598dedf7ccc9841c19d2213d25e09e962014a02ecKristian Monsen
6698dedf7ccc9841c19d2213d25e09e962014a02ecKristian Monsen    public JsDialogHelper(JsPromptResult result, Message msg) {
6798dedf7ccc9841c19d2213d25e09e962014a02ecKristian Monsen        mResult = result;
6898dedf7ccc9841c19d2213d25e09e962014a02ecKristian Monsen        mDefaultValue = msg.getData().getString("default");
6998dedf7ccc9841c19d2213d25e09e962014a02ecKristian Monsen        mMessage = msg.getData().getString("message");
7098dedf7ccc9841c19d2213d25e09e962014a02ecKristian Monsen        mType = msg.getData().getInt("type");
7198dedf7ccc9841c19d2213d25e09e962014a02ecKristian Monsen        mUrl = msg.getData().getString("url");
7298dedf7ccc9841c19d2213d25e09e962014a02ecKristian Monsen    }
7398dedf7ccc9841c19d2213d25e09e962014a02ecKristian Monsen
7498dedf7ccc9841c19d2213d25e09e962014a02ecKristian Monsen    public boolean invokeCallback(WebChromeClient client, WebView webView) {
7598dedf7ccc9841c19d2213d25e09e962014a02ecKristian Monsen        switch (mType) {
7698dedf7ccc9841c19d2213d25e09e962014a02ecKristian Monsen            case ALERT:
7798dedf7ccc9841c19d2213d25e09e962014a02ecKristian Monsen                return client.onJsAlert(webView, mUrl, mMessage, mResult);
7898dedf7ccc9841c19d2213d25e09e962014a02ecKristian Monsen            case CONFIRM:
7998dedf7ccc9841c19d2213d25e09e962014a02ecKristian Monsen                return client.onJsConfirm(webView, mUrl, mMessage, mResult);
8098dedf7ccc9841c19d2213d25e09e962014a02ecKristian Monsen            case UNLOAD:
8198dedf7ccc9841c19d2213d25e09e962014a02ecKristian Monsen                return client.onJsBeforeUnload(webView, mUrl, mMessage, mResult);
8298dedf7ccc9841c19d2213d25e09e962014a02ecKristian Monsen            case PROMPT:
8398dedf7ccc9841c19d2213d25e09e962014a02ecKristian Monsen                return client.onJsPrompt(webView, mUrl, mMessage, mDefaultValue, mResult);
8498dedf7ccc9841c19d2213d25e09e962014a02ecKristian Monsen            default:
8598dedf7ccc9841c19d2213d25e09e962014a02ecKristian Monsen                throw new IllegalArgumentException("Unexpected type: " + mType);
8698dedf7ccc9841c19d2213d25e09e962014a02ecKristian Monsen        }
8798dedf7ccc9841c19d2213d25e09e962014a02ecKristian Monsen    }
8898dedf7ccc9841c19d2213d25e09e962014a02ecKristian Monsen
8998dedf7ccc9841c19d2213d25e09e962014a02ecKristian Monsen    public void showDialog(Context context) {
9098dedf7ccc9841c19d2213d25e09e962014a02ecKristian Monsen        if (!canShowAlertDialog(context)) {
9198dedf7ccc9841c19d2213d25e09e962014a02ecKristian Monsen            Log.w(TAG, "Cannot create a dialog, the WebView context is not an Activity");
9298dedf7ccc9841c19d2213d25e09e962014a02ecKristian Monsen            mResult.cancel();
9398dedf7ccc9841c19d2213d25e09e962014a02ecKristian Monsen            return;
9498dedf7ccc9841c19d2213d25e09e962014a02ecKristian Monsen        }
9598dedf7ccc9841c19d2213d25e09e962014a02ecKristian Monsen
9698dedf7ccc9841c19d2213d25e09e962014a02ecKristian Monsen        String title, displayMessage;
9798dedf7ccc9841c19d2213d25e09e962014a02ecKristian Monsen        int positiveTextId, negativeTextId;
9898dedf7ccc9841c19d2213d25e09e962014a02ecKristian Monsen        if (mType == UNLOAD) {
9998dedf7ccc9841c19d2213d25e09e962014a02ecKristian Monsen            title = context.getString(com.android.internal.R.string.js_dialog_before_unload_title);
10098dedf7ccc9841c19d2213d25e09e962014a02ecKristian Monsen            displayMessage = context.getString(
10198dedf7ccc9841c19d2213d25e09e962014a02ecKristian Monsen                    com.android.internal.R.string.js_dialog_before_unload, mMessage);
10298dedf7ccc9841c19d2213d25e09e962014a02ecKristian Monsen            positiveTextId = com.android.internal.R.string.js_dialog_before_unload_positive_button;
10398dedf7ccc9841c19d2213d25e09e962014a02ecKristian Monsen            negativeTextId = com.android.internal.R.string.js_dialog_before_unload_negative_button;
10498dedf7ccc9841c19d2213d25e09e962014a02ecKristian Monsen        } else {
10598dedf7ccc9841c19d2213d25e09e962014a02ecKristian Monsen            title = getJsDialogTitle(context);
10698dedf7ccc9841c19d2213d25e09e962014a02ecKristian Monsen            displayMessage = mMessage;
10798dedf7ccc9841c19d2213d25e09e962014a02ecKristian Monsen            positiveTextId = com.android.internal.R.string.ok;
10898dedf7ccc9841c19d2213d25e09e962014a02ecKristian Monsen            negativeTextId = com.android.internal.R.string.cancel;
10998dedf7ccc9841c19d2213d25e09e962014a02ecKristian Monsen        }
11098dedf7ccc9841c19d2213d25e09e962014a02ecKristian Monsen        AlertDialog.Builder builder = new AlertDialog.Builder(context);
11198dedf7ccc9841c19d2213d25e09e962014a02ecKristian Monsen        builder.setTitle(title);
11298dedf7ccc9841c19d2213d25e09e962014a02ecKristian Monsen        builder.setOnCancelListener(new CancelListener());
11398dedf7ccc9841c19d2213d25e09e962014a02ecKristian Monsen        if (mType != PROMPT) {
11498dedf7ccc9841c19d2213d25e09e962014a02ecKristian Monsen            builder.setMessage(displayMessage);
11598dedf7ccc9841c19d2213d25e09e962014a02ecKristian Monsen            builder.setPositiveButton(positiveTextId, new PositiveListener(null));
11698dedf7ccc9841c19d2213d25e09e962014a02ecKristian Monsen        } else {
11798dedf7ccc9841c19d2213d25e09e962014a02ecKristian Monsen            final View view = LayoutInflater.from(context).inflate(
11898dedf7ccc9841c19d2213d25e09e962014a02ecKristian Monsen                    com.android.internal.R.layout.js_prompt, null);
11998dedf7ccc9841c19d2213d25e09e962014a02ecKristian Monsen            EditText edit = ((EditText) view.findViewById(com.android.internal.R.id.value));
12098dedf7ccc9841c19d2213d25e09e962014a02ecKristian Monsen            edit.setText(mDefaultValue);
12198dedf7ccc9841c19d2213d25e09e962014a02ecKristian Monsen            builder.setPositiveButton(positiveTextId, new PositiveListener(edit));
12298dedf7ccc9841c19d2213d25e09e962014a02ecKristian Monsen            ((TextView) view.findViewById(com.android.internal.R.id.message)).setText(mMessage);
12398dedf7ccc9841c19d2213d25e09e962014a02ecKristian Monsen            builder.setView(view);
12498dedf7ccc9841c19d2213d25e09e962014a02ecKristian Monsen        }
12598dedf7ccc9841c19d2213d25e09e962014a02ecKristian Monsen        if (mType != ALERT) {
12698dedf7ccc9841c19d2213d25e09e962014a02ecKristian Monsen            builder.setNegativeButton(negativeTextId, new CancelListener());
12798dedf7ccc9841c19d2213d25e09e962014a02ecKristian Monsen        }
12898dedf7ccc9841c19d2213d25e09e962014a02ecKristian Monsen        builder.show();
12998dedf7ccc9841c19d2213d25e09e962014a02ecKristian Monsen    }
13098dedf7ccc9841c19d2213d25e09e962014a02ecKristian Monsen
13198dedf7ccc9841c19d2213d25e09e962014a02ecKristian Monsen    private class CancelListener implements DialogInterface.OnCancelListener,
13298dedf7ccc9841c19d2213d25e09e962014a02ecKristian Monsen            DialogInterface.OnClickListener {
13398dedf7ccc9841c19d2213d25e09e962014a02ecKristian Monsen        @Override
13498dedf7ccc9841c19d2213d25e09e962014a02ecKristian Monsen        public void onCancel(DialogInterface dialog) {
13598dedf7ccc9841c19d2213d25e09e962014a02ecKristian Monsen            mResult.cancel();
13698dedf7ccc9841c19d2213d25e09e962014a02ecKristian Monsen        }
13798dedf7ccc9841c19d2213d25e09e962014a02ecKristian Monsen        @Override
13898dedf7ccc9841c19d2213d25e09e962014a02ecKristian Monsen        public void onClick(DialogInterface dialog, int which) {
13998dedf7ccc9841c19d2213d25e09e962014a02ecKristian Monsen            mResult.cancel();
14098dedf7ccc9841c19d2213d25e09e962014a02ecKristian Monsen        }
14198dedf7ccc9841c19d2213d25e09e962014a02ecKristian Monsen    }
14298dedf7ccc9841c19d2213d25e09e962014a02ecKristian Monsen
14398dedf7ccc9841c19d2213d25e09e962014a02ecKristian Monsen    private class PositiveListener implements DialogInterface.OnClickListener {
14498dedf7ccc9841c19d2213d25e09e962014a02ecKristian Monsen        private final EditText mEdit;
14598dedf7ccc9841c19d2213d25e09e962014a02ecKristian Monsen
14698dedf7ccc9841c19d2213d25e09e962014a02ecKristian Monsen        public PositiveListener(EditText edit) {
14798dedf7ccc9841c19d2213d25e09e962014a02ecKristian Monsen            mEdit = edit;
14898dedf7ccc9841c19d2213d25e09e962014a02ecKristian Monsen        }
14998dedf7ccc9841c19d2213d25e09e962014a02ecKristian Monsen
15098dedf7ccc9841c19d2213d25e09e962014a02ecKristian Monsen        @Override
15198dedf7ccc9841c19d2213d25e09e962014a02ecKristian Monsen        public void onClick(DialogInterface dialog, int which) {
15298dedf7ccc9841c19d2213d25e09e962014a02ecKristian Monsen            if (mEdit == null) {
15398dedf7ccc9841c19d2213d25e09e962014a02ecKristian Monsen                mResult.confirm();
15498dedf7ccc9841c19d2213d25e09e962014a02ecKristian Monsen            } else {
15598dedf7ccc9841c19d2213d25e09e962014a02ecKristian Monsen                mResult.confirm(mEdit.getText().toString());
15698dedf7ccc9841c19d2213d25e09e962014a02ecKristian Monsen            }
15798dedf7ccc9841c19d2213d25e09e962014a02ecKristian Monsen        }
15898dedf7ccc9841c19d2213d25e09e962014a02ecKristian Monsen    }
15998dedf7ccc9841c19d2213d25e09e962014a02ecKristian Monsen
16098dedf7ccc9841c19d2213d25e09e962014a02ecKristian Monsen    private String getJsDialogTitle(Context context) {
16198dedf7ccc9841c19d2213d25e09e962014a02ecKristian Monsen        String title = mUrl;
16298dedf7ccc9841c19d2213d25e09e962014a02ecKristian Monsen        if (URLUtil.isDataUrl(mUrl)) {
16398dedf7ccc9841c19d2213d25e09e962014a02ecKristian Monsen            // For data: urls, we just display 'JavaScript' similar to Chrome.
16498dedf7ccc9841c19d2213d25e09e962014a02ecKristian Monsen            title = context.getString(com.android.internal.R.string.js_dialog_title_default);
16598dedf7ccc9841c19d2213d25e09e962014a02ecKristian Monsen        } else {
16698dedf7ccc9841c19d2213d25e09e962014a02ecKristian Monsen            try {
16798dedf7ccc9841c19d2213d25e09e962014a02ecKristian Monsen                URL alertUrl = new URL(mUrl);
16898dedf7ccc9841c19d2213d25e09e962014a02ecKristian Monsen                // For example: "The page at 'http://www.mit.edu' says:"
16998dedf7ccc9841c19d2213d25e09e962014a02ecKristian Monsen                title = context.getString(com.android.internal.R.string.js_dialog_title,
17098dedf7ccc9841c19d2213d25e09e962014a02ecKristian Monsen                        alertUrl.getProtocol() + "://" + alertUrl.getHost());
17198dedf7ccc9841c19d2213d25e09e962014a02ecKristian Monsen            } catch (MalformedURLException ex) {
17298dedf7ccc9841c19d2213d25e09e962014a02ecKristian Monsen                // do nothing. just use the url as the title
17398dedf7ccc9841c19d2213d25e09e962014a02ecKristian Monsen            }
17498dedf7ccc9841c19d2213d25e09e962014a02ecKristian Monsen        }
17598dedf7ccc9841c19d2213d25e09e962014a02ecKristian Monsen        return title;
17698dedf7ccc9841c19d2213d25e09e962014a02ecKristian Monsen    }
17798dedf7ccc9841c19d2213d25e09e962014a02ecKristian Monsen
17898dedf7ccc9841c19d2213d25e09e962014a02ecKristian Monsen    private static boolean canShowAlertDialog(Context context) {
17998dedf7ccc9841c19d2213d25e09e962014a02ecKristian Monsen        // We can only display the alert dialog if mContext is
18098dedf7ccc9841c19d2213d25e09e962014a02ecKristian Monsen        // an Activity context.
18198dedf7ccc9841c19d2213d25e09e962014a02ecKristian Monsen        // FIXME: Should we display dialogs if mContext does
18298dedf7ccc9841c19d2213d25e09e962014a02ecKristian Monsen        // not have the window focus (e.g. if the user is viewing
18398dedf7ccc9841c19d2213d25e09e962014a02ecKristian Monsen        // another Activity when the alert should be displayed) ?
18498dedf7ccc9841c19d2213d25e09e962014a02ecKristian Monsen        // See bug 3166409
18598dedf7ccc9841c19d2213d25e09e962014a02ecKristian Monsen        return context instanceof Activity;
18698dedf7ccc9841c19d2213d25e09e962014a02ecKristian Monsen    }
18798dedf7ccc9841c19d2213d25e09e962014a02ecKristian Monsen}
188