chromium/chrome/android/java/src/org/chromium/chrome/browser/hub/HubProvider.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.hub;

import android.content.Context;

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

import org.chromium.base.Callback;
import org.chromium.base.CallbackController;
import org.chromium.base.metrics.RecordHistogram;
import org.chromium.base.supplier.LazyOneshotSupplier;
import org.chromium.base.supplier.ObservableSupplier;
import org.chromium.base.supplier.OneshotSupplier;
import org.chromium.base.supplier.Supplier;
import org.chromium.chrome.browser.back_press.BackPressManager;
import org.chromium.chrome.browser.profiles.ProfileProvider;
import org.chromium.chrome.browser.tab.Tab;
import org.chromium.chrome.browser.tabmodel.TabModelSelector;
import org.chromium.chrome.browser.toolbar.menu_button.MenuButtonCoordinator;
import org.chromium.chrome.browser.ui.messages.snackbar.SnackbarManager;
import org.chromium.components.browser_ui.widget.MenuOrKeyboardActionController;

/**
 * Main entrypoint for providing core Hub objects to Chrome.
 *
 * <p>Part of chrome/android/ to use {@link HubManagerFactory} and to use as glue code.
 */
public class HubProvider {
    private final @NonNull LazyOneshotSupplier<HubManager> mHubManagerSupplier;
    private final @NonNull PaneListBuilder mPaneListBuilder;
    private final @NonNull Supplier<TabModelSelector> mTabModelSelectorSupplier;
    private final @NonNull Callback<Pane> mOnPaneFocused;
    private final @NonNull HubShowPaneHelper mHubShowPaneHelper;

    private @Nullable CallbackController mCallbackController = new CallbackController();
    private @Nullable HubTabSwitcherMetricsRecorder mHubTabSwitcherMetricsRecorder;

    /**
     * @param context The Android {@link Context} for the Hub.
     * @param profileProviderSupplier Used to fetch dependencies.
     * @param orderController The {@link PaneOrderController} for the Hub.
     * @param backPressManager The {@link BackPressManager} for the activity.
     * @param menuOrKeyboardActionController The {@link MenuOrKeyboardActionController} for the
     *     activity.
     * @param snackbarManagerSupplier The supplier of the primary {@link SnackbarManager} for the
     *     activity.
     * @param tabModelSelectorSupplier The supplier of the {@link TabModelSelector}.
     * @param menuButtonCoordinatorSupplier A supplier for the root component for the app menu.
     */
    public HubProvider(
            @NonNull Context context,
            @NonNull OneshotSupplier<ProfileProvider> profileProviderSupplier,
            @NonNull PaneOrderController orderController,
            @NonNull BackPressManager backPressManager,
            @NonNull MenuOrKeyboardActionController menuOrKeyboardActionController,
            @NonNull Supplier<SnackbarManager> snackbarManagerSupplier,
            @NonNull Supplier<TabModelSelector> tabModelSelectorSupplier,
            @NonNull Supplier<MenuButtonCoordinator> menuButtonCoordinatorSupplier) {
        mPaneListBuilder = new PaneListBuilder(orderController);
        mTabModelSelectorSupplier = tabModelSelectorSupplier;
        mHubShowPaneHelper = new HubShowPaneHelper();
        mHubManagerSupplier =
                LazyOneshotSupplier.fromSupplier(
                        () -> {
                            assert tabModelSelectorSupplier.hasValue();
                            ObservableSupplier<Tab> tabSupplier =
                                    tabModelSelectorSupplier.get().getCurrentTabSupplier();
                            assert menuButtonCoordinatorSupplier.hasValue();

                            SnackbarManager snackbarManager = snackbarManagerSupplier.get();
                            assert snackbarManager != null;
                            return HubManagerFactory.createHubManager(
                                    context,
                                    profileProviderSupplier,
                                    mPaneListBuilder,
                                    backPressManager,
                                    menuOrKeyboardActionController,
                                    snackbarManager,
                                    tabSupplier,
                                    menuButtonCoordinatorSupplier.get(),
                                    mHubShowPaneHelper);
                        });

        mOnPaneFocused =
                pane -> {
                    boolean isIncognito = pane.getPaneId() == PaneId.INCOGNITO_TAB_SWITCHER;
                    TabModelSelector selector = tabModelSelectorSupplier.get();
                    if (selector.isIncognitoSelected() == isIncognito) return;

                    selector.commitAllTabClosures();
                    selector.selectModel(isIncognito);
                    if (isIncognito) {
                        Integer tabCount = selector.getCurrentModelTabCountSupplier().get();
                        RecordHistogram.recordBooleanHistogram(
                                "Android.TabSwitcher.IncognitoClickedIsEmpty",
                                tabCount == null ? true : tabCount.intValue() == 0);
                    }
                };
        mHubManagerSupplier.onAvailable(
                mCallbackController.makeCancelable(this::onHubManagerAvailable));
    }

    /** Destroys the {@link HubManager} it cannot be used again. */
    public void destroy() {
        if (mCallbackController != null) {
            mCallbackController.destroy();
            mCallbackController = null;
        }

        if (mHubTabSwitcherMetricsRecorder != null) {
            mHubTabSwitcherMetricsRecorder.destroy();
            mHubTabSwitcherMetricsRecorder = null;
        }

        if (mHubManagerSupplier.hasValue()) {
            HubManager hubManager = mHubManagerSupplier.get();
            hubManager.getPaneManager().getFocusedPaneSupplier().removeObserver(mOnPaneFocused);
            hubManager.destroy();
        }
    }

    /** Returns the lazy supplier for {@link HubManager}. */
    public @NonNull LazyOneshotSupplier<HubManager> getHubManagerSupplier() {
        return mHubManagerSupplier;
    }

    /**
     * Returns the {@link PaneListBuilder} for registering Hub {@link Pane}s. Registering a pane
     * throws an {@link IllegalStateException} once {@code #get()} is invoked on the result of
     * {@link #getHubManagerSupplier()}.
     */
    public @NonNull PaneListBuilder getPaneListBuilder() {
        return mPaneListBuilder;
    }

    /**
     * Returns the {@link HubShowPaneHelper} used to select a pane to before opening the {@link
     * HubLayout}.
     */
    public @NonNull HubShowPaneHelper getHubShowPaneHelper() {
        return mHubShowPaneHelper;
    }

    private void onHubManagerAvailable(@NonNull HubManager hubManager) {
        var focusedPaneSupplier = hubManager.getPaneManager().getFocusedPaneSupplier();
        focusedPaneSupplier.addObserver(mOnPaneFocused);
        mHubTabSwitcherMetricsRecorder =
                new HubTabSwitcherMetricsRecorder(
                        mTabModelSelectorSupplier.get(),
                        hubManager.getHubVisibilitySupplier(),
                        focusedPaneSupplier);
    }
}