blob: a5ea5beb21112363fe4a3b27e03a5caa6e7d463e [file] [log] [blame]
// Copyright 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.chromium.chrome.browser;
import android.content.Context;
import android.content.DialogInterface;
import android.support.v7.app.AlertDialog;
import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.CheckBox;
import android.widget.EditText;
import android.widget.TextView;
import org.chromium.base.Log;
import org.chromium.base.VisibleForTesting;
import org.chromium.base.annotations.CalledByNative;
import org.chromium.chrome.R;
import org.chromium.chrome.browser.jsdialog.JavascriptModalDialogView;
import org.chromium.chrome.browser.modaldialog.ModalDialogManager;
import org.chromium.chrome.browser.modaldialog.ModalDialogView;
import org.chromium.chrome.browser.vr.VrShellDelegate;
import org.chromium.ui.base.WindowAndroid;
/**
* A dialog shown via JavaScript. This can be an alert dialog, a prompt dialog, a confirm dialog,
* or an onbeforeunload dialog.
*/
public class JavascriptAppModalDialog
implements DialogInterface.OnClickListener, ModalDialogView.Controller {
private static final String TAG = "JSAppModalDialog";
private final String mTitle;
private final String mMessage;
private final int mPositiveButtonTextId;
private final int mNegativeButtonTextId;
private final boolean mShouldShowSuppressCheckBox;
private long mNativeDialogPointer;
private AlertDialog mDialog;
private CheckBox mSuppressCheckBox;
private TextView mPromptTextView;
private ModalDialogManager mModalDialogManager;
private JavascriptModalDialogView mDialogView;
private JavascriptAppModalDialog(String title, String message,
int positiveButtonTextId, int negativeButtonTextId,
boolean shouldShowSuppressCheckBox) {
mTitle = title;
mMessage = message;
mPositiveButtonTextId = positiveButtonTextId;
mNegativeButtonTextId = negativeButtonTextId;
mShouldShowSuppressCheckBox = shouldShowSuppressCheckBox;
}
@CalledByNative
public static JavascriptAppModalDialog createAlertDialog(String title, String message,
boolean shouldShowSuppressCheckBox) {
return new JavascriptAppModalDialog(title, message, R.string.ok, 0,
shouldShowSuppressCheckBox);
}
@CalledByNative
public static JavascriptAppModalDialog createConfirmDialog(String title, String message,
boolean shouldShowSuppressCheckBox) {
return new JavascriptAppModalDialog(title, message, R.string.ok, R.string.cancel,
shouldShowSuppressCheckBox);
}
@CalledByNative
public static JavascriptAppModalDialog createBeforeUnloadDialog(String title, String message,
boolean isReload, boolean shouldShowSuppressCheckBox) {
return new JavascriptAppModalDialog(title, message,
isReload ? R.string.reload : R.string.leave, R.string.cancel,
shouldShowSuppressCheckBox);
}
@CalledByNative
public static JavascriptAppModalDialog createPromptDialog(String title, String message,
boolean shouldShowSuppressCheckBox, String defaultPromptText) {
return new JavascriptAppPromptDialog(title, message, shouldShowSuppressCheckBox,
defaultPromptText);
}
@CalledByNative
void showJavascriptAppModalDialog(WindowAndroid window, long nativeDialogPointer) {
assert window != null;
Context context = window.getActivity().get();
// If the activity has gone away, then just clean up the native pointer.
if (context == null) {
nativeDidCancelAppModalDialog(nativeDialogPointer, false);
return;
}
// Cache the native dialog pointer so that we can use it to return the response.
mNativeDialogPointer = nativeDialogPointer;
if (VrShellDelegate.isInVr()) {
// Use JavascriptModalDialogView while in VR.
ChromeActivity activity = (ChromeActivity) window.getActivity().get();
mModalDialogManager = activity.getModalDialogManager();
// Only BeforeUnloadDialog should be created this way, so it is safe to set prompt text
// to null. We also disabled suppress checkbox in VR. In the future, it is possible that
// we bring it back. See https://crbug.com/830057.
assert !(this instanceof JavascriptAppPromptDialog);
mDialogView = JavascriptModalDialogView.create(this, mTitle, mMessage, null, false,
mPositiveButtonTextId, mNegativeButtonTextId);
mModalDialogManager.showDialog(mDialogView, ModalDialogManager.TAB_MODAL);
} else {
LayoutInflater inflater = LayoutInflater.from(context);
ViewGroup layout = (ViewGroup) inflater.inflate(R.layout.js_modal_dialog, null);
mSuppressCheckBox = (CheckBox) layout.findViewById(R.id.suppress_js_modal_dialogs);
mPromptTextView = (TextView) layout.findViewById(R.id.js_modal_dialog_prompt);
prepare(layout);
AlertDialog.Builder builder = new AlertDialog.Builder(context, R.style.AlertDialogTheme)
.setView(layout)
.setTitle(mTitle)
.setOnCancelListener(dialog -> cancel(false));
if (mPositiveButtonTextId != 0) builder.setPositiveButton(mPositiveButtonTextId, this);
if (mNegativeButtonTextId != 0) builder.setNegativeButton(mNegativeButtonTextId, this);
mDialog = builder.create();
mDialog.setCanceledOnTouchOutside(false);
mDialog.getDelegate().setHandleNativeActionModesEnabled(false);
mDialog.show();
}
}
@Override
public void onClick(DialogInterface dialog, int which) {
switch (which) {
case DialogInterface.BUTTON_POSITIVE:
confirm(mPromptTextView.getText().toString(), mSuppressCheckBox.isChecked());
mDialog.dismiss();
break;
case DialogInterface.BUTTON_NEGATIVE:
cancel(mSuppressCheckBox.isChecked());
mDialog.dismiss();
break;
default:
Log.e(TAG, "Unexpected button pressed in dialog: " + which);
}
}
@Override
public void onClick(@ModalDialogView.ButtonType int buttonType) {
switch (buttonType) {
case ModalDialogView.BUTTON_POSITIVE:
confirm(mDialogView.getPromptText(), false);
mModalDialogManager.dismissDialog(mDialogView);
break;
case ModalDialogView.BUTTON_NEGATIVE:
cancel(false);
mModalDialogManager.dismissDialog(mDialogView);
break;
default:
Log.e(TAG, "Unexpected button pressed in dialog: " + buttonType);
}
}
@Override
public void onCancel() {
cancel(false);
}
@Override
public void onDismiss() {}
protected void prepare(final ViewGroup layout) {
// Display the checkbox for suppressing dialogs if necessary.
layout.findViewById(R.id.suppress_js_modal_dialogs).setVisibility(
mShouldShowSuppressCheckBox ? View.VISIBLE : View.GONE);
// If the message is null or empty do not display the message text view.
// Hide parent scroll view instead of text view in order to prevent ui discrepancies.
if (TextUtils.isEmpty(mMessage)) {
layout.findViewById(R.id.js_modal_dialog_scroll_view).setVisibility(View.GONE);
} else {
((TextView) layout.findViewById(R.id.js_modal_dialog_message)).setText(mMessage);
layout.findViewById(R.id.js_modal_dialog_scroll_view)
.addOnLayoutChangeListener(
(v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> {
boolean isScrollable =
v.getMeasuredHeight() - v.getPaddingTop()
- v.getPaddingBottom()
< ((ViewGroup) v).getChildAt(0).getMeasuredHeight();
v.setFocusable(isScrollable);
});
}
}
private void confirm(String promptResult, boolean suppressDialogs) {
if (mNativeDialogPointer != 0) {
nativeDidAcceptAppModalDialog(mNativeDialogPointer, promptResult, suppressDialogs);
}
}
private void cancel(boolean suppressDialogs) {
if (mNativeDialogPointer != 0) {
nativeDidCancelAppModalDialog(mNativeDialogPointer, suppressDialogs);
}
}
@CalledByNative
private void dismiss() {
if (mDialog != null) {
mDialog.dismiss();
} else {
mModalDialogManager.dismissDialog(mDialogView);
}
mNativeDialogPointer = 0;
}
/**
* Returns the currently showing dialog, null if none is showing.
*/
@VisibleForTesting
public static JavascriptAppModalDialog getCurrentDialogForTest() {
return nativeGetCurrentModalDialog();
}
/**
* Returns the AlertDialog associated with this JavascriptAppPromptDialog.
*/
@VisibleForTesting
public AlertDialog getDialogForTest() {
return mDialog;
}
private static class JavascriptAppPromptDialog extends JavascriptAppModalDialog {
private final String mDefaultPromptText;
JavascriptAppPromptDialog(String title, String message, boolean shouldShowSuppressCheckBox,
String defaultPromptText) {
super(title, message, R.string.ok, R.string.cancel, shouldShowSuppressCheckBox);
mDefaultPromptText = defaultPromptText;
}
@Override
protected void prepare(ViewGroup layout) {
super.prepare(layout);
EditText prompt = (EditText) layout.findViewById(R.id.js_modal_dialog_prompt);
prompt.setVisibility(View.VISIBLE);
if (mDefaultPromptText.length() > 0) {
prompt.setText(mDefaultPromptText);
prompt.selectAll();
}
}
}
private native void nativeDidAcceptAppModalDialog(long nativeJavascriptAppModalDialogAndroid,
String prompt, boolean suppress);
private native void nativeDidCancelAppModalDialog(long nativeJavascriptAppModalDialogAndroid,
boolean suppress);
private static native JavascriptAppModalDialog nativeGetCurrentModalDialog();
}