Commit 39131ff6 authored by Mei Liang's avatar Mei Liang Committed by Commit Bot

[CloseTabSuggestion] Integration between all components

This CL is the final CL that integrates all components in order to serve
the CloseTabSuggestion to GTS.

component that was previously landed and stripped out by Pro-guard.

Binary-Size: Enables the feature by introducing a code path which used a
Change-Id: If0fc9e2593fc2422494490b470a0c1b4464af65b
Bug: 1004570
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1959783
Commit-Queue: Mei Liang <meiliang@chromium.org>
Reviewed-by: default avatarYusuf Ozuysal <yusufo@chromium.org>
Cr-Commit-Position: refs/heads/master@{#728673}
parent b4248a17
...@@ -6,9 +6,14 @@ package org.chromium.chrome.features.start_surface; ...@@ -6,9 +6,14 @@ package org.chromium.chrome.features.start_surface;
import static android.support.test.espresso.Espresso.onView; import static android.support.test.espresso.Espresso.onView;
import static android.support.test.espresso.action.ViewActions.click; import static android.support.test.espresso.action.ViewActions.click;
import static android.support.test.espresso.assertion.ViewAssertions.doesNotExist;
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.withContentDescription; import static android.support.test.espresso.matcher.ViewMatchers.withContentDescription;
import static android.support.test.espresso.matcher.ViewMatchers.withId; import static android.support.test.espresso.matcher.ViewMatchers.withId;
import static android.support.test.espresso.matcher.ViewMatchers.withParent;
import static org.hamcrest.core.AllOf.allOf;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
...@@ -55,7 +60,10 @@ import org.chromium.chrome.browser.compositor.layouts.Layout; ...@@ -55,7 +60,10 @@ import org.chromium.chrome.browser.compositor.layouts.Layout;
import org.chromium.chrome.browser.flags.FeatureUtilities; import org.chromium.chrome.browser.flags.FeatureUtilities;
import org.chromium.chrome.browser.tab.Tab; import org.chromium.chrome.browser.tab.Tab;
import org.chromium.chrome.browser.tab.TabFeatureUtilities; import org.chromium.chrome.browser.tab.TabFeatureUtilities;
import org.chromium.chrome.browser.tasks.tab_management.TabSelectionEditorTestingRobot;
import org.chromium.chrome.browser.tasks.tab_management.TabSuggestionMessageService;
import org.chromium.chrome.browser.tasks.tab_management.TabSwitcher; import org.chromium.chrome.browser.tasks.tab_management.TabSwitcher;
import org.chromium.chrome.browser.tasks.tab_management.TabSwitcherCoordinator;
import org.chromium.chrome.browser.tasks.tab_management.TabUiTestHelper; import org.chromium.chrome.browser.tasks.tab_management.TabUiTestHelper;
import org.chromium.chrome.tab_ui.R; import org.chromium.chrome.tab_ui.R;
import org.chromium.chrome.test.ChromeJUnit4ClassRunner; import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
...@@ -696,6 +704,75 @@ public class StartSurfaceLayoutTest { ...@@ -696,6 +704,75 @@ public class StartSurfaceLayoutTest {
mUrl, PageTransition.TYPED | PageTransition.FROM_ADDRESS_BAR, tab); mUrl, PageTransition.TYPED | PageTransition.FROM_ADDRESS_BAR, tab);
} }
@Test
@MediumTest
@Feature("TabSuggestion")
// clang-format off
@Features.EnableFeatures(ChromeFeatureList.CLOSE_TAB_SUGGESTIONS + "<Study")
@Features.DisableFeatures(ChromeFeatureList.TAB_TO_GTS_ANIMATION)
@CommandLineFlags.Add({BASE_PARAMS + "/close_tab_suggestions_stale_time_ms/0"})
public void testTabSuggestionMessageCard_dismiss() throws InterruptedException {
// clang-format on
prepareTabs(3, 0, null);
// TODO(meiliang): Avoid using static variable for tracking state,
// TabSuggestionMessageService.isSuggestionAvailableForTesting(). Instead, we can add a
// dummy MessageObserver to track the availability of the suggestions.
CriteriaHelper.pollInstrumentationThread(
()
-> TabSuggestionMessageService.isSuggestionAvailableForTesting()
&& mActivityTestRule.getActivity()
.getTabModelSelector()
.getCurrentModel()
.getCount()
== 3);
enterGTSWithThumbnailChecking();
// TODO(meiliang): Avoid using static variable for tracking state,
// TabSwitcherCoordinator::hasAppendedMessagesForTesting. Instead, we can query the number
// of items that the inner model of the TabSwitcher has.
CriteriaHelper.pollInstrumentationThread(
TabSwitcherCoordinator::hasAppendedMessagesForTesting);
onView(withId(R.id.tab_grid_message_item)).check(matches(isDisplayed()));
onView(allOf(withId(R.id.close_button), withParent(withId(R.id.tab_grid_message_item))))
.perform(click());
onView(withId(R.id.tab_grid_message_item)).check(doesNotExist());
}
@Test
@MediumTest
@Feature("TabSuggestion")
// clang-format off
@Features.EnableFeatures(ChromeFeatureList.CLOSE_TAB_SUGGESTIONS + "<Study")
@Features.DisableFeatures(ChromeFeatureList.TAB_TO_GTS_ANIMATION)
@CommandLineFlags.Add({BASE_PARAMS + "/close_tab_suggestions_stale_time_ms/0"})
public void testTabSuggestionMessageCard_review() throws InterruptedException {
// clang-format on
prepareTabs(3, 0, null);
CriteriaHelper.pollInstrumentationThread(
()
-> TabSuggestionMessageService.isSuggestionAvailableForTesting()
&& mActivityTestRule.getActivity()
.getTabModelSelector()
.getCurrentModel()
.getCount()
== 3);
enterGTSWithThumbnailChecking();
CriteriaHelper.pollInstrumentationThread(
TabSwitcherCoordinator::hasAppendedMessagesForTesting);
onView(withId(R.id.tab_grid_message_item)).check(matches(isDisplayed()));
onView(allOf(withId(R.id.action_button), withParent(withId(R.id.tab_grid_message_item))))
.perform(click());
TabSelectionEditorTestingRobot tabSelectionEditorTestingRobot =
new TabSelectionEditorTestingRobot();
tabSelectionEditorTestingRobot.resultRobot.verifyTabSelectionEditorIsVisible();
}
private static class TabCountAssertion implements ViewAssertion { private static class TabCountAssertion implements ViewAssertion {
private int mExpectedCount; private int mExpectedCount;
......
...@@ -6,8 +6,6 @@ package org.chromium.chrome.browser.tasks.tab_management; ...@@ -6,8 +6,6 @@ package org.chromium.chrome.browser.tasks.tab_management;
import android.content.Context; import android.content.Context;
import org.chromium.base.LifetimeAssert;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
...@@ -18,7 +16,6 @@ import java.util.List; ...@@ -18,7 +16,6 @@ import java.util.List;
* {@link MessageService}. * {@link MessageService}.
*/ */
public class MessageCardProviderCoordinator { public class MessageCardProviderCoordinator {
private final LifetimeAssert mLifetimeAssert = LifetimeAssert.create(this);
private final MessageCardProviderMediator mMediator; private final MessageCardProviderMediator mMediator;
private final List<MessageService> mMessageServices = new ArrayList<>(); private final List<MessageService> mMessageServices = new ArrayList<>();
...@@ -53,8 +50,5 @@ public class MessageCardProviderCoordinator { ...@@ -53,8 +50,5 @@ public class MessageCardProviderCoordinator {
for (int i = 0; i < mMessageServices.size(); i++) { for (int i = 0; i < mMessageServices.size(); i++) {
mMessageServices.get(i).removeObserver(mMediator); mMessageServices.get(i).removeObserver(mMediator);
} }
// If mLifetimeAssert is GC'ed before this is called, it will throw an exception
// with a stack trace showing the stack during LifetimeAssert.create().
LifetimeAssert.setSafeToGc(mLifetimeAssert, true);
} }
} }
...@@ -56,4 +56,4 @@ class MessageCardViewBinder { ...@@ -56,4 +56,4 @@ class MessageCardViewBinder {
}); });
} }
} }
} }
\ No newline at end of file
...@@ -4,6 +4,8 @@ ...@@ -4,6 +4,8 @@
package org.chromium.chrome.browser.tasks.tab_management; package org.chromium.chrome.browser.tasks.tab_management;
import static org.chromium.chrome.browser.tasks.tab_management.TabListModel.TabListModelProperties.MODEL_TYPE;
import org.chromium.ui.modelutil.PropertyKey; import org.chromium.ui.modelutil.PropertyKey;
import org.chromium.ui.modelutil.PropertyModel; import org.chromium.ui.modelutil.PropertyModel;
...@@ -36,8 +38,9 @@ class MessageCardViewProperties { ...@@ -36,8 +38,9 @@ class MessageCardViewProperties {
.WritableObjectPropertyKey<String> DISMISS_BUTTON_CONTENT_DESCRIPTION = .WritableObjectPropertyKey<String> DISMISS_BUTTON_CONTENT_DESCRIPTION =
new PropertyModel.WritableObjectPropertyKey<>(); new PropertyModel.WritableObjectPropertyKey<>();
public static final PropertyKey[] ALL_KEYS = new PropertyKey[] {ACTION_TEXT, DESCRIPTION_TEXT, public static final PropertyKey[] ALL_KEYS =
DESCRIPTION_TEXT_TEMPLATE, MESSAGE_TYPE, ICON_PROVIDER, UI_ACTION_PROVIDER, new PropertyKey[] {ACTION_TEXT, DESCRIPTION_TEXT, DESCRIPTION_TEXT_TEMPLATE,
UI_DISMISS_ACTION_PROVIDER, MESSAGE_SERVICE_ACTION_PROVIDER, MESSAGE_TYPE, ICON_PROVIDER, UI_ACTION_PROVIDER, UI_DISMISS_ACTION_PROVIDER,
MESSAGE_SERVICE_DISMISS_ACTION_PROVIDER, DISMISS_BUTTON_CONTENT_DESCRIPTION}; MESSAGE_SERVICE_ACTION_PROVIDER, MESSAGE_SERVICE_DISMISS_ACTION_PROVIDER,
DISMISS_BUTTON_CONTENT_DESCRIPTION, MODEL_TYPE};
} }
\ No newline at end of file
...@@ -23,6 +23,7 @@ import androidx.annotation.NonNull; ...@@ -23,6 +23,7 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import org.chromium.chrome.browser.ChromeActivity; import org.chromium.chrome.browser.ChromeActivity;
import org.chromium.chrome.browser.ChromeFeatureList;
import org.chromium.chrome.browser.flags.FeatureUtilities; import org.chromium.chrome.browser.flags.FeatureUtilities;
import org.chromium.chrome.browser.lifecycle.Destroyable; import org.chromium.chrome.browser.lifecycle.Destroyable;
import org.chromium.chrome.browser.profiles.Profile; import org.chromium.chrome.browser.profiles.Profile;
...@@ -225,8 +226,11 @@ public class TabListCoordinator implements Destroyable { ...@@ -225,8 +226,11 @@ public class TabListCoordinator implements Destroyable {
false)); false));
} }
// TODO(crbug.com/1004570) : Support drag and drop, and swipe to dismiss when
// CLOSE_TAB_SUGGESTIONS is enabled.
if ((mMode == TabListMode.GRID || mMode == TabListMode.LIST) if ((mMode == TabListMode.GRID || mMode == TabListMode.LIST)
&& selectionDelegateProvider == null) { && selectionDelegateProvider == null
&& !ChromeFeatureList.isEnabled(ChromeFeatureList.CLOSE_TAB_SUGGESTIONS)) {
ItemTouchHelper touchHelper = new ItemTouchHelper(mMediator.getItemTouchHelperCallback( ItemTouchHelper touchHelper = new ItemTouchHelper(mMediator.getItemTouchHelperCallback(
context.getResources().getDimension(R.dimen.swipe_to_dismiss_threshold), context.getResources().getDimension(R.dimen.swipe_to_dismiss_threshold),
context.getResources().getDimension(R.dimen.tab_grid_merge_threshold), context.getResources().getDimension(R.dimen.tab_grid_merge_threshold),
...@@ -369,4 +373,18 @@ public class TabListCoordinator implements Destroyable { ...@@ -369,4 +373,18 @@ public class TabListCoordinator implements Destroyable {
void addSpecialListItem(int index, @UiType int uiType, PropertyModel model) { void addSpecialListItem(int index, @UiType int uiType, PropertyModel model) {
mMediator.addSpecialItemToModel(index, uiType, model); mMediator.addSpecialItemToModel(index, uiType, model);
} }
/**
* Removes a special {@link org.chromium.ui.modelutil.MVCListAdapter.ListItem} that
* has the given {@code uiType} and/or its {@link PropertyModel} has the given
* {@code itemIdentifier}.
*
* @param uiType The uiType to match.
* @param itemIdentifier The itemIdentifier to match. This can be obsoleted if the {@link
* org.chromium.ui.modelutil.MVCListAdapter.ListItem} does not need additional
* identifier.
*/
void removeSpecialListItem(@UiType int uiType, int itemIdentifier) {
mMediator.removeSpecialItemFromModel(uiType, itemIdentifier);
}
} }
...@@ -4,6 +4,10 @@ ...@@ -4,6 +4,10 @@
package org.chromium.chrome.browser.tasks.tab_management; package org.chromium.chrome.browser.tasks.tab_management;
import static org.chromium.chrome.browser.tasks.tab_management.MessageCardViewProperties.MESSAGE_TYPE;
import static org.chromium.chrome.browser.tasks.tab_management.TabListModel.TabListModelProperties.MODEL_TYPE;
import static org.chromium.chrome.browser.tasks.tab_management.TabListModel.TabListModelProperties.ModelType.TAB;
import android.app.Activity; import android.app.Activity;
import android.content.ComponentCallbacks; import android.content.ComponentCallbacks;
import android.content.Context; import android.content.Context;
...@@ -845,6 +849,8 @@ class TabListMediator { ...@@ -845,6 +849,8 @@ class TabListMediator {
assert mVisible; assert mVisible;
int count = 0; int count = 0;
for (int i = 0; i < mModel.size(); i++) { for (int i = 0; i < mModel.size(); i++) {
if (mModel.get(i).model.get(MODEL_TYPE) != TAB) continue;
if (mModel.get(i).model.get(TabProperties.IS_SELECTED)) count++; if (mModel.get(i).model.get(TabProperties.IS_SELECTED)) count++;
mModel.get(i).model.set(TabProperties.IS_SELECTED, false); mModel.get(i).model.set(TabProperties.IS_SELECTED, false);
} }
...@@ -859,7 +865,10 @@ class TabListMediator { ...@@ -859,7 +865,10 @@ class TabListMediator {
} }
if (tabs.size() != mModel.size()) return false; if (tabs.size() != mModel.size()) return false;
for (int i = 0; i < tabs.size(); i++) { for (int i = 0; i < tabs.size(); i++) {
if (tabs.get(i).getId() != mModel.get(i).model.get(TabProperties.TAB_ID)) return false; if (mModel.get(i).model.get(MODEL_TYPE) == TAB
&& mModel.get(i).model.get(TabProperties.TAB_ID) != tabs.get(i).getId()) {
return false;
}
} }
return true; return true;
} }
...@@ -923,7 +932,9 @@ class TabListMediator { ...@@ -923,7 +932,9 @@ class TabListMediator {
void softCleanup() { void softCleanup() {
assert !mVisible; assert !mVisible;
for (int i = 0; i < mModel.size(); i++) { for (int i = 0; i < mModel.size(); i++) {
mModel.get(i).model.set(TabProperties.THUMBNAIL_FETCHER, null); if (mModel.get(i).model.get(MODEL_TYPE) == TAB) {
mModel.get(i).model.set(TabProperties.THUMBNAIL_FETCHER, null);
}
} }
} }
...@@ -1130,6 +1141,7 @@ class TabListMediator { ...@@ -1130,6 +1141,7 @@ class TabListMediator {
.with(TabProperties.TABSTRIP_FAVICON_BACKGROUND_COLOR_ID, .with(TabProperties.TABSTRIP_FAVICON_BACKGROUND_COLOR_ID,
tabstripFaviconBackgroundDrawableId) tabstripFaviconBackgroundDrawableId)
.with(TabProperties.ACCESSIBILITY_DELEGATE, mAccessibilityDelegate) .with(TabProperties.ACCESSIBILITY_DELEGATE, mAccessibilityDelegate)
.with(MODEL_TYPE, TAB)
.build(); .build();
if (mUiType == UiType.SELECTABLE) { if (mUiType == UiType.SELECTABLE) {
...@@ -1284,6 +1296,37 @@ class TabListMediator { ...@@ -1284,6 +1296,37 @@ class TabListMediator {
mModel.add(index, new SimpleRecyclerViewAdapter.ListItem(uiType, model)); mModel.add(index, new SimpleRecyclerViewAdapter.ListItem(uiType, model));
} }
/**
* Removes a special {@link @link org.chromium.ui.modelutil.MVCListAdapter.ListItem} that
* has the given {@code uiType} and/or its {@link PropertyModel} has the given
* {@code itemIdentifier} from the current {@link TabListModel}.
*
* @param uiType The uiType to match.
* @param itemIdentifier The itemIdentifier to match. This can be obsoleted if the {@link @link
* org.chromium.ui.modelutil.MVCListAdapter.ListItem} does not need additional
* identifier.
*/
void removeSpecialItemFromModel(@UiType int uiType, int itemIdentifier) {
int index = TabModel.INVALID_TAB_INDEX;
if (uiType == UiType.MESSAGE) {
index = mModel.lastIndexForMessageItemFromType(itemIdentifier);
}
if (index == TabModel.INVALID_TAB_INDEX) return;
assert validateItemAt(index, uiType, itemIdentifier);
mModel.removeAt(index);
}
private boolean validateItemAt(int index, @UiType int uiType, int itemIdentifier) {
if (uiType == UiType.MESSAGE) {
return mModel.get(index).type == uiType
&& mModel.get(index).model.get(MESSAGE_TYPE) == itemIdentifier;
}
return false;
}
@VisibleForTesting @VisibleForTesting
View.AccessibilityDelegate getAccessibilityDelegateForTesting() { View.AccessibilityDelegate getAccessibilityDelegateForTesting() {
return mAccessibilityDelegate; return mAccessibilityDelegate;
......
...@@ -4,15 +4,26 @@ ...@@ -4,15 +4,26 @@
package org.chromium.chrome.browser.tasks.tab_management; package org.chromium.chrome.browser.tasks.tab_management;
import static org.chromium.chrome.browser.tasks.tab_management.MessageCardViewProperties.MESSAGE_TYPE;
import static org.chromium.chrome.browser.tasks.tab_management.TabListModel.TabListModelProperties.MODEL_TYPE;
import static org.chromium.chrome.browser.tasks.tab_management.TabListModel.TabListModelProperties.ModelType.MESSAGE;
import static org.chromium.chrome.browser.tasks.tab_management.TabListModel.TabListModelProperties.ModelType.OTHERS;
import static org.chromium.chrome.browser.tasks.tab_management.TabListModel.TabListModelProperties.ModelType.TAB;
import static org.chromium.chrome.browser.tasks.tab_management.TabProperties.TAB_ID; import static org.chromium.chrome.browser.tasks.tab_management.TabProperties.TAB_ID;
import android.util.Pair; import android.util.Pair;
import androidx.annotation.IntDef;
import org.chromium.chrome.browser.tab.Tab; import org.chromium.chrome.browser.tab.Tab;
import org.chromium.chrome.browser.tabmodel.TabModel; import org.chromium.chrome.browser.tabmodel.TabModel;
import org.chromium.ui.modelutil.MVCListAdapter;
import org.chromium.ui.modelutil.MVCListAdapter.ModelList; import org.chromium.ui.modelutil.MVCListAdapter.ModelList;
import org.chromium.ui.modelutil.PropertyListModel; import org.chromium.ui.modelutil.PropertyListModel;
import org.chromium.ui.modelutil.PropertyModel;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.List; import java.util.List;
/** /**
...@@ -20,6 +31,23 @@ import java.util.List; ...@@ -20,6 +31,23 @@ import java.util.List;
* {@link org.chromium.chrome.browser.tab.Tab}s. * {@link org.chromium.chrome.browser.tab.Tab}s.
*/ */
class TabListModel extends ModelList { class TabListModel extends ModelList {
/**
* Required properties for each {@link PropertyModel} managed by this {@link ModelList}.
*/
static class TabListModelProperties {
/** Supported Model type within this ModelList. */
@IntDef({TAB, MESSAGE, OTHERS})
@Retention(RetentionPolicy.SOURCE)
public @interface ModelType {
int TAB = 0;
int MESSAGE = 1;
int OTHERS = 2;
}
public static final PropertyModel.ReadableIntPropertyKey MODEL_TYPE =
new PropertyModel.ReadableIntPropertyKey();
}
/** /**
* Convert the given tab ID to an index to match during partial updates. * Convert the given tab ID to an index to match during partial updates.
* @param tabId The tab ID to search for. * @param tabId The tab ID to search for.
...@@ -27,11 +55,42 @@ class TabListModel extends ModelList { ...@@ -27,11 +55,42 @@ class TabListModel extends ModelList {
*/ */
public int indexFromId(int tabId) { public int indexFromId(int tabId) {
for (int i = 0; i < size(); i++) { for (int i = 0; i < size(); i++) {
if (get(i).model.get(TAB_ID) == tabId) return i; PropertyModel model = get(i).model;
if (model.get(MODEL_TYPE) == TAB && model.get(TAB_ID) == tabId) return i;
}
return TabModel.INVALID_TAB_INDEX;
}
/**
* Get the index that matches a message item that has the given message type.
* @param messageType The message type to match.
* @return The index within the model.
*/
public int lastIndexForMessageItemFromType(int messageType) {
for (int i = size() - 1; i >= 0; i--) {
PropertyModel model = get(i).model;
if (model.get(MODEL_TYPE) == MESSAGE && model.get(MESSAGE_TYPE) == messageType) {
return i;
}
} }
return TabModel.INVALID_TAB_INDEX; return TabModel.INVALID_TAB_INDEX;
} }
@Override
public void add(int position, MVCListAdapter.ListItem item) {
assert validateListItem(item);
super.add(position, item);
}
private boolean validateListItem(MVCListAdapter.ListItem item) {
try {
item.model.get(MODEL_TYPE);
} catch (IllegalArgumentException e) {
return false;
}
return true;
}
/** /**
* Sync the {@link TabListModel} with updated information. Update tab id of * Sync the {@link TabListModel} with updated information. Update tab id of
* the item in {@code index} with the current selected {@code tab} of the group. * the item in {@code index} with the current selected {@code tab} of the group.
...@@ -80,8 +139,9 @@ class TabListModel extends ModelList { ...@@ -80,8 +139,9 @@ class TabListModel extends ModelList {
int status = isSelected ? ClosableTabGridView.AnimationStatus.SELECTED_CARD_ZOOM_IN int status = isSelected ? ClosableTabGridView.AnimationStatus.SELECTED_CARD_ZOOM_IN
: ClosableTabGridView.AnimationStatus.SELECTED_CARD_ZOOM_OUT; : ClosableTabGridView.AnimationStatus.SELECTED_CARD_ZOOM_OUT;
if (index < 0 || index >= size() if (index < 0 || index >= size()
|| get(index).model.get(TabProperties.CARD_ANIMATION_STATUS) == status) || get(index).model.get(TabProperties.CARD_ANIMATION_STATUS) == status) {
return; return;
}
get(index).model.set(TabProperties.CARD_ANIMATION_STATUS, status); get(index).model.set(TabProperties.CARD_ANIMATION_STATUS, status);
get(index).model.set(TabProperties.ALPHA, isSelected ? 0.8f : 1f); get(index).model.set(TabProperties.ALPHA, isSelected ? 0.8f : 1f);
...@@ -99,8 +159,10 @@ class TabListModel extends ModelList { ...@@ -99,8 +159,10 @@ class TabListModel extends ModelList {
int status = isHovered ? ClosableTabGridView.AnimationStatus.HOVERED_CARD_ZOOM_IN int status = isHovered ? ClosableTabGridView.AnimationStatus.HOVERED_CARD_ZOOM_IN
: ClosableTabGridView.AnimationStatus.HOVERED_CARD_ZOOM_OUT; : ClosableTabGridView.AnimationStatus.HOVERED_CARD_ZOOM_OUT;
if (index < 0 || index >= size() if (index < 0 || index >= size()
|| get(index).model.get(TabProperties.CARD_ANIMATION_STATUS) == status) || get(index).model.get(TabProperties.CARD_ANIMATION_STATUS) == status) {
return; return;
}
get(index).model.set(TabProperties.CARD_ANIMATION_STATUS, status); get(index).model.set(TabProperties.CARD_ANIMATION_STATUS, status);
} }
} }
...@@ -4,6 +4,8 @@ ...@@ -4,6 +4,8 @@
package org.chromium.chrome.browser.tasks.tab_management; package org.chromium.chrome.browser.tasks.tab_management;
import static org.chromium.chrome.browser.tasks.tab_management.TabListModel.TabListModelProperties.MODEL_TYPE;
import android.content.res.ColorStateList; import android.content.res.ColorStateList;
import android.graphics.drawable.Drawable; import android.graphics.drawable.Drawable;
import android.view.View.AccessibilityDelegate; import android.view.View.AccessibilityDelegate;
...@@ -103,7 +105,8 @@ public class TabProperties { ...@@ -103,7 +105,8 @@ public class TabProperties {
CARD_ANIMATION_STATUS, SELECTABLE_TAB_CLICKED_LISTENER, TAB_SELECTION_DELEGATE, CARD_ANIMATION_STATUS, SELECTABLE_TAB_CLICKED_LISTENER, TAB_SELECTION_DELEGATE,
IS_INCOGNITO, SELECTED_TAB_BACKGROUND_DRAWABLE_ID, TABSTRIP_FAVICON_BACKGROUND_COLOR_ID, IS_INCOGNITO, SELECTED_TAB_BACKGROUND_DRAWABLE_ID, TABSTRIP_FAVICON_BACKGROUND_COLOR_ID,
SELECTABLE_TAB_ACTION_BUTTON_BACKGROUND, SELECTABLE_TAB_ACTION_BUTTON_BACKGROUND,
SELECTABLE_TAB_ACTION_BUTTON_SELECTED_BACKGROUND, URL, ACCESSIBILITY_DELEGATE}; SELECTABLE_TAB_ACTION_BUTTON_SELECTED_BACKGROUND, URL, ACCESSIBILITY_DELEGATE,
MODEL_TYPE};
public static final PropertyKey[] ALL_KEYS_TAB_STRIP = public static final PropertyKey[] ALL_KEYS_TAB_STRIP =
new PropertyKey[] {TAB_ID, TAB_SELECTED_LISTENER, TAB_CLOSED_LISTENER, FAVICON, new PropertyKey[] {TAB_ID, TAB_SELECTED_LISTENER, TAB_CLOSED_LISTENER, FAVICON,
......
...@@ -4,6 +4,9 @@ ...@@ -4,6 +4,9 @@
package org.chromium.chrome.browser.tasks.tab_management; package org.chromium.chrome.browser.tasks.tab_management;
import static org.chromium.chrome.browser.tasks.tab_management.TabListModel.TabListModelProperties.MODEL_TYPE;
import static org.chromium.chrome.browser.tasks.tab_management.TabListModel.TabListModelProperties.ModelType.OTHERS;
import android.content.Context; import android.content.Context;
import android.support.v7.widget.GridLayoutManager; import android.support.v7.widget.GridLayoutManager;
import android.support.v7.widget.RecyclerView; import android.support.v7.widget.RecyclerView;
...@@ -171,10 +174,9 @@ class TabSelectionEditorCoordinator { ...@@ -171,10 +174,9 @@ class TabSelectionEditorCoordinator {
void resetWithListOfTabs(@Nullable List<Tab> tabs, int preSelectedCount) { void resetWithListOfTabs(@Nullable List<Tab> tabs, int preSelectedCount) {
mTabListCoordinator.resetWithListOfTabs(tabs); mTabListCoordinator.resetWithListOfTabs(tabs);
if (tabs != null && preSelectedCount > 0) { if (tabs != null && preSelectedCount > 0 && preSelectedCount < tabs.size()) {
assert preSelectedCount < tabs.size(); mTabListCoordinator.addSpecialListItem(preSelectedCount, TabProperties.UiType.DIVIDER,
mTabListCoordinator.addSpecialListItem( new PropertyModel.Builder(MODEL_TYPE).with(MODEL_TYPE, OTHERS).build());
preSelectedCount, TabProperties.UiType.DIVIDER, new PropertyModel());
} }
} }
......
...@@ -4,6 +4,9 @@ ...@@ -4,6 +4,9 @@
package org.chromium.chrome.browser.tasks.tab_management; package org.chromium.chrome.browser.tasks.tab_management;
import static org.chromium.chrome.browser.tasks.tab_management.TabListModel.TabListModelProperties.MODEL_TYPE;
import static org.chromium.chrome.browser.tasks.tab_management.TabListModel.TabListModelProperties.ModelType.MESSAGE;
import android.content.Context; import android.content.Context;
import android.graphics.drawable.Drawable; import android.graphics.drawable.Drawable;
...@@ -48,6 +51,7 @@ public class TabSuggestionMessageCardViewModel { ...@@ -48,6 +51,7 @@ public class TabSuggestionMessageCardViewModel {
.with(MessageCardViewProperties.ACTION_TEXT, actionText) .with(MessageCardViewProperties.ACTION_TEXT, actionText)
.with(MessageCardViewProperties.DISMISS_BUTTON_CONTENT_DESCRIPTION, .with(MessageCardViewProperties.DISMISS_BUTTON_CONTENT_DESCRIPTION,
dismissButtonContextDescription) dismissButtonContextDescription)
.with(MODEL_TYPE, MESSAGE)
.build(); .build();
} }
......
...@@ -32,6 +32,7 @@ import java.util.Set; ...@@ -32,6 +32,7 @@ import java.util.Set;
*/ */
public class TabSuggestionMessageService extends MessageService implements TabSuggestionsObserver { public class TabSuggestionMessageService extends MessageService implements TabSuggestionsObserver {
static final int CLOSE_SUGGESTION_ACTION_ENABLING_THRESHOLD = 1; static final int CLOSE_SUGGESTION_ACTION_ENABLING_THRESHOLD = 1;
private static boolean sSuggestionAvailableForTesting;
/** /**
* This is the data type that this MessageService is serving to its Observer. * This is the data type that this MessageService is serving to its Observer.
...@@ -209,6 +210,7 @@ public class TabSuggestionMessageService extends MessageService implements TabSu ...@@ -209,6 +210,7 @@ public class TabSuggestionMessageService extends MessageService implements TabSu
Callback<TabSuggestionFeedback> tabSuggestionFeedback) { Callback<TabSuggestionFeedback> tabSuggestionFeedback) {
if (tabSuggestions.size() == 0) return; if (tabSuggestions.size() == 0) return;
sSuggestionAvailableForTesting = true;
mCurrentBestTabSuggestion = tabSuggestions.get(0); mCurrentBestTabSuggestion = tabSuggestions.get(0);
mCurrentTabSuggestionFeedback = tabSuggestionFeedback; mCurrentTabSuggestionFeedback = tabSuggestionFeedback;
sendAvailabilityNotification(new TabSuggestionMessageData( sendAvailabilityNotification(new TabSuggestionMessageData(
...@@ -218,6 +220,12 @@ public class TabSuggestionMessageService extends MessageService implements TabSu ...@@ -218,6 +220,12 @@ public class TabSuggestionMessageService extends MessageService implements TabSu
@Override @Override
public void onTabSuggestionInvalidated() { public void onTabSuggestionInvalidated() {
mCurrentBestTabSuggestion = null; mCurrentBestTabSuggestion = null;
sSuggestionAvailableForTesting = false;
sendInvalidNotification(); sendInvalidNotification();
} }
@VisibleForTesting
public static boolean isSuggestionAvailableForTesting() {
return sSuggestionAvailableForTesting;
}
} }
...@@ -32,6 +32,7 @@ import org.chromium.chrome.browser.tab.Tab; ...@@ -32,6 +32,7 @@ import org.chromium.chrome.browser.tab.Tab;
import org.chromium.chrome.browser.tabmodel.TabCreatorManager; import org.chromium.chrome.browser.tabmodel.TabCreatorManager;
import org.chromium.chrome.browser.tabmodel.TabList; import org.chromium.chrome.browser.tabmodel.TabList;
import org.chromium.chrome.browser.tabmodel.TabModelSelector; import org.chromium.chrome.browser.tabmodel.TabModelSelector;
import org.chromium.chrome.browser.tasks.tab_management.suggestions.TabSuggestionsOrchestrator;
import org.chromium.chrome.browser.ui.messages.snackbar.SnackbarManager; import org.chromium.chrome.browser.ui.messages.snackbar.SnackbarManager;
import org.chromium.chrome.tab_ui.R; import org.chromium.chrome.tab_ui.R;
import org.chromium.ui.modelutil.PropertyModel; import org.chromium.ui.modelutil.PropertyModel;
...@@ -51,6 +52,7 @@ public class TabSwitcherCoordinator ...@@ -51,6 +52,7 @@ public class TabSwitcherCoordinator
// TODO(crbug.com/982018): Rename 'COMPONENT_NAME' so as to add different metrics for carousel // TODO(crbug.com/982018): Rename 'COMPONENT_NAME' so as to add different metrics for carousel
// tab switcher. // tab switcher.
static final String COMPONENT_NAME = "GridTabSwitcher"; static final String COMPONENT_NAME = "GridTabSwitcher";
private static boolean sAppendedMessagesForTesting;
private final PropertyModelChangeProcessor mContainerViewChangeProcessor; private final PropertyModelChangeProcessor mContainerViewChangeProcessor;
private final ActivityLifecycleDispatcher mLifecycleDispatcher; private final ActivityLifecycleDispatcher mLifecycleDispatcher;
private final MenuOrKeyboardActionController mMenuOrKeyboardActionController; private final MenuOrKeyboardActionController mMenuOrKeyboardActionController;
...@@ -62,6 +64,8 @@ public class TabSwitcherCoordinator ...@@ -62,6 +64,8 @@ public class TabSwitcherCoordinator
private final UndoGroupSnackbarController mUndoGroupSnackbarController; private final UndoGroupSnackbarController mUndoGroupSnackbarController;
private final TabModelSelector mTabModelSelector; private final TabModelSelector mTabModelSelector;
private final @TabListCoordinator.TabListMode int mMode; private final @TabListCoordinator.TabListMode int mMode;
private final MessageCardProviderCoordinator mMessageCardProviderCoordinator;
private TabSuggestionsOrchestrator mTabSuggestionsOrchestrator;
private final MenuOrKeyboardActionController private final MenuOrKeyboardActionController
.MenuOrKeyboardActionHandler mTabSwitcherMenuActionHandler = .MenuOrKeyboardActionHandler mTabSwitcherMenuActionHandler =
...@@ -121,6 +125,11 @@ public class TabSwitcherCoordinator ...@@ -121,6 +125,11 @@ public class TabSwitcherCoordinator
mContainerViewChangeProcessor = PropertyModelChangeProcessor.create(containerViewModel, mContainerViewChangeProcessor = PropertyModelChangeProcessor.create(containerViewModel,
mTabListCoordinator.getContainerView(), TabListContainerViewBinder::bind); mTabListCoordinator.getContainerView(), TabListContainerViewBinder::bind);
mMessageCardProviderCoordinator = new MessageCardProviderCoordinator(context,
(identifier)
-> mTabListCoordinator.removeSpecialListItem(
TabProperties.UiType.MESSAGE, identifier));
if (FeatureUtilities.isTabGroupsAndroidUiImprovementsEnabled()) { if (FeatureUtilities.isTabGroupsAndroidUiImprovementsEnabled()) {
mTabGridDialogCoordinator = new TabGridDialogCoordinator(context, tabModelSelector, mTabGridDialogCoordinator = new TabGridDialogCoordinator(context, tabModelSelector,
tabContentManager, tabCreatorManager, tabContentManager, tabCreatorManager,
...@@ -151,6 +160,15 @@ public class TabSwitcherCoordinator ...@@ -151,6 +160,15 @@ public class TabSwitcherCoordinator
return (ViewGroup) LayoutInflater.from(context).inflate( return (ViewGroup) LayoutInflater.from(context).inflate(
R.layout.tab_grid_message_card_item, container, false); R.layout.tab_grid_message_card_item, container, false);
}, MessageCardViewBinder::bind); }, MessageCardViewBinder::bind);
mTabSuggestionsOrchestrator =
new TabSuggestionsOrchestrator(mTabModelSelector, lifecycleDispatcher);
TabSuggestionMessageService tabSuggestionMessageService =
new TabSuggestionMessageService(context, tabModelSelector,
mTabSelectionEditorCoordinator.getController());
mTabSuggestionsOrchestrator.addObserver(tabSuggestionMessageService);
mMessageCardProviderCoordinator.subscribeMessageService(
tabSuggestionMessageService);
} }
assert mTabListCoordinator.getContainerView().getLayoutManager() assert mTabListCoordinator.getContainerView().getLayoutManager()
...@@ -179,6 +197,11 @@ public class TabSwitcherCoordinator ...@@ -179,6 +197,11 @@ public class TabSwitcherCoordinator
mLifecycleDispatcher.register(this); mLifecycleDispatcher.register(this);
} }
@VisibleForTesting
public static boolean hasAppendedMessagesForTesting() {
return sAppendedMessagesForTesting;
}
// TabSwitcher implementation. // TabSwitcher implementation.
@Override @Override
public void setOnTabSelectingListener(OnTabSelectingListener listener) { public void setOnTabSelectingListener(OnTabSelectingListener listener) {
...@@ -283,6 +306,7 @@ public class TabSwitcherCoordinator ...@@ -283,6 +306,7 @@ public class TabSwitcherCoordinator
// ResetHandler implementation. // ResetHandler implementation.
@Override @Override
public boolean resetWithTabList(@Nullable TabList tabList, boolean quickMode, boolean mruMode) { public boolean resetWithTabList(@Nullable TabList tabList, boolean quickMode, boolean mruMode) {
sAppendedMessagesForTesting = false;
List<Tab> tabs = null; List<Tab> tabs = null;
if (tabList != null) { if (tabList != null) {
tabs = new ArrayList<>(); tabs = new ArrayList<>();
...@@ -292,7 +316,21 @@ public class TabSwitcherCoordinator ...@@ -292,7 +316,21 @@ public class TabSwitcherCoordinator
} }
mMediator.registerFirstMeaningfulPaintRecorder(); mMediator.registerFirstMeaningfulPaintRecorder();
return mTabListCoordinator.resetWithListOfTabs(tabs, quickMode, mruMode); boolean showQuickly = mTabListCoordinator.resetWithListOfTabs(tabs, quickMode, mruMode);
if (tabs != null && tabs.size() > 0) appendMessagesTo(tabs.size());
return showQuickly;
}
private void appendMessagesTo(int index) {
List<MessageCardProviderMediator.Message> messages =
mMessageCardProviderCoordinator.getMessageItems();
for (int i = 0; i < messages.size(); i++) {
mTabListCoordinator.addSpecialListItem(
index + i, TabProperties.UiType.MESSAGE, messages.get(i).model);
}
if (messages.size() > 0) sAppendedMessagesForTesting = true;
} }
private View getTabGridDialogAnimationSourceView(int tabId) { private View getTabGridDialogAnimationSourceView(int tabId) {
...@@ -318,6 +356,7 @@ public class TabSwitcherCoordinator ...@@ -318,6 +356,7 @@ public class TabSwitcherCoordinator
mMenuOrKeyboardActionController.unregisterMenuOrKeyboardActionHandler( mMenuOrKeyboardActionController.unregisterMenuOrKeyboardActionHandler(
mTabSwitcherMenuActionHandler); mTabSwitcherMenuActionHandler);
mTabListCoordinator.destroy(); mTabListCoordinator.destroy();
mMessageCardProviderCoordinator.destroy();
mContainerViewChangeProcessor.destroy(); mContainerViewChangeProcessor.destroy();
if (mTabGridDialogCoordinator != null) { if (mTabGridDialogCoordinator != null) {
mTabGridDialogCoordinator.destroy(); mTabGridDialogCoordinator.destroy();
......
...@@ -132,7 +132,7 @@ public class TabSelectionEditorTestingRobot { ...@@ -132,7 +132,7 @@ public class TabSelectionEditorTestingRobot {
public final TabSelectionEditorTestingRobot.Result resultRobot; public final TabSelectionEditorTestingRobot.Result resultRobot;
public final TabSelectionEditorTestingRobot.Action actionRobot; public final TabSelectionEditorTestingRobot.Action actionRobot;
TabSelectionEditorTestingRobot() { public TabSelectionEditorTestingRobot() {
resultRobot = new TabSelectionEditorTestingRobot.Result(); resultRobot = new TabSelectionEditorTestingRobot.Result();
actionRobot = new TabSelectionEditorTestingRobot.Action(); actionRobot = new TabSelectionEditorTestingRobot.Action();
} }
...@@ -140,15 +140,15 @@ public class TabSelectionEditorTestingRobot { ...@@ -140,15 +140,15 @@ public class TabSelectionEditorTestingRobot {
/** /**
* This Robot is used to perform action within the TabSelectionEditor. * This Robot is used to perform action within the TabSelectionEditor.
*/ */
class Action { public class Action {
TabSelectionEditorTestingRobot.Action clickItemAtAdapterPosition(int position) { public TabSelectionEditorTestingRobot.Action clickItemAtAdapterPosition(int position) {
onView(withId(org.chromium.chrome.tab_ui.R.id.tab_list_view)) onView(withId(org.chromium.chrome.tab_ui.R.id.tab_list_view))
.inRoot(isTabSelectionEditorPopup()) .inRoot(isTabSelectionEditorPopup())
.perform(actionOnItemAtPosition(position, click())); .perform(actionOnItemAtPosition(position, click()));
return this; return this;
} }
TabSelectionEditorTestingRobot.Action clickToolbarActionButton() { public TabSelectionEditorTestingRobot.Action clickToolbarActionButton() {
onView(allOf(withId(org.chromium.chrome.tab_ui.R.id.action_button), onView(allOf(withId(org.chromium.chrome.tab_ui.R.id.action_button),
withParent(withId(org.chromium.chrome.tab_ui.R.id.action_bar)))) withParent(withId(org.chromium.chrome.tab_ui.R.id.action_bar))))
.inRoot(isTabSelectionEditorPopup()) .inRoot(isTabSelectionEditorPopup())
...@@ -156,7 +156,7 @@ public class TabSelectionEditorTestingRobot { ...@@ -156,7 +156,7 @@ public class TabSelectionEditorTestingRobot {
return this; return this;
} }
TabSelectionEditorTestingRobot.Action clickToolbarNavigationButton() { public TabSelectionEditorTestingRobot.Action clickToolbarNavigationButton() {
onView(allOf(withContentDescription(org.chromium.chrome.tab_ui.R.string.close), onView(allOf(withContentDescription(org.chromium.chrome.tab_ui.R.string.close),
withParent(withId(org.chromium.chrome.tab_ui.R.id.action_bar)))) withParent(withId(org.chromium.chrome.tab_ui.R.id.action_bar))))
.inRoot(isTabSelectionEditorPopup()) .inRoot(isTabSelectionEditorPopup())
...@@ -168,15 +168,15 @@ public class TabSelectionEditorTestingRobot { ...@@ -168,15 +168,15 @@ public class TabSelectionEditorTestingRobot {
/** /**
* This Robot is used to verify result within the TabSelectionEditor. * This Robot is used to verify result within the TabSelectionEditor.
*/ */
class Result { public class Result {
TabSelectionEditorTestingRobot.Result verifyTabSelectionEditorIsVisible() { public TabSelectionEditorTestingRobot.Result verifyTabSelectionEditorIsVisible() {
onView(withId(org.chromium.chrome.tab_ui.R.id.selectable_list)) onView(withId(org.chromium.chrome.tab_ui.R.id.selectable_list))
.inRoot(isTabSelectionEditorPopup()) .inRoot(isTabSelectionEditorPopup())
.check(matches(isDisplayed())); .check(matches(isDisplayed()));
return this; return this;
} }
TabSelectionEditorTestingRobot.Result verifyTabSelectionEditorIsHidden() { public TabSelectionEditorTestingRobot.Result verifyTabSelectionEditorIsHidden() {
try { try {
onView(withId(org.chromium.chrome.tab_ui.R.id.selectable_list)) onView(withId(org.chromium.chrome.tab_ui.R.id.selectable_list))
.inRoot(isTabSelectionEditorPopup()) .inRoot(isTabSelectionEditorPopup())
...@@ -189,7 +189,7 @@ public class TabSelectionEditorTestingRobot { ...@@ -189,7 +189,7 @@ public class TabSelectionEditorTestingRobot {
return this; return this;
} }
TabSelectionEditorTestingRobot.Result verifyToolbarSelectionTextWithResourceId( public TabSelectionEditorTestingRobot.Result verifyToolbarSelectionTextWithResourceId(
int resourceId) { int resourceId) {
onView(withText(resourceId)) onView(withText(resourceId))
.inRoot(isTabSelectionEditorPopup()) .inRoot(isTabSelectionEditorPopup())
...@@ -197,14 +197,14 @@ public class TabSelectionEditorTestingRobot { ...@@ -197,14 +197,14 @@ public class TabSelectionEditorTestingRobot {
return this; return this;
} }
TabSelectionEditorTestingRobot.Result verifyToolbarSelectionText(String text) { public TabSelectionEditorTestingRobot.Result verifyToolbarSelectionText(String text) {
onView(withText(text)) onView(withText(text))
.inRoot(isTabSelectionEditorPopup()) .inRoot(isTabSelectionEditorPopup())
.check(matches(isDisplayed())); .check(matches(isDisplayed()));
return this; return this;
} }
TabSelectionEditorTestingRobot.Result verifyToolbarActionButtonWithResourceId( public TabSelectionEditorTestingRobot.Result verifyToolbarActionButtonWithResourceId(
int resourceId) { int resourceId) {
onView(allOf(withId(org.chromium.chrome.tab_ui.R.id.action_button), onView(allOf(withId(org.chromium.chrome.tab_ui.R.id.action_button),
withParent(withId(org.chromium.chrome.tab_ui.R.id.action_bar)))) withParent(withId(org.chromium.chrome.tab_ui.R.id.action_bar))))
...@@ -213,7 +213,8 @@ public class TabSelectionEditorTestingRobot { ...@@ -213,7 +213,8 @@ public class TabSelectionEditorTestingRobot {
return this; return this;
} }
TabSelectionEditorTestingRobot.Result verifyToolbarActionButtonWithText(String text) { public TabSelectionEditorTestingRobot.Result verifyToolbarActionButtonWithText(
String text) {
onView(allOf(withId(org.chromium.chrome.tab_ui.R.id.action_button), onView(allOf(withId(org.chromium.chrome.tab_ui.R.id.action_button),
withParent(withId(org.chromium.chrome.tab_ui.R.id.action_bar)))) withParent(withId(org.chromium.chrome.tab_ui.R.id.action_bar))))
.inRoot(isTabSelectionEditorPopup()) .inRoot(isTabSelectionEditorPopup())
...@@ -221,7 +222,7 @@ public class TabSelectionEditorTestingRobot { ...@@ -221,7 +222,7 @@ public class TabSelectionEditorTestingRobot {
return this; return this;
} }
TabSelectionEditorTestingRobot.Result verifyToolbarActionButtonDisabled() { public TabSelectionEditorTestingRobot.Result verifyToolbarActionButtonDisabled() {
onView(allOf(withId(org.chromium.chrome.tab_ui.R.id.action_button), onView(allOf(withId(org.chromium.chrome.tab_ui.R.id.action_button),
withParent(withId(org.chromium.chrome.tab_ui.R.id.action_bar)))) withParent(withId(org.chromium.chrome.tab_ui.R.id.action_bar))))
.inRoot(isTabSelectionEditorPopup()) .inRoot(isTabSelectionEditorPopup())
...@@ -229,7 +230,7 @@ public class TabSelectionEditorTestingRobot { ...@@ -229,7 +230,7 @@ public class TabSelectionEditorTestingRobot {
return this; return this;
} }
TabSelectionEditorTestingRobot.Result verifyToolbarActionButtonEnabled() { public TabSelectionEditorTestingRobot.Result verifyToolbarActionButtonEnabled() {
onView(allOf(withId(org.chromium.chrome.tab_ui.R.id.action_button), onView(allOf(withId(org.chromium.chrome.tab_ui.R.id.action_button),
withParent(withId(org.chromium.chrome.tab_ui.R.id.action_bar)))) withParent(withId(org.chromium.chrome.tab_ui.R.id.action_bar))))
.inRoot(isTabSelectionEditorPopup()) .inRoot(isTabSelectionEditorPopup())
...@@ -237,7 +238,7 @@ public class TabSelectionEditorTestingRobot { ...@@ -237,7 +238,7 @@ public class TabSelectionEditorTestingRobot {
return this; return this;
} }
TabSelectionEditorTestingRobot.Result verifyHasAtLeastNItemVisible(int count) { public TabSelectionEditorTestingRobot.Result verifyHasAtLeastNItemVisible(int count) {
onView(withId(org.chromium.chrome.tab_ui.R.id.tab_list_view)) onView(withId(org.chromium.chrome.tab_ui.R.id.tab_list_view))
.inRoot(isTabSelectionEditorPopup()) .inRoot(isTabSelectionEditorPopup())
.check((v, noMatchException) -> { .check((v, noMatchException) -> {
...@@ -249,14 +250,15 @@ public class TabSelectionEditorTestingRobot { ...@@ -249,14 +250,15 @@ public class TabSelectionEditorTestingRobot {
return this; return this;
} }
TabSelectionEditorTestingRobot.Result verifyAdapterHasItemCount(int count) { public TabSelectionEditorTestingRobot.Result verifyAdapterHasItemCount(int count) {
onView(withId(org.chromium.chrome.tab_ui.R.id.tab_list_view)) onView(withId(org.chromium.chrome.tab_ui.R.id.tab_list_view))
.inRoot(isTabSelectionEditorPopup()) .inRoot(isTabSelectionEditorPopup())
.check(matches(RecyclerViewMatcherUtils.adapterHasItemCount(count))); .check(matches(RecyclerViewMatcherUtils.adapterHasItemCount(count)));
return this; return this;
} }
TabSelectionEditorTestingRobot.Result verifyItemNotSelectedAtAdapterPosition(int position) { public TabSelectionEditorTestingRobot.Result verifyItemNotSelectedAtAdapterPosition(
int position) {
onView(withId(org.chromium.chrome.tab_ui.R.id.tab_list_view)) onView(withId(org.chromium.chrome.tab_ui.R.id.tab_list_view))
.inRoot(isTabSelectionEditorPopup()) .inRoot(isTabSelectionEditorPopup())
.check(matches( .check(matches(
...@@ -264,7 +266,8 @@ public class TabSelectionEditorTestingRobot { ...@@ -264,7 +266,8 @@ public class TabSelectionEditorTestingRobot {
return this; return this;
} }
TabSelectionEditorTestingRobot.Result verifyItemSelectedAtAdapterPosition(int position) { public TabSelectionEditorTestingRobot.Result verifyItemSelectedAtAdapterPosition(
int position) {
onView(withId(org.chromium.chrome.tab_ui.R.id.tab_list_view)) onView(withId(org.chromium.chrome.tab_ui.R.id.tab_list_view))
.inRoot(isTabSelectionEditorPopup()) .inRoot(isTabSelectionEditorPopup())
.check(matches( .check(matches(
...@@ -272,12 +275,13 @@ public class TabSelectionEditorTestingRobot { ...@@ -272,12 +275,13 @@ public class TabSelectionEditorTestingRobot {
return this; return this;
} }
TabSelectionEditorTestingRobot.Result verifyUndoSnackbarWithTextIsShown(String text) { public TabSelectionEditorTestingRobot.Result verifyUndoSnackbarWithTextIsShown(
String text) {
onView(withText(text)).check(matches(isDisplayed())); onView(withText(text)).check(matches(isDisplayed()));
return this; return this;
} }
Result verifyDividerAlwaysStartsAtTheEdgeOfScreen() { public Result verifyDividerAlwaysStartsAtTheEdgeOfScreen() {
onView(allOf(isDivider(), onView(allOf(isDivider(),
withParent(withId(org.chromium.chrome.tab_ui.R.id.tab_list_view)))) withParent(withId(org.chromium.chrome.tab_ui.R.id.tab_list_view))))
.inRoot(isTabSelectionEditorPopup()) .inRoot(isTabSelectionEditorPopup())
...@@ -291,7 +295,7 @@ public class TabSelectionEditorTestingRobot { ...@@ -291,7 +295,7 @@ public class TabSelectionEditorTestingRobot {
return this; return this;
} }
Result verifyDividerAlwaysStartsAtTheEdgeOfScreenAtPosition(int position) { public Result verifyDividerAlwaysStartsAtTheEdgeOfScreenAtPosition(int position) {
onView(withId(org.chromium.chrome.tab_ui.R.id.tab_list_view)) onView(withId(org.chromium.chrome.tab_ui.R.id.tab_list_view))
.inRoot(isTabSelectionEditorPopup()) .inRoot(isTabSelectionEditorPopup())
.perform(scrollToPosition(position)); .perform(scrollToPosition(position));
...@@ -309,7 +313,7 @@ public class TabSelectionEditorTestingRobot { ...@@ -309,7 +313,7 @@ public class TabSelectionEditorTestingRobot {
return this; return this;
} }
Result verifyDividerNotClickableNotFocusable() { public Result verifyDividerNotClickableNotFocusable() {
onView(allOf(isDivider(), onView(allOf(isDivider(),
withParent(withId(org.chromium.chrome.tab_ui.R.id.tab_list_view)))) withParent(withId(org.chromium.chrome.tab_ui.R.id.tab_list_view))))
.inRoot(isTabSelectionEditorPopup()) .inRoot(isTabSelectionEditorPopup())
...@@ -329,7 +333,7 @@ public class TabSelectionEditorTestingRobot { ...@@ -329,7 +333,7 @@ public class TabSelectionEditorTestingRobot {
* @param targetItemViewType The item view type to be matched. * @param targetItemViewType The item view type to be matched.
* @return {@link Result} to do chain verification. * @return {@link Result} to do chain verification.
*/ */
Result verifyHasItemViewTypeAtAdapterPosition(int position, int targetItemViewType) { public Result verifyHasItemViewTypeAtAdapterPosition(int position, int targetItemViewType) {
onView(withId(org.chromium.chrome.tab_ui.R.id.tab_list_view)) onView(withId(org.chromium.chrome.tab_ui.R.id.tab_list_view))
.inRoot(isTabSelectionEditorPopup()) .inRoot(isTabSelectionEditorPopup())
.perform(scrollToPosition(position)); .perform(scrollToPosition(position));
......
...@@ -121,4 +121,4 @@ public class MessageCardProviderMediatorUnitTest { ...@@ -121,4 +121,4 @@ public class MessageCardProviderMediatorUnitTest {
Assert.assertEquals(MessageService.MessageType.TAB_SUGGESTION, Assert.assertEquals(MessageService.MessageType.TAB_SUGGESTION,
model.get(MessageCardViewProperties.MESSAGE_TYPE)); model.get(MessageCardViewProperties.MESSAGE_TYPE));
} }
} }
\ No newline at end of file
...@@ -28,6 +28,11 @@ import static org.mockito.Mockito.when; ...@@ -28,6 +28,11 @@ import static org.mockito.Mockito.when;
import static org.chromium.chrome.browser.ChromeFeatureList.TAB_GROUPS_ANDROID; import static org.chromium.chrome.browser.ChromeFeatureList.TAB_GROUPS_ANDROID;
import static org.chromium.chrome.browser.ChromeFeatureList.TAB_GROUPS_UI_IMPROVEMENTS_ANDROID; import static org.chromium.chrome.browser.ChromeFeatureList.TAB_GROUPS_UI_IMPROVEMENTS_ANDROID;
import static org.chromium.chrome.browser.tasks.tab_management.MessageCardViewProperties.MESSAGE_TYPE;
import static org.chromium.chrome.browser.tasks.tab_management.MessageService.MessageType.FOR_TESTING;
import static org.chromium.chrome.browser.tasks.tab_management.MessageService.MessageType.TAB_SUGGESTION;
import static org.chromium.chrome.browser.tasks.tab_management.TabListModel.TabListModelProperties.MODEL_TYPE;
import static org.chromium.chrome.browser.tasks.tab_management.TabListModel.TabListModelProperties.ModelType.MESSAGE;
import android.app.Activity; import android.app.Activity;
import android.content.ComponentCallbacks; import android.content.ComponentCallbacks;
...@@ -1399,7 +1404,9 @@ public class TabListMediatorUnitTest { ...@@ -1399,7 +1404,9 @@ public class TabListMediatorUnitTest {
@Test @Test
public void addSpecialItem() { public void addSpecialItem() {
mMediator.addSpecialItemToModel(0, TabProperties.UiType.DIVIDER, new PropertyModel()); PropertyModel model = mock(PropertyModel.class);
when(model.get(MODEL_TYPE)).thenReturn(MESSAGE);
mMediator.addSpecialItemToModel(0, TabProperties.UiType.DIVIDER, model);
assertTrue(mModel.size() > 0); assertTrue(mModel.size() > 0);
assertEquals(TabProperties.UiType.DIVIDER, mModel.get(0).type); assertEquals(TabProperties.UiType.DIVIDER, mModel.get(0).type);
...@@ -1407,7 +1414,9 @@ public class TabListMediatorUnitTest { ...@@ -1407,7 +1414,9 @@ public class TabListMediatorUnitTest {
@Test @Test
public void addSpecialItem_notPersistOnReset() { public void addSpecialItem_notPersistOnReset() {
mMediator.addSpecialItemToModel(0, TabProperties.UiType.DIVIDER, new PropertyModel()); PropertyModel model = mock(PropertyModel.class);
when(model.get(MODEL_TYPE)).thenReturn(MESSAGE);
mMediator.addSpecialItemToModel(0, TabProperties.UiType.DIVIDER, model);
assertEquals(TabProperties.UiType.DIVIDER, mModel.get(0).type); assertEquals(TabProperties.UiType.DIVIDER, mModel.get(0).type);
List<Tab> tabs = new ArrayList<>(Arrays.asList(mTab1, mTab2)); List<Tab> tabs = new ArrayList<>(Arrays.asList(mTab1, mTab2));
...@@ -1416,11 +1425,33 @@ public class TabListMediatorUnitTest { ...@@ -1416,11 +1425,33 @@ public class TabListMediatorUnitTest {
assertNotEquals(TabProperties.UiType.DIVIDER, mModel.get(0).type); assertNotEquals(TabProperties.UiType.DIVIDER, mModel.get(0).type);
assertNotEquals(TabProperties.UiType.DIVIDER, mModel.get(1).type); assertNotEquals(TabProperties.UiType.DIVIDER, mModel.get(1).type);
mMediator.addSpecialItemToModel(1, TabProperties.UiType.DIVIDER, new PropertyModel()); mMediator.addSpecialItemToModel(1, TabProperties.UiType.DIVIDER, model);
assertThat(mModel.size(), equalTo(3)); assertThat(mModel.size(), equalTo(3));
assertEquals(TabProperties.UiType.DIVIDER, mModel.get(1).type); assertEquals(TabProperties.UiType.DIVIDER, mModel.get(1).type);
} }
@Test(expected = AssertionError.class)
public void addSpecialItem_withoutTabListModelProperties() {
mMediator.addSpecialItemToModel(0, TabProperties.UiType.DIVIDER, new PropertyModel());
}
@Test
public void removeSpecialItem_Message() {
PropertyModel model = mock(PropertyModel.class);
int expectedMessageType = FOR_TESTING;
int wrongMessageType = TAB_SUGGESTION;
when(model.get(MODEL_TYPE)).thenReturn(MESSAGE);
when(model.get(MESSAGE_TYPE)).thenReturn(expectedMessageType);
mMediator.addSpecialItemToModel(0, TabProperties.UiType.MESSAGE, model);
assertEquals(1, mModel.size());
mMediator.removeSpecialItemFromModel(TabProperties.UiType.MESSAGE, wrongMessageType);
assertEquals(1, mModel.size());
mMediator.removeSpecialItemFromModel(TabProperties.UiType.MESSAGE, expectedMessageType);
assertEquals(0, mModel.size());
}
@Test @Test
@Features.DisableFeatures({ChromeFeatureList.TAB_GROUPS_ANDROID}) @Features.DisableFeatures({ChromeFeatureList.TAB_GROUPS_ANDROID})
public void testUrlUpdated_forSingleTab_GTS_GroupNotEnabled() { public void testUrlUpdated_forSingleTab_GTS_GroupNotEnabled() {
......
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