chromium/chrome/android/java/src/org/chromium/chrome/browser/toolbar/AppThemeColorProvider.java

// Copyright 2018 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.toolbar;

import android.content.Context;
import android.content.res.ColorStateList;

import androidx.annotation.Nullable;

import org.chromium.chrome.browser.layouts.LayoutStateProvider;
import org.chromium.chrome.browser.layouts.LayoutType;
import org.chromium.chrome.browser.lifecycle.ActivityLifecycleDispatcher;
import org.chromium.chrome.browser.lifecycle.TopResumedActivityChangedObserver;
import org.chromium.chrome.browser.tabmodel.IncognitoStateProvider;
import org.chromium.chrome.browser.tabmodel.IncognitoStateProvider.IncognitoStateObserver;
import org.chromium.chrome.browser.theme.ThemeColorProvider;
import org.chromium.chrome.browser.theme.ThemeUtils;
import org.chromium.chrome.browser.ui.desktop_windowing.AppHeaderUtils;
import org.chromium.chrome.browser.ui.desktop_windowing.DesktopWindowStateProvider;
import org.chromium.chrome.browser.ui.theme.BrandedColorScheme;
import org.chromium.components.browser_ui.styles.ChromeColors;

/** A ThemeColorProvider for the app theme (incognito or standard theming). */
public class AppThemeColorProvider extends ThemeColorProvider
        implements IncognitoStateObserver, TopResumedActivityChangedObserver {
    /** Primary color for standard mode. */
    private final int mStandardPrimaryColor;

    /** Primary color for incognito mode. */
    private final int mIncognitoPrimaryColor;

    /** Used to know when incognito mode is entered or exited. */
    private IncognitoStateProvider mIncognitoStateProvider;

    /** Used to know the Layout state. */
    private LayoutStateProvider mLayoutStateProvider;

    /** Observer to know when Layout state is changed, e.g show/hide. */
    private final LayoutStateProvider.LayoutStateObserver mLayoutStateObserver;

    /** Whether app is in incognito mode. */
    private boolean mIsIncognito;

    /** The activity {@link Context}. */
    private final Context mActivityContext;

    /**
     * The {@link ActivityLifecycleDispatcher} instance associated with the current activity, if
     * available.
     */
    @Nullable private ActivityLifecycleDispatcher mActivityLifecycleDispatcher;

    /**
     * Whether the current activity is the top resumed activity. This is only relevant for use in
     * the desktop windowing mode, to determine the tint for the toolbar icons.
     */
    private boolean mIsTopResumedActivity;

    /** Provider for desktop windowing mode state. */
    @Nullable private final DesktopWindowStateProvider mDesktopWindowStateProvider;

    /**
     * @param context The {@link Context} that is used to retrieve color related resources.
     * @param activityLifecycleDispatcher The {@link ActivityLifecycleDispatcher} instance
     *     associated with the current activity. {@code null} if activity lifecycle observation is
     *     not required.
     * @param desktopWindowStateProvider The {@link DesktopWindowStateProvider} for the current
     *     activity. {@code null} if desktop window state observation is not required.
     */
    AppThemeColorProvider(
            Context context,
            @Nullable ActivityLifecycleDispatcher activityLifecycleDispatcher,
            @Nullable DesktopWindowStateProvider desktopWindowStateProvider) {
        super(context);

        mActivityContext = context;
        mStandardPrimaryColor = ChromeColors.getDefaultThemeColor(context, false);
        mIncognitoPrimaryColor = ChromeColors.getDefaultThemeColor(context, true);

        mLayoutStateObserver =
                new LayoutStateProvider.LayoutStateObserver() {
                    @Override
                    public void onStartedShowing(@LayoutType int layoutType) {
                        if (layoutType == LayoutType.TAB_SWITCHER) {
                            updateTheme();
                        }
                    }

                    @Override
                    public void onStartedHiding(@LayoutType int layoutType) {
                        if (layoutType == LayoutType.TAB_SWITCHER) {
                            updateTheme();
                        }
                    }
                };

        mDesktopWindowStateProvider = desktopWindowStateProvider;
        mIsTopResumedActivity =
                mDesktopWindowStateProvider == null
                        || !mDesktopWindowStateProvider.isInUnfocusedDesktopWindow();

        // Activity lifecycle observation for activity focus change.
        if (activityLifecycleDispatcher != null) {
            mActivityLifecycleDispatcher = activityLifecycleDispatcher;
            mActivityLifecycleDispatcher.register(this);
        }
    }

    void setIncognitoStateProvider(IncognitoStateProvider provider) {
        mIncognitoStateProvider = provider;
        mIncognitoStateProvider.addIncognitoStateObserverAndTrigger(this);
    }

    @Override
    public void onIncognitoStateChanged(boolean isIncognito) {
        mIsIncognito = isIncognito;
        updateTheme();
    }

    void setLayoutStateProvider(LayoutStateProvider layoutStateProvider) {
        mLayoutStateProvider = layoutStateProvider;
        mLayoutStateProvider.addObserver(mLayoutStateObserver);
    }

    private void updateTheme() {
        updatePrimaryColor(mIsIncognito ? mIncognitoPrimaryColor : mStandardPrimaryColor, false);
        final @BrandedColorScheme int brandedColorScheme =
                mIsIncognito ? BrandedColorScheme.INCOGNITO : BrandedColorScheme.APP_DEFAULT;
        final ColorStateList iconTint =
                ThemeUtils.getThemedToolbarIconTint(mActivityContext, brandedColorScheme);

        final ColorStateList activityFocusTint =
                calculateActivityFocusTint(mActivityContext, brandedColorScheme);
        updateTint(iconTint, activityFocusTint, brandedColorScheme);
    }

    @Override
    public void destroy() {
        super.destroy();
        if (mIncognitoStateProvider != null) {
            mIncognitoStateProvider.removeObserver(this);
            mIncognitoStateProvider = null;
        }
        if (mLayoutStateProvider != null) {
            mLayoutStateProvider.removeObserver(mLayoutStateObserver);
            mLayoutStateProvider = null;
        }
        if (mActivityLifecycleDispatcher != null) {
            mActivityLifecycleDispatcher.unregister(this);
        }
    }

    @Override
    public void onTopResumedActivityChanged(boolean isTopResumedActivity) {
        // TODO (crbug/328055199): Check if losing focus to a non-Chrome task.
        mIsTopResumedActivity = isTopResumedActivity;
        updateTheme();
    }

    private ColorStateList calculateActivityFocusTint(
            Context context, @BrandedColorScheme int brandedColorScheme) {
        var iconTint = ThemeUtils.getThemedToolbarIconTint(context, brandedColorScheme);
        return mActivityLifecycleDispatcher == null
                        || !AppHeaderUtils.isAppInDesktopWindow(mDesktopWindowStateProvider)
                ? iconTint
                : ThemeUtils.getThemedToolbarIconTintForActivityState(
                        context, brandedColorScheme, mIsTopResumedActivity);
    }
}