chromium/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListEditorManager.java

// Copyright 2023 The Chromium Authors
// 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.app.Activity;
import android.view.ViewGroup;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

import org.chromium.base.supplier.ObservableSupplier;
import org.chromium.base.supplier.ObservableSupplierImpl;
import org.chromium.chrome.browser.browser_controls.BrowserControlsStateProvider;
import org.chromium.chrome.browser.tab_ui.TabContentManager;
import org.chromium.chrome.browser.tab_ui.TabSwitcher;
import org.chromium.chrome.browser.tabmodel.TabModelFilter;
import org.chromium.chrome.browser.tabmodel.TabModelUtils;
import org.chromium.chrome.browser.tasks.tab_management.TabListCoordinator.TabListMode;
import org.chromium.chrome.browser.tasks.tab_management.TabListEditorAction.ButtonType;
import org.chromium.chrome.browser.tasks.tab_management.TabListEditorAction.IconPosition;
import org.chromium.chrome.browser.tasks.tab_management.TabListEditorAction.ShowMode;
import org.chromium.chrome.browser.tasks.tab_management.TabListEditorCoordinator.TabListEditorController;
import org.chromium.chrome.browser.tasks.tab_management.TabUiMetricsHelper.TabListEditorOpenMetricGroups;
import org.chromium.chrome.browser.tinker_tank.TinkerTankDelegateImpl;
import org.chromium.chrome.browser.ui.messages.snackbar.SnackbarManager;
import org.chromium.components.browser_ui.bottomsheet.BottomSheetController;
import org.chromium.ui.modaldialog.ModalDialogManager;

import java.util.ArrayList;
import java.util.List;

/**
 * Manages the {@link TabListEditorCoordinator} and related components for a {@link TabSwitcher}.
 */
public class TabListEditorManager {
    private final @NonNull Activity mActivity;
    private final @NonNull ModalDialogManager mModalDialogManager;
    private final @NonNull ViewGroup mCoordinatorView;
    private final @NonNull ViewGroup mRootView;
    private final @Nullable SnackbarManager mSnackbarManager;
    private final @Nullable BottomSheetController mBottomSheetController;
    private final @NonNull BrowserControlsStateProvider mBrowserControlsStateProvider;
    private final @NonNull ObservableSupplier<TabModelFilter> mCurrentTabModelFilterSupplier;
    private final @NonNull TabContentManager mTabContentManager;
    private final @NonNull TabListCoordinator mTabListCoordinator;
    private final @TabListMode int mMode;
    private final @NonNull ObservableSupplierImpl<TabListEditorController> mControllerSupplier =
            new ObservableSupplierImpl<>();
    private final TabGroupCreationDialogManager mTabGroupCreationDialogManager;

    private @Nullable TabListEditorCoordinator mTabListEditorCoordinator;
    private @Nullable List<TabListEditorAction> mTabListEditorActions;

    /**
     * @param activity The current activity.
     * @param modalDialogManager The modal dialog manager for the activity.
     * @param coordinatorView The overlay view to attach the editor to.
     * @param rootView The root view to attach the snackbar to.
     * @param browserControlsStateProvider The browser controls state provider.
     * @param currentTabModelFilterSupplier The supplier of the current {@link TabModelFilter}.
     * @param tabContentManager The {@link TabContentManager} for thumbnails.
     * @param tabListCoordinator The parent {@link TabListCoordinator}.
     * @param mode The {@link TabListMode} of the tab list (grid, list, etc.).
     * @param onTabGroupCreation Should be run when the UI is used to create a tab group.
     */
    public TabListEditorManager(
            @NonNull Activity activity,
            @NonNull ModalDialogManager modalDialogManager,
            @NonNull ViewGroup coordinatorView,
            @NonNull ViewGroup rootView,
            @NonNull BrowserControlsStateProvider browserControlsStateProvider,
            @NonNull ObservableSupplier<TabModelFilter> currentTabModelFilterSupplier,
            @NonNull TabContentManager tabContentManager,
            @NonNull TabListCoordinator tabListCoordinator,
            BottomSheetController bottomSheetController,
            @TabListMode int mode,
            @Nullable Runnable onTabGroupCreation) {
        mActivity = activity;
        mModalDialogManager = modalDialogManager;
        mCoordinatorView = coordinatorView;
        mRootView = rootView;
        mCurrentTabModelFilterSupplier = currentTabModelFilterSupplier;
        mBrowserControlsStateProvider = browserControlsStateProvider;
        mTabContentManager = tabContentManager;
        mTabListCoordinator = tabListCoordinator;
        mBottomSheetController = bottomSheetController;
        mMode = mode;
        mTabGroupCreationDialogManager =
                new TabGroupCreationDialogManager(activity, modalDialogManager, onTabGroupCreation);

        // The snackbarManager used by mTabListEditorCoordinator. The rootView is the default
        // default parent view of the snackbar. When shown this will be re-parented inside the
        // TabListCoordinator's SelectableListLayout.
        if (!activity.isDestroyed() && !activity.isFinishing()) {
            mSnackbarManager = new SnackbarManager(activity, rootView, null);
        } else {
            mSnackbarManager = null;
        }
    }

    /** Destroys the tab list editor. */
    public void destroy() {
        if (mTabListEditorCoordinator != null) {
            mTabListEditorCoordinator.destroy();
        }
    }

    /** Initializes the tab list editor. */
    public void initTabListEditor() {
        // TODO(crbug.com/40945154): Permit a method of switching between selectable and closable
        // modes (or create separate instances).
        if (mTabListEditorCoordinator == null) {
            assert mSnackbarManager != null
                    : "SnackbarManager should have been created or the activity was already"
                            + " finishing.";
            mTabListEditorCoordinator =
                    new TabListEditorCoordinator(
                            mActivity,
                            mCoordinatorView,
                            mCoordinatorView,
                            mBrowserControlsStateProvider,
                            mCurrentTabModelFilterSupplier,
                            mTabContentManager,
                            mTabListCoordinator::setRecyclerViewPosition,
                            mMode,
                            /* displayGroups= */ true,
                            mSnackbarManager,
                            mBottomSheetController,
                            TabProperties.TabActionState.SELECTABLE,
                            /* gridCardOnClickListenerProvider= */ null,
                            mModalDialogManager);
            mControllerSupplier.set(mTabListEditorCoordinator.getController());
        }
    }

    /** Shows the tab list editor with the default list of actions. */
    public void showTabListEditor() {
        initTabListEditor();
        if (mTabListEditorActions == null) {
            mTabListEditorActions = new ArrayList<>();
            mTabListEditorActions.add(
                    TabListEditorSelectionAction.createAction(
                            mActivity,
                            ShowMode.MENU_ONLY,
                            ButtonType.ICON_AND_TEXT,
                            IconPosition.END));
            mTabListEditorActions.add(
                    TabListEditorCloseAction.createAction(
                            mActivity,
                            ShowMode.MENU_ONLY,
                            ButtonType.ICON_AND_TEXT,
                            IconPosition.START,
                            /* actionConfirmationManager= */ null));
            mTabListEditorActions.add(
                    TabListEditorGroupAction.createAction(
                            mActivity,
                            mTabGroupCreationDialogManager,
                            ShowMode.MENU_ONLY,
                            ButtonType.ICON_AND_TEXT,
                            IconPosition.START));
            mTabListEditorActions.add(
                    TabListEditorBookmarkAction.createAction(
                            mActivity,
                            ShowMode.MENU_ONLY,
                            ButtonType.ICON_AND_TEXT,
                            IconPosition.START));
            if (TinkerTankDelegateImpl.enabled()) {
                mTabListEditorActions.add(
                        TabListEditorTinkerTankAction.createAction(
                                mActivity,
                                ShowMode.MENU_ONLY,
                                ButtonType.ICON_AND_TEXT,
                                IconPosition.START));
            }
            mTabListEditorActions.add(
                    TabListEditorShareAction.createAction(
                            mActivity,
                            ShowMode.MENU_ONLY,
                            ButtonType.ICON_AND_TEXT,
                            IconPosition.START));
        }

        var controller = mControllerSupplier.get();
        controller.show(
                TabModelUtils.convertTabListToListOfTabs(mCurrentTabModelFilterSupplier.get()),
                mTabListCoordinator.getRecyclerViewPosition());
        controller.configureToolbarWithMenuItems(mTabListEditorActions);

        TabUiMetricsHelper.recordSelectionEditorOpenMetrics(
                TabListEditorOpenMetricGroups.OPEN_FROM_GRID, mActivity);
    }

    /** Returns a supplier for {@link TabListEditorController}. */
    public ObservableSupplier<TabListEditorController> getControllerSupplier() {
        return mControllerSupplier;
    }
}