Initial changes for TabGridDialog component.

This change contains the following:
* Introduce the skeleton for the TabGridDialog component.
* Expose TabGridDialog component to GridTabSwitcher.
* Introduce GridCardClickListener to trigger the opening of
  TabGridDialog.
* This feature is hidden behind "Tab Groups UI Improvement" flag.

Bug: 956205
Change-Id: Iaa7acf6cca278b35987300f0584e49dc41744f36
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1582526
Commit-Queue: Yue Zhang <[email protected]>
Reviewed-by: Wei-Yin Chen (陳威尹) <[email protected]>
Reviewed-by: Yusuf Ozuysal <[email protected]>
Cr-Commit-Position: refs/heads/master@{#655656}
diff --git a/chrome/android/features/tab_ui/BUILD.gn b/chrome/android/features/tab_ui/BUILD.gn
index 8e21108..4dea80a 100644
--- a/chrome/android/features/tab_ui/BUILD.gn
+++ b/chrome/android/features/tab_ui/BUILD.gn
@@ -20,6 +20,9 @@
     "java/src/org/chromium/chrome/browser/tasks/tab_management/GridTabSwitcherMediator.java",
     "java/src/org/chromium/chrome/browser/tasks/tab_management/MultiThumbnailCardProvider.java",
     "java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridContainerViewBinder.java",
+    "java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogCoordinator.java",
+    "java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogMediator.java",
+    "java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogParent.java",
     "java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridSheetCoordinator.java",
     "java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridSheetMediator.java",
     "java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridSheetContent.java",
@@ -34,6 +37,7 @@
     "java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupUiToolbarViewBinder.java",
     "java/src/org/chromium/chrome/browser/tasks/tab_management/TabListContainerProperties.java",
     "java/src/org/chromium/chrome/browser/tasks/tab_management/TabListCoordinator.java",
+    "java/src/org/chromium/chrome/browser/tasks/tab_management/TabListFaviconProvider.java",
     "java/src/org/chromium/chrome/browser/tasks/tab_management/TabListMediator.java",
     "java/src/org/chromium/chrome/browser/tasks/tab_management/TabListModel.java",
     "java/src/org/chromium/chrome/browser/tasks/tab_management/TabListRecyclerView.java",
@@ -43,7 +47,6 @@
     "java/src/org/chromium/chrome/browser/tasks/tab_management/TabStripToolbarViewProperties.java",
     "java/src/org/chromium/chrome/browser/tasks/tab_management/TabStripViewBinder.java",
     "java/src/org/chromium/chrome/browser/tasks/tab_management/TabStripViewHolder.java",
-    "java/src/org/chromium/chrome/browser/tasks/tab_management/TabListFaviconProvider.java",
   ]
 
   deps = [
diff --git a/chrome/android/features/tab_ui/java/res/layout/bottom_tab_grid_toolbar.xml b/chrome/android/features/tab_ui/java/res/layout/bottom_tab_grid_toolbar.xml
index e9314d1..5c4d46a 100644
--- a/chrome/android/features/tab_ui/java/res/layout/bottom_tab_grid_toolbar.xml
+++ b/chrome/android/features/tab_ui/java/res/layout/bottom_tab_grid_toolbar.xml
@@ -14,7 +14,8 @@
         android:layout_width="match_parent"
         android:layout_height="@dimen/bottom_sheet_peek_height"
         android:orientation="horizontal"
-        android:gravity="center_vertical">
+        android:gravity="center_vertical"
+        android:clickable="true">
         <org.chromium.ui.widget.ChromeImageView
             android:id="@+id/toolbar_left_button"
             style="@style/BottomToolbarButton"
diff --git a/chrome/android/features/tab_ui/java/res/values/dimens.xml b/chrome/android/features/tab_ui/java/res/values/dimens.xml
index 4578919..d31dd44 100644
--- a/chrome/android/features/tab_ui/java/res/values/dimens.xml
+++ b/chrome/android/features/tab_ui/java/res/values/dimens.xml
@@ -10,6 +10,8 @@
     <dimen name="tab_list_mini_card_radius">4dp</dimen>
     <dimen name="tab_list_mini_card_frame_size">1dp</dimen>
     <dimen name="tab_grid_close_button_size">18dp</dimen>
+    <dimen name="tab_grid_dialog_side_margin">16dp</dimen>
+    <dimen name="tab_grid_dialog_top_margin">85dp</dimen>
     <dimen name="tab_grid_thumbnail_card_default_size">152dp</dimen>
     <dimen name="tab_grid_thumbnail_favicon_frame_padding">16dp</dimen>
     <dimen name="tab_grid_thumbnail_favicon_padding">24dp</dimen>
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/GridTabSwitcherCoordinator.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/GridTabSwitcherCoordinator.java
index b7641780..474a57e4 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/GridTabSwitcherCoordinator.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/GridTabSwitcherCoordinator.java
@@ -14,10 +14,12 @@
 import org.chromium.chrome.browser.lifecycle.ActivityLifecycleDispatcher;
 import org.chromium.chrome.browser.lifecycle.Destroyable;
 import org.chromium.chrome.browser.tab.Tab;
+import org.chromium.chrome.browser.tabmodel.TabCreatorManager;
 import org.chromium.chrome.browser.tabmodel.TabList;
 import org.chromium.chrome.browser.tabmodel.TabModel;
 import org.chromium.chrome.browser.tabmodel.TabModelSelector;
 import org.chromium.chrome.browser.toolbar.ToolbarManager;
+import org.chromium.chrome.browser.util.FeatureUtilities;
 import org.chromium.ui.modelutil.PropertyModel;
 import org.chromium.ui.modelutil.PropertyModelChangeProcessor;
 
@@ -35,15 +37,32 @@
     private final TabListCoordinator mTabGridCoordinator;
     private final GridTabSwitcherMediator mMediator;
     private final MultiThumbnailCardProvider mMultiThumbnailCardProvider;
+    private final TabGridDialogCoordinator mTabGridDialogCoordinator;
 
     public GridTabSwitcherCoordinator(Context context,
             ActivityLifecycleDispatcher lifecycleDispatcher, ToolbarManager toolbarManager,
             TabModelSelector tabModelSelector, TabContentManager tabContentManager,
-            CompositorViewHolder compositorViewHolder, ChromeFullscreenManager fullscreenManager) {
+            CompositorViewHolder compositorViewHolder, ChromeFullscreenManager fullscreenManager,
+            TabCreatorManager tabCreatorManager) {
         PropertyModel containerViewModel = new PropertyModel(TabListContainerProperties.ALL_KEYS);
+        TabListMediator.GridCardOnClickListenerProvider gridCardOnClickListenerProvider;
+        if (FeatureUtilities.isTabGroupsAndroidUiImprovementsEnabled()) {
+            mTabGridDialogCoordinator = new TabGridDialogCoordinator(context, tabModelSelector,
+                    tabContentManager, tabCreatorManager, new CompositorViewHolder(context), this);
 
-        mMediator = new GridTabSwitcherMediator(this, containerViewModel, tabModelSelector,
-                fullscreenManager, compositorViewHolder);
+            mMediator = new GridTabSwitcherMediator(this, containerViewModel, tabModelSelector,
+                    fullscreenManager, compositorViewHolder,
+                    mTabGridDialogCoordinator.getResetHandler());
+
+            gridCardOnClickListenerProvider = mMediator::getGridCardOnClickListener;
+        } else {
+            mTabGridDialogCoordinator = null;
+
+            mMediator = new GridTabSwitcherMediator(this, containerViewModel, tabModelSelector,
+                    fullscreenManager, compositorViewHolder, null);
+
+            gridCardOnClickListenerProvider = null;
+        }
 
         mMultiThumbnailCardProvider =
                 new MultiThumbnailCardProvider(context, tabContentManager, tabModelSelector);
@@ -60,8 +79,8 @@
 
         mTabGridCoordinator = new TabListCoordinator(TabListCoordinator.TabListMode.GRID, context,
                 tabModelSelector, mMultiThumbnailCardProvider, titleProvider, true,
-                mMediator::getCreateGroupButtonOnClickListener, compositorViewHolder, true,
-                COMPONENT_NAME);
+                mMediator::getCreateGroupButtonOnClickListener, gridCardOnClickListenerProvider,
+                compositorViewHolder, true, COMPONENT_NAME);
 
         mContainerViewChangeProcessor = PropertyModelChangeProcessor.create(containerViewModel,
                 mTabGridCoordinator.getContainerView(), TabGridContainerViewBinder::bind);
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/GridTabSwitcherMediator.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/GridTabSwitcherMediator.java
index 31c7bfc..1c3d82a9 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/GridTabSwitcherMediator.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/GridTabSwitcherMediator.java
@@ -38,6 +38,8 @@
 import org.chromium.content_public.browser.LoadUrlParams;
 import org.chromium.ui.modelutil.PropertyModel;
 
+import java.util.List;
+
 /**
  * The Mediator that is responsible for resetting the tab grid based on visibility and model
  * changes.
@@ -55,6 +57,7 @@
     private final TabModelSelectorObserver mTabModelSelectorObserver;
     private final ObserverList<OverviewModeObserver> mObservers = new ObserverList<>();
     private final ChromeFullscreenManager mFullscreenManager;
+    private final TabGridDialogMediator.ResetHandler mTabGridDialogResetHandler;
     private final ChromeFullscreenManager.FullscreenListener mFullscreenListener =
             new ChromeFullscreenManager.FullscreenListener() {
                 @Override
@@ -99,7 +102,8 @@
      */
     GridTabSwitcherMediator(ResetHandler resetHandler, PropertyModel containerViewModel,
             TabModelSelector tabModelSelector, ChromeFullscreenManager fullscreenManager,
-            CompositorViewHolder compositorViewHolder) {
+            CompositorViewHolder compositorViewHolder,
+            TabGridDialogMediator.ResetHandler tabGridDialogResetHandler) {
         mResetHandler = resetHandler;
         mContainerViewModel = containerViewModel;
         mTabModelSelector = tabModelSelector;
@@ -150,6 +154,7 @@
                 BOTTOM_CONTROLS_HEIGHT, fullscreenManager.getBottomControlsHeight());
 
         mCompositorViewHolder = compositorViewHolder;
+        mTabGridDialogResetHandler = tabGridDialogResetHandler;
     }
 
     private void setVisibility(boolean isVisible) {
@@ -245,6 +250,14 @@
     }
 
     @Nullable
+    TabListMediator.TabActionListener getGridCardOnClickListener(Tab tab) {
+        if (!ableToOpenDialog(tab)) return null;
+        return tabId -> {
+            mTabGridDialogResetHandler.resetWithListOfTabs(getRelatedTabs(tabId));
+        };
+    }
+
+    @Nullable
     TabListMediator.TabActionListener getCreateGroupButtonOnClickListener(Tab tab) {
         if (!ableToCreateGroup(tab)) return null;
 
@@ -261,10 +274,18 @@
     private boolean ableToCreateGroup(Tab tab) {
         return FeatureUtilities.isTabGroupsAndroidEnabled()
                 && mTabModelSelector.isIncognitoSelected() == tab.isIncognito()
-                && mTabModelSelector.getTabModelFilterProvider()
-                           .getCurrentTabModelFilter()
-                           .getRelatedTabList(tab.getId())
-                           .size()
-                == 1;
+                && getRelatedTabs(tab.getId()).size() == 1;
+    }
+
+    private boolean ableToOpenDialog(Tab tab) {
+        return FeatureUtilities.isTabGroupsAndroidEnabled()
+                && mTabModelSelector.isIncognitoSelected() == tab.isIncognito()
+                && getRelatedTabs(tab.getId()).size() != 1;
+    }
+
+    private List<Tab> getRelatedTabs(int tabId) {
+        return mTabModelSelector.getTabModelFilterProvider()
+                .getCurrentTabModelFilter()
+                .getRelatedTabList(tabId);
     }
 }
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogCoordinator.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogCoordinator.java
new file mode 100644
index 0000000..5b9ecf11
--- /dev/null
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogCoordinator.java
@@ -0,0 +1,84 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.tasks.tab_management;
+
+import android.content.Context;
+import android.support.annotation.Nullable;
+import android.view.ViewGroup;
+
+import org.chromium.chrome.browser.compositor.CompositorViewHolder;
+import org.chromium.chrome.browser.compositor.layouts.content.TabContentManager;
+import org.chromium.chrome.browser.tab.Tab;
+import org.chromium.chrome.browser.tabmodel.TabCreatorManager;
+import org.chromium.chrome.browser.tabmodel.TabModelSelector;
+import org.chromium.ui.modelutil.PropertyModel;
+
+import java.util.List;
+
+/**
+ * A coordinator for TabGridDialog component. Manages the communication with
+ * {@link TabListCoordinator} as well as the life-cycle of shared component
+ * objects.
+ */
+public class TabGridDialogCoordinator {
+    final static String COMPONENT_NAME = "TabGridDialog";
+    private final Context mContext;
+    private final TabListCoordinator mTabListCoordinator;
+    private final TabGridDialogMediator mMediator;
+    private final PropertyModel mToolbarPropertyModel;
+    private TabGridSheetToolbarCoordinator mToolbarCoordinator;
+    private ViewGroup mParentView;
+    private TabGridDialogParent mParentLayout;
+
+    TabGridDialogCoordinator(Context context, TabModelSelector tabModelSelector,
+            TabContentManager tabContentManager, TabCreatorManager tabCreatorManager,
+            CompositorViewHolder compositorViewHolder,
+            GridTabSwitcherMediator.ResetHandler resetHandler) {
+        mContext = context;
+
+        mToolbarPropertyModel = new PropertyModel(TabGridSheetProperties.ALL_KEYS);
+
+        mTabListCoordinator = new TabListCoordinator(TabListCoordinator.TabListMode.GRID, context,
+                tabModelSelector, tabContentManager::getTabThumbnailWithCallback, null, false, null,
+                null, compositorViewHolder, false, COMPONENT_NAME);
+
+        mMediator = new TabGridDialogMediator(context, this::resetWithListOfTabs,
+                mToolbarPropertyModel, tabModelSelector, tabCreatorManager, resetHandler);
+
+        mParentView = compositorViewHolder;
+
+        mParentLayout = new TabGridDialogParent(context);
+    }
+
+    /**
+     * Destroy any members that needs clean up.
+     */
+    public void destroy() {
+        mTabListCoordinator.destroy();
+        mMediator.destroy();
+    }
+
+    private void updateDialogContent(List<Tab> tabs) {
+        if (tabs != null) {
+            TabListRecyclerView recyclerView = mTabListCoordinator.getContainerView();
+            mToolbarCoordinator = new TabGridSheetToolbarCoordinator(
+                    mContext, recyclerView, mToolbarPropertyModel, mParentView, mParentLayout);
+        } else {
+            if (mToolbarCoordinator != null) {
+                mToolbarCoordinator.destroy();
+            }
+        }
+    }
+
+    TabGridDialogMediator.ResetHandler getResetHandler() {
+        return this::resetWithListOfTabs;
+    }
+
+    public void resetWithListOfTabs(@Nullable List<Tab> tabs) {
+        mTabListCoordinator.resetWithListOfTabs(tabs);
+        updateDialogContent(tabs);
+        mMediator.onReset(tabs == null ? null : tabs.get(0).getId());
+    }
+}
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogMediator.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogMediator.java
new file mode 100644
index 0000000..a777fa501
--- /dev/null
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogMediator.java
@@ -0,0 +1,192 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.tasks.tab_management;
+
+import android.content.Context;
+import android.support.annotation.Nullable;
+import android.view.View;
+
+import org.chromium.base.metrics.RecordUserAction;
+import org.chromium.chrome.browser.UrlConstants;
+import org.chromium.chrome.browser.tab.Tab;
+import org.chromium.chrome.browser.tabmodel.EmptyTabModelObserver;
+import org.chromium.chrome.browser.tabmodel.TabCreatorManager;
+import org.chromium.chrome.browser.tabmodel.TabLaunchType;
+import org.chromium.chrome.browser.tabmodel.TabModelObserver;
+import org.chromium.chrome.browser.tabmodel.TabModelSelector;
+import org.chromium.chrome.browser.tabmodel.TabSelectionType;
+import org.chromium.chrome.browser.widget.ScrimView;
+import org.chromium.content_public.browser.LoadUrlParams;
+import org.chromium.ui.modelutil.PropertyModel;
+
+import java.util.List;
+
+/**
+ * A mediator for the TabGridDialog component, responsible for communicating
+ * with the components' coordinator as well as managing the business logic
+ * for dialog show/hide.
+ */
+public class TabGridDialogMediator {
+    /**
+     * Defines an interface for a {@link TabGridDialogMediator} reset event handler.
+     */
+    interface ResetHandler {
+        /**
+         * Handles a reset event originated from {@link TabGridDialogMediator} and {@link
+         * GridTabSwitcherMediator}.
+         *
+         * @param tabs List of Tabs to reset.
+         */
+        void resetWithListOfTabs(@Nullable List<Tab> tabs);
+    }
+
+    private final Context mContext;
+    private final PropertyModel mModel;
+    private final TabModelSelector mTabModelSelector;
+    private final TabModelObserver mTabModelObserver;
+    private final TabCreatorManager mTabCreatorManager;
+    private final TabGridDialogMediator.ResetHandler mDialogResetHandler;
+    private final GridTabSwitcherMediator.ResetHandler mGridTabSwitcherResetHandler;
+    private int mCurrentTabId = Tab.INVALID_TAB_ID;
+
+    TabGridDialogMediator(Context context, TabGridDialogMediator.ResetHandler dialogResetHandler,
+            PropertyModel model, TabModelSelector tabModelSelector,
+            TabCreatorManager tabCreatorManager,
+            GridTabSwitcherMediator.ResetHandler gridTabSwitcherResetHandler) {
+        mContext = context;
+        mModel = model;
+        mTabModelSelector = tabModelSelector;
+        mTabCreatorManager = tabCreatorManager;
+        mDialogResetHandler = dialogResetHandler;
+        mGridTabSwitcherResetHandler = gridTabSwitcherResetHandler;
+
+        // Register for tab model.
+        mTabModelObserver = new EmptyTabModelObserver() {
+            @Override
+            public void didAddTab(Tab tab, @TabLaunchType int type) {
+                updateDialog();
+                updateGridTabSwitcher();
+            }
+
+            @Override
+            public void tabClosureUndone(Tab tab) {
+                updateDialog();
+                updateGridTabSwitcher();
+            }
+
+            @Override
+            public void didSelectTab(Tab tab, int type, int lastId) {
+                if (type == TabSelectionType.FROM_USER)
+                    mModel.set(TabGridSheetProperties.IS_DIALOG_VISIBLE, false);
+            }
+
+            @Override
+            public void willCloseTab(Tab tab, boolean animate) {
+                updateDialog();
+                updateGridTabSwitcher();
+                List<Tab> relatedTabs = getRelatedTabs(tab.getId());
+                // If current tab is closed and tab group is not empty, hand over ID of the next
+                // tab in the group to mCurrentTabId.
+                if (relatedTabs.size() == 0) return;
+                if (tab.getId() == mCurrentTabId) {
+                    mCurrentTabId = relatedTabs.get(0).getId();
+                }
+            }
+        };
+        mTabModelSelector.getTabModelFilterProvider().addTabModelFilterObserver(mTabModelObserver);
+
+        // Setup toolbar property model.
+        setupToolbarClickHandlers();
+
+        // Setup ScrimView observer.
+        setupScrimViewObserver();
+    }
+
+    void onReset(Integer tabId) {
+        if (tabId != null) {
+            mCurrentTabId = tabId;
+            updateDialog();
+            mModel.set(TabGridSheetProperties.IS_DIALOG_VISIBLE, true);
+        } else {
+            mModel.set(TabGridSheetProperties.IS_DIALOG_VISIBLE, false);
+        }
+    }
+
+    /**
+     * Destroy any members that needs clean up.
+     */
+    public void destroy() {
+        if (mTabModelObserver != null) {
+            mTabModelSelector.getTabModelFilterProvider().removeTabModelFilterObserver(
+                    mTabModelObserver);
+        }
+    }
+
+    private void updateGridTabSwitcher() {
+        mGridTabSwitcherResetHandler.resetWithTabList(
+                mTabModelSelector.getTabModelFilterProvider().getCurrentTabModelFilter());
+    }
+
+    private void updateDialog() {
+        if (mCurrentTabId == Tab.INVALID_TAB_ID) return;
+        List<Tab> relatedTabs = getRelatedTabs(mCurrentTabId);
+        int tabsCount = relatedTabs.size();
+        if (tabsCount == 0) {
+            mDialogResetHandler.resetWithListOfTabs(null);
+            return;
+        }
+        mModel.set(TabGridSheetProperties.HEADER_TITLE,
+                mContext.getResources().getQuantityString(
+                        org.chromium.chrome.R.plurals.bottom_tab_grid_title_placeholder, tabsCount,
+                        tabsCount));
+    }
+
+    private void setupToolbarClickHandlers() {
+        mModel.set(
+                TabGridSheetProperties.COLLAPSE_CLICK_LISTENER, getCollapseButtonClickListener());
+        mModel.set(TabGridSheetProperties.ADD_CLICK_LISTENER, getAddButtonClickListener());
+    }
+
+    private void setupScrimViewObserver() {
+        ScrimView.ScrimObserver scrimObserver = new ScrimView.ScrimObserver() {
+            @Override
+            public void onScrimClick() {
+                mModel.set(TabGridSheetProperties.IS_DIALOG_VISIBLE, false);
+            }
+            @Override
+            public void onScrimVisibilityChanged(boolean visible) {}
+        };
+        mModel.set(TabGridSheetProperties.SCRIMVIEW_OBSERVER, scrimObserver);
+    }
+
+    private View.OnClickListener getCollapseButtonClickListener() {
+        return view -> {
+            RecordUserAction.record("TabGroup.DialogMinimizedFromGrid");
+            mModel.set(TabGridSheetProperties.IS_DIALOG_VISIBLE, false);
+        };
+    }
+
+    private View.OnClickListener getAddButtonClickListener() {
+        return view -> {
+            Tab currentTab = mTabModelSelector.getTabById(mCurrentTabId);
+            List<Tab> relatedTabs = getRelatedTabs(currentTab.getId());
+
+            assert relatedTabs.size() > 0;
+
+            Tab parentTabToAttach = relatedTabs.get(relatedTabs.size() - 1);
+            mTabCreatorManager.getTabCreator(currentTab.isIncognito())
+                    .createNewTab(new LoadUrlParams(UrlConstants.NTP_URL),
+                            TabLaunchType.FROM_CHROME_UI, parentTabToAttach);
+            RecordUserAction.record(
+                    "MobileNewTabOpened." + TabGridDialogCoordinator.COMPONENT_NAME);
+        };
+    }
+
+    private List<Tab> getRelatedTabs(int tabId) {
+        return mTabModelSelector.getTabModelFilterProvider()
+                .getCurrentTabModelFilter()
+                .getRelatedTabList(tabId);
+    }
+}
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogParent.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogParent.java
new file mode 100644
index 0000000..632f0200
--- /dev/null
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogParent.java
@@ -0,0 +1,130 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.tasks.tab_management;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ObjectAnimator;
+import android.animation.ValueAnimator;
+import android.content.Context;
+import android.graphics.Color;
+import android.util.DisplayMetrics;
+import android.view.Gravity;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.WindowManager;
+import android.widget.FrameLayout;
+import android.widget.LinearLayout;
+import android.widget.PopupWindow;
+
+import org.chromium.chrome.browser.widget.ScrimView;
+import org.chromium.chrome.tab_ui.R;
+import org.chromium.ui.interpolators.BakedBezierInterpolator;
+
+/**
+ * Parent for TabGridDialog component.
+ * TODO(yuezhanggg): Add animations of card scales up to dialog and dialog scales down to card when
+ * show/hide dialog.
+ */
+public class TabGridDialogParent {
+    private PopupWindow mPopupWindow;
+    private LinearLayout mDialogContainerView;
+    private ScrimView mScrimView;
+    private ScrimView.ScrimParams mScrimParams;
+    private ValueAnimator mDialogFadeIn;
+    private ValueAnimator mDialogFadeOut;
+    private Animator mCurrentAnimator;
+
+    TabGridDialogParent(Context context) {
+        setUpDialog(context);
+    }
+
+    private void setUpDialog(Context context) {
+        FrameLayout backgroundView = new FrameLayout(context);
+        mDialogContainerView = new LinearLayout(context);
+        FrameLayout.LayoutParams containerParams = new FrameLayout.LayoutParams(
+                ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
+        int sideMargin =
+                (int) context.getResources().getDimension(R.dimen.tab_grid_dialog_side_margin);
+        int topMargin =
+                (int) context.getResources().getDimension(R.dimen.tab_grid_dialog_top_margin);
+        containerParams.setMargins(sideMargin, topMargin, sideMargin, topMargin);
+        mDialogContainerView.setLayoutParams(containerParams);
+        mDialogContainerView.setBackgroundColor(Color.WHITE);
+        mDialogContainerView.setOrientation(LinearLayout.VERTICAL);
+        backgroundView.addView(mDialogContainerView);
+
+        DisplayMetrics displayMetrics = new DisplayMetrics();
+        ((WindowManager) context.getSystemService(Context.WINDOW_SERVICE))
+                .getDefaultDisplay()
+                .getMetrics(displayMetrics);
+        mPopupWindow = new PopupWindow(
+                backgroundView, displayMetrics.widthPixels, displayMetrics.heightPixels);
+        mScrimView = new ScrimView(context, null, backgroundView);
+
+        mDialogFadeIn = ObjectAnimator.ofFloat(mDialogContainerView, View.ALPHA, 0f, 1f);
+        mDialogFadeIn.setInterpolator(BakedBezierInterpolator.FADE_IN_CURVE);
+        mDialogFadeIn.setDuration(TabListRecyclerView.BASE_ANIMATION_DURATION_MS);
+        mDialogFadeIn.addListener(new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                mCurrentAnimator = null;
+            }
+
+            @Override
+            public void onAnimationCancel(Animator animation) {
+                mCurrentAnimator = null;
+            }
+        });
+
+        mDialogFadeOut = ObjectAnimator.ofFloat(mDialogContainerView, View.ALPHA, 1f, 0f);
+        mDialogFadeOut.setInterpolator(BakedBezierInterpolator.FADE_OUT_CURVE);
+        mDialogFadeOut.setDuration(TabListRecyclerView.BASE_ANIMATION_DURATION_MS);
+        mDialogFadeOut.addListener(new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                mPopupWindow.dismiss();
+                mCurrentAnimator = null;
+            }
+
+            @Override
+            public void onAnimationCancel(Animator animation) {
+                mPopupWindow.dismiss();
+                mCurrentAnimator = null;
+            }
+        });
+    }
+
+    void setScrimViewObserver(ScrimView.ScrimObserver scrimViewObserver) {
+        mScrimParams =
+                new ScrimView.ScrimParams(mDialogContainerView, false, true, 0, scrimViewObserver);
+    }
+
+    void updateDialog(View toolbarView, View recyclerView) {
+        mDialogContainerView.removeAllViews();
+        mDialogContainerView.addView(toolbarView);
+        mDialogContainerView.addView(recyclerView);
+        recyclerView.setVisibility(View.VISIBLE);
+    }
+
+    void showDialog(View parent) {
+        if (mCurrentAnimator != null && mCurrentAnimator != mDialogFadeIn) {
+            mCurrentAnimator.cancel();
+        }
+        mPopupWindow.showAtLocation(parent, Gravity.CENTER, 0, 0);
+        mScrimView.showScrim(mScrimParams);
+        mDialogFadeIn.start();
+        mCurrentAnimator = mDialogFadeIn;
+    }
+
+    void hideDialog() {
+        if (mCurrentAnimator != null && mCurrentAnimator != mDialogFadeOut) {
+            mCurrentAnimator.cancel();
+        }
+        mScrimView.hideScrim(true);
+        mDialogFadeOut.start();
+        mCurrentAnimator = mDialogFadeOut;
+    }
+}
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridSheetCoordinator.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridSheetCoordinator.java
index bf65a036..504d183 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridSheetCoordinator.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridSheetCoordinator.java
@@ -44,7 +44,7 @@
 
         mTabGridCoordinator = new TabListCoordinator(TabListCoordinator.TabListMode.GRID, context,
                 tabModelSelector, tabContentManager::getTabThumbnailWithCallback, null, false, null,
-                bottomSheetController.getBottomSheet(), false, COMPONENT_NAME);
+                null, bottomSheetController.getBottomSheet(), false, COMPONENT_NAME);
 
         mMediator = new TabGridSheetMediator(mContext, bottomSheetController,
                 this::resetWithListOfTabs, mToolbarPropertyModel, tabModelSelector,
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridSheetMediator.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridSheetMediator.java
index 36d84f28..a98353a 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridSheetMediator.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridSheetMediator.java
@@ -30,7 +30,7 @@
 import java.util.List;
 
 /**
- * A mediator for the TabGridSheet component, respoonsible for communicating
+ * A mediator for the TabGridSheet component, responsible for communicating
  * with the components' coordinator as well as managing the state of the bottom
  * sheet.
  */
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridSheetProperties.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridSheetProperties.java
index d8897903..6f94201 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridSheetProperties.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridSheetProperties.java
@@ -7,6 +7,7 @@
 import android.content.res.ColorStateList;
 import android.view.View.OnClickListener;
 
+import org.chromium.chrome.browser.widget.ScrimView;
 import org.chromium.ui.modelutil.PropertyKey;
 import org.chromium.ui.modelutil.PropertyModel;
 
@@ -25,7 +26,13 @@
             new PropertyModel.WritableIntPropertyKey();
     public static final PropertyModel.WritableObjectPropertyKey<ColorStateList> TINT =
             new PropertyModel.WritableObjectPropertyKey<>();
+    public static final PropertyModel.WritableBooleanPropertyKey IS_DIALOG_VISIBLE =
+            new PropertyModel.WritableBooleanPropertyKey();
+    public static final PropertyModel
+            .WritableObjectPropertyKey<ScrimView.ScrimObserver> SCRIMVIEW_OBSERVER =
+            new PropertyModel.WritableObjectPropertyKey<>();
 
-    public static final PropertyKey[] ALL_KEYS = new PropertyKey[] {COLLAPSE_CLICK_LISTENER,
-            ADD_CLICK_LISTENER, HEADER_TITLE, CONTENT_TOP_MARGIN, PRIMARY_COLOR, TINT};
+    public static final PropertyKey[] ALL_KEYS =
+            new PropertyKey[] {COLLAPSE_CLICK_LISTENER, ADD_CLICK_LISTENER, HEADER_TITLE,
+                    CONTENT_TOP_MARGIN, PRIMARY_COLOR, TINT, IS_DIALOG_VISIBLE, SCRIMVIEW_OBSERVER};
 }
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridSheetToolbarCoordinator.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridSheetToolbarCoordinator.java
index 557c080..3aada02 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridSheetToolbarCoordinator.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridSheetToolbarCoordinator.java
@@ -33,10 +33,16 @@
      */
     TabGridSheetToolbarCoordinator(
             Context context, ViewGroup contentView, PropertyModel toolbarPropertyModel) {
+        this(context, contentView, toolbarPropertyModel, null, null);
+    }
+
+    TabGridSheetToolbarCoordinator(Context context, ViewGroup contentView,
+            PropertyModel toolbarPropertyModel, ViewGroup parentView, TabGridDialogParent dialog) {
         mToolbarView = (TabGroupUiToolbarView) LayoutInflater.from(context).inflate(
                 R.layout.bottom_tab_grid_toolbar, contentView, false);
         mModelChangeProcessor = PropertyModelChangeProcessor.create(toolbarPropertyModel,
-                new TabGridSheetViewBinder.ViewHolder(mToolbarView, contentView),
+                new TabGridSheetViewBinder.ViewHolder(
+                        mToolbarView, contentView, parentView, dialog),
                 TabGridSheetViewBinder::bind);
     }
 
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridSheetViewBinder.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridSheetViewBinder.java
index 6c39e67d..458f9c8 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridSheetViewBinder.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridSheetViewBinder.java
@@ -8,10 +8,14 @@
 import static org.chromium.chrome.browser.tasks.tab_management.TabGridSheetProperties.COLLAPSE_CLICK_LISTENER;
 import static org.chromium.chrome.browser.tasks.tab_management.TabGridSheetProperties.CONTENT_TOP_MARGIN;
 import static org.chromium.chrome.browser.tasks.tab_management.TabGridSheetProperties.HEADER_TITLE;
+import static org.chromium.chrome.browser.tasks.tab_management.TabGridSheetProperties.IS_DIALOG_VISIBLE;
 import static org.chromium.chrome.browser.tasks.tab_management.TabGridSheetProperties.PRIMARY_COLOR;
+import static org.chromium.chrome.browser.tasks.tab_management.TabGridSheetProperties.SCRIMVIEW_OBSERVER;
 import static org.chromium.chrome.browser.tasks.tab_management.TabGridSheetProperties.TINT;
 
+import android.support.annotation.Nullable;
 import android.view.View;
+import android.view.ViewGroup;
 import android.widget.FrameLayout;
 
 import org.chromium.ui.modelutil.PropertyKey;
@@ -27,10 +31,17 @@
     public static class ViewHolder {
         public final TabGroupUiToolbarView toolbarView;
         public final View contentView;
+        @Nullable
+        public ViewGroup parentView;
+        @Nullable
+        public TabGridDialogParent dialogView;
 
-        ViewHolder(TabGroupUiToolbarView toolbarView, View contentView) {
+        ViewHolder(TabGroupUiToolbarView toolbarView, View contentView,
+                @Nullable ViewGroup parentView, @Nullable TabGridDialogParent dialogView) {
             this.toolbarView = toolbarView;
             this.contentView = contentView;
+            this.parentView = parentView;
+            this.dialogView = dialogView;
         }
     }
 
@@ -56,6 +67,15 @@
             viewHolder.contentView.setBackgroundColor(model.get(PRIMARY_COLOR));
         } else if (TINT == propertyKey) {
             viewHolder.toolbarView.setTint(model.get(TINT));
+        } else if (SCRIMVIEW_OBSERVER == propertyKey) {
+            viewHolder.dialogView.setScrimViewObserver(model.get(SCRIMVIEW_OBSERVER));
+        } else if (IS_DIALOG_VISIBLE == propertyKey) {
+            if (model.get(IS_DIALOG_VISIBLE)) {
+                viewHolder.dialogView.updateDialog(viewHolder.toolbarView, viewHolder.contentView);
+                viewHolder.dialogView.showDialog(viewHolder.parentView);
+            } else {
+                viewHolder.dialogView.hideDialog();
+            }
         }
     }
 }
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupUiCoordinator.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupUiCoordinator.java
index 0ddf740..32bf5b1 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupUiCoordinator.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupUiCoordinator.java
@@ -74,7 +74,7 @@
         TabContentManager tabContentManager = activity.getTabContentManager();
 
         mTabStripCoordinator = new TabListCoordinator(TabListCoordinator.TabListMode.STRIP,
-                mContext, tabModelSelector, null, null, false, null,
+                mContext, tabModelSelector, null, null, false, null, null,
                 mTabStripToolbarCoordinator.getTabListContainerView(), true, COMPONENT_NAME);
 
         mTabGridSheetCoordinator =
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupUiToolbarView.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupUiToolbarView.java
index 87657a0..78f9939 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupUiToolbarView.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupUiToolbarView.java
@@ -50,6 +50,10 @@
         mRightButton.setOnClickListener(listener);
     }
 
+    void setTitleOnClickListener(OnClickListener listener) {
+        mTitleTextView.setOnClickListener(listener);
+    }
+
     ViewGroup getViewContainer() {
         return mContainerView;
     }
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListCoordinator.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListCoordinator.java
index 12fcb47..5824872 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListCoordinator.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListCoordinator.java
@@ -71,6 +71,8 @@
             TabListMediator.ThumbnailProvider thumbnailProvider,
             TabListMediator.TitleProvider titleProvider, boolean closeRelatedTabs,
             @Nullable TabListMediator.CreateGroupButtonProvider createGroupButtonProvider,
+            @Nullable TabListMediator
+                    .GridCardOnClickListenerProvider gridCardOnClickListenerProvider,
             @NonNull ViewGroup parentView, boolean attachToParent, String componentName) {
         TabListModel tabListModel = new TabListModel();
         mMode = mode;
@@ -122,7 +124,7 @@
 
         mMediator = new TabListMediator(tabListModel, tabModelSelector, thumbnailProvider,
                 titleProvider, tabListFaviconProvider, closeRelatedTabs, createGroupButtonProvider,
-                componentName);
+                gridCardOnClickListenerProvider, componentName);
 
         if (mMode == TabListMode.GRID) {
             ItemTouchHelper touchHelper = new ItemTouchHelper(mMediator.getItemTouchHelperCallback(
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListMediator.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListMediator.java
index c72995a..92768311c 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListMediator.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListMediator.java
@@ -120,6 +120,18 @@
         TabActionListener getCreateGroupButtonOnClickListener(Tab tab);
     }
 
+    /**
+     * An interface to get the onClickListener for opening dialog when click on a grid card.
+     */
+    public interface GridCardOnClickListenerProvider {
+        /**
+         * @return {@link TabActionListener} to open tabgrid dialog. If the given {@link Tab} is not
+         * able to create group, return null;
+         */
+        @Nullable
+        TabActionListener getGridCardOnClickListener(Tab tab);
+    }
+
     @IntDef({TabClosedFrom.TAB_STRIP, TabClosedFrom.TAB_GRID_SHEET, TabClosedFrom.GRID_TAB_SWITCHER,
             TabClosedFrom.GRID_TAB_SWITCHER_GROUP})
     @Retention(RetentionPolicy.SOURCE)
@@ -141,6 +153,7 @@
     private final TabActionListener mTabClosedListener;
     private final TitleProvider mTitleProvider;
     private final CreateGroupButtonProvider mCreateGroupButtonProvider;
+    private final GridCardOnClickListenerProvider mGridCardOnClickListenerProvider;
     private final String mComponentName;
     private boolean mCloseAllRelatedTabs;
     private ComponentCallbacks mComponentCallbacks;
@@ -239,7 +252,9 @@
     public TabListMediator(TabListModel model, TabModelSelector tabModelSelector,
             @Nullable ThumbnailProvider thumbnailProvider, @Nullable TitleProvider titleProvider,
             TabListFaviconProvider tabListFaviconProvider, boolean closeRelatedTabs,
-            @Nullable CreateGroupButtonProvider createGroupButtonProvider, String componentName) {
+            @Nullable CreateGroupButtonProvider createGroupButtonProvider,
+            @Nullable GridCardOnClickListenerProvider gridCardOnClickListenerProvider,
+            String componentName) {
         mTabModelSelector = tabModelSelector;
         mThumbnailProvider = thumbnailProvider;
         mModel = model;
@@ -247,6 +262,7 @@
         mComponentName = componentName;
         mTitleProvider = titleProvider != null ? titleProvider : Tab::getTitle;
         mCreateGroupButtonProvider = createGroupButtonProvider;
+        mGridCardOnClickListenerProvider = gridCardOnClickListenerProvider;
         mCloseAllRelatedTabs = closeRelatedTabs;
 
         mTabModelObserver = new EmptyTabModelObserver() {
@@ -502,6 +518,13 @@
         if (mCloseAllRelatedTabs && !mShownIPH) {
             showIPH = getRelatedTabsForId(tab.getId()).size() > 1;
         }
+        TabActionListener tabSelectedListener;
+        if (mGridCardOnClickListenerProvider == null
+                || getRelatedTabsForId(tab.getId()).size() == 1) {
+            tabSelectedListener = mTabSelectedListener;
+        } else {
+            tabSelectedListener = mGridCardOnClickListenerProvider.getGridCardOnClickListener(tab);
+        }
 
         PropertyModel tabInfo =
                 new PropertyModel.Builder(TabProperties.ALL_KEYS_TAB_GRID)
@@ -511,7 +534,7 @@
                                 mTabListFaviconProvider.getDefaultFaviconDrawable())
                         .with(TabProperties.IS_SELECTED, isSelected)
                         .with(TabProperties.IPH_PROVIDER, showIPH ? mIphProvider : null)
-                        .with(TabProperties.TAB_SELECTED_LISTENER, mTabSelectedListener)
+                        .with(TabProperties.TAB_SELECTED_LISTENER, tabSelectedListener)
                         .with(TabProperties.TAB_CLOSED_LISTENER, mTabClosedListener)
                         .with(TabProperties.CREATE_GROUP_LISTENER, createGroupButtonOnClickListener)
                         .with(TabProperties.ALPHA, 1f)
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabManagementModuleImpl.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabManagementModuleImpl.java
index 3c94a78..5879a14 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabManagementModuleImpl.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabManagementModuleImpl.java
@@ -29,7 +29,7 @@
         return new GridTabSwitcherCoordinator(activity, activity.getLifecycleDispatcher(),
                 activity.getToolbarManager(), activity.getTabModelSelector(),
                 activity.getTabContentManager(), activity.getCompositorViewHolder(),
-                activity.getFullscreenManager());
+                activity.getFullscreenManager(), activity);
     }
 
     @Override
diff --git a/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/GridTabSwitcherMediatorUnitTest.java b/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/GridTabSwitcherMediatorUnitTest.java
index 97fb1ae7..f0d4773f 100644
--- a/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/GridTabSwitcherMediatorUnitTest.java
+++ b/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/GridTabSwitcherMediatorUnitTest.java
@@ -156,7 +156,7 @@
         mModel = new PropertyModel(TabListContainerProperties.ALL_KEYS);
         mModel.addObserver(mPropertyObserver);
         mMediator = new GridTabSwitcherMediator(mResetHandler, mModel, mTabModelSelector,
-                mFullscreenManager, mCompositorViewHolder);
+                mFullscreenManager, mCompositorViewHolder, null);
         mMediator.addOverviewModeObserver(mOverviewModeObserver);
     }
 
diff --git a/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabListMediatorUnitTest.java b/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabListMediatorUnitTest.java
index 6bdf32a..ff94d4d 100644
--- a/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabListMediatorUnitTest.java
+++ b/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabListMediatorUnitTest.java
@@ -131,7 +131,7 @@
         mModel = new TabListModel();
         mMediator = new TabListMediator(mModel, mTabModelSelector,
                 mTabContentManager::getTabThumbnailWithCallback, null, mTabListFaviconProvider,
-                false, null, getClass().getSimpleName());
+                false, null, null, getClass().getSimpleName());
     }
 
     @After
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ChromeFeatureList.java b/chrome/android/java/src/org/chromium/chrome/browser/ChromeFeatureList.java
index ed32631..2a64da80 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeFeatureList.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromeFeatureList.java
@@ -313,6 +313,8 @@
     public static final String QUERY_IN_OMNIBOX = "QueryInOmnibox";
     public static final String TAB_ENGAGEMENT_REPORTING_ANDROID = "TabEngagementReportingAndroid";
     public static final String TAB_GROUPS_ANDROID = "TabGroupsAndroid";
+    public static final String TAB_GROUPS_UI_IMPROVEMENTS_ANDROID =
+            "TabGroupsUiImprovementsAndroid";
     public static final String TAB_GRID_LAYOUT_ANDROID = "TabGridLayoutAndroid";
     public static final String TAB_PERSISTENT_STORE_TASK_RUNNER = "TabPersistentStoreTaskRunner";
     public static final String TAB_REPARENTING = "TabReparenting";
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/preferences/ChromePreferenceManager.java b/chrome/android/java/src/org/chromium/chrome/browser/preferences/ChromePreferenceManager.java
index 24f6322..0313ce6 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/preferences/ChromePreferenceManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/preferences/ChromePreferenceManager.java
@@ -316,6 +316,13 @@
     public static final String TAB_GROUPS_ANDROID_ENABLED_KEY = "tab_group_android_enabled";
 
     /**
+     * Whether or not the tab group UI improvement is enabled.
+     * Default value is false.
+     */
+    public static final String TAB_GROUPS_UI_IMPROVEMENTS_ANDROID_ENABLED_KEY =
+            "tab_group_ui_improvements_android_enabled";
+
+    /**
      * Key for whether PrefetchBackgroundTask should load native in service manager only mode.
      * Default value is false.
      */
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/util/FeatureUtilities.java b/chrome/android/java/src/org/chromium/chrome/browser/util/FeatureUtilities.java
index 477b1887..67e707d 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/util/FeatureUtilities.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/util/FeatureUtilities.java
@@ -83,6 +83,7 @@
     private static Boolean sShouldPrioritizeBootstrapTasks;
     private static Boolean sIsGridTabSwitcherEnabled;
     private static Boolean sIsTabGroupsAndroidEnabled;
+    private static Boolean sIsTabGroupUiImprovementsAndroidEnabled;
     private static Boolean sFeedEnabled;
     private static Boolean sServiceManagerForBackgroundPrefetch;
     private static Boolean sIsNetworkServiceWarmUpEnabled;
@@ -213,6 +214,7 @@
 
         if (isHighEndPhone()) cacheGridTabSwitcherEnabled();
         if (isHighEndPhone()) cacheTabGroupsAndroidEnabled();
+        if (isHighEndPhone()) cacheTabGroupsAndroidUiImprovementsEnabled();
 
         // Propagate REACHED_CODE_PROFILER feature value to LibraryLoader. This can't be done in
         // LibraryLoader itself because it lives in //base and can't depend on ChromeFeatureList.
@@ -639,6 +641,34 @@
                         ContextUtils.getApplicationContext());
     }
 
+    private static void cacheTabGroupsAndroidUiImprovementsEnabled() {
+        ChromePreferenceManager.getInstance().writeBoolean(
+                ChromePreferenceManager.TAB_GROUPS_UI_IMPROVEMENTS_ANDROID_ENABLED_KEY,
+                !DeviceClassManager.enableAccessibilityLayout()
+                        && (ChromeFeatureList.isEnabled(
+                                    ChromeFeatureList.DOWNLOAD_TAB_MANAGEMENT_MODULE)
+                                || ChromeFeatureList.isEnabled(
+                                        ChromeFeatureList.TAB_GROUPS_UI_IMPROVEMENTS_ANDROID))
+                        && TabManagementModuleProvider.getTabManagementModule() != null
+                        && ChromeFeatureList.isEnabled(
+                                ChromeFeatureList.TAB_GROUPS_UI_IMPROVEMENTS_ANDROID));
+    }
+
+    /**
+     * @return Whether the tab group ui improvement feature is enabled and available for use.
+     */
+    public static boolean isTabGroupsAndroidUiImprovementsEnabled() {
+        if (!isTabGroupsAndroidEnabled()) return false;
+
+        if (sIsTabGroupUiImprovementsAndroidEnabled == null) {
+            ChromePreferenceManager preferenceManager = ChromePreferenceManager.getInstance();
+
+            sIsTabGroupUiImprovementsAndroidEnabled = preferenceManager.readBoolean(
+                    ChromePreferenceManager.TAB_GROUPS_UI_IMPROVEMENTS_ANDROID_ENABLED_KEY, false);
+        }
+        return sIsTabGroupUiImprovementsAndroidEnabled;
+    }
+
     /**
      * @return Whether this device is running Android Go. This is assumed when we're running Android
      * O or later and we're on a low-end device.
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index ec1d9ac..e727a763 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -3074,6 +3074,11 @@
      flag_descriptions::kTabGroupsAndroidDescription, kOsAndroid,
      FEATURE_VALUE_TYPE(chrome::android::kTabGroupsAndroid)},
 
+    {"enable-tab-groups-ui-improvements",
+     flag_descriptions::kTabGroupsUiImprovementsAndroidName,
+     flag_descriptions::kTabGroupsUiImprovementsAndroidDescription, kOsAndroid,
+     FEATURE_VALUE_TYPE(chrome::android::kTabGroupsUiImprovementsAndroid)},
+
     {"enable-tab-switcher-on-return",
      flag_descriptions::kTabSwitcherOnReturnName,
      flag_descriptions::kTabSwitcherOnReturnDescription, kOsAndroid,
diff --git a/chrome/browser/android/chrome_feature_list.cc b/chrome/browser/android/chrome_feature_list.cc
index c4269ed..5c84557 100644
--- a/chrome/browser/android/chrome_feature_list.cc
+++ b/chrome/browser/android/chrome_feature_list.cc
@@ -170,6 +170,7 @@
     &kSpecialUserDecision,
     &kTabEngagementReportingAndroid,
     &kTabGroupsAndroid,
+    &kTabGroupsUiImprovementsAndroid,
     &kTabGridLayoutAndroid,
     &kTabPersistentStoreTaskRunner,
     &kTabReparenting,
@@ -499,6 +500,9 @@
 const base::Feature kTabGroupsAndroid{"TabGroupsAndroid",
                                       base::FEATURE_DISABLED_BY_DEFAULT};
 
+const base::Feature kTabGroupsUiImprovementsAndroid{
+    "TabGroupsUiImprovementsAndroid", base::FEATURE_DISABLED_BY_DEFAULT};
+
 const base::Feature kTabGridLayoutAndroid{"TabGridLayoutAndroid",
                                           base::FEATURE_DISABLED_BY_DEFAULT};
 
diff --git a/chrome/browser/android/chrome_feature_list.h b/chrome/browser/android/chrome_feature_list.h
index 4296287..0d6c4d1 100644
--- a/chrome/browser/android/chrome_feature_list.h
+++ b/chrome/browser/android/chrome_feature_list.h
@@ -101,6 +101,7 @@
 extern const base::Feature kSpecialUserDecision;
 extern const base::Feature kTabEngagementReportingAndroid;
 extern const base::Feature kTabGroupsAndroid;
+extern const base::Feature kTabGroupsUiImprovementsAndroid;
 extern const base::Feature kTabGridLayoutAndroid;
 extern const base::Feature kTabPersistentStoreTaskRunner;
 extern const base::Feature kTabReparenting;
diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json
index d77e410..c19ce9b 100644
--- a/chrome/browser/flag-metadata.json
+++ b/chrome/browser/flag-metadata.json
@@ -1716,6 +1716,11 @@
     "expiry_milestone": 80
   },
   {
+    "name": "enable-tab-groups-ui-improvements",
+    "owners": [ "[email protected]" ],
+    "expiry_milestone": 80
+  },
+  {
     "name": "enable-tab-switcher-on-return",
     "owners": [ "[email protected]" ],
     "expiry_milestone": 76
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc
index 4026c19d..e935e0f 100644
--- a/chrome/browser/flag_descriptions.cc
+++ b/chrome/browser/flag_descriptions.cc
@@ -1851,6 +1851,10 @@
 const char kTabGroupsAndroidDescription[] =
     "Allows users to create groups to better organize their tabs.";
 
+const char kTabGroupsUiImprovementsAndroidName[] = "Tab Groups UI Improvements";
+const char kTabGroupsUiImprovementsAndroidDescription[] =
+    "Allows users to access new features in Tab Group UI.";
+
 const char kTabGroupsName[] = "Tab Groups";
 const char kTabGroupsDescription[] =
     "Allows users to organize tabs into visually distinct groups, e.g. to "
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h
index 032284e5..8450a5b7 100644
--- a/chrome/browser/flag_descriptions.h
+++ b/chrome/browser/flag_descriptions.h
@@ -1104,6 +1104,9 @@
 extern const char kTabGroupsAndroidName[];
 extern const char kTabGroupsAndroidDescription[];
 
+extern const char kTabGroupsUiImprovementsAndroidName[];
+extern const char kTabGroupsUiImprovementsAndroidDescription[];
+
 extern const char kTabGroupsName[];
 extern const char kTabGroupsDescription[];
 
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index 48263e8..ec44b9e 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -32950,6 +32950,7 @@
   <int value="-1160941363" label="AffiliationBasedMatching:disabled"/>
   <int value="-1160026273" label="enable-web-notification-custom-layouts"/>
   <int value="-1159563774" label="enable-accessibility-script-injection"/>
+  <int value="-1159369873" label="TabGroupsUiImprovementsAndroid:disabled"/>
   <int value="-1158993534" label="PrintScaling:enabled"/>
   <int value="-1156179600" label="OmniboxRichEntitySuggestions:enabled"/>
   <int value="-1155543191" label="CopylessPaste:disabled"/>
@@ -33307,6 +33308,7 @@
   <int value="-612480090" label="FasterLocationReload:enabled"/>
   <int value="-610411643" label="enable-printer-app-search"/>
   <int value="-606898702" label="MaterialDesignSettings:disabled"/>
+  <int value="-606696801" label="TabGroupsUiImprovementsAndroid:enabled"/>
   <int value="-606431158" label="DrawVerticallyEdgeToEdge:enabled"/>
   <int value="-604814313" label="enable-pinch"/>
   <int value="-604269405"