Commit cb333fc8 authored by Mei Liang's avatar Mei Liang Committed by Commit Bot

[MessageCardProvider] Component initial implementation

This CL implements the basic component functionality and adds some
general tests.

Change-Id: Idbacd856128e3b584a618d85c400beec36918fa4
Bug: 1004570
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1879331
Commit-Queue: Mei Liang <meiliang@chromium.org>
Reviewed-by: default avatarYusuf Ozuysal <yusufo@chromium.org>
Reviewed-by: default avatarWei-Yin Chen (陳威尹) <wychen@chromium.org>
Cr-Commit-Position: refs/heads/master@{#712802}
parent 44c89b28
......@@ -105,9 +105,12 @@ android_library("java") {
"java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridPanelProperties.java",
"java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridPanelToolbarCoordinator.java",
"java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridPanelViewBinder.java",
"java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridMessageCardViewProperties.java",
"java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridMessageCardView.java",
"java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridMessageCardViewBinder.java",
"java/src/org/chromium/chrome/browser/tasks/tab_management/MessageCardProviderCoordinator.java",
"java/src/org/chromium/chrome/browser/tasks/tab_management/MessageCardProviderMediator.java",
"java/src/org/chromium/chrome/browser/tasks/tab_management/MessageCardViewProperties.java",
"java/src/org/chromium/chrome/browser/tasks/tab_management/MessageCardView.java",
"java/src/org/chromium/chrome/browser/tasks/tab_management/MessageCardViewBinder.java",
"java/src/org/chromium/chrome/browser/tasks/tab_management/MessageService.java",
"java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridSheetCoordinator.java",
"java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridSheetMediator.java",
"java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridSheetContent.java",
......@@ -136,6 +139,7 @@ android_library("java") {
"java/src/org/chromium/chrome/browser/tasks/tab_management/TabStripToolbarCoordinator.java",
"java/src/org/chromium/chrome/browser/tasks/tab_management/TabStripToolbarViewProperties.java",
"java/src/org/chromium/chrome/browser/tasks/tab_management/TabStripViewBinder.java",
"java/src/org/chromium/chrome/browser/tasks/tab_management/TabSuggestionMessageCardViewModel.java",
"java/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherCoordinator.java",
"java/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherMediator.java",
"java/src/org/chromium/chrome/browser/tasks/tab_management/TabUiColorProvider.java",
......
......@@ -2,9 +2,9 @@
<!-- 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. -->
<org.chromium.chrome.browser.tasks.tab_management.TabGridMessageCardView
<org.chromium.chrome.browser.tasks.tab_management.MessageCardView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/tab_grid_iph_item"
android:id="@+id/tab_grid_message_item"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
......@@ -42,4 +42,4 @@
style="@style/BottomToolbarButton"
android:contentDescription="@string/close"
android:tint="@color/default_icon_color" />
</org.chromium.chrome.browser.tasks.tab_management.TabGridMessageCardView>
</org.chromium.chrome.browser.tasks.tab_management.MessageCardView>
// 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.
package org.chromium.chrome.browser.tasks.tab_management;
import java.util.ArrayList;
import java.util.List;
/**
* This is the coordinator for MessageCardProvider component. This component is used to build a
* TabGridMessageCardView for each {@link MessageService.MessageType}. This coordinator manages the
* life-cycle of all shared components and the connection between all subscribed
* {@link MessageService}.
*/
public class MessageCardProviderCoordinator {
private final MessageCardProviderMediator mMediator;
private final List<MessageService> mMessageServices = new ArrayList<>();
MessageCardProviderCoordinator(MessageCardView.DismissActionProvider uiDismissActionProvider) {
mMediator = new MessageCardProviderMediator(uiDismissActionProvider);
}
/**
* Subscribes to a {@link MessageService} to get any message changes. @see
* MessageObserver.
* @param service The {@link MessageService} to subscribe.
*/
public void subscribeMessageService(MessageService service) {
mMessageServices.add(service);
service.addObserver(mMediator);
}
/**
* Get all messages.
* @return a list of {@link
* MessageCardProviderMediator.Message}.
*/
public List<MessageCardProviderMediator.Message> getMessageItems() {
return mMediator.getMessageItems();
}
/**
* Clean up all member fields.
*/
public void destroy() {
for (int i = 0; i < mMessageServices.size(); i++) {
mMessageServices.get(i).removeObserver(mMediator);
}
}
}
// 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.
package org.chromium.chrome.browser.tasks.tab_management;
import org.chromium.base.VisibleForTesting;
import org.chromium.ui.modelutil.PropertyModel;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* This is a {@link MessageService.MessageObserver} that creates and owns different
* {@link PropertyModel} based on the message type.
*/
public class MessageCardProviderMediator implements MessageService.MessageObserver {
/**
* A class represents a Message.
*/
public class Message {
public final @MessageService.MessageType int type;
public final PropertyModel model;
Message(int type, PropertyModel model) {
this.type = type;
this.model = model;
}
}
private Map<Integer, Message> mMessageItems = new HashMap<>();
private Map<Integer, Message> mShownMessageItems = new HashMap<>();
private MessageCardView.DismissActionProvider mUiDismissActionProvider;
public MessageCardProviderMediator(
MessageCardView.DismissActionProvider uiDismissActionProvider) {
mUiDismissActionProvider = uiDismissActionProvider;
}
/**
* @return A list of {@link Message} that can be shown.
*/
public List<Message> getMessageItems() {
mShownMessageItems.putAll(mMessageItems);
mMessageItems.clear();
return new ArrayList<>(mShownMessageItems.values());
}
// MessageObserver implementations.
@Override
public void messageReady(@MessageService.MessageType int type) {
assert !mShownMessageItems.containsKey(type);
PropertyModel model = new TabSuggestionMessageCardViewModel();
model.set(MessageCardViewProperties.UI_DISMISS_ACTION_PROVIDER, mUiDismissActionProvider);
mMessageItems.put(type, new Message(type, model));
}
@Override
public void messageInvalidate(@MessageService.MessageType int type) {
if (mMessageItems.containsKey(type)) {
mMessageItems.remove(type);
} else if (mShownMessageItems.containsKey(type)) {
// run ui dismiss handler;
MessageCardView.DismissActionProvider dismissActionProvider =
mShownMessageItems.get(type).model.get(
MessageCardViewProperties.UI_DISMISS_ACTION_PROVIDER);
if (dismissActionProvider != null) {
dismissActionProvider.dismiss();
}
mShownMessageItems.remove(type);
}
}
@VisibleForTesting
Map<Integer, Message> getReadyMessageItemsForTesting() {
return mMessageItems;
}
@VisibleForTesting
Map<Integer, Message> getShownMessageItemsForTesting() {
return mShownMessageItems;
}
}
\ No newline at end of file
......@@ -22,15 +22,30 @@ import java.lang.ref.WeakReference;
* Represents a secondary card view in Grid Tab Switcher. The view contains an icon, a description,
* an action button for acceptance, and a close button for dismissal.
*/
class TabGridMessageCardView extends LinearLayout {
class MessageCardView extends LinearLayout {
private static WeakReference<Bitmap> sCloseButtonBitmapWeakRef;
/**
* An interface to get the icon to be shown inside the message card.
*/
public interface IconProvider { Drawable getIconDrawable(); }
/**
* An interface to handle the review action.
*/
public interface ReviewActionProvider { void review(); }
/**
* An interface to handle the dismiss action.
*/
public interface DismissActionProvider { void dismiss(); }
private ChromeImageView mIcon;
private TemplatePreservingTextView mDescription;
private ButtonCompat mActionButton;
private ChromeImageView mCloseButton;
public TabGridMessageCardView(Context context, AttributeSet attrs) {
public MessageCardView(Context context, AttributeSet attrs) {
super(context, attrs);
}
......@@ -72,7 +87,7 @@ class TabGridMessageCardView extends LinearLayout {
* Set action text for the action button.
* @param actionText Text to be displayed.
*/
void setAction(String actionText) {
void setActionText(String actionText) {
mActionButton.setText(actionText);
}
......@@ -83,4 +98,28 @@ class TabGridMessageCardView extends LinearLayout {
void setIcon(Drawable iconDrawable) {
mIcon.setImageDrawable(iconDrawable);
}
/**
* Set click listener for the action button.
* @param listener {@link android.view.View.OnClickListener} for the action button.
*/
void setActionButtonOnClickListener(OnClickListener listener) {
mActionButton.setOnClickListener(listener);
}
/**
* Set content description for dismiss button.
* @param description The content description.
*/
void setDismissButtonContentDescription(String description) {
mCloseButton.setContentDescription(description);
}
/**
* Set {@link android.view.View.OnClickListener} for dismiss button.
* @param listener {@link android.view.View.OnClickListener} to set.
*/
void setDismissButtonOnClickListener(OnClickListener listener) {
mCloseButton.setOnClickListener(listener);
}
}
// 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.
package org.chromium.chrome.browser.tasks.tab_management;
import android.view.ViewGroup;
import org.chromium.ui.modelutil.PropertyKey;
import org.chromium.ui.modelutil.PropertyModel;
/**
* ViewBinder for TabGridSecondaryItem.
*/
class MessageCardViewBinder {
public static void bind(PropertyModel model, ViewGroup view, PropertyKey propertyKey) {
assert view instanceof MessageCardView;
MessageCardView itemView = (MessageCardView) view;
if (MessageCardViewProperties.ACTION_TEXT == propertyKey) {
itemView.setActionText(model.get(MessageCardViewProperties.ACTION_TEXT));
itemView.setActionButtonOnClickListener(v -> {
MessageCardView.ReviewActionProvider uiProvider =
model.get(MessageCardViewProperties.UI_ACTION_PROVIDER);
if (uiProvider != null) uiProvider.review();
MessageCardView.ReviewActionProvider serviceProvider =
model.get(MessageCardViewProperties.MESSAGE_SERVICE_ACTION_PROVIDER);
if (serviceProvider != null) serviceProvider.review();
});
} else if (MessageCardViewProperties.DESCRIPTION_TEXT == propertyKey) {
itemView.setDescriptionText(model.get(MessageCardViewProperties.DESCRIPTION_TEXT));
} else if (MessageCardViewProperties.DESCRIPTION_TEXT_TEMPLATE == propertyKey) {
itemView.setDescriptionTextTemplate(
model.get(MessageCardViewProperties.DESCRIPTION_TEXT_TEMPLATE));
} else if (MessageCardViewProperties.ICON_PROVIDER == propertyKey) {
itemView.setIcon(model.get(MessageCardViewProperties.ICON_PROVIDER).getIconDrawable());
} else if (MessageCardViewProperties.DISMISS_BUTTON_CONTENT_DESCRIPTION == propertyKey) {
itemView.setDismissButtonContentDescription(
model.get(MessageCardViewProperties.DISMISS_BUTTON_CONTENT_DESCRIPTION));
itemView.setDismissButtonOnClickListener(v -> {
MessageCardView.DismissActionProvider uiProvider =
model.get(MessageCardViewProperties.UI_DISMISS_ACTION_PROVIDER);
if (uiProvider != null) uiProvider.dismiss();
MessageCardView.DismissActionProvider serviceProvider = model.get(
MessageCardViewProperties.MESSAGE_SERVICE_DISMISS_ACTION_PROVIDER);
if (serviceProvider != null) serviceProvider.dismiss();
});
}
}
}
\ No newline at end of file
......@@ -10,14 +10,34 @@ import org.chromium.ui.modelutil.PropertyModel;
/**
* List of properties used by TabGridSecondaryItem.
*/
class TabGridMessageCardViewProperties {
class MessageCardViewProperties {
public static PropertyModel.ReadableIntPropertyKey MESSAGE_TYPE =
new PropertyModel.ReadableIntPropertyKey();
public static final PropertyModel.WritableObjectPropertyKey<String> ACTION_TEXT =
new PropertyModel.WritableObjectPropertyKey<>();
public static final PropertyModel.WritableObjectPropertyKey<String> DESCRIPTION_TEXT =
new PropertyModel.WritableObjectPropertyKey<>();
public static final PropertyModel.WritableObjectPropertyKey<String> DESCRIPTION_TEXT_TEMPLATE =
new PropertyModel.WritableObjectPropertyKey<>();
public static PropertyModel
.WritableObjectPropertyKey<MessageCardView.IconProvider> ICON_PROVIDER =
new PropertyModel.WritableObjectPropertyKey<>();
public static PropertyModel
.WritableObjectPropertyKey<MessageCardView.ReviewActionProvider> UI_ACTION_PROVIDER =
new PropertyModel.WritableObjectPropertyKey<>();
public static PropertyModel.WritableObjectPropertyKey<MessageCardView.DismissActionProvider>
UI_DISMISS_ACTION_PROVIDER = new PropertyModel.WritableObjectPropertyKey<>();
public static PropertyModel.WritableObjectPropertyKey<MessageCardView.ReviewActionProvider>
MESSAGE_SERVICE_ACTION_PROVIDER = new PropertyModel.WritableObjectPropertyKey<>();
public static PropertyModel.WritableObjectPropertyKey<MessageCardView.DismissActionProvider>
MESSAGE_SERVICE_DISMISS_ACTION_PROVIDER =
new PropertyModel.WritableObjectPropertyKey<>();
public static final PropertyModel
.WritableObjectPropertyKey<String> DISMISS_BUTTON_CONTENT_DESCRIPTION =
new PropertyModel.WritableObjectPropertyKey<>();
public static final PropertyKey[] ALL_KEYS =
new PropertyKey[] {ACTION_TEXT, DESCRIPTION_TEXT, DESCRIPTION_TEXT_TEMPLATE};
public static final PropertyKey[] ALL_KEYS = new PropertyKey[] {ACTION_TEXT, DESCRIPTION_TEXT,
DESCRIPTION_TEXT_TEMPLATE, MESSAGE_TYPE, ICON_PROVIDER, UI_ACTION_PROVIDER,
UI_DISMISS_ACTION_PROVIDER, MESSAGE_SERVICE_ACTION_PROVIDER,
MESSAGE_SERVICE_DISMISS_ACTION_PROVIDER, DISMISS_BUTTON_CONTENT_DESCRIPTION};
}
\ No newline at end of file
// 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.
package org.chromium.chrome.browser.tasks.tab_management;
import androidx.annotation.IntDef;
import org.chromium.base.ObserverList;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
/**
* Ideally, for each of the {@link MessageType} requires a MessageService class. This is the
* base class. All the concrete subclass should contain logic that convert the data from the
* corresponding external service to a data structure that the TabGridMessageCardProvider
* understands.
*/
public class MessageService {
@IntDef({MessageType.TAB_SUGGESTION})
@Retention(RetentionPolicy.SOURCE)
public @interface MessageType {
int FOR_TESTING = 0;
int TAB_SUGGESTION = 1;
}
/**
* An interface to be notified about changes to a Message.
* TODO(meiliang): Need to define this interface in more detail.
*/
public interface MessageObserver {
/**
* Called when a message is available.
* TODO(meiliang): message data is needed.
* @param type The type of the message.
*/
void messageReady(@MessageType int type);
/**
* Called when a message is invalidated.
* @param type The type of the message.
*/
void messageInvalidate(@MessageType int type);
}
ObserverList<MessageObserver> mObservers = new ObserverList<>();
@MessageType
int mMessageType;
MessageService(@MessageType int mMessageType) {
this.mMessageType = mMessageType;
}
/**
* Add a {@link MessageObserver} to be notified when message from external service is changes.
* @param observer a {@link MessageObserver} to add.
*/
public void addObserver(MessageObserver observer) {
mObservers.addObserver(observer);
}
/**
* Remove a {@link MessageObserver}.
* @param observer The {@link MessageObserver} to remove.
*/
public void removeObserver(MessageObserver observer) {
mObservers.removeObserver(observer);
}
/**
* Notifies all {@link MessageObserver} that a message is available.
*/
public void sendAvailabilityNotification() {
for (MessageObserver observer : mObservers) {
observer.messageReady(mMessageType);
}
}
/**
* Notifies all {@link MessageObserver} that a message is became invalid.
*/
public void sendInvalidNotification() {
for (MessageObserver observer : mObservers) {
observer.messageInvalidate(mMessageType);
}
}
}
// 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.
package org.chromium.chrome.browser.tasks.tab_management;
import android.view.ViewGroup;
import org.chromium.ui.modelutil.PropertyKey;
import org.chromium.ui.modelutil.PropertyModel;
/**
* ViewBinder for TabGridSecondaryItem.
*/
class TabGridMessageCardViewBinder {
public static void bind(PropertyModel model, ViewGroup view, PropertyKey propertyKey) {
assert view instanceof TabGridMessageCardView;
TabGridMessageCardView itemView = (TabGridMessageCardView) view;
if (TabGridMessageCardViewProperties.ACTION_TEXT == propertyKey) {
itemView.setAction(model.get(TabGridMessageCardViewProperties.ACTION_TEXT));
} else if (TabGridMessageCardViewProperties.DESCRIPTION_TEXT == propertyKey) {
itemView.setDescriptionText(
model.get(TabGridMessageCardViewProperties.DESCRIPTION_TEXT));
} else if (TabGridMessageCardViewProperties.DESCRIPTION_TEXT_TEMPLATE == propertyKey) {
itemView.setDescriptionTextTemplate(
model.get(TabGridMessageCardViewProperties.DESCRIPTION_TEXT_TEMPLATE));
}
}
}
\ No newline at end of file
......@@ -23,13 +23,13 @@ import java.lang.annotation.RetentionPolicy;
*/
public class TabProperties {
/** IDs for possible types of UI in the tab list. */
@IntDef({UiType.SELECTABLE, UiType.CLOSABLE, UiType.STRIP, UiType.SUGGESTION})
@IntDef({UiType.SELECTABLE, UiType.CLOSABLE, UiType.STRIP, UiType.MESSAGE})
@Retention(RetentionPolicy.SOURCE)
public @interface UiType {
int SELECTABLE = 0;
int CLOSABLE = 1;
int STRIP = 2;
int SUGGESTION = 3;
int MESSAGE = 3;
}
public static final PropertyModel.WritableIntPropertyKey TAB_ID =
......
// 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.
package org.chromium.chrome.browser.tasks.tab_management;
import android.graphics.drawable.Drawable;
import org.chromium.ui.modelutil.PropertyModel;
/**
* This is a {@link PropertyModel} for the TabSuggestionMessageCard.
*/
public class TabSuggestionMessageCardViewModel
extends PropertyModel implements MessageCardView.IconProvider {
// TODO(meiliang): should take in a TabSuggestion object from the suggestion service, and set
// the property model based on the TabSuggestion object. ACTION_TEXT and DESCRIPTION_TEXT should
// set based on the TabSuggestion object.
public TabSuggestionMessageCardViewModel() {
super(MessageCardViewProperties.ALL_KEYS);
set(MessageCardViewProperties.ICON_PROVIDER, this);
}
@Override
public Drawable getIconDrawable() {
// TODO(meiliang): returns a drawable with first tab suggested tab's favicon.
return null;
}
}
......@@ -141,10 +141,10 @@ public class TabSwitcherCoordinator implements Destroyable, TabSwitcher,
if (mode == TabListCoordinator.TabListMode.GRID) {
if (ChromeFeatureList.isEnabled(ChromeFeatureList.CLOSE_TAB_SUGGESTIONS)) {
mTabListCoordinator.registerItemType(TabProperties.UiType.SUGGESTION, () -> {
mTabListCoordinator.registerItemType(TabProperties.UiType.MESSAGE, () -> {
return (ViewGroup) LayoutInflater.from(context).inflate(
R.layout.tab_suggestion_card_item, container, false);
}, TabGridMessageCardViewBinder::bind);
R.layout.tab_grid_message_card_item, container, false);
}, MessageCardViewBinder::bind);
}
assert mTabListCoordinator.getContainerView().getLayoutManager()
......@@ -159,7 +159,7 @@ public class TabSwitcherCoordinator implements Destroyable, TabSwitcher,
.getAdapter()
.getItemViewType(position);
if (itemType == TabProperties.UiType.SUGGESTION) return 2;
if (itemType == TabProperties.UiType.MESSAGE) return 2;
return 1;
}
});
......
// 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.
package org.chromium.chrome.browser.tasks.tab_management;
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.withId;
import android.support.test.filters.SmallTest;
import android.support.v7.widget.GridLayoutManager;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TestRule;
import org.chromium.chrome.browser.tab.TabFeatureUtilities;
import org.chromium.chrome.tab_ui.R;
import org.chromium.chrome.test.ui.DummyUiActivityTestCase;
import org.chromium.chrome.test.util.browser.Features;
import org.chromium.content_public.browser.test.util.CriteriaHelper;
import org.chromium.content_public.browser.test.util.TestThreadUtils;
import org.chromium.ui.modelutil.MVCListAdapter;
import org.chromium.ui.modelutil.SimpleRecyclerViewAdapter;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
/**
* Integration tests for TabGridMessageCardProvider component.
*/
public class MessageCardProviderTest extends DummyUiActivityTestCase {
@Rule
public TestRule mProcessor = new Features.JUnitProcessor();
private TabListRecyclerView mRecyclerView;
private TabListRecyclerView.VisibilityListener mRecyclerViewVisibilityListener =
new TabListRecyclerView.VisibilityListener() {
@Override
public void startedShowing(boolean isAnimating) {
List<MessageCardProviderMediator.Message> messageList =
mCoordinator.getMessageItems();
for (int i = 0; i < messageList.size(); i++) {
MessageCardProviderMediator.Message message = messageList.get(i);
mModelList.add(new MVCListAdapter.ListItem(
TabProperties.UiType.MESSAGE, message.model));
}
}
@Override
public void finishedShowing() {
mFinishedShowing.set(true);
}
@Override
public void startedHiding(boolean isAnimating) {
mFinishedShowing.set(false);
mModelList.clear();
}
@Override
public void finishedHiding() {}
};
private TabListModel mModelList = new TabListModel();
private SimpleRecyclerViewAdapter mAdapter;
private AtomicBoolean mFinishedShowing = new AtomicBoolean(false);
private MessageService mTestingService =
new MessageService(MessageService.MessageType.FOR_TESTING);
private MessageService mSuggestionService =
new MessageService(MessageService.MessageType.TAB_SUGGESTION);
private MessageCardProviderCoordinator mCoordinator;
private MessageCardView.DismissActionProvider mUiDismissActionProvider = () -> {};
@Override
public void setUpTest() throws Exception {
super.setUpTest();
TabFeatureUtilities.setIsTabToGtsAnimationEnabledForTesting(false);
// TODO(meiliang): Replace with TabSwitcher instead when ready to integrate with
// TabSwitcher.
ViewGroup view = new FrameLayout(getActivity());
mAdapter = new SimpleRecyclerViewAdapter(mModelList);
TestThreadUtils.runOnUiThreadBlocking(() -> {
getActivity().setContentView(view);
mRecyclerView = (TabListRecyclerView) getActivity().getLayoutInflater().inflate(
R.layout.tab_list_recycler_view_layout, view, false);
mRecyclerView.setVisibilityListener(mRecyclerViewVisibilityListener);
mRecyclerView.setVisibility(View.INVISIBLE);
mAdapter.registerType(TabProperties.UiType.MESSAGE, () -> {
return (ViewGroup) getActivity().getLayoutInflater().inflate(
R.layout.tab_grid_message_card_item, mRecyclerView, false);
}, MessageCardViewBinder::bind);
GridLayoutManager layoutManager = new GridLayoutManager(mRecyclerView.getContext(), 2);
layoutManager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {
@Override
public int getSpanSize(int i) {
int itemType = mAdapter.getItemViewType(i);
if (itemType == TabProperties.UiType.MESSAGE) return 2;
return 1;
}
});
mRecyclerView.setLayoutManager(layoutManager);
mRecyclerView.setAdapter(mAdapter);
view.addView(mRecyclerView);
});
mCoordinator = new MessageCardProviderCoordinator(mUiDismissActionProvider);
mCoordinator.subscribeMessageService(mTestingService);
mCoordinator.subscribeMessageService(mSuggestionService);
}
@Test
@SmallTest
public void testShowingMessage() {
mSuggestionService.sendAvailabilityNotification();
TestThreadUtils.runOnUiThreadBlocking(() -> { mRecyclerView.startShowing(false); });
CriteriaHelper.pollInstrumentationThread(
() -> mRecyclerView.getVisibility() == View.VISIBLE && mFinishedShowing.get());
onView(withId(R.id.tab_grid_message_item)).check(matches(isDisplayed()));
}
}
......@@ -5,6 +5,7 @@
package org.chromium.chrome.browser.tasks.tab_management;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import android.support.test.annotation.UiThreadTest;
import android.support.test.filters.SmallTest;
......@@ -22,17 +23,38 @@ import org.chromium.content_public.browser.test.util.TestThreadUtils;
import org.chromium.ui.modelutil.PropertyModel;
import org.chromium.ui.modelutil.PropertyModelChangeProcessor;
import java.util.concurrent.atomic.AtomicBoolean;
/**
* Tests for {@link TabGridMessageCardViewBinder}.
* Tests for {@link MessageCardViewBinder}.
*/
@RunWith(ChromeJUnit4ClassRunner.class)
public class TabGridMessageCardViewBinderTest extends DummyUiActivityTestCase {
public class MessageCardViewBinderTest extends DummyUiActivityTestCase {
private static final String ACTION_TEXT = "actionText";
private static final String DESCRIPTION_TEXT = "descriptionText";
private static final String DISMISS_BUTTON_CONTENT_DESCRIPTION = "dismiss";
private ViewGroup mItemView;
private PropertyModel mItemViewModel;
private PropertyModelChangeProcessor mItemMCP;
private AtomicBoolean mReviewButtonClicked = new AtomicBoolean();
private AtomicBoolean mDismissButtonClicked = new AtomicBoolean();
private AtomicBoolean mMessageServiceReviewCallbackRan = new AtomicBoolean();
private AtomicBoolean mMessageServiceDismissCallbackRan = new AtomicBoolean();
private MessageCardView.ReviewActionProvider mUiReviewHandler = () -> {
mReviewButtonClicked.set(true);
};
private MessageCardView.DismissActionProvider mUiDismissHandler = () -> {
mDismissButtonClicked.set(true);
};
private MessageCardView.ReviewActionProvider mMessageServiceActionHandler = () -> {
mMessageServiceReviewCallbackRan.set(true);
};
private MessageCardView.DismissActionProvider mMessageServiceDismissHandler = () -> {
mMessageServiceDismissCallbackRan.set(true);
};
@Override
public void setUpTest() throws Exception {
......@@ -44,18 +66,17 @@ public class TabGridMessageCardViewBinderTest extends DummyUiActivityTestCase {
getActivity().setContentView(view);
mItemView = (ViewGroup) getActivity().getLayoutInflater().inflate(
R.layout.tab_suggestion_card_item, null);
R.layout.tab_grid_message_card_item, null);
view.addView(mItemView);
});
mItemViewModel =
new PropertyModel.Builder(TabGridMessageCardViewProperties.ALL_KEYS)
.with(TabGridMessageCardViewProperties.ACTION_TEXT, ACTION_TEXT)
.with(TabGridMessageCardViewProperties.DESCRIPTION_TEXT, DESCRIPTION_TEXT)
mItemViewModel = new PropertyModel.Builder(MessageCardViewProperties.ALL_KEYS)
.with(MessageCardViewProperties.ACTION_TEXT, ACTION_TEXT)
.with(MessageCardViewProperties.DESCRIPTION_TEXT, DESCRIPTION_TEXT)
.build();
mItemMCP = PropertyModelChangeProcessor.create(
mItemViewModel, mItemView, TabGridMessageCardViewBinder::bind);
mItemViewModel, mItemView, MessageCardViewBinder::bind);
}
private String getDescriptionText() {
......@@ -75,7 +96,7 @@ public class TabGridMessageCardViewBinderTest extends DummyUiActivityTestCase {
@UiThreadTest
@SmallTest
public void testBindingDescription_WithoutTemplate() {
mItemViewModel.set(TabGridMessageCardViewProperties.DESCRIPTION_TEXT, "test");
mItemViewModel.set(MessageCardViewProperties.DESCRIPTION_TEXT, "test");
assertEquals("test", getDescriptionText());
}
......@@ -83,12 +104,44 @@ public class TabGridMessageCardViewBinderTest extends DummyUiActivityTestCase {
@UiThreadTest
@SmallTest
public void testBindingDescription_WithTemplate() {
mItemViewModel.set(
TabGridMessageCardViewProperties.DESCRIPTION_TEXT_TEMPLATE, "%s template");
mItemViewModel.set(TabGridMessageCardViewProperties.DESCRIPTION_TEXT, "test");
mItemViewModel.set(MessageCardViewProperties.DESCRIPTION_TEXT_TEMPLATE, "%s template");
mItemViewModel.set(MessageCardViewProperties.DESCRIPTION_TEXT, "test");
assertEquals("test template", getDescriptionText());
}
@Test
@UiThreadTest
@SmallTest
public void testBindingAndClickingReviewHandler() {
mReviewButtonClicked.set(false);
mMessageServiceReviewCallbackRan.set(false);
mItemViewModel.set(MessageCardViewProperties.UI_ACTION_PROVIDER, mUiReviewHandler);
mItemViewModel.set(MessageCardViewProperties.MESSAGE_SERVICE_ACTION_PROVIDER,
mMessageServiceActionHandler);
mItemViewModel.set(MessageCardViewProperties.ACTION_TEXT, ACTION_TEXT);
mItemView.findViewById(R.id.action_button).performClick();
assertTrue(mReviewButtonClicked.get());
assertTrue(mMessageServiceReviewCallbackRan.get());
}
@Test
@UiThreadTest
@SmallTest
public void testBindingAndClickingDismissHandler() {
mDismissButtonClicked.set(false);
mMessageServiceDismissCallbackRan.set(false);
mItemViewModel.set(MessageCardViewProperties.UI_DISMISS_ACTION_PROVIDER, mUiDismissHandler);
mItemViewModel.set(MessageCardViewProperties.MESSAGE_SERVICE_DISMISS_ACTION_PROVIDER,
mMessageServiceDismissHandler);
mItemViewModel.set(MessageCardViewProperties.DISMISS_BUTTON_CONTENT_DESCRIPTION,
DISMISS_BUTTON_CONTENT_DESCRIPTION);
mItemView.findViewById(R.id.close_button).performClick();
assertTrue(mDismissButtonClicked.get());
assertTrue(mMessageServiceDismissCallbackRan.get());
}
@Override
public void tearDownTest() throws Exception {
mItemMCP.destroy();
......
// 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.
package org.chromium.chrome.browser.tasks.tab_management;
import static org.mockito.Mockito.doNothing;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.chromium.testing.local.LocalRobolectricTestRunner;
/**
* Unit tests for {@link MessageCardProviderMediator}.
*/
@RunWith(LocalRobolectricTestRunner.class)
public class MessageCardProviderMediatorUnitTest {
MessageCardProviderMediator mMediator;
@Mock
MessageCardView.DismissActionProvider mUiDismissActionProvider;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
doNothing().when(mUiDismissActionProvider).dismiss();
mMediator = new MessageCardProviderMediator(mUiDismissActionProvider);
}
private void enqueueMessageItem(int type) {
mMediator.messageReady(type);
}
@Test
public void getMessageItemsTest() {
enqueueMessageItem(MessageService.MessageType.TAB_SUGGESTION);
Assert.assertEquals(1, mMediator.getMessageItems().size());
Assert.assertTrue(mMediator.getReadyMessageItemsForTesting().isEmpty());
Assert.assertFalse(mMediator.getShownMessageItemsForTesting().isEmpty());
}
@Test
public void getMessageItemsTest_TwoDifferentTypeMessage() {
enqueueMessageItem(MessageService.MessageType.TAB_SUGGESTION);
Assert.assertEquals(1, mMediator.getMessageItems().size());
Assert.assertTrue(mMediator.getReadyMessageItemsForTesting().isEmpty());
Assert.assertFalse(mMediator.getShownMessageItemsForTesting().isEmpty());
enqueueMessageItem(MessageService.MessageType.FOR_TESTING);
Assert.assertEquals(2, mMediator.getMessageItems().size());
Assert.assertTrue(mMediator.getReadyMessageItemsForTesting().isEmpty());
Assert.assertFalse(mMediator.getShownMessageItemsForTesting().isEmpty());
}
@Test
public void invalidate_queuedMessage() {
enqueueMessageItem(MessageService.MessageType.TAB_SUGGESTION);
mMediator.messageInvalidate(MessageService.MessageType.TAB_SUGGESTION);
Assert.assertFalse(mMediator.getReadyMessageItemsForTesting().containsKey(
MessageService.MessageType.TAB_SUGGESTION));
Assert.assertFalse(mMediator.getShownMessageItemsForTesting().containsKey(
MessageService.MessageType.TAB_SUGGESTION));
}
@Test
public void invalidate_shownMessage() {
enqueueMessageItem(MessageService.MessageType.TAB_SUGGESTION);
mMediator.getMessageItems();
mMediator.messageInvalidate(MessageService.MessageType.TAB_SUGGESTION);
Assert.assertFalse(mMediator.getShownMessageItemsForTesting().containsKey(
MessageService.MessageType.TAB_SUGGESTION));
Assert.assertFalse(mMediator.getReadyMessageItemsForTesting().containsKey(
MessageService.MessageType.TAB_SUGGESTION));
}
}
\ No newline at end of file
......@@ -25,11 +25,12 @@ public_tab_management_java_sources += start_surface_public_java_sources
tab_management_test_java_sources = [
"//chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/AssertsTest.java",
"//chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/MessageCardViewBinderTest.java",
"//chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/MessageCardProviderTest.java",
"//chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogTest.java",
"//chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogParentTest.java",
"//chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabGridIphItemTest.java",
"//chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabGridIphItemViewBinderTest.java",
"//chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabGridMessageCardViewBinderTest.java",
"//chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabGridPanelViewBinderTest.java",
"//chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabListContainerViewBinderTest.java",
"//chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabListViewHolderTest.java",
......@@ -47,6 +48,7 @@ tab_management_junit_java_sources = [
"//chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_groups/TabGroupModelFilterUnitTest.java",
"//chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_groups/TabGroupUtilsUnitTest.java",
"//chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogMediatorUnitTest.java",
"//chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/MessageCardProviderMediatorUnitTest.java",
"//chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabGridIphItemMediatorUnitTest.java",
"//chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabGridItemTouchHelperCallbackUnitTest.java",
"//chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupTitleEditorUnitTest.java",
......
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