chromium/chrome/browser/ui/android/theme/java/src/org/chromium/chrome/browser/theme/ThemeColorProvider.java

// Copyright 2019 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.theme;

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

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

import org.chromium.base.ObserverList;
import org.chromium.chrome.browser.ui.theme.BrandedColorScheme;

/** An abstract class that provides the current theme color. */
public abstract class ThemeColorProvider {
    /** An interface to be notified about changes to the theme color. */
    public interface ThemeColorObserver {
        /**
         * @param color The new color the observer should use.
         * @param shouldAnimate Whether the change of color should be animated.
         */
        void onThemeColorChanged(@ColorInt int color, boolean shouldAnimate);
    }

    /** An interface to be notified about changes to the tint. */
    public interface TintObserver {
        /**
         * @param tint The new tint the observer should use, without applying Activity state
         *     (focused vs unfocused) rules. This should be used for elements that don't adjust tint
         *     based on Activity focus.
         * @param activityFocusTint The tint the observer should use including consideration for
         *     whether the Activity is focused. This should be used for elements that do adjust tint
         *     based on Activity focus.
         * @param brandedColorScheme The {@link BrandedColorScheme} the observer should use.
         */
        void onTintChanged(
                ColorStateList tint,
                ColorStateList activityFocusTint,
                @BrandedColorScheme int brandedColorScheme);
    }

    /** Current primary color. */
    private @ColorInt int mPrimaryColor;

    /** The {@link BrandedColorScheme} for the current theme. */
    private @Nullable @BrandedColorScheme Integer mBrandedColorScheme;

    /**
     * The primary icon tint for the current theme, that does not take the activity focus state into
     * account.
     */
    private ColorStateList mTint;

    /** The icon tint for the current theme, that takes the activity focus state into account. */
    private ColorStateList mActivityFocusTint;

    /** List of {@link ThemeColorObserver}s. These are used to broadcast events to listeners. */
    private final ObserverList<ThemeColorObserver> mThemeColorObservers;

    /** List of {@link TintObserver}s. These are used to broadcast events to listeners. */
    private final ObserverList<TintObserver> mTintObservers;

    /**
     * @param context The {@link Context} that is used to retrieve color related resources.
     */
    public ThemeColorProvider(Context context) {
        mThemeColorObservers = new ObserverList<>();
        mTintObservers = new ObserverList<>();
        mTint = ThemeUtils.getThemedToolbarIconTint(context, BrandedColorScheme.APP_DEFAULT);
    }

    /**
     * @param observer Adds a {@link ThemeColorObserver} that will be notified when the theme color
     *     changes. This method does not trigger the observer.
     */
    public void addThemeColorObserver(ThemeColorObserver observer) {
        mThemeColorObservers.addObserver(observer);
    }

    /**
     * @param observer Removes the observer so it no longer receives theme color changes.
     */
    public void removeThemeColorObserver(ThemeColorObserver observer) {
        mThemeColorObservers.removeObserver(observer);
    }

    /**
     * @param observer Adds a {@link TintObserver} that will be notified when the tint changes. This
     *                 method does not trigger the observer.
     */
    public void addTintObserver(TintObserver observer) {
        mTintObservers.addObserver(observer);
    }

    /**
     * @param observer Removes the observer so it no longer receives tint changes.
     */
    public void removeTintObserver(TintObserver observer) {
        mTintObservers.removeObserver(observer);
    }

    /**
     * @return The current theme color of this provider.
     */
    public @ColorInt int getThemeColor() {
        return mPrimaryColor;
    }

    /**
     * @return The current tint of this provider, that does not take the activity focus state into
     *     account.
     */
    public ColorStateList getTint() {
        return mTint;
    }

    /**
     * @return The tint that takes the current activity's focus state into account.
     */
    public ColorStateList getActivityFocusTint() {
        return mActivityFocusTint;
    }

    /**
     * @return The current {@link BrandedColorScheme} of this provider.
     */
    public @BrandedColorScheme int getBrandedColorScheme() {
        return mBrandedColorScheme != null ? mBrandedColorScheme : BrandedColorScheme.APP_DEFAULT;
    }

    /** Clears out the observer lists. */
    public void destroy() {
        mThemeColorObservers.clear();
        mTintObservers.clear();
    }

    protected void updatePrimaryColor(@ColorInt int color, boolean shouldAnimate) {
        if (mPrimaryColor == color) return;
        mPrimaryColor = color;
        for (ThemeColorObserver observer : mThemeColorObservers) {
            observer.onThemeColorChanged(color, shouldAnimate);
        }
    }

    protected void updateTint(
            @NonNull ColorStateList tint,
            @NonNull ColorStateList activityFocusTint,
            @BrandedColorScheme int brandedColorScheme) {
        if (tint == mTint && activityFocusTint == mActivityFocusTint) return;
        mTint = tint;
        mActivityFocusTint = activityFocusTint;
        mBrandedColorScheme = brandedColorScheme;

        for (TintObserver observer : mTintObservers) {
            observer.onTintChanged(tint, activityFocusTint, brandedColorScheme);
        }
    }
}