chromium/chrome/browser/incognito/android/java/src/org/chromium/chrome/browser/incognito/reauth/IncognitoReauthCoordinatorFactory.java

// Copyright 2022 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.incognito.reauth;

import android.content.Context;
import android.content.Intent;

import androidx.activity.OnBackPressedCallback;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;

import org.chromium.base.supplier.OneshotSupplier;
import org.chromium.base.supplier.OneshotSupplierImpl;
import org.chromium.chrome.browser.hub.HubManager;
import org.chromium.chrome.browser.hub.PaneId;
import org.chromium.chrome.browser.incognito.reauth.IncognitoReauthManager.IncognitoReauthCallback;
import org.chromium.chrome.browser.layouts.LayoutManager;
import org.chromium.chrome.browser.layouts.LayoutType;
import org.chromium.chrome.browser.tab_ui.TabSwitcherCustomViewManager;
import org.chromium.chrome.browser.tabmodel.IncognitoTabHostUtils;
import org.chromium.chrome.browser.tabmodel.TabModelSelector;
import org.chromium.ui.modaldialog.ModalDialogManager;

/** A factory to create an {@link IncognitoReauthCoordinator} instance. */
public class IncognitoReauthCoordinatorFactory {
    /** The context to use in order to inflate the re-auth view and fetch the resources. */
    private final @NonNull Context mContext;

    /**
     * The {@link TabModelSelector} instance used for providing functionality to toggle between tab
     * models and to close Incognito tabs.
     */
    private final @NonNull TabModelSelector mTabModelSelector;

    /** The manager responsible for showing the full screen Incognito re-auth dialog. */
    private final @NonNull ModalDialogManager mModalDialogManager;

    /** The manager responsible for invoking the underlying native re-authentication. */
    private final @NonNull IncognitoReauthManager mIncognitoReauthManager;

    /**
     * A boolean to distinguish between tabbedActivity or CCT, during coordinator creation.
     *
     * <p>TODO(crbug.com/40056462): Remove the need for this and instead populate the various {@link
     * Runnable} that we create here at the client site directly.
     */
    private final boolean mIsTabbedActivity;

    /**
     * This allows to pass the re-auth view to the tab switcher. Non-null contents for {@link
     * TabSwitcherIncognitoReauthCoordinator}.
     */
    private final OneshotSupplierImpl<TabSwitcherCustomViewManager>
            mTabSwitcherCustomViewManagerSupplier = new OneshotSupplierImpl<>();

    /**
     * This allows to show the regular overview mode.
     * Non-null for {@link FullScreenIncognitoReauthCoordinator}.
     */
    private @Nullable LayoutManager mLayoutManager;

    /** Supplier for the HubManager. Non-null for tabbed mode. */
    private final @Nullable OneshotSupplier<HubManager> mHubManagerSupplier;

    /** An {@link Intent} which allows to opens regular overview mode from a non-tabbed Activity. */
    private @Nullable Intent mShowRegularOverviewIntent;

    /** A test-only variable used to mock the menu delegate instead of creating one. */
    @VisibleForTesting IncognitoReauthMenuDelegate mIncognitoReauthMenuDelegateForTesting;

    /**
     * @param context The {@link Context} to use for fetching the re-auth resources.
     * @param tabModelSelector The {@link TabModelSelector} to use for toggling between regular and
     *     incognito model and to close all Incognito tabs.
     * @param modalDialogManager The {@link ModalDialogManager} to use for firing the dialog
     *     containing the Incognito re-auth view.
     * @param incognitoReauthManager The {@link IncognitoReauthManager} instance which would be used
     *     to initiate re-authentication.
     * @param layoutManager {@link LayoutManager} to use for showing the regular overview mode.
     * @param hubManagerSupplier The supplier of the {@link HubManager}.
     * @param showRegularOverviewIntent An {@link Intent} to show the regular overview mode.
     * @param isTabbedActivity A boolean to indicate if the re-auth screen being fired from
     */
    public IncognitoReauthCoordinatorFactory(
            @NonNull Context context,
            @NonNull TabModelSelector tabModelSelector,
            @NonNull ModalDialogManager modalDialogManager,
            @NonNull IncognitoReauthManager incognitoReauthManager,
            @Nullable LayoutManager layoutManager,
            @Nullable OneshotSupplier<HubManager> hubManagerSupplier,
            @Nullable Intent showRegularOverviewIntent,
            boolean isTabbedActivity) {
        mContext = context;
        mTabModelSelector = tabModelSelector;
        mModalDialogManager = modalDialogManager;
        mIncognitoReauthManager = incognitoReauthManager;
        mLayoutManager = layoutManager;
        mHubManagerSupplier = hubManagerSupplier;
        mShowRegularOverviewIntent = showRegularOverviewIntent;
        mIsTabbedActivity = isTabbedActivity;

        assert isTabbedActivity || mShowRegularOverviewIntent != null
                : "A valid intent is required to be able to"
                        + " open regular overview mode from inside non-tabbed Activity.";
    }

    /**
     * @param tabSwitcherCustomViewManager {@link TabSwitcherCustomViewManager} to use for
     *         communicating with tab switcher to show the re-auth screen.
     */
    public void setTabSwitcherCustomViewManager(
            @NonNull TabSwitcherCustomViewManager tabSwitcherCustomViewManager) {
        mTabSwitcherCustomViewManagerSupplier.set(tabSwitcherCustomViewManager);
    }

    /**
     * Returns the {@link OneshotSupplier<TabSwitcherCustomViewManager>} that is used to pass the
     * re-auth screen to tab switcher.
     */
    public OneshotSupplier<TabSwitcherCustomViewManager> getTabSwitcherCustomViewManagerSupplier() {
        return mTabSwitcherCustomViewManagerSupplier;
    }

    /**
     * This method is responsible for clean-up work. Typically, called when the Activity is being
     * destroyed.
     */
    void destroy() {
        mIncognitoReauthManager.destroy();
    }

    private IncognitoReauthMenuDelegate getIncognitoReauthMenuDelegate() {
        if (mIncognitoReauthMenuDelegateForTesting != null) {
            return mIncognitoReauthMenuDelegateForTesting;
        }

        return new IncognitoReauthMenuDelegate(mContext, getCloseAllIncognitoTabsRunnable());
    }

    /**
     * @return {@link Runnable} to use when the user click on "See other tabs".
     */
    @VisibleForTesting
    Runnable getSeeOtherTabsRunnable() {
        if (mIsTabbedActivity) {
            return () -> {
                mTabModelSelector.selectModel(/* incognito= */ false);
                if (mLayoutManager.isLayoutVisible(LayoutType.TAB_SWITCHER)) {
                    mHubManagerSupplier.get().getPaneManager().focusPane(PaneId.TAB_SWITCHER);
                    return;
                }
                mLayoutManager.showLayout(LayoutType.TAB_SWITCHER, /* animate= */ false);
            };
        } else {
            return () -> mContext.startActivity(mShowRegularOverviewIntent);
        }
    }

    /**
     * @return {@link Runnable} to use when the user clicks on "Close all Incognito tabs" option.
     */
    @VisibleForTesting
    Runnable getCloseAllIncognitoTabsRunnable() {
        return IncognitoTabHostUtils::closeAllIncognitoTabs;
    }

    /**
     * @return {@link Runnable} to use when the user presses back while the re-auth is being shown.
     */
    Runnable getBackPressRunnable() {
        // Both "See other tabs" and back-press shares the same logic.
        return getSeeOtherTabsRunnable();
    }

    /**
     * @return True, if the re-auth is being triggered from a tabbed Activity, false otherwise.
     */
    boolean getIsTabbedActivity() {
        return mIsTabbedActivity;
    }

    /**
     * @param showFullScreen The type of dependency to check for.
     * @return whether the factory has all the dependencies ready to produce the requested type of
     *     coordinator.
     */
    boolean areDependenciesReadyFor(boolean showFullScreen) {
        return showFullScreen || mTabSwitcherCustomViewManagerSupplier.hasValue();
    }

    /**
     * @param incognitoReauthCallback The {@link IncognitoReauthCallback}
     *                               which would be executed after an authentication attempt.
     * @param showFullScreen A boolean indicating whether to show a fullscreen or tab switcher
     *                      Incognito reauth.
     *
     * @param backPressedCallback {@link OnBackPressedCallback} which would be called when a user
     *         presses back while the fullscreen re-auth is shown.
     *
     * @return {@link IncognitoReauthCoordinator} instance spliced on fullscreen/tab-switcher and
     * tabbedActivity/CCT.
     */
    IncognitoReauthCoordinator createIncognitoReauthCoordinator(
            @NonNull IncognitoReauthCallback incognitoReauthCallback,
            boolean showFullScreen,
            @NonNull OnBackPressedCallback backPressedCallback) {
        assert areDependenciesReadyFor(showFullScreen)
                : "Dependencies for the IncognitoReauthCoordinator were not ready.";
        return (showFullScreen)
                ? new FullScreenIncognitoReauthCoordinator(
                        mContext,
                        mIncognitoReauthManager,
                        incognitoReauthCallback,
                        getSeeOtherTabsRunnable(),
                        mModalDialogManager,
                        getIncognitoReauthMenuDelegate(),
                        backPressedCallback)
                : new TabSwitcherIncognitoReauthCoordinator(
                        mContext,
                        mIncognitoReauthManager,
                        incognitoReauthCallback,
                        getSeeOtherTabsRunnable(),
                        getBackPressRunnable(),
                        mTabSwitcherCustomViewManagerSupplier.get());
    }
}