Commit 32fd0454 authored by Becky Zhou's avatar Becky Zhou Committed by Commit Bot

[ModalDialog] Add icon support on title for modal dialog

Bug: 903858
Change-Id: Ie4e05d6d19fb03c08e9dc1f1e2b0d6108f33eaaf
Reviewed-on: https://chromium-review.googlesource.com/c/1333782
Commit-Queue: Becky Zhou <huayinz@chromium.org>
Reviewed-by: default avatarTheresa <twellington@chromium.org>
Cr-Commit-Position: refs/heads/master@{#607879}
parent 306f6525
<?xml version="1.0" encoding="utf-8"?>
<!-- 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. -->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
style="@style/AlertDialogContent">
<org.chromium.ui.widget.ChromeImageView
android:id="@+id/title_icon"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_marginEnd="8dp"
android:importantForAccessibility="no" />
<android.support.v7.widget.DialogTitle
android:id="@+id/title"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:textAppearance="@style/BlackHeadline" />
</LinearLayout>
\ No newline at end of file
......@@ -12,18 +12,14 @@
app:maxWidthLandscape="@dimen/abc_dialog_min_width_major"
app:maxWidthPortrait="@dimen/abc_dialog_min_width_minor" >
<android.support.v7.widget.DialogTitle
android:id="@+id/title"
android:textAppearance="@style/BlackHeadline"
android:visibility="gone"
style="@style/AlertDialogContent" />
<include layout="@layout/modal_dialog_title"
android:id="@+id/title_container" />
<org.chromium.chrome.browser.widget.FadingEdgeScrollView
android:id="@+id/modal_dialog_scroll_view"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:visibility="gone"
android:fadeScrollbars="false">
<LinearLayout
......@@ -31,16 +27,13 @@
android:layout_height="wrap_content"
android:orientation="vertical">
<android.support.v7.widget.DialogTitle
android:id="@+id/scrollable_title"
android:textAppearance="@style/BlackHeadline"
android:visibility="gone"
style="@style/AlertDialogContent" />
<include layout="@layout/modal_dialog_title"
android:id="@+id/scrollable_title_container"
android:visibility="gone" />
<org.chromium.ui.widget.TextViewWithLeading
android:id="@+id/message"
android:textAppearance="@style/BlackBody"
android:visibility="gone"
app:leading="20sp"
style="@style/AlertDialogContent" />
......@@ -59,21 +52,18 @@
android:id="@+id/button_bar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:visibility="gone"
style="?attr/buttonBarStyle">
<android.support.v7.widget.AppCompatButton
android:id="@+id/negative_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:visibility="gone"
style="?attr/buttonBarNegativeButtonStyle" />
<android.support.v7.widget.AppCompatButton
android:id="@+id/positive_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:visibility="gone"
style="?attr/buttonBarPositiveButtonStyle" />
</android.support.v7.widget.ButtonBarLayout>
......
......@@ -22,7 +22,11 @@ import org.chromium.chrome.R;
import org.chromium.chrome.browser.ChromeActivity;
import org.chromium.chrome.browser.modaldialog.DialogDismissalCause;
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.ModalDialogViewBinder;
import org.chromium.chrome.browser.modelutil.PropertyModel;
import org.chromium.chrome.browser.modelutil.PropertyModelChangeProcessor;
import java.util.ArrayList;
import java.util.List;
......@@ -129,9 +133,9 @@ public class AutofillNameFixFlowPrompt implements ModalDialogView.Controller {
String confirmButtonLabel, int drawableId) {
mDelegate = delegate;
LayoutInflater inflater = LayoutInflater.from(context);
View v = inflater.inflate(R.layout.autofill_name_fixflow, null);
View customView = inflater.inflate(R.layout.autofill_name_fixflow, null);
mUserNameInput = (EditText) v.findViewById(R.id.cc_name_edit);
mUserNameInput = (EditText) customView.findViewById(R.id.cc_name_edit);
mUserNameInput.setText(inferredName, BufferType.EDITABLE);
SpannableStringBuilder legalMessageText = new SpannableStringBuilder();
......@@ -147,17 +151,24 @@ public class AutofillNameFixFlowPrompt implements ModalDialogView.Controller {
}
legalMessageText.append(legalMessageText);
}
TextView legalMessageView = (TextView) v.findViewById(R.id.cc_name_legal_message);
TextView legalMessageView = (TextView) customView.findViewById(R.id.cc_name_legal_message);
legalMessageView.setText(legalMessageText);
legalMessageView.setMovementMethod(LinkMovementMethod.getInstance());
ModalDialogView.Params params = new ModalDialogView.Params();
params.title = title;
params.customView = v;
params.negativeButtonTextId = R.string.cancel;
params.positiveButtonText = confirmButtonLabel;
params.cancelOnTouchOutside = true;
mDialog = new ModalDialogView(this, params);
PropertyModel model =
new PropertyModel.Builder(ModalDialogProperties.ALL_KEYS)
.with(ModalDialogProperties.CONTROLLER, this)
.with(ModalDialogProperties.TITLE, title)
.with(ModalDialogProperties.TITLE_ICON, context, drawableId)
.with(ModalDialogProperties.CUSTOM_VIEW, customView)
.with(ModalDialogProperties.POSITIVE_BUTTON_TEXT, confirmButtonLabel)
.with(ModalDialogProperties.NEGATIVE_BUTTON_TEXT, context.getResources(),
R.string.cancel)
.with(ModalDialogProperties.CANCEL_ON_TOUCH_OUTSIDE, true)
.build();
mDialog = new ModalDialogView(context);
PropertyModelChangeProcessor.create(model, mDialog, new ModalDialogViewBinder());
// Hitting the "submit" button on the software keyboard should submit.
mUserNameInput.setOnEditorActionListener((view, actionId, event) -> {
......
......@@ -4,6 +4,7 @@
package org.chromium.chrome.browser.modaldialog;
import android.graphics.drawable.Drawable;
import android.view.View;
import org.chromium.chrome.browser.modelutil.PropertyKey;
......@@ -22,6 +23,10 @@ public class ModalDialogProperties {
/** The title of the dialog. */
public static final WritableObjectPropertyKey<String> TITLE = new WritableObjectPropertyKey<>();
/** The title icon of the dialog. */
public static final WritableObjectPropertyKey<Drawable> TITLE_ICON =
new WritableObjectPropertyKey<>();
/** The message of the dialog. */
public static final WritableObjectPropertyKey<String> MESSAGE =
new WritableObjectPropertyKey<>();
......@@ -54,7 +59,8 @@ public class ModalDialogProperties {
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,
public static final PropertyKey[] ALL_KEYS =
new PropertyKey[] {CONTROLLER, TITLE, TITLE_ICON, MESSAGE, CUSTOM_VIEW,
POSITIVE_BUTTON_TEXT, POSITIVE_BUTTON_DISABLED, NEGATIVE_BUTTON_TEXT,
NEGATIVE_BUTTON_DISABLED, CANCEL_ON_TOUCH_OUTSIDE, TITLE_SCROLLABLE};
}
......@@ -6,6 +6,7 @@ package org.chromium.chrome.browser.modaldialog;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.drawable.Drawable;
import android.support.annotation.IntDef;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
......@@ -16,6 +17,7 @@ import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.LinearLayout.LayoutParams;
import android.widget.TextView;
......@@ -114,7 +116,9 @@ public class ModalDialogView implements View.OnClickListener {
private final View mDialogView;
private FadingEdgeScrollView mScrollView;
private ViewGroup mTitleContainer;
private TextView mTitleView;
private ImageView mTitleIcon;
private TextView mMessageView;
private ViewGroup mCustomViewContainer;
private View mButtonBar;
......@@ -160,7 +164,9 @@ public class ModalDialogView implements View.OnClickListener {
private void initialize() {
mScrollView = mDialogView.findViewById(R.id.modal_dialog_scroll_view);
mTitleView = mDialogView.findViewById(R.id.title);
mTitleContainer = mDialogView.findViewById(R.id.title_container);
mTitleView = mTitleContainer.findViewById(R.id.title);
mTitleIcon = mTitleContainer.findViewById(R.id.title_icon);
mMessageView = mDialogView.findViewById(R.id.message);
mCustomViewContainer = mDialogView.findViewById(R.id.custom);
mButtonBar = mDialogView.findViewById(R.id.button_bar);
......@@ -169,6 +175,8 @@ public class ModalDialogView implements View.OnClickListener {
mPositiveButton.setOnClickListener(this);
mNegativeButton.setOnClickListener(this);
updateContentVisibility();
updateButtonVisibility();
}
@Override
......@@ -257,19 +265,32 @@ public class ModalDialogView implements View.OnClickListener {
updateContentVisibility();
}
/**
* @param drawable The icon drawable on the title.
*/
public void setTitleIcon(Drawable drawable) {
mTitleIcon.setImageDrawable(drawable);
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();
Drawable icon = mTitleIcon.getDrawable();
// 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);
// Hide the previous title container since the scrollable and non-scrollable title container
// should not be shown at the same time.
mTitleContainer.setVisibility(View.GONE);
mTitleView = mDialogView.findViewById(titleScrollable ? R.id.scrollable_title : R.id.title);
mTitleContainer = mDialogView.findViewById(
titleScrollable ? R.id.scrollable_title_container : R.id.title_container);
mTitleView = mTitleContainer.findViewById(R.id.title);
mTitleIcon = mTitleContainer.findViewById(R.id.title_icon);
setTitle(title);
setTitleIcon(icon);
LayoutParams layoutParams = (LayoutParams) mCustomViewContainer.getLayoutParams();
if (titleScrollable) {
......@@ -341,10 +362,14 @@ public class ModalDialogView implements View.OnClickListener {
private void updateContentVisibility() {
boolean titleVisible = !TextUtils.isEmpty(mTitleView.getText());
boolean titleIconVisible = mTitleIcon.getDrawable() != null;
boolean titleContainerVisible = titleVisible || titleIconVisible;
boolean messageVisible = !TextUtils.isEmpty(mMessageView.getText());
boolean scrollViewVisible = (mTitleScrollable && titleVisible) || messageVisible;
boolean scrollViewVisible = (mTitleScrollable && titleContainerVisible) || messageVisible;
mTitleView.setVisibility(titleVisible ? View.VISIBLE : View.GONE);
mTitleIcon.setVisibility(titleIconVisible ? View.VISIBLE : View.GONE);
mTitleContainer.setVisibility(titleContainerVisible ? View.VISIBLE : View.GONE);
mMessageView.setVisibility(messageVisible ? View.VISIBLE : View.GONE);
mScrollView.setVisibility(scrollViewVisible ? View.VISIBLE : View.GONE);
}
......
......@@ -19,6 +19,8 @@ public class ModalDialogViewBinder
public void bind(PropertyModel model, ModalDialogView view, PropertyKey propertyKey) {
if (ModalDialogProperties.TITLE == propertyKey) {
view.setTitle(model.get(ModalDialogProperties.TITLE));
} else if (ModalDialogProperties.TITLE_ICON == propertyKey) {
view.setTitleIcon(model.get(ModalDialogProperties.TITLE_ICON));
} else if (ModalDialogProperties.MESSAGE == propertyKey) {
view.setMessage(model.get(ModalDialogProperties.MESSAGE));
} else if (ModalDialogProperties.CUSTOM_VIEW == propertyKey) {
......
......@@ -4,9 +4,13 @@
package org.chromium.chrome.browser.modelutil;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.drawable.Drawable;
import android.support.annotation.DrawableRes;
import android.support.annotation.StringRes;
import android.support.v4.util.ObjectsCompat;
import android.support.v7.content.res.AppCompatResources;
import org.chromium.base.annotations.RemovableInRelease;
......@@ -255,6 +259,18 @@ public class PropertyModel extends PropertyObservable<PropertyKey> {
return this;
}
/**
* @param key The key of the specified {@link ReadableObjectPropertyKey<Drawable>}.
* @param context The {@link Context} for obtaining the specified drawable resource.
* @param resId The specified drawable resource id.
* @return The {@link Builder} with the specified key and drawable resource set.
*/
public Builder with(
ReadableObjectPropertyKey<Drawable> key, Context context, @DrawableRes int resId) {
if (resId != 0) with(key, AppCompatResources.getDrawable(context, resId));
return this;
}
public PropertyModel build() {
return new PropertyModel(mData);
}
......
......@@ -10,6 +10,7 @@ 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.withParent;
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;
......@@ -87,6 +88,16 @@ public class ModalDialogViewTest {
});
}
@Test
@MediumTest
@Feature({"ModalDialog", "RenderTest"})
public void testRender_TitleAndTitleIcon() throws IOException {
createModel(mModelBuilder.with(ModalDialogProperties.TITLE, mResources, R.string.title)
.with(ModalDialogProperties.TITLE_ICON, mActivityTestRule.getActivity(),
R.drawable.ic_add));
mRenderTestRule.render(mModalDialogView.getView(), "title_and_title_icon");
}
@Test
@MediumTest
@Feature({"ModalDialog", "RenderTest"})
......@@ -137,9 +148,9 @@ public class ModalDialogViewTest {
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.title_container)).check(matches(not(isDisplayed())));
onView(withId(R.id.scrollable_title_container)).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())));
......@@ -154,18 +165,24 @@ public class ModalDialogViewTest {
// 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(allOf(withId(R.id.title), withParent(withId(R.id.title_container))))
.check(matches(allOf(isDisplayed(), withText(R.string.title))));
onView(withId(R.id.title_container)).check(matches(isDisplayed()));
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(allOf(withId(R.id.title), withParent(withId(R.id.title_container))))
.check(matches(not(isDisplayed())));
onView(withId(R.id.title_container)).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(allOf(withId(R.id.title), withParent(withId(R.id.title_container))))
.check(matches(allOf(isDisplayed(), withText("My Test Title"))));
onView(withId(R.id.title_container)).check(matches(isDisplayed()));
onView(withId(R.id.modal_dialog_scroll_view)).check(matches(not(isDisplayed())));
}
......@@ -177,21 +194,48 @@ public class ModalDialogViewTest {
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))
onView(allOf(withId(R.id.title), withParent(withId(R.id.scrollable_title_container))))
.check(matches(allOf(isDisplayed(), withText(R.string.title))));
onView(withId(R.id.title_container)).check(matches(not(isDisplayed())));
onView(withId(R.id.scrollable_title_container)).check(matches(isDisplayed()));
onView(withId(R.id.modal_dialog_scroll_view)).check(matches(isDisplayed()));
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(allOf(withId(R.id.title), withParent(withId(R.id.title_container))))
.check(matches(allOf(isDisplayed(), withText(R.string.title))));
onView(withId(R.id.title_container)).check(matches(isDisplayed()));
onView(withId(R.id.scrollable_title_container)).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 testTitleIcon() {
// Verify that the icon set from builder is displayed.
PropertyModel model = createModel(mModelBuilder.with(ModalDialogProperties.TITLE_ICON,
mActivityTestRule.getActivity(), R.drawable.ic_add));
onView(allOf(withId(R.id.title), withParent(withId(R.id.title_container))))
.check(matches(not(isDisplayed())));
onView(allOf(withId(R.id.title_icon), withParent(withId(R.id.title_container))))
.check(matches(isDisplayed()));
onView(withId(R.id.title_container)).check(matches(isDisplayed()));
onView(withId(R.id.scrollable_title_container)).check(matches(not(isDisplayed())));
// Set icon to null and verify that icon is not shown.
ThreadUtils.runOnUiThreadBlocking(() -> model.set(ModalDialogProperties.TITLE_ICON, null));
onView(allOf(withId(R.id.title), withParent(withId(R.id.title_container))))
.check(matches(not(isDisplayed())));
onView(allOf(withId(R.id.title_icon), withParent(withId(R.id.title_container))))
.check(matches(not(isDisplayed())));
onView(withId(R.id.title_container)).check(matches(not(isDisplayed())));
onView(withId(R.id.scrollable_title_container)).check(matches(not(isDisplayed())));
}
@Test
@MediumTest
@Feature({"ModalDialog"})
......@@ -199,16 +243,16 @@ public class ModalDialogViewTest {
// 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.title_container)).check(matches(not(isDisplayed())));
onView(withId(R.id.scrollable_title_container)).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.title_container)).check(matches(not(isDisplayed())));
onView(withId(R.id.scrollable_title_container)).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())));
}
......
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