chromium/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/text/TextViewWithCompoundDrawables.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.components.browser_ui.widget.text;

import android.content.Context;
import android.content.res.ColorStateList;
import android.content.res.TypedArray;
import android.graphics.PorterDuff;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.widget.TextView;

import androidx.annotation.Nullable;
import androidx.appcompat.widget.AppCompatTextView;

import org.chromium.components.browser_ui.widget.R;

/**
 * A {@link TextView} with support for explicitly sizing and tinting compound drawables.
 *
 * To specify the drawable size, use the {@code drawableWidth} and {@code drawableHeight}
 * attributes. To specify the drawable tint, use the {@code chromeDrawableTint} attribute.
 */
public class TextViewWithCompoundDrawables extends AppCompatTextView {
    private int mDrawableWidth;
    private int mDrawableHeight;
    private ColorStateList mDrawableTint;

    public TextViewWithCompoundDrawables(Context context) {
        this(context, null);
    }

    public TextViewWithCompoundDrawables(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public TextViewWithCompoundDrawables(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(context, attrs, defStyleAttr);
    }

    /**
     * Set the tint color of the compound drawables.
     *
     * @param color The tint color. May be null to clear the tint color.
     */
    public void setDrawableTintColor(@Nullable ColorStateList color) {
        mDrawableTint = color;
        setDrawableTint(getCompoundDrawablesRelative());
    }

    /**
     * Set the width of the compound drawables.
     * @param width The drawable width.
     */
    public void setDrawableWidth(int width) {
        mDrawableWidth = width;
        setDrawableBounds(getCompoundDrawables());
    }

    /**
     * Set the height of the compound drawables.
     * @param height The drawable height.
     */
    public void setDrawableHeight(int height) {
        mDrawableHeight = height;
        setDrawableBounds(getCompoundDrawables());
    }

    @Override
    protected void drawableStateChanged() {
        super.drawableStateChanged();

        if (mDrawableTint != null) {
            setDrawableTint(getCompoundDrawablesRelative());
        }
    }

    @Override
    public void setCompoundDrawablesRelative(
            @Nullable Drawable start,
            @Nullable Drawable top,
            @Nullable Drawable end,
            @Nullable Drawable bottom) {
        Drawable[] drawables = {start, top, end, bottom};
        setDrawableBounds(drawables);

        if (mDrawableTint != null) {
            setDrawableTint(drawables);
        }

        super.setCompoundDrawablesRelative(drawables[0], drawables[1], drawables[2], drawables[3]);
    }

    private void init(Context context, AttributeSet attrs, int defStyleAttr) {
        TypedArray array =
                context.obtainStyledAttributes(
                        attrs, R.styleable.TextViewWithCompoundDrawables, defStyleAttr, 0);

        mDrawableWidth =
                array.getDimensionPixelSize(
                        R.styleable.TextViewWithCompoundDrawables_drawableWidth, -1);
        mDrawableHeight =
                array.getDimensionPixelSize(
                        R.styleable.TextViewWithCompoundDrawables_drawableHeight, -1);
        mDrawableTint =
                array.getColorStateList(
                        R.styleable.TextViewWithCompoundDrawables_chromeDrawableTint);

        array.recycle();

        if (mDrawableWidth <= 0 && mDrawableHeight <= 0 && mDrawableTint == null) return;

        Drawable[] drawables = getCompoundDrawablesRelative();

        setDrawableBounds(drawables);

        if (mDrawableTint != null) setDrawableTint(drawables);

        setCompoundDrawablesRelative(drawables[0], drawables[1], drawables[2], drawables[3]);
    }

    private void setDrawableTint(Drawable[] drawables) {
        for (Drawable drawable : drawables) {
            if (drawable == null) continue;

            drawable.mutate();
            if (mDrawableTint == null) {
                drawable.clearColorFilter();
            } else {
                drawable.setColorFilter(
                        mDrawableTint.getColorForState(getDrawableState(), 0),
                        PorterDuff.Mode.SRC_IN);
            }
        }
    }

    private void setDrawableBounds(Drawable[] drawables) {
        for (Drawable drawable : drawables) {
            if (drawable == null) continue;

            if (mDrawableWidth > 0 || mDrawableHeight > 0) {
                Rect bounds = drawable.copyBounds();
                if (mDrawableWidth > 0) {
                    bounds.right = bounds.left + mDrawableWidth;
                }
                if (mDrawableHeight > 0) {
                    bounds.bottom = bounds.top + mDrawableHeight;
                }
                drawable.setBounds(bounds);
            }
        }
    }
}