Commit 18572d12 authored by Lijin Shen's avatar Lijin Shen Committed by Commit Bot

Implement basic MVC of message banner

1. A simple container on the main layout for locating a message
2. The basic message mvc components(properties and viewbinder)
3. A simple render test for the mvc

Bug: 1111911
Change-Id: Id6830571995d9c2696283a7eb4128c469a8c4462
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2331638Reviewed-by: default avatarYaron Friedman <yfriedman@chromium.org>
Reviewed-by: default avatarTheresa  <twellington@chromium.org>
Reviewed-by: default avatarMatthew Jones <mdjones@chromium.org>
Reviewed-by: default avatarPavel Yatsuk <pavely@chromium.org>
Commit-Queue: Lijin Shen <lazzzis@google.com>
Cr-Commit-Position: refs/heads/master@{#800126}
parent f0f50f4b
......@@ -207,6 +207,7 @@ android_resources("chrome_app_java_resources") {
"//components/browser_ui/widget/android:java_resources",
"//components/find_in_page/android:java_resources",
"//components/javascript_dialogs/android:java_resources",
"//components/messages/android:java_resources",
"//components/omnibox/browser:java_resources",
"//components/page_info/android:java_resources",
"//components/payments/content/android:java_resources",
......@@ -402,6 +403,7 @@ android_library("chrome_java") {
"//components/language/android:language_bridge_java",
"//components/location/android:location_java",
"//components/location/android:settings_java",
"//components/messages/android:java",
"//components/minidump_uploader:minidump_uploader_java",
"//components/module_installer/android:module_installer_java",
"//components/module_installer/android:module_interface_java",
......@@ -1069,6 +1071,8 @@ android_library("chrome_test_java") {
"//components/infobars/core:infobar_enums_java",
"//components/javascript_dialogs/android:java",
"//components/location/android:location_java",
"//components/messages/android:java",
"//components/messages/android:javatests",
"//components/metrics:metrics_java",
"//components/minidump_uploader:minidump_uploader_java",
"//components/minidump_uploader:minidump_uploader_javatests",
......
pavely@chromium.org
twellington@chromium.org
mdjones@chromium.org
lazzzis@google.com
# TEAM: chrome-android-app@chromium.org
# COMPONENT: UI>Browser>Mobile>Messages
\ No newline at end of file
# Copyright 2020 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.
import("//build/config/android/rules.gni")
android_library("java") {
sources = [
"java/src/org/chromium/components/messages/MessageBannerProperties.java",
"java/src/org/chromium/components/messages/MessageBannerView.java",
"java/src/org/chromium/components/messages/MessageBannerViewBinder.java",
]
deps = [
":java_resources",
"//base:base_java",
"//components/browser_ui/banners/android:java",
"//third_party/android_deps:androidx_annotation_annotation_java",
"//ui/android:ui_java",
]
}
android_resources("java_resources") {
custom_package = "org.chromium.components.messages"
sources = [
"java/res/drawable/message_bg_tinted.xml",
"java/res/layout/message_banner_view.xml",
"java/res/values/dimens.xml",
]
deps = [
"//components/browser_ui/strings/android:browser_ui_strings_grd",
"//components/browser_ui/styles/android:java_resources",
"//ui/android:ui_java_resources",
]
}
android_library("javatests") {
testonly = true
sources = [
"java/src/org/chromium/components/messages/MessageBannerRenderTest.java",
]
deps = [
":java",
":java_resources",
"//base:base_java",
"//base:base_java_test_support",
"//content/public/test/android:content_java_test_support",
"//third_party/android_deps:androidx_annotation_annotation_java",
"//third_party/android_deps:androidx_appcompat_appcompat_java",
"//third_party/android_deps:androidx_appcompat_appcompat_resources_java",
"//third_party/android_deps:androidx_core_core_java",
"//third_party/android_deps:androidx_test_runner_java",
"//third_party/android_support_test_runner:rules_java",
"//third_party/android_support_test_runner:runner_java",
"//third_party/junit",
"//ui/android:ui_java",
"//ui/android:ui_java_test_support",
]
}
include_rules = [
"+ui/android",
"+content/public/test/android",
"+components/browser_ui/banners/android",
]
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright 2019 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. -->
<layer-list xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
tools:ignore="UnusedResources">
<item>
<shape>
<corners android:radius="@dimen/message_banner_radius" />
<solid android:color="@color/popup_bg_color" />
</shape>
</item>
</layer-list>
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright 2020 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. -->
<!--
TODO(crbug.com/1111911): remove "UnusedResources" when this view is
used outside of tests. -->
<org.chromium.components.messages.MessageBannerView
xmlns:tools="http://schemas.android.com/tools"
xmlns:android="http://schemas.android.com/apk/res/android"
tools:ignore="UnusedResources"
android:layout_height="@dimen/message_banner_height"
android:layout_width="match_parent"
android:orientation="horizontal"
android:elevation="@dimen/message_banner_elevation"
android:background="@drawable/message_bg_tinted">
<ImageView
android:id="@+id/message_icon"
android:paddingStart="@dimen/message_icon_padding"
android:paddingEnd="@dimen/message_icon_padding"
android:tint="@color/default_icon_color_blue"
android:layout_weight="0"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:importantForAccessibility="no" />
<LinearLayout
android:layout_width="wrap_content"
android:layout_weight="1"
android:gravity="center_vertical"
android:layout_gravity="center_vertical"
android:orientation="vertical"
android:layout_height="match_parent">
<TextView
android:id="@+id/message_title"
style="@style/TextAppearance.TextLarge.Primary"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<TextView
android:id="@+id/message_description"
style="@style/TextAppearance.TextSmall.Secondary"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
<!-- Content description is set programmatically according to secondary button icon. -->
<ImageView
android:id="@+id/message_secondary_button"
android:tint="@color/default_icon_color_secondary"
android:layout_weight="0"
android:paddingStart="@dimen/message_icon_padding"
android:paddingEnd="@dimen/message_icon_padding"
android:visibility="gone"
android:contentDescription="@null"
android:minWidth="@dimen/message_banner_button_min_width"
android:layout_width="wrap_content"
android:layout_height="match_parent" />
<ImageView
android:id="@+id/message_divider"
android:layout_marginTop="@dimen/message_divider_margin"
android:layout_marginBottom="@dimen/message_divider_margin"
android:background="@color/divider_line_bg_color"
android:visibility="gone"
android:importantForAccessibility="no"
android:layout_width="1dp"
android:layout_height="match_parent" />
<TextView
style="@style/TextAppearance.Button.Text.Blue"
android:id="@+id/message_primary_button"
android:paddingStart="@dimen/message_button_padding"
android:paddingEnd="@dimen/message_button_padding"
android:layout_weight="0"
android:gravity="center_vertical"
android:minWidth="@dimen/message_banner_button_min_width"
android:layout_width="wrap_content"
android:layout_height="match_parent" />
</org.chromium.components.messages.MessageBannerView>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright 2020 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. -->
<resources xmlns:tools="http://schemas.android.com/tools">
<!-- 88dp = 64dp (required height of message banner) + 24dp (shadow padding) -->
<dimen name="message_banner_height">64dp</dimen>
<dimen name="message_banner_radius">12dp</dimen>
<dimen name="message_banner_elevation">6dp</dimen>
<!-- Min width to ensure the min touchable size. -->
<dimen name="message_banner_button_min_width">12dp</dimen>
<dimen name="message_icon_padding">12dp</dimen>
<dimen name="message_button_padding">16dp</dimen>
<dimen name="message_divider_margin">12dp</dimen>
</resources>
\ No newline at end of file
// Copyright 2020 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.components.messages;
import android.graphics.drawable.Drawable;
import android.view.View.OnClickListener;
import org.chromium.ui.modelutil.PropertyKey;
import org.chromium.ui.modelutil.PropertyModel.WritableObjectPropertyKey;
/**
* Properties of message banner.
*/
public class MessageBannerProperties {
public static final WritableObjectPropertyKey<String> PRIMARY_BUTTON_TEXT =
new WritableObjectPropertyKey<>();
public static final WritableObjectPropertyKey<OnClickListener> PRIMARY_BUTTON_CLICK_LISTENER =
new WritableObjectPropertyKey<>();
public static final WritableObjectPropertyKey<String> TITLE = new WritableObjectPropertyKey<>();
public static final WritableObjectPropertyKey<String> DESCRIPTION =
new WritableObjectPropertyKey<>();
public static final WritableObjectPropertyKey<Drawable> ICON =
new WritableObjectPropertyKey<>();
// Secondary icon is shown as a button, so content description should be always set.
public static final WritableObjectPropertyKey<Drawable> SECONDARY_ICON =
new WritableObjectPropertyKey<>();
public static final WritableObjectPropertyKey<String> SECONDARY_ICON_CONTENT_DESCRIPTION =
new WritableObjectPropertyKey<>();
public static final PropertyKey[] ALL_KEYS =
new PropertyKey[] {PRIMARY_BUTTON_TEXT, PRIMARY_BUTTON_CLICK_LISTENER, TITLE,
DESCRIPTION, ICON, SECONDARY_ICON, SECONDARY_ICON_CONTENT_DESCRIPTION};
}
// Copyright 2020 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.components.messages;
import android.app.Activity;
import android.graphics.drawable.Drawable;
import android.view.LayoutInflater;
import android.view.ViewGroup.LayoutParams;
import androidx.test.filters.SmallTest;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.chromium.base.ApiCompatibilityUtils;
import org.chromium.base.test.params.BaseJUnit4RunnerDelegate;
import org.chromium.base.test.params.ParameterAnnotations.ClassParameter;
import org.chromium.base.test.params.ParameterAnnotations.UseRunnerDelegate;
import org.chromium.base.test.params.ParameterSet;
import org.chromium.base.test.params.ParameterizedRunner;
import org.chromium.base.test.util.Feature;
import org.chromium.content_public.browser.test.util.TestThreadUtils;
import org.chromium.ui.modelutil.PropertyModel;
import org.chromium.ui.modelutil.PropertyModelChangeProcessor;
import org.chromium.ui.test.util.DummyUiActivityTestCase;
import org.chromium.ui.test.util.NightModeTestUtils;
import org.chromium.ui.test.util.RenderTestRule;
import java.util.List;
/**
* Render tests for Message Banner.
*/
@RunWith(ParameterizedRunner.class)
@UseRunnerDelegate(BaseJUnit4RunnerDelegate.class)
public class MessageBannerRenderTest extends DummyUiActivityTestCase {
@ClassParameter
private static List<ParameterSet> sClassParams =
new NightModeTestUtils.NightModeParams().getParameters();
@Rule
public RenderTestRule mRenderTestRule = RenderTestRule.Builder.withPublicCorpus().build();
public MessageBannerRenderTest(boolean nightModeEnabled) {
NightModeTestUtils.setUpNightModeForDummyUiActivity(nightModeEnabled);
mRenderTestRule.setNightModeEnabled(nightModeEnabled);
}
@Test
@SmallTest
@Feature({"RenderTest", "Messages"})
public void testBasic() throws Exception {
Activity activity = getActivity();
Drawable drawable = ApiCompatibilityUtils.getDrawable(
activity.getResources(), android.R.drawable.ic_delete);
PropertyModel model = new PropertyModel.Builder(MessageBannerProperties.ALL_KEYS)
.with(MessageBannerProperties.ICON, drawable)
.with(MessageBannerProperties.TITLE, "Primary Title")
.with(MessageBannerProperties.DESCRIPTION, "Secondary Title")
.with(MessageBannerProperties.PRIMARY_BUTTON_TEXT, "Action")
.build();
MessageBannerView view = (MessageBannerView) LayoutInflater.from(activity).inflate(
R.layout.message_banner_view, null, false);
PropertyModelChangeProcessor.create(model, view, MessageBannerViewBinder::bind);
LayoutParams params = new LayoutParams(LayoutParams.MATCH_PARENT,
activity.getResources().getDimensionPixelSize(R.dimen.message_banner_height));
TestThreadUtils.runOnUiThreadBlocking(
() -> { getActivity().setContentView(view, params); });
mRenderTestRule.render(view, "message_banner_basic");
}
@Test
@SmallTest
@Feature({"RenderTest", "Messages"})
public void testBasic_withSecondaryIcon() throws Exception {
Activity activity = getActivity();
Drawable drawable = ApiCompatibilityUtils.getDrawable(
activity.getResources(), android.R.drawable.ic_delete);
Drawable drawable2 = ApiCompatibilityUtils.getDrawable(
activity.getResources(), android.R.drawable.ic_btn_speak_now);
PropertyModel model = new PropertyModel.Builder(MessageBannerProperties.ALL_KEYS)
.with(MessageBannerProperties.ICON, drawable)
.with(MessageBannerProperties.TITLE, "Primary Title")
.with(MessageBannerProperties.DESCRIPTION, "Secondary Title")
.with(MessageBannerProperties.PRIMARY_BUTTON_TEXT, "Action")
.with(MessageBannerProperties.SECONDARY_ICON, drawable2)
.build();
MessageBannerView view = (MessageBannerView) LayoutInflater.from(activity).inflate(
R.layout.message_banner_view, null, false);
PropertyModelChangeProcessor.create(model, view, MessageBannerViewBinder::bind);
LayoutParams params = new LayoutParams(LayoutParams.MATCH_PARENT,
activity.getResources().getDimensionPixelSize(R.dimen.message_banner_height));
TestThreadUtils.runOnUiThreadBlocking(
() -> { getActivity().setContentView(view, params); });
mRenderTestRule.render(view, "message_banner_basic_with_secondary_icon");
}
}
\ No newline at end of file
// Copyright 2020 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.components.messages;
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.view.View;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import androidx.annotation.Nullable;
/**
* View representing the message banner.
*/
public class MessageBannerView extends LinearLayout {
private ImageView mIconView;
private TextView mTitle;
private TextView mDescription;
private TextView mPrimaryButton;
private ImageView mSecondaryButton;
private View mDivider;
public MessageBannerView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
@Override
protected void onFinishInflate() {
super.onFinishInflate();
mTitle = findViewById(R.id.message_title);
mDescription = findViewById(R.id.message_description);
mPrimaryButton = findViewById(R.id.message_primary_button);
mIconView = findViewById(R.id.message_icon);
mSecondaryButton = findViewById(R.id.message_secondary_button);
mDivider = findViewById(R.id.message_divider);
}
void setTitle(String title) {
mTitle.setText(title);
}
void setDescription(String description) {
mDescription.setVisibility(VISIBLE);
mDescription.setText(description);
}
void setIcon(Drawable icon) {
mIconView.setImageDrawable(icon);
}
void setPrimaryButtonText(String text) {
mPrimaryButton.setText(text);
}
void setPrimaryButtonClickListener(OnClickListener listener) {
mPrimaryButton.setOnClickListener(listener);
}
void setSecondaryIcon(Drawable icon) {
mSecondaryButton.setImageDrawable(icon);
mSecondaryButton.setVisibility(VISIBLE);
mDivider.setVisibility(VISIBLE);
}
void setSecondaryIconContentDescription(String description) {
mSecondaryButton.setContentDescription(description);
}
}
// Copyright 2020 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.components.messages;
import static org.chromium.components.messages.MessageBannerProperties.DESCRIPTION;
import static org.chromium.components.messages.MessageBannerProperties.ICON;
import static org.chromium.components.messages.MessageBannerProperties.PRIMARY_BUTTON_CLICK_LISTENER;
import static org.chromium.components.messages.MessageBannerProperties.PRIMARY_BUTTON_TEXT;
import static org.chromium.components.messages.MessageBannerProperties.SECONDARY_ICON;
import static org.chromium.components.messages.MessageBannerProperties.SECONDARY_ICON_CONTENT_DESCRIPTION;
import static org.chromium.components.messages.MessageBannerProperties.TITLE;
import org.chromium.ui.modelutil.PropertyKey;
import org.chromium.ui.modelutil.PropertyModel;
/**
* View binder of Message banner.
*/
public class MessageBannerViewBinder {
public static void bind(PropertyModel model, MessageBannerView view, PropertyKey propertyKey) {
if (propertyKey == PRIMARY_BUTTON_TEXT) {
view.setPrimaryButtonText(model.get(PRIMARY_BUTTON_TEXT));
} else if (propertyKey == PRIMARY_BUTTON_CLICK_LISTENER) {
view.setPrimaryButtonClickListener(model.get(PRIMARY_BUTTON_CLICK_LISTENER));
} else if (propertyKey == TITLE) {
view.setTitle(model.get(TITLE));
} else if (propertyKey == DESCRIPTION) {
view.setDescription(model.get(DESCRIPTION));
} else if (propertyKey == ICON) {
view.setIcon(model.get(ICON));
} else if (propertyKey == SECONDARY_ICON) {
view.setSecondaryIcon(model.get(SECONDARY_ICON));
} else if (propertyKey == SECONDARY_ICON_CONTENT_DESCRIPTION) {
view.setSecondaryIconContentDescription(model.get(SECONDARY_ICON_CONTENT_DESCRIPTION));
}
}
}
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