Commit 31b3c895 authored by Becky Zhou's avatar Becky Zhou Committed by Commit Bot

[ModalDialog] Add ModalDialogModel and ViewBinder

+ Added a ModalDialogModel and ModalDialogViewBinder
+ Migrate DownloadLocationDialog to use the model

Bug: 899318
Change-Id: I61d019cb4d707a65b09d30c2c3e185a63fd6caca
Reviewed-on: https://chromium-review.googlesource.com/c/1313763
Commit-Queue: Becky Zhou <huayinz@chromium.org>
Reviewed-by: default avatarTheresa <twellington@chromium.org>
Cr-Commit-Position: refs/heads/master@{#606297}
parent 3e2b2d9a
...@@ -3,7 +3,6 @@ ...@@ -3,7 +3,6 @@
Use of this source code is governed by a BSD-style license that can be Use of this source code is governed by a BSD-style license that can be
found in the LICENSE file. --> found in the LICENSE file. -->
<!-- TODO(huayinz): rename menu_bg or change the dialog background to the desired one. -->
<org.chromium.chrome.browser.widget.BoundedLinearLayout <org.chromium.chrome.browser.widget.BoundedLinearLayout
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
...@@ -24,6 +23,7 @@ ...@@ -24,6 +23,7 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="0dp" android:layout_height="0dp"
android:layout_weight="1" android:layout_weight="1"
android:visibility="gone"
android:fadeScrollbars="false"> android:fadeScrollbars="false">
<LinearLayout <LinearLayout
...@@ -40,6 +40,7 @@ ...@@ -40,6 +40,7 @@
<org.chromium.ui.widget.TextViewWithLeading <org.chromium.ui.widget.TextViewWithLeading
android:id="@+id/message" android:id="@+id/message"
android:textAppearance="@style/BlackBody" android:textAppearance="@style/BlackBody"
android:visibility="gone"
app:leading="20sp" app:leading="20sp"
style="@style/AlertDialogContent" /> style="@style/AlertDialogContent" />
...@@ -51,23 +52,28 @@ ...@@ -51,23 +52,28 @@
android:id="@+id/custom" android:id="@+id/custom"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="0dp" android:layout_height="0dp"
android:layout_weight="1" /> android:layout_weight="1"
android:visibility="gone" />
<android.support.v7.widget.ButtonBarLayout <android.support.v7.widget.ButtonBarLayout
android:id="@+id/button_bar"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:visibility="gone"
style="?attr/buttonBarStyle"> style="?attr/buttonBarStyle">
<android.support.v7.widget.AppCompatButton <android.support.v7.widget.AppCompatButton
android:id="@+id/negative_button" android:id="@+id/negative_button"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:visibility="gone"
style="?attr/buttonBarNegativeButtonStyle" /> style="?attr/buttonBarNegativeButtonStyle" />
<android.support.v7.widget.AppCompatButton <android.support.v7.widget.AppCompatButton
android:id="@+id/positive_button" android:id="@+id/positive_button"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:visibility="gone"
style="?attr/buttonBarPositiveButtonStyle" /> style="?attr/buttonBarPositiveButtonStyle" />
</android.support.v7.widget.ButtonBarLayout> </android.support.v7.widget.ButtonBarLayout>
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
Use of this source code is governed by a BSD-style license that can be Use of this source code is governed by a BSD-style license that can be
found in the LICENSE file. --> found in the LICENSE file. -->
<ScrollView <org.chromium.chrome.browser.download.DownloadLocationCustomView
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent" android:layout_width="match_parent"
...@@ -74,5 +74,5 @@ ...@@ -74,5 +74,5 @@
</LinearLayout> </LinearLayout>
</ScrollView> </org.chromium.chrome.browser.download.DownloadLocationCustomView>
...@@ -7,15 +7,15 @@ package org.chromium.chrome.browser.download; ...@@ -7,15 +7,15 @@ package org.chromium.chrome.browser.download;
import static org.chromium.chrome.browser.preferences.download.DownloadDirectoryAdapter.NO_SELECTED_ITEM_ID; import static org.chromium.chrome.browser.preferences.download.DownloadDirectoryAdapter.NO_SELECTED_ITEM_ID;
import android.content.Context; import android.content.Context;
import android.view.LayoutInflater; import android.util.AttributeSet;
import android.view.View; import android.view.View;
import android.widget.CheckBox; import android.widget.CheckBox;
import android.widget.CompoundButton; import android.widget.CompoundButton;
import android.widget.CompoundButton.OnCheckedChangeListener; import android.widget.CompoundButton.OnCheckedChangeListener;
import android.widget.ScrollView;
import android.widget.Spinner; import android.widget.Spinner;
import android.widget.TextView; import android.widget.TextView;
import org.chromium.chrome.browser.modaldialog.ModalDialogView;
import org.chromium.chrome.browser.preferences.PrefServiceBridge; import org.chromium.chrome.browser.preferences.PrefServiceBridge;
import org.chromium.chrome.browser.preferences.download.DownloadDirectoryAdapter; import org.chromium.chrome.browser.preferences.download.DownloadDirectoryAdapter;
import org.chromium.chrome.browser.widget.AlertDialogEditText; import org.chromium.chrome.browser.widget.AlertDialogEditText;
...@@ -28,91 +28,61 @@ import javax.annotation.Nullable; ...@@ -28,91 +28,61 @@ import javax.annotation.Nullable;
/** /**
* Dialog that is displayed to ask user where they want to download the file. * Dialog that is displayed to ask user where they want to download the file.
*/ */
public class DownloadLocationDialog extends ModalDialogView public class DownloadLocationCustomView
implements OnCheckedChangeListener, DownloadDirectoryAdapter.Delegate { extends ScrollView implements OnCheckedChangeListener, DownloadDirectoryAdapter.Delegate {
private DownloadDirectoryAdapter mDirectoryAdapter; private DownloadDirectoryAdapter mDirectoryAdapter;
private TextView mSubtitleView;
private AlertDialogEditText mFileName; private AlertDialogEditText mFileName;
private Spinner mFileLocation; private Spinner mFileLocation;
private CheckBox mDontShowAgain; private CheckBox mDontShowAgain;
private @DownloadLocationDialogType int mDialogType; private @DownloadLocationDialogType int mDialogType;
/** public DownloadLocationCustomView(Context context, AttributeSet attrs) {
* Create a {@link DownloadLocationDialog} with the given properties. super(context, attrs);
* mDirectoryAdapter = new DownloadDirectoryAdapter(context, this);
* @param controller Controller that listens to the events from the dialog. }
* @param context Context from which the dialog emerged.
* @param totalBytes The total bytes of the file. Can be 0 if size is unknown. @Override
* @param dialogType Type of dialog that should be displayed, dictates title/subtitle. protected void onFinishInflate() {
* @param suggestedPath The path that was automatically generated, used as a starting point. super.onFinishInflate();
* @return A {@link DownloadLocationDialog} with the given properties.
*/ mSubtitleView = findViewById(R.id.subtitle);
public static DownloadLocationDialog create(Controller controller, Context context, mFileName = findViewById(R.id.file_name);
long totalBytes, @DownloadLocationDialogType int dialogType, File suggestedPath) { mFileLocation = findViewById(R.id.file_location);
Params params = new Params(); mDontShowAgain = findViewById(R.id.show_again_checkbox);
params.positiveButtonTextId = R.string.duplicate_download_infobar_download_button; }
params.negativeButtonTextId = R.string.cancel;
params.customView =
LayoutInflater.from(context).inflate(R.layout.download_location_dialog, null);
params.title = context.getString(R.string.download_location_dialog_title);
TextView subtitleText = params.customView.findViewById(R.id.subtitle);
subtitleText.setVisibility(
dialogType == DownloadLocationDialogType.DEFAULT ? View.GONE : View.VISIBLE);
void initialize(@DownloadLocationDialogType int dialogType, File suggestedPath) {
mDialogType = dialogType;
// Automatically check "don't show again" the first time the user is seeing the dialog.
boolean isInitial = PrefServiceBridge.getInstance().getPromptForDownloadAndroid()
== DownloadPromptStatus.SHOW_INITIAL;
mDontShowAgain.setChecked(isInitial);
mDontShowAgain.setOnCheckedChangeListener(this);
mFileName.setText(suggestedPath.getName());
mSubtitleView.setVisibility(
dialogType == DownloadLocationDialogType.DEFAULT ? View.GONE : View.VISIBLE);
switch (dialogType) { switch (dialogType) {
case DownloadLocationDialogType.LOCATION_FULL: case DownloadLocationDialogType.LOCATION_FULL:
params.title = context.getString(R.string.download_location_not_enough_space); mSubtitleView.setText(R.string.download_location_download_to_default_folder);
subtitleText.setText(R.string.download_location_download_to_default_folder);
break; break;
case DownloadLocationDialogType.LOCATION_NOT_FOUND: case DownloadLocationDialogType.LOCATION_NOT_FOUND:
params.title = context.getString(R.string.download_location_no_sd_card); mSubtitleView.setText(R.string.download_location_download_to_default_folder);
subtitleText.setText(R.string.download_location_download_to_default_folder);
break; break;
case DownloadLocationDialogType.NAME_CONFLICT: case DownloadLocationDialogType.NAME_CONFLICT:
params.title = context.getString(R.string.download_location_download_again); mSubtitleView.setText(R.string.download_location_name_exists);
subtitleText.setText(R.string.download_location_name_exists);
break; break;
case DownloadLocationDialogType.NAME_TOO_LONG: case DownloadLocationDialogType.NAME_TOO_LONG:
params.title = context.getString(R.string.download_location_rename_file); mSubtitleView.setText(R.string.download_location_name_too_long);
subtitleText.setText(R.string.download_location_name_too_long);
break;
case DownloadLocationDialogType.DEFAULT:
if (totalBytes > 0) {
StringBuilder title = new StringBuilder(params.title);
title.append(" ");
title.append(DownloadUtils.getStringForBytes(context, totalBytes));
params.title = title.toString();
}
break; break;
} }
return new DownloadLocationDialog(controller, context, dialogType, suggestedPath, params);
}
private DownloadLocationDialog(Controller controller, Context context,
@DownloadLocationDialogType int dialogType, File suggestedPath, Params params) {
super(controller, params);
mFileName = (AlertDialogEditText) params.customView.findViewById(R.id.file_name);
mFileName.setText(suggestedPath.getName());
mFileLocation = (Spinner) params.customView.findViewById(R.id.file_location);
// Automatically check "don't show again" the first time the user is seeing the dialog.
mDontShowAgain = (CheckBox) params.customView.findViewById(R.id.show_again_checkbox);
boolean isInitial = PrefServiceBridge.getInstance().getPromptForDownloadAndroid()
== DownloadPromptStatus.SHOW_INITIAL;
mDontShowAgain.setChecked(isInitial);
mDontShowAgain.setOnCheckedChangeListener(this);
mDialogType = dialogType;
mDirectoryAdapter = new DownloadDirectoryAdapter(context, this);
mDirectoryAdapter.update(); mDirectoryAdapter.update();
} }
......
...@@ -5,15 +5,22 @@ ...@@ -5,15 +5,22 @@
package org.chromium.chrome.browser.download; package org.chromium.chrome.browser.download;
import android.content.Context; import android.content.Context;
import android.content.res.Resources;
import android.text.TextUtils; import android.text.TextUtils;
import android.view.LayoutInflater;
import org.chromium.base.annotations.CalledByNative; import org.chromium.base.annotations.CalledByNative;
import org.chromium.base.metrics.RecordHistogram; import org.chromium.base.metrics.RecordHistogram;
import org.chromium.chrome.browser.ChromeActivity; import org.chromium.chrome.browser.ChromeActivity;
import org.chromium.chrome.browser.modaldialog.DialogDismissalCause; import org.chromium.chrome.browser.modaldialog.DialogDismissalCause;
import org.chromium.chrome.browser.modaldialog.ModalDialogManager; import org.chromium.chrome.browser.modaldialog.ModalDialogManager;
import org.chromium.chrome.browser.modaldialog.ModalDialogProperties;
import org.chromium.chrome.browser.modaldialog.ModalDialogView; import org.chromium.chrome.browser.modaldialog.ModalDialogView;
import org.chromium.chrome.browser.modaldialog.ModalDialogViewBinder;
import org.chromium.chrome.browser.modelutil.PropertyModel;
import org.chromium.chrome.browser.modelutil.PropertyModelChangeProcessor;
import org.chromium.chrome.browser.preferences.PrefServiceBridge; import org.chromium.chrome.browser.preferences.PrefServiceBridge;
import org.chromium.chrome.download.R;
import org.chromium.ui.base.WindowAndroid; import org.chromium.ui.base.WindowAndroid;
import java.io.File; import java.io.File;
...@@ -24,7 +31,8 @@ import java.util.ArrayList; ...@@ -24,7 +31,8 @@ import java.util.ArrayList;
*/ */
public class DownloadLocationDialogBridge implements ModalDialogView.Controller { public class DownloadLocationDialogBridge implements ModalDialogView.Controller {
private long mNativeDownloadLocationDialogBridge; private long mNativeDownloadLocationDialogBridge;
private DownloadLocationDialog mLocationDialog; private ModalDialogView mLocationDialog;
private DownloadLocationCustomView mCustomView;
private ModalDialogManager mModalDialogManager; private ModalDialogManager mModalDialogManager;
private long mTotalBytes; private long mTotalBytes;
private @DownloadLocationDialogType int mDialogType; private @DownloadLocationDialogType int mDialogType;
...@@ -82,22 +90,21 @@ public class DownloadLocationDialogBridge implements ModalDialogView.Controller ...@@ -82,22 +90,21 @@ public class DownloadLocationDialogBridge implements ModalDialogView.Controller
break; break;
default: default:
} }
mLocationDialog = null;
} }
@Override @Override
public void onDismiss(@DialogDismissalCause int dismissalCause) { public void onDismiss(@DialogDismissalCause int dismissalCause) {
switch (dismissalCause) { switch (dismissalCause) {
case DialogDismissalCause.POSITIVE_BUTTON_CLICKED: case DialogDismissalCause.POSITIVE_BUTTON_CLICKED:
handleResponses(mLocationDialog.getFileName(), mLocationDialog.getDirectoryOption(), handleResponses(mCustomView.getFileName(), mCustomView.getDirectoryOption(),
mLocationDialog.getDontShowAgain()); mCustomView.getDontShowAgain());
break; break;
default: default:
cancel(); cancel();
break; break;
} }
mLocationDialog = null; mLocationDialog = null;
mCustomView = null;
} }
/** /**
...@@ -123,11 +130,56 @@ public class DownloadLocationDialogBridge implements ModalDialogView.Controller ...@@ -123,11 +130,56 @@ public class DownloadLocationDialogBridge implements ModalDialogView.Controller
if (mLocationDialog != null) return; if (mLocationDialog != null) return;
// Actually show the dialog. // Actually show the dialog.
mLocationDialog = DownloadLocationDialog.create( mCustomView = (DownloadLocationCustomView) LayoutInflater.from(mContext).inflate(
this, mContext, mTotalBytes, mDialogType, new File(mSuggestedPath)); R.layout.download_location_dialog, null);
mCustomView.initialize(mDialogType, new File(mSuggestedPath));
Resources resources = mContext.getResources();
PropertyModel model =
new PropertyModel.Builder(ModalDialogProperties.ALL_KEYS)
.with(ModalDialogProperties.CONTROLLER, this)
.with(ModalDialogProperties.TITLE, getTitle(mTotalBytes, mDialogType))
.with(ModalDialogProperties.CUSTOM_VIEW, mCustomView)
.with(ModalDialogProperties.POSITIVE_BUTTON_TEXT, resources,
R.string.duplicate_download_infobar_download_button)
.with(ModalDialogProperties.NEGATIVE_BUTTON_TEXT, resources,
R.string.cancel)
.build();
mLocationDialog = new ModalDialogView(mContext);
PropertyModelChangeProcessor.create(model, mLocationDialog, new ModalDialogViewBinder());
mModalDialogManager.showDialog(mLocationDialog, ModalDialogManager.ModalDialogType.APP); mModalDialogManager.showDialog(mLocationDialog, ModalDialogManager.ModalDialogType.APP);
} }
private String getTitle(long totalBytes, @DownloadLocationDialogType int dialogType) {
switch (dialogType) {
case DownloadLocationDialogType.LOCATION_FULL:
return mContext.getString(R.string.download_location_not_enough_space);
case DownloadLocationDialogType.LOCATION_NOT_FOUND:
return mContext.getString(R.string.download_location_no_sd_card);
case DownloadLocationDialogType.NAME_CONFLICT:
return mContext.getString(R.string.download_location_download_again);
case DownloadLocationDialogType.NAME_TOO_LONG:
return mContext.getString(R.string.download_location_rename_file);
case DownloadLocationDialogType.DEFAULT:
String title = mContext.getString(R.string.download_location_dialog_title);
if (totalBytes > 0) {
StringBuilder stringBuilder = new StringBuilder(title);
stringBuilder.append(" ");
stringBuilder.append(DownloadUtils.getStringForBytes(mContext, totalBytes));
title = stringBuilder.toString();
}
return title;
}
assert false;
return null;
}
/** /**
* Pass along information from location dialog to native. * Pass along information from location dialog to native.
* *
......
// Copyright 2018 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.modaldialog;
import android.view.View;
import org.chromium.chrome.browser.modelutil.PropertyKey;
import org.chromium.chrome.browser.modelutil.PropertyModel.ReadableObjectPropertyKey;
import org.chromium.chrome.browser.modelutil.PropertyModel.WritableBooleanPropertyKey;
import org.chromium.chrome.browser.modelutil.PropertyModel.WritableObjectPropertyKey;
/**
* The model properties for a modal dialog.
*/
public class ModalDialogProperties {
/** The {@link ModalDialogView.Controller} that handles events on user actions. */
public static final ReadableObjectPropertyKey<ModalDialogView.Controller> CONTROLLER =
new ReadableObjectPropertyKey<>();
/** The title of the dialog. */
public static final WritableObjectPropertyKey<String> TITLE = new WritableObjectPropertyKey<>();
/** The message of the dialog. */
public static final WritableObjectPropertyKey<String> MESSAGE =
new WritableObjectPropertyKey<>();
/** The customized content view of the dialog. */
public static final WritableObjectPropertyKey<View> CUSTOM_VIEW =
new WritableObjectPropertyKey<>();
/** The text on the positive button. */
public static final WritableObjectPropertyKey<String> POSITIVE_BUTTON_TEXT =
new WritableObjectPropertyKey<>();
/** The enabled state on the positive button. */
public static final WritableBooleanPropertyKey POSITIVE_BUTTON_DISABLED =
new WritableBooleanPropertyKey();
/** The text on the negative button. */
public static final WritableObjectPropertyKey<String> NEGATIVE_BUTTON_TEXT =
new WritableObjectPropertyKey<>();
/** The enabled state on the negative button. */
public static final WritableBooleanPropertyKey NEGATIVE_BUTTON_DISABLED =
new WritableBooleanPropertyKey();
/** Whether the dialog should be dismissed on user tapping the scrim. */
public static final WritableBooleanPropertyKey CANCEL_ON_TOUCH_OUTSIDE =
new WritableBooleanPropertyKey();
/** Whether the title is scrollable with the message. */
public static final WritableBooleanPropertyKey TITLE_SCROLLABLE =
new WritableBooleanPropertyKey();
public static final PropertyKey[] ALL_KEYS = new PropertyKey[] {CONTROLLER, TITLE, MESSAGE,
CUSTOM_VIEW, POSITIVE_BUTTON_TEXT, POSITIVE_BUTTON_DISABLED, NEGATIVE_BUTTON_TEXT,
NEGATIVE_BUTTON_DISABLED, CANCEL_ON_TOUCH_OUTSIDE, TITLE_SCROLLABLE};
}
...@@ -5,8 +5,10 @@ ...@@ -5,8 +5,10 @@
package org.chromium.chrome.browser.modaldialog; package org.chromium.chrome.browser.modaldialog;
import android.content.Context; import android.content.Context;
import android.content.res.Resources;
import android.support.annotation.IntDef; import android.support.annotation.IntDef;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.annotation.StringRes; import android.support.annotation.StringRes;
import android.text.TextUtils; import android.text.TextUtils;
import android.view.ContextThemeWrapper; import android.view.ContextThemeWrapper;
...@@ -52,7 +54,10 @@ public class ModalDialogView implements View.OnClickListener { ...@@ -52,7 +54,10 @@ public class ModalDialogView implements View.OnClickListener {
void onDismiss(@DialogDismissalCause int dismissalCause); void onDismiss(@DialogDismissalCause int dismissalCause);
} }
/** Parameters that can be used to create a new ModalDialogView. */ /**
* Parameters that can be used to create a new ModalDialogView.
* Deprecated. Use {@link ModalDialogProperties} instead.
*/
public static class Params { public static class Params {
/** Optional: The String to show as the dialog title. */ /** Optional: The String to show as the dialog title. */
public String title; public String title;
...@@ -103,39 +108,67 @@ public class ModalDialogView implements View.OnClickListener { ...@@ -103,39 +108,67 @@ public class ModalDialogView implements View.OnClickListener {
int NEGATIVE = 1; int NEGATIVE = 1;
} }
private final Controller mController; private Controller mController;
private final Params mParams; private @Nullable Params mParams;
private final View mDialogView; private final View mDialogView;
private final TextView mTitleView;
private final TextView mMessageView; private FadingEdgeScrollView mScrollView;
private final ViewGroup mCustomView; private TextView mTitleView;
private final Button mPositiveButton; private TextView mMessageView;
private final Button mNegativeButton; private ViewGroup mCustomViewContainer;
private View mButtonBar;
private Button mPositiveButton;
private Button mNegativeButton;
private boolean mTitleScrollable;
// TODO(huayinz): Remove this temporary variable once ModalDialogManager takes a model.
private boolean mCancelOnTouchOutside;
/** /**
* @return The {@link Context} with the modal dialog theme set. * @return The {@link Context} with the modal dialog theme set.
* Deprecated.
*/ */
public static Context getContext() { public static Context getContext() {
return new ContextThemeWrapper( return new ContextThemeWrapper(
ContextUtils.getApplicationContext(), R.style.ModalDialogTheme); ContextUtils.getApplicationContext(), R.style.ModalDialogTheme);
} }
/**
* Temporary constructor before ModalDialogManager takes a model.
* @param context The {@link Context} from which the dialog view should be inflated.
* TODO(huayinz): Change this once ModalDialogManager takes a model.
*/
public ModalDialogView(Context context) {
mDialogView =
LayoutInflater.from(new ContextThemeWrapper(context, R.style.ModalDialogTheme))
.inflate(R.layout.modal_dialog_view, null);
initialize();
}
/** /**
* Constructor for initializing controller and views. * Constructor for initializing controller and views.
* Deprecated. Use {@link ModalDialogView(Context)} instead.
* @param controller The controller for this dialog. * @param controller The controller for this dialog.
*/ */
public ModalDialogView(@NonNull Controller controller, @NonNull Params params) { public ModalDialogView(@NonNull Controller controller, @NonNull Params params) {
mController = controller; mController = controller;
mParams = params; mParams = params;
mDialogView = LayoutInflater.from(getContext()).inflate(R.layout.modal_dialog_view, null); mDialogView = LayoutInflater.from(getContext()).inflate(R.layout.modal_dialog_view, null);
mTitleView = mDialogView.findViewById( initialize();
mParams.titleScrollable ? R.id.scrollable_title : R.id.title); }
private void initialize() {
mScrollView = mDialogView.findViewById(R.id.modal_dialog_scroll_view);
mTitleView = mDialogView.findViewById(R.id.title);
mMessageView = mDialogView.findViewById(R.id.message); mMessageView = mDialogView.findViewById(R.id.message);
mCustomView = mDialogView.findViewById(R.id.custom); mCustomViewContainer = mDialogView.findViewById(R.id.custom);
mButtonBar = mDialogView.findViewById(R.id.button_bar);
mPositiveButton = mDialogView.findViewById(R.id.positive_button); mPositiveButton = mDialogView.findViewById(R.id.positive_button);
mNegativeButton = mDialogView.findViewById(R.id.negative_button); mNegativeButton = mDialogView.findViewById(R.id.negative_button);
mPositiveButton.setOnClickListener(this);
mNegativeButton.setOnClickListener(this);
} }
@Override @Override
...@@ -149,63 +182,30 @@ public class ModalDialogView implements View.OnClickListener { ...@@ -149,63 +182,30 @@ public class ModalDialogView implements View.OnClickListener {
/** /**
* Prepare the contents before showing the dialog. * Prepare the contents before showing the dialog.
* Deprecated.
*/ */
protected void prepareBeforeShow() { protected void prepareBeforeShow() {
FadingEdgeScrollView scrollView = mDialogView.findViewById(R.id.modal_dialog_scroll_view); if (mParams == null) return;
if (!TextUtils.isEmpty(mParams.title)) { setTitle(mParams.title);
mTitleView.setText(mParams.title); setTitleScrollable(mParams.titleScrollable);
mTitleView.setVisibility(View.VISIBLE); setMessage(mParams.message);
} setCustomView(mParams.customView);
if (TextUtils.isEmpty(mParams.message)) {
if (mParams.titleScrollable && mTitleView.getVisibility() != View.GONE) {
mMessageView.setVisibility(View.GONE);
} else {
scrollView.setVisibility(View.GONE);
}
} else {
assert mParams.titleScrollable || mParams.customView == null;
mMessageView.setText(mParams.message);
}
if (mParams.customView != null) {
UiUtils.removeViewFromParent(mParams.customView);
mCustomView.addView(mParams.customView);
} else {
mCustomView.setVisibility(View.GONE);
}
Resources resources = mDialogView.getResources();
assert(mParams.positiveButtonTextId == 0 || mParams.positiveButtonText == null); assert(mParams.positiveButtonTextId == 0 || mParams.positiveButtonText == null);
if (mParams.positiveButtonTextId != 0) { if (mParams.positiveButtonTextId != 0) {
mPositiveButton.setText(mParams.positiveButtonTextId); setButtonText(ButtonType.POSITIVE, resources.getString(mParams.positiveButtonTextId));
mPositiveButton.setOnClickListener(this);
} else if (mParams.positiveButtonText != null) { } else if (mParams.positiveButtonText != null) {
mPositiveButton.setText(mParams.positiveButtonText); setButtonText(ButtonType.POSITIVE, mParams.positiveButtonText);
mPositiveButton.setOnClickListener(this);
} else {
mPositiveButton.setVisibility(View.GONE);
} }
assert(mParams.negativeButtonTextId == 0 || mParams.negativeButtonText == null); assert(mParams.negativeButtonTextId == 0 || mParams.negativeButtonText == null);
if (mParams.negativeButtonTextId != 0) { if (mParams.negativeButtonTextId != 0) {
mNegativeButton.setText(mParams.negativeButtonTextId); setButtonText(ButtonType.NEGATIVE, resources.getString(mParams.negativeButtonTextId));
mNegativeButton.setOnClickListener(this); mNegativeButton.setOnClickListener(this);
} else if (mParams.negativeButtonText != null) { } else if (mParams.negativeButtonText != null) {
mNegativeButton.setText(mParams.negativeButtonText); setButtonText(ButtonType.POSITIVE, mParams.negativeButtonText);
mNegativeButton.setOnClickListener(this);
} else {
mNegativeButton.setVisibility(View.GONE);
}
if (mParams.titleScrollable) {
LayoutParams layoutParams = (LayoutParams) mCustomView.getLayoutParams();
layoutParams.height = LayoutParams.WRAP_CONTENT;
layoutParams.weight = 0;
mCustomView.setLayoutParams(layoutParams);
} else {
scrollView.setEdgeVisibility(
FadingEdgeScrollView.EdgeType.NONE, FadingEdgeScrollView.EdgeType.NONE);
} }
} }
...@@ -238,26 +238,124 @@ public class ModalDialogView implements View.OnClickListener { ...@@ -238,26 +238,124 @@ public class ModalDialogView implements View.OnClickListener {
} }
/** /**
* @return The content description of the dialog view. * @param controller The {@link Controller} that handles events on user actions.
*/ */
public String getContentDescription() { void setController(Controller controller) {
return mParams.title; mController = controller;
} }
/** /**
* TODO(huayinz): Should we consider adding a model change processor now that the params are * @return The content description of the dialog view.
* mutable
*
* @param title Updates the title string to the new title.
*/ */
public void setTitle(String title) { public CharSequence getContentDescription() {
return mTitleView.getText();
}
/** @param title The title of the dialog. */
public void setTitle(CharSequence title) {
mTitleView.setText(title); mTitleView.setText(title);
updateContentVisibility();
}
/** @param titleScrollable Whether the title is scrollable with the message. */
void setTitleScrollable(boolean titleScrollable) {
if (mTitleScrollable == titleScrollable) return;
mTitleScrollable = titleScrollable;
CharSequence title = mTitleView.getText();
// Hide the previous title view since the scrollable and non-scrollable title view should
// not be shown at the same time.
mTitleView.setVisibility(View.GONE);
mTitleView = mDialogView.findViewById(titleScrollable ? R.id.scrollable_title : R.id.title);
setTitle(title);
LayoutParams layoutParams = (LayoutParams) mCustomViewContainer.getLayoutParams();
if (titleScrollable) {
layoutParams.height = LayoutParams.WRAP_CONTENT;
layoutParams.weight = 0;
mScrollView.setEdgeVisibility(
FadingEdgeScrollView.EdgeType.FADING, FadingEdgeScrollView.EdgeType.FADING);
} else {
layoutParams.height = 0;
layoutParams.weight = 1;
mScrollView.setEdgeVisibility(
FadingEdgeScrollView.EdgeType.NONE, FadingEdgeScrollView.EdgeType.NONE);
}
mCustomViewContainer.setLayoutParams(layoutParams);
}
/** @param message The message in the dialog content. */
void setMessage(String message) {
mMessageView.setText(message);
updateContentVisibility();
}
/** @param view The customized view in the dialog content. */
void setCustomView(View view) {
if (mCustomViewContainer.getChildCount() > 0) mCustomViewContainer.removeAllViews();
if (view != null) {
UiUtils.removeViewFromParent(view);
mCustomViewContainer.addView(view);
mCustomViewContainer.setVisibility(View.VISIBLE);
} else {
mCustomViewContainer.setVisibility(View.GONE);
}
}
/**
* Sets button text for the specified button. If {@code buttonText} is empty or null, the
* specified button will not be visible.
* @param buttonType The {@link ButtonType} of the button.
* @param buttonText The text to be set on the specified button.
*/
void setButtonText(@ButtonType int buttonType, String buttonText) {
getButton(buttonType).setText(buttonText);
updateButtonVisibility();
}
/**
* @param buttonType The {@link ButtonType} of the button.
* @param enabled Whether the specified button should be enabled.
*/
void setButtonEnabled(@ButtonType int buttonType, boolean enabled) {
getButton(buttonType).setEnabled(enabled);
} }
/** /**
* @return Returns true if the dialog is dismissed when the user touches outside of the dialog. * @return Returns true if the dialog is dismissed when the user touches outside of the dialog.
*/ */
public boolean getCancelOnTouchOutside() { public boolean getCancelOnTouchOutside() {
return mParams.cancelOnTouchOutside; return mCancelOnTouchOutside;
}
/**
* @param cancelOnTouchOutside Whether the dialog can be cancelled on touch outside.
* TODO(huayinz): Remove this method once ModalDialogManager takes a model.
*/
void setCancelOnTouchOutside(boolean cancelOnTouchOutside) {
mCancelOnTouchOutside = cancelOnTouchOutside;
}
private void updateContentVisibility() {
boolean titleVisible = !TextUtils.isEmpty(mTitleView.getText());
boolean messageVisible = !TextUtils.isEmpty(mMessageView.getText());
boolean scrollViewVisible = (mTitleScrollable && titleVisible) || messageVisible;
mTitleView.setVisibility(titleVisible ? View.VISIBLE : View.GONE);
mMessageView.setVisibility(messageVisible ? View.VISIBLE : View.GONE);
mScrollView.setVisibility(scrollViewVisible ? View.VISIBLE : View.GONE);
}
private void updateButtonVisibility() {
boolean positiveButtonVisible = !TextUtils.isEmpty(mPositiveButton.getText());
boolean negativeButtonVisible = !TextUtils.isEmpty(mNegativeButton.getText());
boolean buttonBarVisible = positiveButtonVisible || negativeButtonVisible;
mPositiveButton.setVisibility(positiveButtonVisible ? View.VISIBLE : View.GONE);
mNegativeButton.setVisibility(negativeButtonVisible ? View.VISIBLE : View.GONE);
mButtonBar.setVisibility(buttonBarVisible ? View.VISIBLE : View.GONE);
} }
} }
// Copyright 2018 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.modaldialog;
import org.chromium.chrome.browser.modelutil.PropertyKey;
import org.chromium.chrome.browser.modelutil.PropertyModel;
import org.chromium.chrome.browser.modelutil.PropertyModelChangeProcessor;
/**
* This class is responsible for binding view properties from {@link ModalDialogProperties} to a
* {@link ModalDialogView}.
*/
public class ModalDialogViewBinder
implements PropertyModelChangeProcessor
.ViewBinder<PropertyModel, ModalDialogView, PropertyKey> {
@Override
public void bind(PropertyModel model, ModalDialogView view, PropertyKey propertyKey) {
if (ModalDialogProperties.TITLE == propertyKey) {
view.setTitle(model.get(ModalDialogProperties.TITLE));
} else if (ModalDialogProperties.MESSAGE == propertyKey) {
view.setMessage(model.get(ModalDialogProperties.MESSAGE));
} else if (ModalDialogProperties.CUSTOM_VIEW == propertyKey) {
view.setCustomView(model.get(ModalDialogProperties.CUSTOM_VIEW));
} else if (ModalDialogProperties.POSITIVE_BUTTON_TEXT == propertyKey) {
view.setButtonText(ModalDialogView.ButtonType.POSITIVE,
model.get(ModalDialogProperties.POSITIVE_BUTTON_TEXT));
} else if (ModalDialogProperties.POSITIVE_BUTTON_DISABLED == propertyKey) {
view.setButtonEnabled(ModalDialogView.ButtonType.POSITIVE,
!model.get(ModalDialogProperties.POSITIVE_BUTTON_DISABLED));
} else if (ModalDialogProperties.NEGATIVE_BUTTON_TEXT == propertyKey) {
view.setButtonText(ModalDialogView.ButtonType.NEGATIVE,
model.get(ModalDialogProperties.NEGATIVE_BUTTON_TEXT));
} else if (ModalDialogProperties.NEGATIVE_BUTTON_DISABLED == propertyKey) {
view.setButtonEnabled(ModalDialogView.ButtonType.NEGATIVE,
!model.get(ModalDialogProperties.NEGATIVE_BUTTON_DISABLED));
} else if (ModalDialogProperties.CANCEL_ON_TOUCH_OUTSIDE == propertyKey) {
view.setCancelOnTouchOutside(model.get(ModalDialogProperties.CANCEL_ON_TOUCH_OUTSIDE));
} else if (ModalDialogProperties.TITLE_SCROLLABLE == propertyKey) {
view.setTitleScrollable(model.get(ModalDialogProperties.TITLE_SCROLLABLE));
} else if (ModalDialogProperties.CONTROLLER == propertyKey) {
view.setController(model.get(ModalDialogProperties.CONTROLLER));
} else {
assert false : "Unhandled property detected in ModalDialogViewBinder!";
}
}
}
...@@ -4,6 +4,8 @@ ...@@ -4,6 +4,8 @@
package org.chromium.chrome.browser.modelutil; package org.chromium.chrome.browser.modelutil;
import android.content.res.Resources;
import android.support.annotation.StringRes;
import android.support.v4.util.ObjectsCompat; import android.support.v4.util.ObjectsCompat;
import org.chromium.base.annotations.RemovableInRelease; import org.chromium.base.annotations.RemovableInRelease;
...@@ -241,6 +243,18 @@ public class PropertyModel extends PropertyObservable<PropertyKey> { ...@@ -241,6 +243,18 @@ public class PropertyModel extends PropertyObservable<PropertyKey> {
return this; return this;
} }
/**
* @param key The key of the specified {@link ReadableObjectPropertyKey<String>}.
* @param resources The {@link Resources} for obtaining the specified string resource.
* @param resId The specified string resource id.
* @return The {@link Builder} with the specified key and string resource set.
*/
public Builder with(
ReadableObjectPropertyKey<String> key, Resources resources, @StringRes int resId) {
if (resId != 0) with(key, resources.getString(resId));
return this;
}
public PropertyModel build() { public PropertyModel build() {
return new PropertyModel(mData); return new PropertyModel(mData);
} }
......
...@@ -458,7 +458,7 @@ chrome_java_sources = [ ...@@ -458,7 +458,7 @@ chrome_java_sources = [
"java/src/org/chromium/chrome/browser/download/DownloadInfo.java", "java/src/org/chromium/chrome/browser/download/DownloadInfo.java",
"java/src/org/chromium/chrome/browser/download/DownloadInfoBarController.java", "java/src/org/chromium/chrome/browser/download/DownloadInfoBarController.java",
"java/src/org/chromium/chrome/browser/download/DownloadItem.java", "java/src/org/chromium/chrome/browser/download/DownloadItem.java",
"java/src/org/chromium/chrome/browser/download/DownloadLocationDialog.java", "java/src/org/chromium/chrome/browser/download/DownloadLocationCustomView.java",
"java/src/org/chromium/chrome/browser/download/DownloadLocationDialogBridge.java", "java/src/org/chromium/chrome/browser/download/DownloadLocationDialogBridge.java",
"java/src/org/chromium/chrome/browser/download/DownloadManagerDelegate.java", "java/src/org/chromium/chrome/browser/download/DownloadManagerDelegate.java",
"java/src/org/chromium/chrome/browser/download/DownloadManagerService.java", "java/src/org/chromium/chrome/browser/download/DownloadManagerService.java",
...@@ -899,7 +899,9 @@ chrome_java_sources = [ ...@@ -899,7 +899,9 @@ chrome_java_sources = [
"java/src/org/chromium/chrome/browser/modaldialog/AppModalPresenter.java", "java/src/org/chromium/chrome/browser/modaldialog/AppModalPresenter.java",
"java/src/org/chromium/chrome/browser/modaldialog/DialogDismissalCause.java", "java/src/org/chromium/chrome/browser/modaldialog/DialogDismissalCause.java",
"java/src/org/chromium/chrome/browser/modaldialog/ModalDialogManager.java", "java/src/org/chromium/chrome/browser/modaldialog/ModalDialogManager.java",
"java/src/org/chromium/chrome/browser/modaldialog/ModalDialogProperties.java",
"java/src/org/chromium/chrome/browser/modaldialog/ModalDialogView.java", "java/src/org/chromium/chrome/browser/modaldialog/ModalDialogView.java",
"java/src/org/chromium/chrome/browser/modaldialog/ModalDialogViewBinder.java",
"java/src/org/chromium/chrome/browser/modaldialog/TabModalLifetimeHandler.java", "java/src/org/chromium/chrome/browser/modaldialog/TabModalLifetimeHandler.java",
"java/src/org/chromium/chrome/browser/modaldialog/TabModalPresenter.java", "java/src/org/chromium/chrome/browser/modaldialog/TabModalPresenter.java",
"java/src/org/chromium/chrome/browser/modelutil/ForwardingListObservable.java", "java/src/org/chromium/chrome/browser/modelutil/ForwardingListObservable.java",
...@@ -1964,6 +1966,7 @@ chrome_test_java_sources = [ ...@@ -1964,6 +1966,7 @@ chrome_test_java_sources = [
"javatests/src/org/chromium/chrome/browser/metrics/StartupLoadingMetricsTest.java", "javatests/src/org/chromium/chrome/browser/metrics/StartupLoadingMetricsTest.java",
"javatests/src/org/chromium/chrome/browser/metrics/UkmTest.java", "javatests/src/org/chromium/chrome/browser/metrics/UkmTest.java",
"javatests/src/org/chromium/chrome/browser/modaldialog/ModalDialogManagerTest.java", "javatests/src/org/chromium/chrome/browser/modaldialog/ModalDialogManagerTest.java",
"javatests/src/org/chromium/chrome/browser/modaldialog/ModalDialogViewTest.java",
"javatests/src/org/chromium/chrome/browser/multiwindow/MultiWindowIntegrationTest.java", "javatests/src/org/chromium/chrome/browser/multiwindow/MultiWindowIntegrationTest.java",
"javatests/src/org/chromium/chrome/browser/multiwindow/MultiWindowTestHelper.java", "javatests/src/org/chromium/chrome/browser/multiwindow/MultiWindowTestHelper.java",
"javatests/src/org/chromium/chrome/browser/multiwindow/MultiWindowUtilsTest.java", "javatests/src/org/chromium/chrome/browser/multiwindow/MultiWindowUtilsTest.java",
......
// Copyright 2018 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.modaldialog;
import static android.support.test.espresso.Espresso.onView;
import static android.support.test.espresso.assertion.ViewAssertions.matches;
import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed;
import static android.support.test.espresso.matcher.ViewMatchers.isEnabled;
import static android.support.test.espresso.matcher.ViewMatchers.withChild;
import static android.support.test.espresso.matcher.ViewMatchers.withId;
import static android.support.test.espresso.matcher.ViewMatchers.withText;
import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
import static org.hamcrest.Matchers.allOf;
import static org.hamcrest.Matchers.not;
import android.content.res.Resources;
import android.support.test.filters.MediumTest;
import android.text.TextUtils;
import android.widget.FrameLayout;
import android.widget.ScrollView;
import android.widget.TextView;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.chromium.base.ThreadUtils;
import org.chromium.base.test.util.CommandLineFlags;
import org.chromium.base.test.util.Feature;
import org.chromium.chrome.R;
import org.chromium.chrome.browser.ChromeActivity;
import org.chromium.chrome.browser.ChromeSwitches;
import org.chromium.chrome.browser.modelutil.PropertyModel;
import org.chromium.chrome.browser.modelutil.PropertyModelChangeProcessor;
import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
import org.chromium.chrome.test.ChromeTabbedActivityTestRule;
import org.chromium.chrome.test.util.RenderTestRule;
import java.io.IOException;
import java.util.Collections;
/**
* Tests for {@link ModalDialogView}.
*/
@RunWith(ChromeJUnit4ClassRunner.class)
@CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE})
public class ModalDialogViewTest {
@Rule
public ChromeTabbedActivityTestRule mActivityTestRule = new ChromeTabbedActivityTestRule();
@Rule
public RenderTestRule mRenderTestRule =
new RenderTestRule("chrome/test/data/android/render_tests");
private Resources mResources;
private PropertyModel.Builder mModelBuilder;
private FrameLayout mContentView;
private ModalDialogView mModalDialogView;
private ScrollView mCustomScrollView;
private TextView mCustomTextView1;
private TextView mCustomTextView2;
@Before
public void setUp() throws Exception {
mActivityTestRule.startMainActivityOnBlankPage();
ThreadUtils.runOnUiThreadBlocking(() -> {
ChromeActivity activity = mActivityTestRule.getActivity();
mResources = activity.getResources();
mModelBuilder = new PropertyModel.Builder(ModalDialogProperties.ALL_KEYS);
mContentView = new FrameLayout(activity);
mModalDialogView = new ModalDialogView(activity);
activity.setContentView(mContentView);
mContentView.addView(mModalDialogView.getView(), MATCH_PARENT, WRAP_CONTENT);
mCustomScrollView = new ScrollView(activity);
mCustomTextView1 = new TextView(activity);
mCustomTextView1.setId(R.id.button_one);
mCustomTextView2 = new TextView(activity);
mCustomTextView2.setId(R.id.button_two);
});
}
@Test
@MediumTest
@Feature({"ModalDialog", "RenderTest"})
public void testRender_TitleAndMessage() throws IOException {
createModel(
mModelBuilder.with(ModalDialogProperties.TITLE, mResources, R.string.title)
.with(ModalDialogProperties.MESSAGE,
TextUtils.join("\n", Collections.nCopies(100, "Message")))
.with(ModalDialogProperties.POSITIVE_BUTTON_TEXT, mResources, R.string.ok)
.with(ModalDialogProperties.POSITIVE_BUTTON_DISABLED, true)
.with(ModalDialogProperties.NEGATIVE_BUTTON_TEXT, mResources,
R.string.cancel));
mRenderTestRule.render(mModalDialogView.getView(), "title_and_message");
}
@Test
@MediumTest
@Feature({"ModalDialog", "RenderTest"})
public void testRender_ScrollableTitle() throws IOException {
createModel(
mModelBuilder.with(ModalDialogProperties.TITLE, mResources, R.string.title)
.with(ModalDialogProperties.TITLE_SCROLLABLE, true)
.with(ModalDialogProperties.MESSAGE,
TextUtils.join("\n", Collections.nCopies(100, "Message")))
.with(ModalDialogProperties.POSITIVE_BUTTON_TEXT, mResources, R.string.ok));
mRenderTestRule.render(mModalDialogView.getView(), "scrollable_title");
}
@Test
@MediumTest
@Feature({"ModalDialog", "RenderTest"})
public void testRender_CustomView() throws IOException {
ThreadUtils.runOnUiThreadBlocking(() -> {
mCustomTextView1.setText(
TextUtils.join("\n", Collections.nCopies(100, "Custom Message")));
mCustomScrollView.addView(mCustomTextView1);
});
createModel(
mModelBuilder.with(ModalDialogProperties.TITLE, mResources, R.string.title)
.with(ModalDialogProperties.CUSTOM_VIEW, mCustomScrollView)
.with(ModalDialogProperties.POSITIVE_BUTTON_TEXT, mResources, R.string.ok));
mRenderTestRule.render(mModalDialogView.getView(), "custom_view");
}
@Test
@MediumTest
@Feature({"ModalDialog"})
public void testInitialStates() {
// Verify that the default states are correct when properties are not set.
createModel(mModelBuilder);
onView(withId(R.id.title)).check(matches(not(isDisplayed())));
onView(withId(R.id.modal_dialog_scroll_view)).check(matches(not(isDisplayed())));
onView(withId(R.id.scrollable_title)).check(matches(not(isDisplayed())));
onView(withId(R.id.message)).check(matches(not(isDisplayed())));
onView(withId(R.id.custom)).check(matches(not(isDisplayed())));
onView(withId(R.id.button_bar)).check(matches(not(isDisplayed())));
onView(withId(R.id.positive_button)).check(matches(allOf(not(isDisplayed()), isEnabled())));
onView(withId(R.id.negative_button)).check(matches(allOf(not(isDisplayed()), isEnabled())));
}
@Test
@MediumTest
@Feature({"ModalDialog"})
public void testTitle() {
// Verify that the title set from builder is displayed.
PropertyModel model = createModel(
mModelBuilder.with(ModalDialogProperties.TITLE, mResources, R.string.title));
onView(withId(R.id.title)).check(matches(allOf(isDisplayed(), withText(R.string.title))));
onView(withId(R.id.modal_dialog_scroll_view)).check(matches(not(isDisplayed())));
// Set an empty title and verify that title is not shown.
ThreadUtils.runOnUiThreadBlocking(() -> model.set(ModalDialogProperties.TITLE, ""));
onView(withId(R.id.title)).check(matches(not(isDisplayed())));
onView(withId(R.id.modal_dialog_scroll_view)).check(matches(not(isDisplayed())));
// Set a String title and verify that title is displayed.
ThreadUtils.runOnUiThreadBlocking(
() -> model.set(ModalDialogProperties.TITLE, "My Test Title"));
onView(withId(R.id.title)).check(matches(allOf(isDisplayed(), withText("My Test Title"))));
onView(withId(R.id.modal_dialog_scroll_view)).check(matches(not(isDisplayed())));
}
@Test
@MediumTest
@Feature({"ModalDialog"})
public void testTitle_Scrollable() {
// Verify that the title set from builder is displayed.
PropertyModel model = createModel(
mModelBuilder.with(ModalDialogProperties.TITLE, mResources, R.string.title)
.with(ModalDialogProperties.TITLE_SCROLLABLE, true));
onView(withId(R.id.title)).check(matches(not(isDisplayed())));
onView(withId(R.id.modal_dialog_scroll_view)).check(matches(isDisplayed()));
onView(withId(R.id.scrollable_title))
.check(matches(allOf(isDisplayed(), withText(R.string.title))));
onView(withId(R.id.message)).check(matches(not(isDisplayed())));
// Set title to not scrollable and verify that non-scrollable title is displayed.
ThreadUtils.runOnUiThreadBlocking(
() -> model.set(ModalDialogProperties.TITLE_SCROLLABLE, false));
onView(withId(R.id.title)).check(matches(allOf(isDisplayed(), withText(R.string.title))));
onView(withId(R.id.modal_dialog_scroll_view)).check(matches(not(isDisplayed())));
onView(withId(R.id.scrollable_title)).check(matches(not(isDisplayed())));
onView(withId(R.id.message)).check(matches(not(isDisplayed())));
}
@Test
@MediumTest
@Feature({"ModalDialog"})
public void testMessage() {
// Verify that the message set from builder is displayed.
PropertyModel model = createModel(
mModelBuilder.with(ModalDialogProperties.MESSAGE, mResources, R.string.more));
onView(withId(R.id.title)).check(matches(not(isDisplayed())));
onView(withId(R.id.modal_dialog_scroll_view)).check(matches(isDisplayed()));
onView(withId(R.id.scrollable_title)).check(matches(not(isDisplayed())));
onView(withId(R.id.message)).check(matches(allOf(isDisplayed(), withText(R.string.more))));
// Set an empty message and verify that message is not shown.
ThreadUtils.runOnUiThreadBlocking(() -> model.set(ModalDialogProperties.MESSAGE, ""));
onView(withId(R.id.title)).check(matches(not(isDisplayed())));
onView(withId(R.id.modal_dialog_scroll_view)).check(matches(not(isDisplayed())));
onView(withId(R.id.scrollable_title)).check(matches(not(isDisplayed())));
onView(withId(R.id.message)).check(matches(not(isDisplayed())));
}
@Test
@MediumTest
@Feature({"ModalDialog"})
public void testCustomView() {
// Verify custom view set from builder is displayed.
PropertyModel model = createModel(
mModelBuilder.with(ModalDialogProperties.CUSTOM_VIEW, mCustomTextView1));
onView(withId(R.id.custom))
.check(matches(allOf(isDisplayed(), withChild(withId(R.id.button_one)))));
// Change custom view.
ThreadUtils.runOnUiThreadBlocking(
() -> model.set(ModalDialogProperties.CUSTOM_VIEW, mCustomTextView2));
onView(withId(R.id.custom))
.check(matches(allOf(isDisplayed(), not(withChild(withId(R.id.button_one))),
withChild(withId(R.id.button_two)))));
// Set custom view to null.
ThreadUtils.runOnUiThreadBlocking(() -> model.set(ModalDialogProperties.CUSTOM_VIEW, null));
onView(withId(R.id.custom))
.check(matches(allOf(not(isDisplayed()), not(withChild(withId(R.id.button_one))),
not(withChild(withId(R.id.button_two))))));
}
@Test
@MediumTest
@Feature({"ModalDialog"})
public void testButtonBar() {
// Set text for both positive button and negative button.
PropertyModel model = createModel(
mModelBuilder
.with(ModalDialogProperties.POSITIVE_BUTTON_TEXT, mResources, R.string.ok)
.with(ModalDialogProperties.NEGATIVE_BUTTON_TEXT, mResources,
R.string.cancel));
onView(withId(R.id.button_bar)).check(matches(isDisplayed()));
onView(withId(R.id.positive_button))
.check(matches(allOf(isDisplayed(), isEnabled(), withText(R.string.ok))));
onView(withId(R.id.negative_button))
.check(matches(allOf(isDisplayed(), isEnabled(), withText(R.string.cancel))));
// Set positive button to be disabled state.
ThreadUtils.runOnUiThreadBlocking(
() -> model.set(ModalDialogProperties.POSITIVE_BUTTON_DISABLED, true));
onView(withId(R.id.button_bar)).check(matches(isDisplayed()));
onView(withId(R.id.positive_button))
.check(matches(allOf(isDisplayed(), not(isEnabled()), withText(R.string.ok))));
onView(withId(R.id.negative_button))
.check(matches(allOf(isDisplayed(), isEnabled(), withText(R.string.cancel))));
// Set positive button text to empty.
ThreadUtils.runOnUiThreadBlocking(
() -> model.set(ModalDialogProperties.POSITIVE_BUTTON_TEXT, ""));
onView(withId(R.id.button_bar)).check(matches(isDisplayed()));
onView(withId(R.id.positive_button)).check(matches(not(isDisplayed())));
onView(withId(R.id.negative_button))
.check(matches(allOf(isDisplayed(), isEnabled(), withText(R.string.cancel))));
// Set negative button to be disabled state.
ThreadUtils.runOnUiThreadBlocking(
() -> model.set(ModalDialogProperties.NEGATIVE_BUTTON_DISABLED, true));
onView(withId(R.id.button_bar)).check(matches(isDisplayed()));
onView(withId(R.id.positive_button)).check(matches(not(isDisplayed())));
onView(withId(R.id.negative_button))
.check(matches(allOf(isDisplayed(), not(isEnabled()), withText(R.string.cancel))));
// Set negative button text to empty.
ThreadUtils.runOnUiThreadBlocking(
() -> model.set(ModalDialogProperties.NEGATIVE_BUTTON_TEXT, ""));
onView(withId(R.id.button_bar)).check(matches(not(isDisplayed())));
onView(withId(R.id.positive_button)).check(matches(not(isDisplayed())));
onView(withId(R.id.negative_button)).check(matches(not(isDisplayed())));
}
private PropertyModel createModel(PropertyModel.Builder modelBuilder) {
return ThreadUtils.runOnUiThreadBlockingNoException(() -> {
PropertyModel model = modelBuilder.build();
PropertyModelChangeProcessor.create(
model, mModalDialogView, new ModalDialogViewBinder());
return model;
});
}
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment