package com.alexvasilkov.android.commons.ui; import android.content.Context; import android.content.res.ColorStateList; import android.content.res.TypedArray; import android.graphics.drawable.Drawable; import android.support.annotation.AttrRes; import android.support.annotation.ColorInt; import android.support.annotation.ColorRes; import android.support.annotation.DrawableRes; import android.support.annotation.NonNull; import android.support.v4.content.ContextCompat; import android.support.v4.graphics.drawable.DrawableCompat; import android.util.StateSet; import android.util.TypedValue; import android.view.View; import android.widget.ImageView; import android.widget.TextView; /** * Simplified tinting for drawables and views. */ @SuppressWarnings({ "unused", "WeakerAccess" }) // Public API public class Tints { public static final Tint PRIMARY = fromAttr("colorPrimary"); public static final Tint PRIMARY_DARK = fromAttr("colorPrimaryDark"); public static final Tint ACCENT = fromAttr("colorAccent"); public static final Tint NORMAL = fromAttr("colorControlNormal"); public static final Tint ACTIVATED = fromAttr("colorControlActivated"); public static final Tint DISABLED = withDisabledAlpha(fromAttr("colorControlNormal")); private static final int ALPHA_DISABLED = -1; private static final int[][] states = new int[][] { new int[] { -android.R.attr.state_enabled }, // disabled new int[] { android.R.attr.state_pressed }, // pressed new int[] { android.R.attr.state_focused }, // focused new int[] { android.R.attr.state_selected }, // selected new int[] { android.R.attr.state_activated }, // activated new int[] { android.R.attr.state_checked }, // checked StateSet.WILD_CARD // other }; private static final TypedValue tmpValue = new TypedValue(); private static final int[] tmpAttrs = new int[1]; private Tints() {} public static Drawable tint(@NonNull Context context, @NonNull Drawable drawable, @NonNull Tint tint) { return tint(drawable, tint.getColor(context)); } public static Drawable tint(@NonNull Context context, @DrawableRes int drawableId, @NonNull Tint tint) { return tint(ContextCompat.getDrawable(context, drawableId), tint.getColor(context)); } private static Drawable tint(Drawable drawable, ColorStateList color) { Drawable wrapped = DrawableCompat.wrap(drawable); wrapped.mutate(); DrawableCompat.setTintList(wrapped, color); return wrapped; } /** * Tints ImageView drawable or TextView compound drawables to given tint color. */ public static void tint(@NonNull View view, @NonNull Tint tint) { final ColorStateList color = tint.getColor(view.getContext()); if (view instanceof ImageView) { tint(((ImageView) view).getDrawable(), color); } else if (view instanceof TextView) { TextView text = (TextView) view; Drawable[] comp = text.getCompoundDrawables(); for (int i = 0; i < comp.length; i++) { if (comp[i] != null) { comp[i] = tint(comp[i], color); } } text.setCompoundDrawablesWithIntrinsicBounds(comp[0], comp[1], comp[2], comp[3]); } else { throw new IllegalArgumentException("Unsupported view type"); } } private static Tint fromAttr(@NonNull final String attrName) { return new Tint() { @Override public ColorStateList getColor(Context context) { int attrId = context.getResources() .getIdentifier(attrName, "attr", context.getPackageName()); return getThemeAttrColor(context, attrId); } }; } public static Tint fromAttr(@AttrRes final int attrId) { return new Tint() { @Override public ColorStateList getColor(Context context) { return getThemeAttrColor(context, attrId); } }; } public static Tint fromColor(@ColorInt final int color) { return new Tint() { @Override public ColorStateList getColor(Context context) { return ColorStateList.valueOf(color); } }; } public static Tint fromColorRes(@ColorRes final int colorId) { return new Tint() { @Override public ColorStateList getColor(Context context) { return ContextCompat.getColorStateList(context, colorId); } }; } public static Tint fromColorList(@NonNull final ColorStateList color) { return new Tint() { @Override public ColorStateList getColor(Context context) { return color; } }; } public static Tint fromDefaultStates() { return new Tint() { @Override public ColorStateList getColor(Context context) { final int normal = NORMAL.getDefaultColor(context); final int activated = ACTIVATED.getDefaultColor(context); final int disabled = DISABLED.getDefaultColor(context); final int[] colors = new int[] { disabled, activated, activated, activated, activated, activated, normal }; return new ColorStateList(states, colors); } }; } public static Tint withAlpha(@NonNull final Tint tint, final int alpha) { return new Tint() { @Override public ColorStateList getColor(Context context) { int appliedAlpha = alpha == ALPHA_DISABLED ? getThemeDisabledAlpha(context) : alpha; ColorStateList stateColor = tint.getColor(context); if (stateColor.isStateful()) { // New ColorStateList object will be created each time calling .withAlpha() return stateColor.withAlpha(appliedAlpha); } else { // Created ColorStateList object will be cached int color = (stateColor.getDefaultColor() & 0xFFFFFF) | (appliedAlpha << 24); return ColorStateList.valueOf(color); } } }; } public static Tint withDisabledAlpha(@NonNull Tint tint) { return withAlpha(tint, ALPHA_DISABLED); } private static ColorStateList getThemeAttrColor(Context context, @AttrRes int attr) { tmpAttrs[0] = attr; TypedArray arr = context.obtainStyledAttributes(null, tmpAttrs); ColorStateList color = arr.getColorStateList(0); arr.recycle(); return color; } private static int getThemeDisabledAlpha(Context context) { context.getTheme().resolveAttribute(android.R.attr.disabledAlpha, tmpValue, true); return Math.round(0xFF * tmpValue.getFloat()); } public abstract static class Tint { /** * Returns ColorStateList for tinting. */ public abstract ColorStateList getColor(Context context); /** * Returns default color from ColorStateList for tinting. * Useful if it is known that tinting color is stateless (representing single color). */ public final int getDefaultColor(Context context) { return getColor(context).getDefaultColor(); } } }