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

import android.content.Context;
import android.content.res.ColorStateList;
import android.graphics.Canvas;
import android.graphics.Paint.Align;
import android.graphics.PixelFormat;
import android.graphics.PorterDuff;
import android.graphics.Rect;
import android.graphics.Typeface;
import android.graphics.drawable.DrawableWrapper;
import android.text.TextPaint;

import androidx.appcompat.content.res.AppCompatResources;

import org.chromium.chrome.R;
import org.chromium.ui.UiUtils;

import java.util.Locale;

/**
 * Class for drawing a tab count icon on the Recent Tabs Page for bulk tab closures.
 *
 * Loosely based on {@link TabSwitcherDrawable} and modified to handle an SVG asset.
 */
public class RecentTabCountDrawable extends DrawableWrapper {
    // Avoid allocations during draw by pre-allocating a rect.
    private final Rect mTextBounds = new Rect();
    private final TextPaint mTextPaint;
    private ColorStateList mTint;

    private int mTabCount;

    /**
     * Creates a recent tab count icon. A stacked squircle with a number inside for a count.
     * @param context The context for getting resources.
     */
    public RecentTabCountDrawable(Context context) {
        super(
                UiUtils.getTintedDrawable(
                        context,
                        R.drawable.ic_recent_tabs_bulk_20dp,
                        R.color.default_icon_color_tint_list));

        mTextPaint = new TextPaint();
        setTint(
                AppCompatResources.getColorStateList(
                        context, R.color.default_icon_color_tint_list));

        mTextPaint.setAntiAlias(true);
        mTextPaint.setTextAlign(Align.CENTER);
        mTextPaint.setTypeface(Typeface.create("google-sans-medium", Typeface.BOLD));
        mTextPaint.setColor(getColorForState());
        mTextPaint.setTextSize(
                context.getResources().getDimension(R.dimen.recent_tabs_count_text_size));
    }

    /**
     * Update the count value inside the icon.
     * @param tabCount the number to display in the icon.
     */
    public void updateTabCount(int tabCount) {
        if (tabCount == mTabCount) return;
        mTabCount = tabCount;
        invalidateSelf();
    }

    public void setTint(ColorStateList tint) {
        if (mTint == tint) return;
        mTint = tint;
        updateTintColor();
        super.setTint(getColorForState());
        if (mTextPaint != null) mTextPaint.setColor(getColorForState());
    }

    // DrawableWrapper implementation.
    @Override
    public void setTint(int tint) {
        setTint(ColorStateList.valueOf(tint));
    }

    @Override
    public void draw(Canvas canvas) {
        super.draw(canvas);

        // Add text string.
        String textString = getTabCountString();
        if (!textString.isEmpty()) {
            mTextPaint.getTextBounds(textString, 0, textString.length(), mTextBounds);
            Rect bounds = super.getBounds();
            // Constants are based on X/Y position in the icon from the redlines the UX designer
            // provided.
            final int textX = bounds.left + Math.round(0.583f * bounds.width());
            final int textY =
                    bounds.top
                            + Math.round(14.0f / 24.0f * bounds.height())
                            + (mTextBounds.bottom - mTextBounds.top) / 2
                            - mTextBounds.bottom;

            canvas.drawText(textString, textX, textY, mTextPaint);
        }
    }

    @Override
    protected boolean onStateChange(int[] state) {
        boolean ret = updateTintColor();
        if (ret) mTextPaint.setColor(getColorForState());
        return ret || super.onStateChange(state);
    }

    @Override
    public boolean isStateful() {
        return true;
    }

    @Override
    public int getOpacity() {
        return PixelFormat.TRANSLUCENT;
    }

    @Override
    public void setAlpha(int alpha) {
        super.setAlpha(alpha);
        mTextPaint.setAlpha(alpha);
    }

    private boolean updateTintColor() {
        if (mTint == null) return false;
        setColorFilter(getColorForState(), PorterDuff.Mode.SRC_IN);
        return true;
    }

    private String getTabCountString() {
        if (mTabCount <= 0) {
            return "";
        } else if (mTabCount > 99) {
            return ":D";
        } else {
            return String.format(Locale.getDefault(), "%d", mTabCount);
        }
    }

    private int getColorForState() {
        return mTint.getColorForState(getState(), 0);
    }
}