Home | History | Annotate | Download | only in webkit
      1 /*
      2  * Copyright (C) 2013 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package android.webkit;
     18 
     19 import android.annotation.SystemApi;
     20 import android.app.Activity;
     21 import android.app.AlertDialog;
     22 import android.content.Context;
     23 import android.content.DialogInterface;
     24 import android.os.Message;
     25 import android.util.Log;
     26 import android.view.LayoutInflater;
     27 import android.view.View;
     28 import android.widget.EditText;
     29 import android.widget.TextView;
     30 
     31 import java.net.MalformedURLException;
     32 import java.net.URL;
     33 
     34 /**
     35  * Helper class to create JavaScript dialogs. It is used by
     36  * different WebView implementations.
     37  *
     38  * @hide
     39  */
     40 @SystemApi
     41 public class JsDialogHelper {
     42 
     43     private static final String TAG = "JsDialogHelper";
     44 
     45     // Dialog types
     46     public static final int ALERT   = 1;
     47     public static final int CONFIRM = 2;
     48     public static final int PROMPT  = 3;
     49     public static final int UNLOAD  = 4;
     50 
     51     private final String mDefaultValue;
     52     private final JsPromptResult mResult;
     53     private final String mMessage;
     54     private final int mType;
     55     private final String mUrl;
     56 
     57     public JsDialogHelper(JsPromptResult result, int type, String defaultValue, String message,
     58             String url) {
     59         mResult = result;
     60         mDefaultValue = defaultValue;
     61         mMessage = message;
     62         mType = type;
     63         mUrl = url;
     64     }
     65 
     66     public JsDialogHelper(JsPromptResult result, Message msg) {
     67         mResult = result;
     68         mDefaultValue = msg.getData().getString("default");
     69         mMessage = msg.getData().getString("message");
     70         mType = msg.getData().getInt("type");
     71         mUrl = msg.getData().getString("url");
     72     }
     73 
     74     public boolean invokeCallback(WebChromeClient client, WebView webView) {
     75         switch (mType) {
     76             case ALERT:
     77                 return client.onJsAlert(webView, mUrl, mMessage, mResult);
     78             case CONFIRM:
     79                 return client.onJsConfirm(webView, mUrl, mMessage, mResult);
     80             case UNLOAD:
     81                 return client.onJsBeforeUnload(webView, mUrl, mMessage, mResult);
     82             case PROMPT:
     83                 return client.onJsPrompt(webView, mUrl, mMessage, mDefaultValue, mResult);
     84             default:
     85                 throw new IllegalArgumentException("Unexpected type: " + mType);
     86         }
     87     }
     88 
     89     public void showDialog(Context context) {
     90         if (!canShowAlertDialog(context)) {
     91             Log.w(TAG, "Cannot create a dialog, the WebView context is not an Activity");
     92             mResult.cancel();
     93             return;
     94         }
     95 
     96         String title, displayMessage;
     97         int positiveTextId, negativeTextId;
     98         if (mType == UNLOAD) {
     99             title = context.getString(com.android.internal.R.string.js_dialog_before_unload_title);
    100             displayMessage = context.getString(
    101                     com.android.internal.R.string.js_dialog_before_unload, mMessage);
    102             positiveTextId = com.android.internal.R.string.js_dialog_before_unload_positive_button;
    103             negativeTextId = com.android.internal.R.string.js_dialog_before_unload_negative_button;
    104         } else {
    105             title = getJsDialogTitle(context);
    106             displayMessage = mMessage;
    107             positiveTextId = com.android.internal.R.string.ok;
    108             negativeTextId = com.android.internal.R.string.cancel;
    109         }
    110         AlertDialog.Builder builder = new AlertDialog.Builder(context);
    111         builder.setTitle(title);
    112         builder.setOnCancelListener(new CancelListener());
    113         if (mType != PROMPT) {
    114             builder.setMessage(displayMessage);
    115             builder.setPositiveButton(positiveTextId, new PositiveListener(null));
    116         } else {
    117             final View view = LayoutInflater.from(context).inflate(
    118                     com.android.internal.R.layout.js_prompt, null);
    119             EditText edit = ((EditText) view.findViewById(com.android.internal.R.id.value));
    120             edit.setText(mDefaultValue);
    121             builder.setPositiveButton(positiveTextId, new PositiveListener(edit));
    122             ((TextView) view.findViewById(com.android.internal.R.id.message)).setText(mMessage);
    123             builder.setView(view);
    124         }
    125         if (mType != ALERT) {
    126             builder.setNegativeButton(negativeTextId, new CancelListener());
    127         }
    128         builder.show();
    129     }
    130 
    131     private class CancelListener implements DialogInterface.OnCancelListener,
    132             DialogInterface.OnClickListener {
    133         @Override
    134         public void onCancel(DialogInterface dialog) {
    135             mResult.cancel();
    136         }
    137         @Override
    138         public void onClick(DialogInterface dialog, int which) {
    139             mResult.cancel();
    140         }
    141     }
    142 
    143     private class PositiveListener implements DialogInterface.OnClickListener {
    144         private final EditText mEdit;
    145 
    146         public PositiveListener(EditText edit) {
    147             mEdit = edit;
    148         }
    149 
    150         @Override
    151         public void onClick(DialogInterface dialog, int which) {
    152             if (mEdit == null) {
    153                 mResult.confirm();
    154             } else {
    155                 mResult.confirm(mEdit.getText().toString());
    156             }
    157         }
    158     }
    159 
    160     private String getJsDialogTitle(Context context) {
    161         String title = mUrl;
    162         if (URLUtil.isDataUrl(mUrl)) {
    163             // For data: urls, we just display 'JavaScript' similar to Chrome.
    164             title = context.getString(com.android.internal.R.string.js_dialog_title_default);
    165         } else {
    166             try {
    167                 URL alertUrl = new URL(mUrl);
    168                 // For example: "The page at 'http://www.mit.edu' says:"
    169                 title = context.getString(com.android.internal.R.string.js_dialog_title,
    170                         alertUrl.getProtocol() + "://" + alertUrl.getHost());
    171             } catch (MalformedURLException ex) {
    172                 // do nothing. just use the url as the title
    173             }
    174         }
    175         return title;
    176     }
    177 
    178     private static boolean canShowAlertDialog(Context context) {
    179         // We can only display the alert dialog if mContext is
    180         // an Activity context.
    181         // FIXME: Should we display dialogs if mContext does
    182         // not have the window focus (e.g. if the user is viewing
    183         // another Activity when the alert should be displayed) ?
    184         // See bug 3166409
    185         return context instanceof Activity;
    186     }
    187 }
    188