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

// Copyright 2024 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.content.Context;
import android.content.res.ColorStateList;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.GradientDrawable;
import android.graphics.drawable.LayerDrawable;
import android.graphics.drawable.VectorDrawable;

import androidx.annotation.ColorInt;
import androidx.annotation.DrawableRes;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import androidx.core.content.res.ResourcesCompat;
import androidx.core.graphics.drawable.DrawableCompat;

import com.google.android.material.color.MaterialColors;

import org.chromium.base.Callback;
import org.chromium.chrome.R;
import org.chromium.chrome.browser.flags.ChromeFeatureList;
import org.chromium.chrome.browser.tab.Tab;
import org.chromium.chrome.browser.tab_group_sync.TabGroupSyncFeatures;
import org.chromium.chrome.browser.tab_group_sync.TabGroupSyncServiceFactory;
import org.chromium.chrome.browser.tab_ui.TabListFaviconProvider.TabFavicon;
import org.chromium.chrome.browser.tab_ui.TabListFaviconProvider.TabFaviconFetcher;
import org.chromium.chrome.browser.tabmodel.TabModel;
import org.chromium.chrome.browser.tasks.tab_groups.TabGroupColorUtils;
import org.chromium.components.tab_group_sync.TabGroupSyncService;

public class TabGroupColorFaviconProvider {
    static final String TAG = "TabGroupColorFaviconProvider";
    static final int TAB_GROUP_FAVICON_COLOR_LEVEL = 1;
    static final int TAB_GROUP_FAVICON_SHARE_ICON_LEVEL = 2;
    static final int FAVICON_BACKGROUND_DEFAULT_ALPHA = 255;
    static final int FAVICON_BACKGROUND_SELECTED_ALPHA = 0;
    private final Context mContext;

    private TabGroupSyncService mTabGroupSyncService;

    public TabGroupColorFaviconProvider(Context context) {
        mContext = context;
    }

    /** A favicon represented by a full color circle when displaying tab group colors. */
    @VisibleForTesting
    public static class TabGroupColorFavicon extends TabFavicon {
        private final @org.chromium.components.tab_groups.TabGroupColorId int mColorId;

        private TabGroupColorFavicon(
                @NonNull Drawable defaultDrawable,
                @NonNull Drawable selectedDrawable,
                boolean allowRecolor,
                @org.chromium.components.tab_groups.TabGroupColorId int colorId) {
            super(defaultDrawable, selectedDrawable, allowRecolor);
            mColorId = colorId;
        }

        @VisibleForTesting
        public TabGroupColorFavicon(
                @NonNull Drawable defaultDrawable,
                @org.chromium.components.tab_groups.TabGroupColorId int colorId) {
            this(defaultDrawable, defaultDrawable, false, colorId);
        }

        @VisibleForTesting
        public TabGroupColorFavicon(
                @NonNull Drawable defaultDrawable,
                @NonNull Drawable selectedDrawable,
                @org.chromium.components.tab_groups.TabGroupColorId int colorId) {
            this(defaultDrawable, selectedDrawable, false, colorId);
        }

        @Override
        public int hashCode() {
            return Integer.hashCode(mColorId);
        }

        @Override
        public boolean equals(Object other) {
            if (!(other instanceof TabGroupColorFavicon)) {
                return false;
            }
            return this.mColorId == ((TabGroupColorFavicon) other).mColorId;
        }
    }

    /**
     * Asynchronously get a full color favicon for tab group color icon display. A share group icon
     * based on the user's dynamic color scheme is shown beside the full color favicon if tab group
     * sharing is in use for the tab group in question.
     *
     * @param colorId The color id associated with the chosen color to be displayed.
     */
    public TabFaviconFetcher getFaviconFromTabGroupColorFetcher(
            @NonNull @org.chromium.components.tab_groups.TabGroupColorId int colorId,
            TabModel tabModel,
            Tab tab) {
        return new TabFaviconFetcher() {
            @Override
            public void fetch(Callback<TabFavicon> faviconCallback) {
                if (colorId != TabGroupColorUtils.INVALID_COLOR_ID) {
                    boolean isTabGroupShareActive = isTabGroupShareActive(tabModel, tab);
                    final @ColorInt int color =
                            ColorPickerUtils.getTabGroupColorPickerItemColor(
                                    mContext, colorId, tabModel.isIncognitoBranded());
                    final @DrawableRes int iconRes =
                            isTabGroupShareActive && !tabModel.isIncognitoBranded()
                                    ? org.chromium.chrome.tab_ui.R.drawable
                                            .tab_group_share_with_color_icon
                                    : org.chromium.chrome.tab_ui.R.drawable.tab_group_color_icon;

                    LayerDrawable tabGroupColorIcon =
                            (LayerDrawable)
                                    ResourcesCompat.getDrawable(
                                            mContext.getResources(), iconRes, mContext.getTheme());
                    ((GradientDrawable)
                                    tabGroupColorIcon.getDrawable(TAB_GROUP_FAVICON_COLOR_LEVEL))
                            .setColor(color);

                    if (isTabGroupShareActive && !tabModel.isIncognitoBranded()) {
                        LayerDrawable tabGroupColorIconSelected =
                                (LayerDrawable)
                                        tabGroupColorIcon.getConstantState().newDrawable().mutate();
                        VectorDrawable shareGroupDrawable =
                                (VectorDrawable)
                                        tabGroupColorIconSelected.getDrawable(
                                                TAB_GROUP_FAVICON_SHARE_ICON_LEVEL);
                        DrawableCompat.setTintList(
                                shareGroupDrawable,
                                ColorStateList.valueOf(
                                        MaterialColors.getColor(
                                                mContext, R.attr.colorPrimary, TAG)));

                        faviconCallback.onResult(
                                new TabGroupColorFavicon(
                                        tabGroupColorIcon, tabGroupColorIconSelected, colorId));
                        return;
                    }

                    faviconCallback.onResult(new TabGroupColorFavicon(tabGroupColorIcon, colorId));
                } else {
                    // If the color is invalid, don't set a drawable.
                    faviconCallback.onResult(
                            new TabGroupColorFavicon(
                                    null, org.chromium.components.tab_groups.TabGroupColorId.GREY));
                }
            }
        };
    }

    private boolean isTabGroupShareActive(TabModel tabModel, Tab tab) {
        if (!ChromeFeatureList.isEnabled(ChromeFeatureList.DATA_SHARING)) {
            return false;
        }

        if (TabGroupSyncFeatures.isTabGroupSyncEnabled(tab.getProfile())) {
            mTabGroupSyncService =
                    TabGroupSyncServiceFactory.getForProfile(tab.getProfile().getOriginalProfile());
        }

        if (tabModel == null || mTabGroupSyncService == null) {
            return false;
        }

        final @Nullable String collaborationId =
                TabShareUtils.getCollaborationIdOrNull(tab.getId(), tabModel, mTabGroupSyncService);
        return TabShareUtils.isCollaborationIdValid(collaborationId);
    }
}