[Merge M104][TabSwitcher][Tablet] Create start surface on first switcher load instead of on startup
(cherry picked from commit 5162b6e71f750cd98596ef7f8c0ea718eac8ed57)
Bug: 1333479
Change-Id: Idb40978699078b7f3b99d1470b4fe14eec993bb1
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3735147
Reviewed-by: Theresa Sullivan <twellington@chromium.org>
Commit-Queue: Sirisha Kavuluru <skavuluru@google.com>
Auto-Submit: Sirisha Kavuluru <skavuluru@google.com>
Reviewed-by: Mei Liang <meiliang@chromium.org>
Reviewed-by: Neil Coronado <nemco@google.com>
Reviewed-by: Michael Thiessen <mthiesse@chromium.org>
Cr-Original-Commit-Position: refs/heads/main@{#1021262}
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3756816
Cr-Commit-Position: refs/branch-heads/5112@{#818}
Cr-Branched-From: b13d3fe7b3c47a56354ef54b221008afa754412e-refs/heads/main@{#1012729}
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherCoordinator.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherCoordinator.java
index 305c6ee..70f0903 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherCoordinator.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherCoordinator.java
@@ -8,6 +8,7 @@
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Rect;
+import android.os.SystemClock;
import android.view.View;
import android.view.ViewGroup;
import android.view.accessibility.AccessibilityEvent;
@@ -18,6 +19,8 @@
import androidx.recyclerview.widget.RecyclerView.ViewHolder;
import org.chromium.base.Callback;
+import org.chromium.base.TraceEvent;
+import org.chromium.base.metrics.RecordHistogram;
import org.chromium.base.metrics.RecordUserAction;
import org.chromium.base.supplier.Supplier;
import org.chromium.chrome.browser.browser_controls.BrowserControlsStateProvider;
@@ -39,6 +42,7 @@
import org.chromium.chrome.browser.tasks.pseudotab.TabAttributeCache;
import org.chromium.chrome.browser.tasks.tab_management.PriceMessageService.PriceMessageType;
import org.chromium.chrome.browser.tasks.tab_management.TabListCoordinator.TabListMode;
+import org.chromium.chrome.browser.tasks.tab_management.TabSelectionEditorCoordinator.TabSelectionEditorController;
import org.chromium.chrome.browser.tasks.tab_management.TabSelectionEditorCoordinator.TabSelectionEditorNavigationProvider;
import org.chromium.chrome.browser.tasks.tab_management.suggestions.TabSuggestionsOrchestrator;
import org.chromium.chrome.browser.ui.messages.snackbar.SnackbarManager;
@@ -107,6 +111,7 @@
private final TabListCoordinator mTabListCoordinator;
private final TabSwitcherMediator mMediator;
private final MultiThumbnailCardProvider mMultiThumbnailCardProvider;
+ @Nullable
private final TabGridDialogCoordinator mTabGridDialogCoordinator;
private final TabModelSelector mTabModelSelector;
private final @TabListCoordinator.TabListMode int mMode;
@@ -115,8 +120,9 @@
private final Supplier<DynamicResourceLoader> mDynamicResourceLoaderSupplier;
private final SnackbarManager mSnackbarManager;
private final ModalDialogManager mModalDialogManager;
-
+ @Nullable
private TabSelectionEditorCoordinator mTabSelectionEditorCoordinator;
+ @Nullable
private TabGroupManualSelectionMode mTabGroupManualSelectionMode;
private TabSuggestionsOrchestrator mTabSuggestionsOrchestrator;
private NewTabTileCoordinator mNewTabTileCoordinator;
@@ -144,7 +150,7 @@
|| (!sIsGridTabSwitcherShowing && mMode == TabListMode.GRID)) {
return false;
}
- if (id == R.id.menu_group_tabs) {
+ if (id == R.id.menu_group_tabs && mTabSelectionEditorCoordinator != null) {
assert mTabGroupManualSelectionMode != null;
mTabSelectionEditorCoordinator.getController().configureToolbar(
@@ -218,6 +224,7 @@
R.plurals.bottom_tab_grid_title_placeholder, numRelatedTabs, numRelatedTabs);
};
+ long startTimeMs = SystemClock.uptimeMillis();
mTabListCoordinator = new TabListCoordinator(mode, activity, tabModelSelector,
mMultiThumbnailCardProvider, titleProvider, true, mMediator, null,
TabProperties.UiType.CLOSABLE, null, this, container, true, COMPONENT_NAME,
@@ -225,6 +232,9 @@
mContainerViewChangeProcessor = PropertyModelChangeProcessor.create(containerViewModel,
mTabListCoordinator.getContainerView(), TabListContainerViewBinder::bind);
+ RecordHistogram.recordTimesHistogram("Android.TabSwitcher.SetupRecyclerView.Time",
+ SystemClock.uptimeMillis() - startTimeMs);
+
mMediator.addTabSwitcherViewObserver(new TabSwitcherViewObserver() {
@Override
public void startedShowing() {
@@ -378,68 +388,84 @@
@Override
public void initWithNative() {
if (mIsInitialized) return;
+ try (TraceEvent e = TraceEvent.scoped("TabSwitcherCoordinator.initWithNative")) {
+ mTabListCoordinator.initWithNative(mDynamicResourceLoaderSupplier.get());
- setUpTabGroupManualSelectionMode(mActivity, mTabContentManager, mSnackbarManager);
-
- mTabListCoordinator.initWithNative(mDynamicResourceLoaderSupplier.get());
- if (mTabGridDialogCoordinator != null) {
- mTabGridDialogCoordinator.initWithNative(mActivity, mTabModelSelector,
- mTabContentManager, mTabListCoordinator.getTabGroupTitleEditor());
- }
-
- mMultiThumbnailCardProvider.initWithNative();
-
- if (mMode == TabListCoordinator.TabListMode.GRID) {
- if (CachedFeatureFlags.isEnabled(ChromeFeatureList.CLOSE_TAB_SUGGESTIONS)) {
- mTabSuggestionsOrchestrator = new TabSuggestionsOrchestrator(
- mActivity, mTabModelSelector, mLifecycleDispatcher);
- TabSuggestionMessageService tabSuggestionMessageService =
- new TabSuggestionMessageService(mActivity, mTabModelSelector,
- mTabSelectionEditorCoordinator.getController());
- mTabSuggestionsOrchestrator.addObserver(tabSuggestionMessageService);
- mMessageCardProviderCoordinator.subscribeMessageService(
- tabSuggestionMessageService);
- }
-
- if (TabUiFeatureUtilities.isTabGridLayoutAndroidNewTabTileEnabled()) {
- mNewTabTileCoordinator =
- new NewTabTileCoordinator(mTabModelSelector, mTabCreatorManager);
- }
-
+ // Selector editor required for tab groups and close tab suggestions.
if (TabUiFeatureUtilities.isTabGroupsAndroidEnabled(mActivity)
- && !TabSwitcherCoordinator.isShowingTabsInMRUOrder(mMode)) {
- mTabGridIphDialogCoordinator =
- new TabGridIphDialogCoordinator(mActivity, mContainer, mModalDialogManager);
- IphMessageService iphMessageService =
- new IphMessageService(mTabGridIphDialogCoordinator);
- mMessageCardProviderCoordinator.subscribeMessageService(iphMessageService);
+ || CachedFeatureFlags.isEnabled(ChromeFeatureList.CLOSE_TAB_SUGGESTIONS)) {
+ setUpTabSelectionEditorCoordinator(mActivity, mTabContentManager);
}
+ if (TabUiFeatureUtilities.isTabGroupsAndroidEnabled(mActivity)) {
+ setUpTabGroupManualSelectionMode(mActivity);
+ if (mTabGridDialogCoordinator != null) {
+ mTabGridDialogCoordinator.initWithNative(mActivity, mTabModelSelector,
+ mTabContentManager, mTabListCoordinator.getTabGroupTitleEditor());
+ }
+ }
+
+ final TabSelectionEditorController controller = mTabSelectionEditorCoordinator != null
+ ? mTabSelectionEditorCoordinator.getController()
+ : null;
+
+ if (mMode == TabListCoordinator.TabListMode.GRID) {
+ if (CachedFeatureFlags.isEnabled(ChromeFeatureList.CLOSE_TAB_SUGGESTIONS)) {
+ mTabSuggestionsOrchestrator = new TabSuggestionsOrchestrator(
+ mActivity, mTabModelSelector, mLifecycleDispatcher);
+ TabSuggestionMessageService tabSuggestionMessageService =
+ new TabSuggestionMessageService(
+ mActivity, mTabModelSelector, controller);
+ mTabSuggestionsOrchestrator.addObserver(tabSuggestionMessageService);
+ mMessageCardProviderCoordinator.subscribeMessageService(
+ tabSuggestionMessageService);
+ }
+
+ if (TabUiFeatureUtilities.isTabGridLayoutAndroidNewTabTileEnabled()) {
+ mNewTabTileCoordinator =
+ new NewTabTileCoordinator(mTabModelSelector, mTabCreatorManager);
+ }
+
+ if (TabUiFeatureUtilities.isTabGroupsAndroidEnabled(mActivity)
+ && !TabSwitcherCoordinator.isShowingTabsInMRUOrder(mMode)) {
+ mTabGridIphDialogCoordinator = new TabGridIphDialogCoordinator(
+ mActivity, mContainer, mModalDialogManager);
+ IphMessageService iphMessageService =
+ new IphMessageService(mTabGridIphDialogCoordinator);
+ mMessageCardProviderCoordinator.subscribeMessageService(iphMessageService);
+ }
+ }
+
+ mMultiThumbnailCardProvider.initWithNative();
+ mMediator.initWithNative(controller, mSnackbarManager);
+ // TODO(crbug.com/1222762): Only call setUpPriceTracking in GRID TabSwitcher.
+ setUpPriceTracking(mActivity, mModalDialogManager);
+
+ mIsInitialized = true;
}
-
- // TODO(crbug.com/1222762): Only call setUpPriceTracking in GRID TabSwitcher.
- setUpPriceTracking(mActivity, mModalDialogManager);
-
- mIsInitialized = true;
}
- private void setUpTabGroupManualSelectionMode(
- Context context, TabContentManager tabContentManager, SnackbarManager snackbarManager) {
- // For tab switcher in carousel mode, the selection editor should still follow grid style.
- int selectionEditorMode = mMode == TabListCoordinator.TabListMode.CAROUSEL
- ? TabListCoordinator.TabListMode.GRID
- : mMode;
+ private void setUpTabGroupManualSelectionMode(Context context) {
+ try (TraceEvent e = TraceEvent.scoped(
+ "TabSwitcherCoordintor.setUpTabGroupManualSelectionMode")) {
+ mTabGroupManualSelectionMode = new TabGroupManualSelectionMode(
+ context.getString(R.string.tab_selection_editor_group),
+ R.plurals.accessibility_tab_selection_editor_group_button, 2,
+ new TabSelectionEditorActionProvider(
+ mTabSelectionEditorCoordinator.getController(),
+ TabSelectionEditorActionProvider.TabSelectionEditorAction.GROUP),
+ new TabSelectionEditorNavigationProvider(
+ mTabSelectionEditorCoordinator.getController()));
+ }
+ }
+
+ private void setUpTabSelectionEditorCoordinator(
+ Context context, TabContentManager tabContentManager) {
+ // For tab switcher in carousel mode, the selection editor should still follow grid
+ // style.
+ int selectionEditorMode = mMode == TabListMode.CAROUSEL ? TabListMode.GRID : mMode;
mTabSelectionEditorCoordinator =
new TabSelectionEditorCoordinator(context, mCoordinatorView, mTabModelSelector,
tabContentManager, selectionEditorMode, mRootView);
- mMediator.initWithNative(mTabSelectionEditorCoordinator.getController(), snackbarManager);
-
- mTabGroupManualSelectionMode = new TabGroupManualSelectionMode(
- context.getString(R.string.tab_selection_editor_group),
- R.plurals.accessibility_tab_selection_editor_group_button, 2,
- new TabSelectionEditorActionProvider(mTabSelectionEditorCoordinator.getController(),
- TabSelectionEditorActionProvider.TabSelectionEditorAction.GROUP),
- new TabSelectionEditorNavigationProvider(
- mTabSelectionEditorCoordinator.getController()));
}
private void setUpPriceTracking(Context context, ModalDialogManager modalDialogManager) {
@@ -474,7 +500,10 @@
@Override
public Supplier<Boolean> getTabGridDialogVisibilitySupplier() {
- return mTabGridDialogCoordinator::isVisible;
+ if (mTabGridDialogCoordinator != null) {
+ return mTabGridDialogCoordinator::isVisible;
+ }
+ return () -> false;
}
@Override
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherMediator.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherMediator.java
index 40293c0b..66fb460 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherMediator.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherMediator.java
@@ -457,12 +457,14 @@
* @param tabSelectionEditorController The controller that can control the visibility of the
* TabSelectionEditor.
*/
- public void initWithNative(
- TabSelectionEditorCoordinator.TabSelectionEditorController tabSelectionEditorController,
+ public void initWithNative(@Nullable TabSelectionEditorCoordinator
+ .TabSelectionEditorController tabSelectionEditorController,
@Nullable SnackbarManager snackbarManager) {
- mTabSelectionEditorController = tabSelectionEditorController;
- mTabSelectionEditorController.getHandleBackPressChangedSupplier().addObserver(
- this::notifyBackPressStateChanged);
+ if (tabSelectionEditorController != null) {
+ mTabSelectionEditorController = tabSelectionEditorController;
+ mTabSelectionEditorController.getHandleBackPressChangedSupplier().addObserver(
+ this::notifyBackPressStateChanged);
+ }
mSnackbarManager = snackbarManager;
}
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabUiFeatureUtilities.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabUiFeatureUtilities.java
index 2bdb98d..d1ee3b11 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabUiFeatureUtilities.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabUiFeatureUtilities.java
@@ -106,6 +106,12 @@
new BooleanCachedFieldTrialParameter(ChromeFeatureList.GRID_TAB_SWITCHER_FOR_TABLETS,
GRID_TAB_SWITCHER_FOR_TABLETS_POLISH_PARAM, false);
+ // Field trial parameter for controlling delay grid tab switcher creation for tablets.
+ private static final String DELAY_GTS_CREATION_PARAM = "delay_creation";
+ public static final BooleanCachedFieldTrialParameter DELAY_GTS_CREATION =
+ new BooleanCachedFieldTrialParameter(ChromeFeatureList.GRID_TAB_SWITCHER_FOR_TABLETS,
+ DELAY_GTS_CREATION_PARAM, false);
+
// Field trial parameter for defining tab width for tab strip improvements.
private static final String TAB_STRIP_IMPROVEMENTS_TAB_WIDTH_PARAM = "min_tab_width";
public static final DoubleCachedFieldTrialParameter TAB_STRIP_TAB_WIDTH =
@@ -176,6 +182,14 @@
}
/**
+ * @return Whether the tablet Grid Tab Switcher creation should be delayed to on GTS load
+ * instead of on startup.
+ */
+ public static boolean isTabletGridTabSwitcherDelayCreationEnabled() {
+ return DELAY_GTS_CREATION.getValue();
+ }
+
+ /**
* @return Whether the tab group feature is enabled and available for use.
* @param context The activity context.
*/
diff --git a/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherTabletTest.java b/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherTabletTest.java
index c36dd05..1623912 100644
--- a/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherTabletTest.java
+++ b/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherTabletTest.java
@@ -18,10 +18,14 @@
import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import android.graphics.Bitmap;
import android.support.test.InstrumentationRegistry;
+import android.view.ViewGroup;
+import android.view.ViewStub;
import androidx.test.filters.MediumTest;
@@ -35,10 +39,13 @@
import org.chromium.base.Callback;
import org.chromium.base.GarbageCollectionTestUtils;
+import org.chromium.base.test.util.Batch;
import org.chromium.base.test.util.CallbackHelper;
import org.chromium.base.test.util.CommandLineFlags;
import org.chromium.base.test.util.CriteriaHelper;
+import org.chromium.base.test.util.RequiresRestart;
import org.chromium.base.test.util.Restriction;
+import org.chromium.chrome.browser.ChromeTabbedActivity;
import org.chromium.chrome.browser.compositor.layouts.Layout;
import org.chromium.chrome.browser.compositor.layouts.LayoutManagerImpl;
import org.chromium.chrome.browser.compositor.overlays.strip.StripLayoutHelperManager;
@@ -57,6 +64,7 @@
import org.chromium.chrome.tab_ui.R;
import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
import org.chromium.chrome.test.ChromeTabbedActivityTestRule;
+import org.chromium.chrome.test.batch.BlankCTATabInitialStateRule;
import org.chromium.chrome.test.util.ChromeTabUtils;
import org.chromium.chrome.test.util.TabStripUtils;
import org.chromium.chrome.test.util.browser.Features.DisableFeatures;
@@ -83,13 +91,18 @@
@DisableFeatures(ChromeFeatureList.TAB_TO_GTS_ANIMATION)
@Restriction(
{Restriction.RESTRICTION_TYPE_NON_LOW_END_DEVICE, UiRestriction.RESTRICTION_TYPE_TABLET})
+@Batch(Batch.PER_CLASS)
public class TabSwitcherTabletTest {
private static final long CALLBACK_WAIT_TIMEOUT = 15L;
@ClassRule
public static DisableAnimationsTestRule sEnableAnimationsRule =
new DisableAnimationsTestRule(false);
+ @ClassRule
+ public static final ChromeTabbedActivityTestRule sActivityTestRule =
+ new ChromeTabbedActivityTestRule();
@Rule
- public ChromeTabbedActivityTestRule mActivityTestRule = new ChromeTabbedActivityTestRule();
+ public final BlankCTATabInitialStateRule mInitialStateRule =
+ new BlankCTATabInitialStateRule(sActivityTestRule, false);
private List<WeakReference<Bitmap>> mAllBitmaps = new LinkedList<>();
private TabSwitcher.TabListDelegate mTabListDelegate;
@@ -100,21 +113,10 @@
@Before
public void setUp() throws ExecutionException {
- mActivityTestRule.startMainActivityOnBlankPage();
- CriteriaHelper.pollUiThread(mActivityTestRule.getActivity()
+ CriteriaHelper.pollUiThread(sActivityTestRule.getActivity()
.getTabModelSelectorSupplier()
.get()::isTabStateInitialized);
- Layout layout = mActivityTestRule.getActivity().getLayoutManager().getOverviewLayout();
- assertTrue(layout instanceof TabSwitcherAndStartSurfaceLayout);
- TabSwitcherAndStartSurfaceLayout mTabSwitcherAndStartSurfaceLayout =
- (TabSwitcherAndStartSurfaceLayout) layout;
-
- mTabListDelegate = mTabSwitcherAndStartSurfaceLayout.getStartSurfaceForTesting()
- .getGridTabListDelegate();
- Callback<Bitmap> mBitmapListener = (bitmap) -> mAllBitmaps.add(new WeakReference<>(bitmap));
- mTabListDelegate.setBitmapCallbackForTesting(mBitmapListener);
-
LayoutStateObserver mLayoutObserver = new LayoutStateProvider.LayoutStateObserver() {
@Override
public void onFinishedHiding(int layoutType) {
@@ -125,50 +127,51 @@
mLayoutManagerCallback = (manager) -> manager.addObserver(mLayoutObserver);
TestThreadUtils.runOnUiThreadBlocking(
()
- -> mActivityTestRule.getActivity().getLayoutManagerSupplier().addObserver(
+ -> sActivityTestRule.getActivity().getLayoutManagerSupplier().addObserver(
mLayoutManagerCallback));
}
@After
- public void cleanup() {
- TestThreadUtils.runOnUiThreadBlocking(
- ()
- -> mActivityTestRule.getActivity()
- .getLayoutManagerSupplier()
- .removeObserver(mLayoutManagerCallback));
+ public void cleanup() throws TimeoutException {
+ final ChromeTabbedActivity activity = sActivityTestRule.getActivity();
+ TestThreadUtils.runOnUiThreadBlocking(() -> {
+ activity.getLayoutManagerSupplier().removeObserver(mLayoutManagerCallback);
+ });
}
@Test
@MediumTest
+ @RequiresRestart
public void testEnterAndExitTabSwitcherVerifyThumbnails()
throws ExecutionException, TimeoutException {
- prepareTabs(1, 1);
+ prepareTabsWithThumbnail(1, 1);
enterGTSWithThumbnailChecking();
exitGTSAndVerifyThumbnailsAreReleased();
}
@Test
@MediumTest
- public void testToggleIncognitoSwitcher() throws InterruptedException, ExecutionException {
+ public void testToggleIncognitoSwitcher() throws Exception {
prepareTabs(1, 1);
- TabUiTestHelper.enterTabSwitcher(mActivityTestRule.getActivity());
+ TabUiTestHelper.enterTabSwitcher(sActivityTestRule.getActivity());
// Start with incognito switcher.
- final Tab currTab = mActivityTestRule.getActivity().getCurrentTabModel().getTabAt(0);
- assertTrue(currTab.isIncognito());
+ assertTrue("Expected to be in Incognito model",
+ sActivityTestRule.getActivity().getCurrentTabModel().isIncognito());
// Toggle to normal switcher.
clickIncognitoToggleButton();
- final Tab newTab = mActivityTestRule.getActivity().getCurrentTabModel().getTabAt(0);
+ final Tab newTab = sActivityTestRule.getActivity().getCurrentTabModel().getTabAt(0);
assertFalse(newTab.isIncognito());
+
+ exitSwitcherWithTabClick(0);
}
@Test
@MediumTest
- public void testTabSwitcherToolbar()
- throws InterruptedException, ExecutionException, TimeoutException {
+ public void testTabSwitcherToolbar() throws Exception {
prepareTabs(1, 1);
- TabUiTestHelper.enterTabSwitcher(mActivityTestRule.getActivity());
+ TabUiTestHelper.enterTabSwitcher(sActivityTestRule.getActivity());
// Assert hidden views.
onView(allOf(withId(R.id.toolbar), withClassName(is(ToolbarTablet.class.getName()))))
@@ -206,10 +209,9 @@
@Test
@MediumTest
@CommandLineFlags.Add({"force-fieldtrial-params=Study.Group:enable_launch_polish/true"})
- public void testTabSwitcherToolbar_withPolishFlag_incognitoTabsOpen()
- throws InterruptedException, ExecutionException, TimeoutException {
+ public void testTabSwitcherToolbar_withPolishFlag_incognitoTabsOpen() throws Exception {
prepareTabs(1, 1);
- TabUiTestHelper.enterTabSwitcher(mActivityTestRule.getActivity());
+ TabUiTestHelper.enterTabSwitcher(sActivityTestRule.getActivity());
// Assert hidden views.
onView(allOf(withId(R.id.incognito_switch),
@@ -250,9 +252,9 @@
@CommandLineFlags.Add({"force-fieldtrial-params=Study.Group:enable_launch_polish/true"})
public void testTabSwitcherV1Scrim() throws TimeoutException {
prepareTabs(1, 1);
- TabUiTestHelper.enterTabSwitcher(mActivityTestRule.getActivity());
+ TabUiTestHelper.enterTabSwitcher(sActivityTestRule.getActivity());
- ScrimCoordinator scrimCoordinator = mActivityTestRule.getActivity()
+ ScrimCoordinator scrimCoordinator = sActivityTestRule.getActivity()
.getRootUiCoordinatorForTesting()
.getScrimCoordinator();
assertTrue(scrimCoordinator.isShowingScrim());
@@ -264,19 +266,45 @@
@Test
@MediumTest
@CommandLineFlags.Add({"force-fieldtrial-params=Study.Group:enable_launch_polish/true"})
- public void testGridTabSwitcherOnNoNextTab() {
- TabUiTestHelper.createTabs(mActivityTestRule.getActivity(), false, 1);
-
+ public void testGridTabSwitcherOnNoNextTab() throws ExecutionException {
// Assert the grid tab switcher is not yet showing.
onView(withId(R.id.grid_tab_switcher_view_holder))
.check(matches(withEffectiveVisibility(GONE)));
// Close the only tab through the tab strip.
- closeTab(false, mActivityTestRule.getActivity().getCurrentTabModel().getTabAt(0).getId());
+ closeTab(false, sActivityTestRule.getActivity().getCurrentTabModel().getTabAt(0).getId());
// Assert the grid tab switcher is shown automatically, since there is no next tab.
onView(withId(R.id.grid_tab_switcher_view_holder))
.check(matches(withEffectiveVisibility(VISIBLE)));
+
+ TestThreadUtils.runOnUiThreadBlocking(()
+ -> sActivityTestRule.getActivity()
+ .findViewById(R.id.new_tab_button)
+ .performClick());
+ }
+
+ @Test
+ @MediumTest
+ @CommandLineFlags.
+ Add({"force-fieldtrial-params=Study.Group:enable_launch_polish/true/delay_creation/true"})
+ public void testGridTabSwitcherV1DelayCreate() {
+ Layout layout = sActivityTestRule.getActivity().getLayoutManager().getOverviewLayout();
+ assertNull("StartSurface layout should not be initialized", layout);
+ ViewStub tabSwitcherStub = (ViewStub) sActivityTestRule.getActivity().findViewById(
+ R.id.grid_tab_switcher_view_holder_stub);
+ assertTrue("TabSwitcher view stub should not be inflated",
+ tabSwitcherStub.getParent() != null);
+
+ // Click tab switcher button
+ TabUiTestHelper.enterTabSwitcher(sActivityTestRule.getActivity());
+
+ layout = sActivityTestRule.getActivity().getLayoutManager().getOverviewLayout();
+ assertTrue("OverviewLayout should be TabSwitcherAndStartSurfaceLayout layout",
+ layout instanceof TabSwitcherAndStartSurfaceLayout);
+ ViewGroup tabSwitcherViewHolder =
+ sActivityTestRule.getActivity().findViewById(R.id.grid_tab_switcher_view_holder);
+ assertNotNull("TabSwitcher view should be inflated", tabSwitcherViewHolder);
}
protected void clickIncognitoToggleButton() {
@@ -288,68 +316,86 @@
}
};
TestThreadUtils.runOnUiThreadBlocking(()
- -> mActivityTestRule.getActivity()
+ -> sActivityTestRule.getActivity()
.getTabModelSelectorSupplier()
.get()
.addObserver(observer));
StripLayoutHelperManager manager =
- TabStripUtils.getStripLayoutHelperManager(mActivityTestRule.getActivity());
+ TabStripUtils.getStripLayoutHelperManager(sActivityTestRule.getActivity());
TabStripUtils.clickCompositorButton(manager.getModelSelectorButton(),
- InstrumentationRegistry.getInstrumentation(), mActivityTestRule.getActivity());
+ InstrumentationRegistry.getInstrumentation(), sActivityTestRule.getActivity());
try {
tabModelSelectedCallback.waitForCallback(0);
} catch (TimeoutException e) {
Assert.fail("Tab model selected event never occurred.");
}
TestThreadUtils.runOnUiThreadBlocking(() -> {
- mActivityTestRule.getActivity().getTabModelSelector().removeObserver(observer);
+ sActivityTestRule.getActivity().getTabModelSelector().removeObserver(observer);
});
}
- private void prepareTabs(int numTabs, int numIncognitoTabs) {
+ private void prepareTabsWithThumbnail(int numTabs, int numIncognitoTabs) {
+ setupForThumbnailCheck();
int oldCount = mTabListDelegate.getBitmapFetchCountForTesting();
TabUiTestHelper.prepareTabsWithThumbnail(
- mActivityTestRule, numTabs, numIncognitoTabs, null);
+ sActivityTestRule, numTabs, numIncognitoTabs, null);
assertEquals(0, mTabListDelegate.getBitmapFetchCountForTesting() - oldCount);
}
+ private void prepareTabs(int numTabs, int numIncognitoTabs) {
+ TabUiTestHelper.createTabs(sActivityTestRule.getActivity(), false, numTabs);
+ TabUiTestHelper.createTabs(sActivityTestRule.getActivity(), true, numIncognitoTabs);
+ }
+
+ private void setupForThumbnailCheck() {
+ Layout layout = sActivityTestRule.getActivity().getLayoutManager().getOverviewLayout();
+ assertTrue(layout instanceof TabSwitcherAndStartSurfaceLayout);
+ TabSwitcherAndStartSurfaceLayout mTabSwitcherAndStartSurfaceLayout =
+ (TabSwitcherAndStartSurfaceLayout) layout;
+
+ mTabListDelegate = mTabSwitcherAndStartSurfaceLayout.getStartSurfaceForTesting()
+ .getGridTabListDelegate();
+ Callback<Bitmap> mBitmapListener = (bitmap) -> mAllBitmaps.add(new WeakReference<>(bitmap));
+ mTabListDelegate.setBitmapCallbackForTesting(mBitmapListener);
+ }
+
private void exitGTSAndVerifyThumbnailsAreReleased()
throws TimeoutException, ExecutionException {
- assertTrue(mActivityTestRule.getActivity().getLayoutManager().isLayoutVisible(
+ assertTrue(sActivityTestRule.getActivity().getLayoutManager().isLayoutVisible(
LayoutType.TAB_SWITCHER));
- final int index = mActivityTestRule.getActivity().getCurrentTabModel().index();
+ final int index = sActivityTestRule.getActivity().getCurrentTabModel().index();
exitSwitcherWithTabClick(index);
assertThumbnailsAreReleased();
}
private void exitSwitcherWithTabClick(int index) throws TimeoutException {
- TabUiTestHelper.clickNthCardFromTabSwitcher(mActivityTestRule.getActivity(), index);
+ TabUiTestHelper.clickNthCardFromTabSwitcher(sActivityTestRule.getActivity(), index);
mLayoutChangedCallbackHelper.waitForCallback(1, 1, CALLBACK_WAIT_TIMEOUT, TimeUnit.SECONDS);
assertTrue(mCurrentlyActiveLayout == LayoutType.TAB_SWITCHER);
}
private void exitSwitcherPolishWithTabClick(int index) throws TimeoutException {
TabUiTestHelper.clickNthCardFromTabletTabSwitcherPolish(
- mActivityTestRule.getActivity(), index);
+ sActivityTestRule.getActivity(), index);
mLayoutChangedCallbackHelper.waitForCallback(1, 1, CALLBACK_WAIT_TIMEOUT, TimeUnit.SECONDS);
assertTrue(mCurrentlyActiveLayout == LayoutType.TAB_SWITCHER);
}
private void enterGTSWithThumbnailChecking() {
- Tab currentTab = mActivityTestRule.getActivity().getTabModelSelector().getCurrentTab();
+ Tab currentTab = sActivityTestRule.getActivity().getTabModelSelector().getCurrentTab();
// Native tabs need to be invalidated first to trigger thumbnail taking, so skip them.
boolean checkThumbnail = !currentTab.isNativePage();
if (checkThumbnail) {
- mActivityTestRule.getActivity().getTabContentManager().removeTabThumbnail(
+ sActivityTestRule.getActivity().getTabContentManager().removeTabThumbnail(
currentTab.getId());
}
- TabUiTestHelper.enterTabSwitcher(mActivityTestRule.getActivity());
+ TabUiTestHelper.enterTabSwitcher(sActivityTestRule.getActivity());
TabUiTestHelper.verifyAllTabsHaveThumbnail(
- mActivityTestRule.getActivity().getCurrentTabModel());
+ sActivityTestRule.getActivity().getCurrentTabModel());
}
private void assertThumbnailsAreReleased() {
@@ -365,12 +411,12 @@
private void closeTab(final boolean incognito, final int id) {
ChromeTabUtils.closeTabWithAction(InstrumentationRegistry.getInstrumentation(),
- mActivityTestRule.getActivity(), () -> {
+ sActivityTestRule.getActivity(), () -> {
StripLayoutTab tab = TabStripUtils.findStripLayoutTab(
- mActivityTestRule.getActivity(), incognito, id);
+ sActivityTestRule.getActivity(), incognito, id);
TabStripUtils.clickCompositorButton(tab.getCloseButton(),
InstrumentationRegistry.getInstrumentation(),
- mActivityTestRule.getActivity());
+ sActivityTestRule.getActivity());
});
}
}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java
index 7502070..97a5fc2 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java
@@ -22,6 +22,7 @@
import android.view.View.OnClickListener;
import android.view.ViewConfiguration;
import android.view.ViewGroup;
+import android.view.ViewStub;
import android.view.Window;
import android.view.WindowManager;
@@ -285,6 +286,8 @@
// Count histogram used to track number of tabs when we show the Overview on Return to Chrome.
private static final String TAB_COUNT_ON_RETURN = "Tabs.TabCountOnStartScreenShown";
+ // Time histogram used to track time to inflate tab switcher views.
+ private static final String TAB_SWITCHER_CREATION_TIME = "Android.TabSwitcher.CreationTime";
private final MainIntentBehaviorMetrics mMainIntentMetrics;
private @Nullable MultiInstanceManager mMultiInstanceManager;
@@ -694,12 +697,12 @@
// TODO(1239025): Remove all GTS enabled checks after GTS is enabled by default on
// tablets.
if (TabUiFeatureUtilities.isGridTabSwitcherEnabled(this)) {
- createStartSurface(compositorViewHolder);
+ createStartSurface(compositorViewHolder, compositorViewHolder);
}
// clang-format off
mLayoutManager = new LayoutManagerChromePhone(compositorViewHolder, mContentContainer,
- mStartSurfaceSupplier.get(), getTabContentManagerSupplier(),
+ mStartSurfaceSupplier, getTabContentManagerSupplier(),
mRootUiCoordinator::getTopUiThemeColorProvider, mJankTracker);
mLayoutStateProviderSupplier.set(mLayoutManager);
// clang-format on
@@ -715,33 +718,32 @@
// TODO(1239025): Remove all GTS enabled checks after GTS is enabled by default on
// tablets.
- if (TabUiFeatureUtilities.isGridTabSwitcherEnabled(this)) {
- createStartSurface(compositorViewHolder);
+ if (TabUiFeatureUtilities.isGridTabSwitcherEnabled(this)
+ && !TabUiFeatureUtilities.isTabletGridTabSwitcherDelayCreationEnabled()) {
+ createAndSetStartSurfaceForTablet();
}
// clang-format off
ViewGroup tabSwitcherViewHolder = findViewById(R.id.grid_tab_switcher_view_holder);
mLayoutManager = new LayoutManagerChromeTablet(compositorViewHolder, mContentContainer,
- mStartSurfaceSupplier.get(), getTabContentManagerSupplier(),
- mRootUiCoordinator::getTopUiThemeColorProvider, mJankTracker,
- tabSwitcherViewHolder, mRootUiCoordinator.getScrimCoordinator(),
- getLifecycleDispatcher());
+ mStartSurfaceSupplier, getTabContentManagerSupplier(),
+ mRootUiCoordinator::getTopUiThemeColorProvider, mJankTracker,
+ tabSwitcherViewHolder, mRootUiCoordinator.getScrimCoordinator(),
+ getLifecycleDispatcher(), () -> createAndSetStartSurfaceForTablet());
mLayoutStateProviderSupplier.set(mLayoutManager);
// clang-format on
}
}
- private void createStartSurface(CompositorViewHolder compositorViewHolder) {
- ViewGroup containerView = TabUiFeatureUtilities.isTabletGridTabSwitcherPolishEnabled(this)
- ? findViewById(R.id.grid_tab_switcher_view_holder)
- : compositorViewHolder;
+ private void createStartSurface(
+ CompositorViewHolder compositorViewHolder, ViewGroup tabSwitcherContainer) {
StartSurfaceDelegate.createStartSurface(this, mRootUiCoordinator.getScrimCoordinator(),
mRootUiCoordinator.getBottomSheetController(), mStartSurfaceSupplier,
- mStartSurfaceParentTabSupplier, hadWarmStart(), getWindowAndroid(), containerView,
- compositorViewHolder::getDynamicResourceLoader, getTabModelSelector(),
- getBrowserControlsManager(), getSnackbarManager(), getShareDelegateSupplier(),
- getToolbarManager()::getOmniboxStub, getTabContentManager(),
- getModalDialogManager(),
+ mStartSurfaceParentTabSupplier, hadWarmStart(), getWindowAndroid(),
+ tabSwitcherContainer, compositorViewHolder::getDynamicResourceLoader,
+ getTabModelSelector(), getBrowserControlsManager(), getSnackbarManager(),
+ getShareDelegateSupplier(), getToolbarManager()::getOmniboxStub,
+ getTabContentManager(), getModalDialogManager(),
/* chromeActivityNativeDelegate= */ this, getLifecycleDispatcher(),
getTabCreatorManagerSupplier().get(), getMenuOrKeyboardActionController(),
getMultiWindowModeStateDispatcher(), mJankTracker, getToolbarManager()::getToolbar,
@@ -841,6 +843,29 @@
}
}
+ private ViewGroup createAndSetStartSurfaceForTablet() {
+ assert isTablet();
+ final long startTimeMs = SystemClock.uptimeMillis();
+ CompositorViewHolder compositorViewHolder = getCompositorViewHolderSupplier().get();
+ ViewGroup containerView = compositorViewHolder;
+ if (TabUiFeatureUtilities.isTabletGridTabSwitcherPolishEnabled(this)) {
+ // Inflate view holder for polish GTS.
+ containerView =
+ (ViewGroup) ((ViewStub) findViewById(R.id.grid_tab_switcher_view_holder_stub))
+ .inflate();
+ // Set view in toolbar manager to set toolbar stub.
+ getToolbarManager().setTabSwitcherFullScreenView(containerView);
+ }
+
+ // create start surface.
+ createStartSurface(compositorViewHolder, containerView);
+ mStartSurfaceSupplier.get().getController().enableRecordingFirstMeaningfulPaint(
+ getOnCreateTimestampMs());
+ RecordHistogram.recordTimesHistogram(
+ TAB_SWITCHER_CREATION_TIME, SystemClock.uptimeMillis() - startTimeMs);
+ return containerView;
+ }
+
private void maybeCreateIncognitoTabSnapshotController() {
try (TraceEvent e = TraceEvent.scoped(
"ChromeTabbedActivity.maybeCreateIncognitoTabSnapshotController")) {
@@ -1154,7 +1179,8 @@
RecordHistogram.recordCount1MHistogram(
TAB_COUNT_ON_RETURN, getCurrentTabModel().getCount());
}
- if (TabUiFeatureUtilities.isGridTabSwitcherEnabled(this)) {
+
+ if (mStartSurfaceSupplier.hasValue()) {
mStartSurfaceSupplier.get().getController().enableRecordingFirstMeaningfulPaint(
getOnCreateTimestampMs());
}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/app/flags/ChromeCachedFlags.java b/chrome/android/java/src/org/chromium/chrome/browser/app/flags/ChromeCachedFlags.java
index ee1e395..b2e701d1 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/app/flags/ChromeCachedFlags.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/app/flags/ChromeCachedFlags.java
@@ -167,6 +167,7 @@
add(TabContentManager.ALLOW_TO_REFETCH_TAB_THUMBNAIL_VARIATION);
add(TabUiFeatureUtilities.ENABLE_LAUNCH_BUG_FIX);
add(TabUiFeatureUtilities.ENABLE_LAUNCH_POLISH);
+ add(TabUiFeatureUtilities.DELAY_GTS_CREATION);
add(TabUiFeatureUtilities.ENABLE_SEARCH_CHIP);
add(TabUiFeatureUtilities.ENABLE_SEARCH_CHIP_ADAPTIVE);
add(TabUiFeatureUtilities.ENABLE_TAB_GROUP_AUTO_CREATION);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/LayoutManagerChrome.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/LayoutManagerChrome.java
index 1ca6b4d..7d24d2c 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/LayoutManagerChrome.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/LayoutManagerChrome.java
@@ -8,7 +8,7 @@
import android.view.MotionEvent;
import android.view.ViewGroup;
-import androidx.annotation.Nullable;
+import androidx.annotation.NonNull;
import androidx.annotation.VisibleForTesting;
import org.chromium.base.Callback;
@@ -67,28 +67,28 @@
private LayoutStateObserver mTabSwitcherFocusLayoutStateObserver;
protected ObservableSupplier<TabContentManager> mTabContentManagerSupplier;
+ private boolean mFinishNativeInitialization;
/**
* Creates the {@link LayoutManagerChrome} instance.
* @param host A {@link LayoutManagerHost} instance.
* @param contentContainer A {@link ViewGroup} for Android views to be bound to.
- * @param createOverviewLayout Whether overview layout should be created or not.
- * @param startSurface An interface to talk to the Grid Tab Switcher. If it's NULL, VTS
- * should be used, otherwise GTS should be used.
+ * @param startSurfaceSupplier Supplier for an interface to talk to the Grid Tab Switcher.
+ * Creates overviewLayout with this surface if this is has value. If not, {@link
+ * #showLayout(int, boolean)} will create overviewLayout.
* @param tabContentManagerSupplier Supplier of the {@link TabContentManager} instance.
* @param topUiThemeColorProvider {@link ThemeColorProvider} for top UI.
+ * @param jankTracker Tracker for surface jank.
* @param tabSwitcherScrimAnchor {@link ViewGroup} used by tab switcher layout to show scrim
* when overview is visible.
* @param scrimCoordinator {@link ScrimCoordinator} to show/hide scrim.
*/
public LayoutManagerChrome(LayoutManagerHost host, ViewGroup contentContainer,
- boolean createOverviewLayout, @Nullable StartSurface startSurface,
+ Supplier<StartSurface> startSurfaceSupplier,
ObservableSupplier<TabContentManager> tabContentManagerSupplier,
Supplier<TopUiThemeColorProvider> topUiThemeColorProvider, JankTracker jankTracker,
ViewGroup tabSwitcherScrimAnchor, ScrimCoordinator scrimCoordinator) {
super(host, contentContainer, tabContentManagerSupplier, topUiThemeColorProvider);
- Context context = host.getContext();
- LayoutRenderHost renderHost = host.getLayoutRenderHost();
// Build Event Filter Handlers
mToolbarSwipeHandler = createToolbarSwipeHandler(/* supportSwipeDown = */ true);
@@ -105,26 +105,50 @@
}
});
- if (createOverviewLayout) {
- if (startSurface != null) {
- assert TabUiFeatureUtilities.isGridTabSwitcherEnabled(context);
+ if (startSurfaceSupplier.hasValue()) {
+ createOverviewLayout(startSurfaceSupplier.get(), jankTracker, scrimCoordinator,
+ tabSwitcherScrimAnchor);
+ }
+ }
- mOverviewLayout = StartSurfaceDelegate.createTabSwitcherAndStartSurfaceLayout(
- context, this, renderHost, startSurface, jankTracker,
- tabSwitcherScrimAnchor, scrimCoordinator);
+ /**
+ * Creates @{@link org.chromium.chrome.features.start_surface.TabSwitcherAndStartSurfaceLayout}
+ * @param startSurface An interface to talk to the Grid Tab Switcher
+ * @param jankTracker Jank tracker.
+ * @param scrimCoordinator scrim coordinator for GTS
+ * @param tabSwitcherScrimAnchor scrim anchor view for GTS
+ */
+ protected void createOverviewLayout(@NonNull StartSurface startSurface,
+ @NonNull JankTracker jankTracker, ScrimCoordinator scrimCoordinator,
+ ViewGroup tabSwitcherScrimAnchor) {
+ assert mOverviewLayout == null
+ && TabUiFeatureUtilities.isGridTabSwitcherEnabled(mHost.getContext());
+ mOverviewLayout = StartSurfaceDelegate.createTabSwitcherAndStartSurfaceLayout(
+ mHost.getContext(), this, mHost.getLayoutRenderHost(), startSurface, jankTracker,
+ tabSwitcherScrimAnchor, scrimCoordinator);
- if (TabUiFeatureUtilities.isTabletGridTabSwitcherEnabled(context)) {
- mTabSwitcherFocusLayoutStateObserver = new LayoutStateObserver() {
- @Override
- public void onFinishedShowing(int layoutType) {
- if (layoutType == LayoutType.TAB_SWITCHER) {
- startSurface.getGridTabListDelegate().requestFocusOnCurrentTab();
- }
- }
- };
- addObserver(mTabSwitcherFocusLayoutStateObserver);
+ if (TabUiFeatureUtilities.isTabletGridTabSwitcherEnabled(mHost.getContext())) {
+ mTabSwitcherFocusLayoutStateObserver = new LayoutStateObserver() {
+ @Override
+ public void onFinishedShowing(int layoutType) {
+ if (layoutType == LayoutType.TAB_SWITCHER) {
+ startSurface.getGridTabListDelegate().requestFocusOnCurrentTab();
+ }
}
- }
+ };
+ addObserver(mTabSwitcherFocusLayoutStateObserver);
+ }
+
+ if (mTabContentManagerSupplier.hasValue()) {
+ mOverviewLayout.setTabContentManager(mTabContentManagerSupplier.get());
+ }
+
+ if (getTabModelSelector() != null) {
+ mOverviewLayout.setTabModelSelector(
+ getTabModelSelector(), mTabContentManagerSupplier.get());
+ }
+ if (mFinishNativeInitialization) {
+ mOverviewLayout.onFinishNativeInitialization();
}
}
@@ -178,6 +202,7 @@
mOverviewLayout.setTabModelSelector(selector, content);
mOverviewLayout.onFinishNativeInitialization();
}
+ mFinishNativeInitialization = true;
}
@Override
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/LayoutManagerChromePhone.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/LayoutManagerChromePhone.java
index 488aeec..739534a 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/LayoutManagerChromePhone.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/LayoutManagerChromePhone.java
@@ -36,16 +36,18 @@
* Creates an instance of a {@link LayoutManagerChromePhone}.
* @param host A {@link LayoutManagerHost} instance.
* @param contentContainer A {@link ViewGroup} for Android views to be bound to.
- * @param startSurface An interface to talk to the Grid Tab Switcher. If it's NULL, VTS
- * should be used, otherwise GTS should be used.
+ * @param startSurfaceSupplier Supplier for an interface to talk to the Grid Tab Switcher. Used
+ * to create overviewLayout if it has value, otherwise will use the accessibility
+ * overview layout.
* @param tabContentManagerSupplier Supplier of the {@link TabContentManager} instance.
* @param topUiThemeColorProvider {@link ThemeColorProvider} for top UI.
+ * @param jankTracker tracker for surface jank.
*/
public LayoutManagerChromePhone(LayoutManagerHost host, ViewGroup contentContainer,
- StartSurface startSurface,
+ Supplier<StartSurface> startSurfaceSupplier,
ObservableSupplier<TabContentManager> tabContentManagerSupplier,
Supplier<TopUiThemeColorProvider> topUiThemeColorProvider, JankTracker jankTracker) {
- super(host, contentContainer, true, startSurface, tabContentManagerSupplier,
+ super(host, contentContainer, startSurfaceSupplier, tabContentManagerSupplier,
topUiThemeColorProvider, jankTracker, null, null);
}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/LayoutManagerChromeTablet.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/LayoutManagerChromeTablet.java
index f684c72..3c79fb4 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/LayoutManagerChromeTablet.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/LayoutManagerChromeTablet.java
@@ -13,6 +13,7 @@
import org.chromium.chrome.browser.compositor.layouts.content.TabContentManager;
import org.chromium.chrome.browser.compositor.overlays.strip.StripLayoutHelperManager;
import org.chromium.chrome.browser.device.DeviceClassManager;
+import org.chromium.chrome.browser.layouts.LayoutType;
import org.chromium.chrome.browser.lifecycle.ActivityLifecycleDispatcher;
import org.chromium.chrome.browser.tab.TabLaunchType;
import org.chromium.chrome.browser.tabmodel.TabCreatorManager;
@@ -26,11 +27,17 @@
import org.chromium.components.browser_ui.widget.scrim.ScrimCoordinator;
import org.chromium.ui.resources.dynamics.DynamicResourceLoader;
+import java.util.concurrent.Callable;
+
/**
* {@link LayoutManagerChromeTablet} is the specialization of {@link LayoutManagerChrome} for
* the tablet.
*/
public class LayoutManagerChromeTablet extends LayoutManagerChrome {
+ // Tab Switcher
+ private final JankTracker mJankTracker;
+ private final ScrimCoordinator mScrimCoordinator;
+ private final Callable<ViewGroup> mCreateStartSurfaceCallable;
// Tab Strip
private StripLayoutHelperManager mTabStripLayoutHelperManager;
@@ -42,40 +49,41 @@
/** A {@link TitleCache} instance that stores all title/favicon bitmaps as CC resources. */
protected LayerTitleCache mLayerTitleCache;
+ private final Supplier<StartSurface> mStartSurfaceSupplier;
+
/**
* Creates an instance of a {@link LayoutManagerChromePhone}.
* @param host A {@link LayoutManagerHost} instance.
* @param contentContainer A {@link ViewGroup} for Android views to be bound to.
- * @param startSurface An interface to talk to the Grid Tab Switcher.
+ * @param startSurfaceSupplier Supplier for an interface to talk to the Grid Tab Switcher.
* @param tabContentManagerSupplier Supplier of the {@link TabContentManager} instance.
* @param topUiThemeColorProvider {@link ThemeColorProvider} for top UI.
+ * @param jankTracker Tracker for surface jank.
* @param tabSwitcherViewHolder {@link ViewGroup} used by tab switcher layout to show scrim
* when overview is visible.
* @param scrimCoordinator {@link ScrimCoordinator} to show/hide scrim.
+ * @param lifecycleDispatcher @{@link ActivityLifecycleDispatcher} to be passed to TabStrip
+ * helper.
+ * @param delayedStartSurfaceCallable Callable to create StartSurface/GTS views.
*/
public LayoutManagerChromeTablet(LayoutManagerHost host, ViewGroup contentContainer,
- StartSurface startSurface,
+ Supplier<StartSurface> startSurfaceSupplier,
ObservableSupplier<TabContentManager> tabContentManagerSupplier,
Supplier<TopUiThemeColorProvider> topUiThemeColorProvider, JankTracker jankTracker,
ViewGroup tabSwitcherViewHolder, ScrimCoordinator scrimCoordinator,
- ActivityLifecycleDispatcher lifecycleDispatcher) {
- super(host, contentContainer,
- TabUiFeatureUtilities.isGridTabSwitcherEnabled(host.getContext()), startSurface,
- tabContentManagerSupplier, topUiThemeColorProvider, jankTracker,
- tabSwitcherViewHolder, scrimCoordinator);
-
+ ActivityLifecycleDispatcher lifecycleDispatcher,
+ Callable<ViewGroup> delayedStartSurfaceCallable) {
+ super(host, contentContainer, startSurfaceSupplier, tabContentManagerSupplier,
+ topUiThemeColorProvider, jankTracker, tabSwitcherViewHolder, scrimCoordinator);
+ mStartSurfaceSupplier = startSurfaceSupplier;
mTabStripLayoutHelperManager = new StripLayoutHelperManager(host.getContext(), this,
mHost.getLayoutRenderHost(), () -> mLayerTitleCache, lifecycleDispatcher);
+ mJankTracker = jankTracker;
+ mScrimCoordinator = scrimCoordinator;
+ mCreateStartSurfaceCallable = delayedStartSurfaceCallable;
addSceneOverlay(mTabStripLayoutHelperManager);
addObserver(mTabStripLayoutHelperManager.getTabSwitcherObserver());
- if (TabUiFeatureUtilities.isTabletGridTabSwitcherPolishEnabled(mHost.getContext())) {
- mThemeColorObserver =
- (color, shouldAnimate) -> tabSwitcherViewHolder.setBackgroundColor(color);
- mTopUiThemeColorProvider = topUiThemeColorProvider.get();
- mTopUiThemeColorProvider.addThemeColorObserver(mThemeColorObserver);
- }
-
setNextLayout(null, true);
}
@@ -122,7 +130,6 @@
DynamicResourceLoader dynamicResourceLoader,
TopUiThemeColorProvider topUiColorProvider) {
super.init(selector, creator, controlContainer, dynamicResourceLoader, topUiColorProvider);
-
if (DeviceClassManager.enableLayerDecorationCache()) {
mLayerTitleCache = new LayerTitleCache(mHost.getContext(), getResourceManager());
// TODO: TitleCache should be a part of the ResourceManager.
@@ -135,6 +142,30 @@
}
@Override
+ public void showLayout(int layoutType, boolean animate) {
+ if (layoutType == LayoutType.TAB_SWITCHER && mOverviewLayout == null
+ && TabUiFeatureUtilities.isTabletGridTabSwitcherEnabled(mHost.getContext())) {
+ try {
+ if (!mStartSurfaceSupplier.hasValue()) {
+ final ViewGroup containerView = mCreateStartSurfaceCallable.call();
+ createOverviewLayout(mStartSurfaceSupplier.get(), mJankTracker,
+ mScrimCoordinator, containerView);
+ if (TabUiFeatureUtilities.isTabletGridTabSwitcherPolishEnabled(
+ mHost.getContext())) {
+ mThemeColorObserver =
+ (color, shouldAnimate) -> containerView.setBackgroundColor(color);
+ mTopUiThemeColorProvider = getTopUiThemeColorProvider().get();
+ mTopUiThemeColorProvider.addThemeColorObserver(mThemeColorObserver);
+ }
+ }
+ } catch (Exception e) {
+ throw new RuntimeException("Failed to initialize start surface.", e);
+ }
+ }
+ super.showLayout(layoutType, animate);
+ }
+
+ @Override
protected void emptyCachesExcept(int tabId) {
super.emptyCachesExcept(tabId);
if (mLayerTitleCache != null) mLayerTitleCache.clearExcept(tabId);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/LayoutManagerImpl.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/LayoutManagerImpl.java
index 3ea3c98..5c9eaae 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/LayoutManagerImpl.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/LayoutManagerImpl.java
@@ -764,6 +764,10 @@
getActiveLayout().onTabsAllClosing(incognito);
}
+ protected Supplier<TopUiThemeColorProvider> getTopUiThemeColorProvider() {
+ return mTopUiThemeColorProvider;
+ }
+
@Override
public void initLayoutTabFromHost(final int tabId) {
if (getTabModelSelector() == null || getActiveLayout() == null) return;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarManager.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarManager.java
index c2b591f..421b405 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarManager.java
@@ -988,6 +988,16 @@
}
/**
+ * Set container view on which GTS toolbar needs to inflate.
+ * @param containerView view containing GTS fullscreen toolbar.
+ */
+ public void setTabSwitcherFullScreenView(ViewGroup containerView) {
+ ViewStub toolbarStub =
+ containerView.findViewById(R.id.fullscreen_tab_switcher_toolbar_stub);
+ mToolbar.setFullScreenToolbarStub(toolbarStub);
+ }
+
+ /**
* Handle a layout change event.
* @param layoutType The layout being switched to.
* @param showToolbar Whether the toolbar should be shown.
@@ -1035,13 +1045,7 @@
Callback<LoadUrlParams> logoClickedCallback) {
ViewStub tabSwitcherToolbarStub = mActivity.findViewById(R.id.tab_switcher_toolbar_stub);
ViewStub tabSwitcherFullscreenToolbarStub = null;
- if (TabUiFeatureUtilities.isTabletGridTabSwitcherPolishEnabled(mActivity)) {
- // Need to inflate grid_tab_switcher_view_holder_stub, as it contains
- // fullscreen_tab_switcher_toolbar_stub.
- ((ViewStub) mActivity.findViewById(R.id.grid_tab_switcher_view_holder_stub)).inflate();
- tabSwitcherFullscreenToolbarStub =
- mActivity.findViewById(R.id.fullscreen_tab_switcher_toolbar_stub);
- }
+
// clang-format off
TopToolbarCoordinator toolbar = new TopToolbarCoordinator(controlContainer,
tabSwitcherToolbarStub, tabSwitcherFullscreenToolbarStub, toolbarLayout,
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/compositor/layouts/LayoutManagerTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/compositor/layouts/LayoutManagerTest.java
index 7a44b526..c05ebaac 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/compositor/layouts/LayoutManagerTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/compositor/layouts/LayoutManagerTest.java
@@ -45,6 +45,7 @@
import org.chromium.base.MathUtils;
import org.chromium.base.jank_tracker.DummyJankTracker;
import org.chromium.base.supplier.ObservableSupplierImpl;
+import org.chromium.base.supplier.Supplier;
import org.chromium.base.test.UiThreadTest;
import org.chromium.base.test.util.CallbackHelper;
import org.chromium.base.test.util.CommandLineFlags;
@@ -118,6 +119,7 @@
private long mLastDownTime;
private TabModelSelector mTabModelSelector;
+ private Supplier<StartSurface> mStartSurfaceSupplier;
private LayoutManagerChrome mManager;
private LayoutManagerChromePhone mManagerPhone;
@@ -214,9 +216,9 @@
ObservableSupplierImpl<TabContentManager> tabContentManagerSupplier =
new ObservableSupplierImpl<>();
-
- mManagerPhone = new LayoutManagerChromePhone(layoutManagerHost, container, mStartSurface,
- tabContentManagerSupplier, () -> mTopUiThemeColorProvider, new DummyJankTracker());
+ mManagerPhone = new LayoutManagerChromePhone(layoutManagerHost, container,
+ mStartSurfaceSupplier, tabContentManagerSupplier,
+ () -> mTopUiThemeColorProvider, new DummyJankTracker());
verify(mStartSurfaceController)
.addTabSwitcherViewObserver(mTabSwitcherViewObserverArgumentCaptor.capture());
@@ -705,6 +707,8 @@
// Load the browser process.
TestThreadUtils.runOnUiThreadBlocking(
() -> { ChromeBrowserInitializer.getInstance().handleSynchronousStartup(); });
+
+ mStartSurfaceSupplier = () -> mStartSurface;
}
@After
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index 334c4e7..0a6de01 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -3144,10 +3144,17 @@
const FeatureEntry::FeatureParam kGridTabSwitcherForTabletsPolished[] = {
{"enable_launch_polish", "true"}};
+const FeatureEntry::FeatureParam kGridTabSwitcherForTabletsDelayCreation[] = {
+ {"delay_creation", "true"},
+ {"enable_launch_polish", "true"}};
+
const FeatureEntry::FeatureVariation kGridTabSwitcherForTabletsVariations[] = {
{"(Polished)", kGridTabSwitcherForTabletsPolished,
std::size(kGridTabSwitcherForTabletsPolished), nullptr},
+ {"(DelayCreatePolish)", kGridTabSwitcherForTabletsDelayCreation,
+ std::size(kGridTabSwitcherForTabletsDelayCreation), nullptr},
};
+
#endif // BUILDFLAG(IS_ANDROID)
#if BUILDFLAG(IS_ANDROID)
diff --git a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/TabSwitcherModeTTCoordinator.java b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/TabSwitcherModeTTCoordinator.java
index 5ed0c07..8fc1088 100644
--- a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/TabSwitcherModeTTCoordinator.java
+++ b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/TabSwitcherModeTTCoordinator.java
@@ -27,7 +27,7 @@
*/
class TabSwitcherModeTTCoordinator {
private final ViewStub mTabSwitcherToolbarStub;
- private final ViewStub mTabSwitcherFullscreenToolbarStub;
+ private ViewStub mTabSwitcherFullscreenToolbarStub;
// TODO(twellington): Create a model to hold all of these properties. Consider using
// LazyConstructionPropertyMcp to collect all of the properties since it is designed to
@@ -69,6 +69,14 @@
}
/**
+ * Set stub for GTS fullscreen toolbar.
+ * @param toolbarStub stub to set.
+ */
+ void setFullScreenToolbarStub(ViewStub toolbarStub) {
+ mTabSwitcherFullscreenToolbarStub = toolbarStub;
+ }
+
+ /**
* Cleans up any code and removes observers as necessary.
*/
void destroy() {
@@ -202,6 +210,7 @@
private TabSwitcherModeTopToolbar maybeInflateActiveToolbar(boolean useFullscreenToolbar) {
if (useFullscreenToolbar) {
if (mTabSwitcherFullscreenToolbar == null) {
+ assert mTabSwitcherFullscreenToolbarStub != null;
mTabSwitcherFullscreenToolbar =
(TabSwitcherModeTopToolbar) mTabSwitcherFullscreenToolbarStub.inflate();
initializeToolbar(mTabSwitcherFullscreenToolbar, true);
diff --git a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/TopToolbarCoordinator.java b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/TopToolbarCoordinator.java
index c478f5c..7f697bc 100644
--- a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/TopToolbarCoordinator.java
+++ b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/TopToolbarCoordinator.java
@@ -201,6 +201,16 @@
mToolbarLayout.setInvalidatorCallback(invalidatorCallback);
}
+ /**
+ * Set fullscreen GTS toolbar stub
+ * @param toolbarStub stub to set.
+ */
+ public void setFullScreenToolbarStub(ViewStub toolbarStub) {
+ if (mTabSwitcherModeCoordinator != null) {
+ mTabSwitcherModeCoordinator.setFullScreenToolbarStub(toolbarStub);
+ }
+ }
+
private boolean isTabletGridTabSwitcherEnabled() {
return CachedFeatureFlags.isEnabled(ChromeFeatureList.GRID_TAB_SWITCHER_FOR_TABLETS);
}
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json
index 1c1e56b..845e18a 100644
--- a/testing/variations/fieldtrial_testing_config.json
+++ b/testing/variations/fieldtrial_testing_config.json
@@ -4124,11 +4124,32 @@
{
"name": "Enabled",
"params": {
+ "delay_creation": "false",
"enable_launch_polish": "false"
},
"enable_features": [
"GridTabSwitcherForTablets"
]
+ },
+ {
+ "name": "Enabled_Polish",
+ "params": {
+ "delay_creation": "false",
+ "enable_launch_polish": "true"
+ },
+ "enable_features": [
+ "GridTabSwitcherForTablets"
+ ]
+ },
+ {
+ "name": "Enabled_Polish_Delay",
+ "params": {
+ "delay_creation": "true",
+ "enable_launch_polish": "true"
+ },
+ "enable_features": [
+ "GridTabSwitcherForTablets"
+ ]
}
]
}
diff --git a/tools/metrics/histograms/metadata/android/histograms.xml b/tools/metrics/histograms/metadata/android/histograms.xml
index a538537..e0350f1 100644
--- a/tools/metrics/histograms/metadata/android/histograms.xml
+++ b/tools/metrics/histograms/metadata/android/histograms.xml
@@ -3312,6 +3312,17 @@
</summary>
</histogram>
+<histogram name="Android.TabSwitcher.CreationTime" units="ms"
+ expires_after="2022-12-31">
+ <owner>skavuluru@chromium.org</owner>
+ <owner>twellington@chromium.org</owner>
+ <owner>clank-large-form-factors@google.com</owner>
+ <summary>
+ Records the time taken to create the StartSurfaceCoordinator and grid tab
+ switcher views. Only recorded for tablets.
+ </summary>
+</histogram>
+
<histogram name="Android.TabSwitcher.IncognitoClickedIsEmpty" enum="Boolean"
expires_after="2022-09-01">
<owner>sideyilmaz@chromium.org</owner>
@@ -3322,6 +3333,17 @@
</summary>
</histogram>
+<histogram name="Android.TabSwitcher.SetupRecyclerView.Time" units="ms"
+ expires_after="2022-12-31">
+ <owner>skavuluru@chromium.org</owner>
+ <owner>twellington@chromium.org</owner>
+ <owner>clank-large-form-factors@google.com</owner>
+ <summary>
+ Records the time taken to create TabListCoordinator, which inflates the
+ RecyclerView for the grid tab switcher, and bind views.
+ </summary>
+</histogram>
+
<histogram name="Android.TaskScheduling.BootstrapTaskRunnerType"
enum="BootstrapTaskRunnerType" expires_after="2022-12-04">
<owner>blundell@chromium.org</owner>