TintManager.java revision 7e82b99953680915596eaf0eb35927388e574ca8
1469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes/*
2469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes * Copyright (C) 2014 The Android Open Source Project
3469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes *
4469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes * Licensed under the Apache License, Version 2.0 (the "License");
5469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes * you may not use this file except in compliance with the License.
6469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes * You may obtain a copy of the License at
7469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes *
8469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes *      http://www.apache.org/licenses/LICENSE-2.0
9469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes *
10469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes * Unless required by applicable law or agreed to in writing, software
11469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes * distributed under the License is distributed on an "AS IS" BASIS,
12469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes * See the License for the specific language governing permissions and
14469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes * limitations under the License.
15469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes */
16469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes
17469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banespackage android.support.v7.internal.widget;
18469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes
19469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banesimport android.content.Context;
20469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banesimport android.content.res.ColorStateList;
21469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banesimport android.content.res.Resources;
22469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banesimport android.graphics.Color;
23469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banesimport android.graphics.PorterDuff;
24469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banesimport android.graphics.PorterDuffColorFilter;
25469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banesimport android.graphics.drawable.Drawable;
26fd1eb27a3700de31507de34fd1bcc51830fe876cChris Banesimport android.os.Build;
27469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banesimport android.support.v4.content.ContextCompat;
287e82b99953680915596eaf0eb35927388e574ca8Chris Banesimport android.support.v4.graphics.drawable.DrawableCompat;
29469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banesimport android.support.v4.util.LruCache;
30469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banesimport android.support.v7.appcompat.R;
31469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banesimport android.util.Log;
3290075479814eb758d97b822606b448e1a521c298Chris Banesimport android.util.SparseArray;
33469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banesimport android.util.TypedValue;
34a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banesimport android.view.View;
35469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes
36469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes/**
37469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes * @hide
38469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes */
39a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banespublic final class TintManager {
40469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes
41fd1eb27a3700de31507de34fd1bcc51830fe876cChris Banes    static final boolean SHOULD_BE_USED = Build.VERSION.SDK_INT < 21;
42fd1eb27a3700de31507de34fd1bcc51830fe876cChris Banes
43469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes    private static final String TAG = TintManager.class.getSimpleName();
44469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes    private static final boolean DEBUG = false;
45415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
46415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    static final PorterDuff.Mode DEFAULT_MODE = PorterDuff.Mode.SRC_IN;
47469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes
48469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes    private static final ColorFilterLruCache COLOR_FILTER_CACHE = new ColorFilterLruCache(6);
49469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes
50469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes    /**
51469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes     * Drawables which should be tinted with the value of {@code R.attr.colorControlNormal},
52469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes     * using the default mode.
53469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes     */
54469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes    private static final int[] TINT_COLOR_CONTROL_NORMAL = {
55469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes            R.drawable.abc_ic_ab_back_mtrl_am_alpha,
56469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes            R.drawable.abc_ic_go_search_api_mtrl_alpha,
57469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes            R.drawable.abc_ic_search_api_mtrl_alpha,
58469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes            R.drawable.abc_ic_commit_search_api_mtrl_alpha,
59469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes            R.drawable.abc_ic_clear_mtrl_alpha,
60469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes            R.drawable.abc_ic_menu_share_mtrl_alpha,
61b37a31664b07243ca9e86c8dac58b9be6a417e8cChris Banes            R.drawable.abc_ic_menu_copy_mtrl_am_alpha,
62b37a31664b07243ca9e86c8dac58b9be6a417e8cChris Banes            R.drawable.abc_ic_menu_cut_mtrl_alpha,
63b37a31664b07243ca9e86c8dac58b9be6a417e8cChris Banes            R.drawable.abc_ic_menu_selectall_mtrl_alpha,
64b37a31664b07243ca9e86c8dac58b9be6a417e8cChris Banes            R.drawable.abc_ic_menu_paste_mtrl_am_alpha,
65469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes            R.drawable.abc_ic_menu_moreoverflow_mtrl_alpha,
66469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes            R.drawable.abc_ic_voice_search_api_mtrl_alpha,
67469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes            R.drawable.abc_textfield_search_default_mtrl_alpha,
68b645de790756e27bbe92d133216b7ac79cca7679Chris Banes            R.drawable.abc_textfield_default_mtrl_alpha,
69b645de790756e27bbe92d133216b7ac79cca7679Chris Banes            R.drawable.abc_ab_share_pack_mtrl_alpha
70469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes    };
71469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes
72469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes    /**
73469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes     * Drawables which should be tinted with the value of {@code R.attr.colorControlActivated},
74469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes     * using the default mode.
75469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes     */
76469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes    private static final int[] TINT_COLOR_CONTROL_ACTIVATED = {
77469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes            R.drawable.abc_textfield_activated_mtrl_alpha,
7814f8f0dfae09445074dc0f7b5bbe5732d6922da7Chris Banes            R.drawable.abc_textfield_search_activated_mtrl_alpha,
79911642499da7d796aa1e7c19178c3552a590f48bChris Banes            R.drawable.abc_cab_background_top_mtrl_alpha,
80911642499da7d796aa1e7c19178c3552a590f48bChris Banes            R.drawable.abc_text_cursor_mtrl_alpha
81469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes    };
82469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes
83469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes    /**
84469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes     * Drawables which should be tinted with the value of {@code android.R.attr.colorBackground},
85469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes     * using the {@link android.graphics.PorterDuff.Mode#MULTIPLY} mode.
86469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes     */
87469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes    private static final int[] TINT_COLOR_BACKGROUND_MULTIPLY = {
881073132946bb0a53a788949fe4c060f72051cd57Chris Banes            R.drawable.abc_popup_background_mtrl_mult,
8957c6de90985a63358129b99b9f0cd4d6afe887d6Chris Banes            R.drawable.abc_cab_background_internal_bg,
9057c6de90985a63358129b99b9f0cd4d6afe887d6Chris Banes            R.drawable.abc_menu_hardkey_panel_mtrl_mult
91469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes    };
92469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes
93469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes    /**
94469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes     * Drawables which should be tinted using a state list containing values of
95469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes     * {@code R.attr.colorControlNormal} and {@code R.attr.colorControlActivated}
96469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes     */
97469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes    private static final int[] TINT_COLOR_CONTROL_STATE_LIST = {
98469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes            R.drawable.abc_edit_text_material,
99469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes            R.drawable.abc_tab_indicator_material,
100469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes            R.drawable.abc_textfield_search_material,
101415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            R.drawable.abc_spinner_mtrl_am_alpha,
102415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            R.drawable.abc_btn_check_material,
103f67cc46fd515df18c4b70dce9e5dcdd4e3245cd8Chris Banes            R.drawable.abc_btn_radio_material,
104a9585dae398a69dd67797e7ca86f44ffcabd9e86Chris Banes            R.drawable.abc_spinner_textfield_background_material,
10590075479814eb758d97b822606b448e1a521c298Chris Banes            R.drawable.abc_ratingbar_full_material,
10690075479814eb758d97b822606b448e1a521c298Chris Banes            R.drawable.abc_switch_track_mtrl_alpha,
10790075479814eb758d97b822606b448e1a521c298Chris Banes            R.drawable.abc_switch_thumb_material,
10816e8d4dd91083260223c007c797e46bc8f631055Chris Banes            R.drawable.abc_btn_default_mtrl_shape,
10916e8d4dd91083260223c007c797e46bc8f631055Chris Banes            R.drawable.abc_btn_borderless_material
110469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes    };
111469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes
112469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes    /**
113469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes     * Drawables which contain other drawables which should be tinted. The child drawable IDs
114469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes     * should be defined in one of the arrays above.
115469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes     */
116469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes    private static final int[] CONTAINERS_WITH_TINT_CHILDREN = {
1171073132946bb0a53a788949fe4c060f72051cd57Chris Banes            R.drawable.abc_cab_background_top_material
118469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes    };
119469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes
120469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes    private final Context mContext;
121469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes    private final Resources mResources;
122469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes    private final TypedValue mTypedValue;
123469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes
12490075479814eb758d97b822606b448e1a521c298Chris Banes    private final SparseArray<ColorStateList> mColorStateLists;
125469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes    private ColorStateList mDefaultColorStateList;
126469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes
127469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes    /**
128469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes     * A helper method to instantiate a {@link TintManager} and then call {@link #getDrawable(int)}.
129469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes     * This method should not be used routinely.
130469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes     */
131469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes    public static Drawable getDrawable(Context context, int resId) {
132b37a31664b07243ca9e86c8dac58b9be6a417e8cChris Banes        if (isInTintList(resId)) {
133911642499da7d796aa1e7c19178c3552a590f48bChris Banes            final TintManager tm = (context instanceof TintContextWrapper)
134911642499da7d796aa1e7c19178c3552a590f48bChris Banes                    ? ((TintContextWrapper) context).getTintManager()
135911642499da7d796aa1e7c19178c3552a590f48bChris Banes                    : new TintManager(context);
136911642499da7d796aa1e7c19178c3552a590f48bChris Banes            return tm.getDrawable(resId);
137b37a31664b07243ca9e86c8dac58b9be6a417e8cChris Banes        } else {
138b37a31664b07243ca9e86c8dac58b9be6a417e8cChris Banes            return ContextCompat.getDrawable(context, resId);
139b37a31664b07243ca9e86c8dac58b9be6a417e8cChris Banes        }
140469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes    }
141469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes
142469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes    public TintManager(Context context) {
14390075479814eb758d97b822606b448e1a521c298Chris Banes        mColorStateLists = new SparseArray<>();
144469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes        mContext = context;
145469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes        mTypedValue = new TypedValue();
146911642499da7d796aa1e7c19178c3552a590f48bChris Banes        mResources = new TintResources(context.getResources(), this);
147911642499da7d796aa1e7c19178c3552a590f48bChris Banes    }
148911642499da7d796aa1e7c19178c3552a590f48bChris Banes
149911642499da7d796aa1e7c19178c3552a590f48bChris Banes    Resources getResources() {
150911642499da7d796aa1e7c19178c3552a590f48bChris Banes        return mResources;
151469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes    }
152469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes
153469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes    public Drawable getDrawable(int resId) {
154469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes        Drawable drawable = ContextCompat.getDrawable(mContext, resId);
155469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes
156469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes        if (drawable != null) {
157c7d6de1eb6692c627a02a4ed439a8a4b849ca66cChris Banes            drawable = drawable.mutate();
158c7d6de1eb6692c627a02a4ed439a8a4b849ca66cChris Banes
159469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes            if (arrayContains(TINT_COLOR_CONTROL_STATE_LIST, resId)) {
160a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banes                ColorStateList colorStateList = getColorStateListForKnownDrawableId(resId);
161a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banes                PorterDuff.Mode tintMode = DEFAULT_MODE;
162a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banes                if (resId == R.drawable.abc_switch_thumb_material) {
163a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banes                    tintMode = PorterDuff.Mode.MULTIPLY;
164a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banes                }
165a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banes
166a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banes                if (colorStateList != null) {
1677e82b99953680915596eaf0eb35927388e574ca8Chris Banes                    drawable = DrawableCompat.wrap(drawable);
1687e82b99953680915596eaf0eb35927388e574ca8Chris Banes                    DrawableCompat.setTintList(drawable, colorStateList);
1697e82b99953680915596eaf0eb35927388e574ca8Chris Banes                    DrawableCompat.setTintMode(drawable, tintMode);
170a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banes                }
171469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes            } else if (arrayContains(CONTAINERS_WITH_TINT_CHILDREN, resId)) {
172469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes                drawable = mResources.getDrawable(resId);
173469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes            } else {
174469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes                tintDrawable(resId, drawable);
175469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes            }
176469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes        }
177469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes        return drawable;
178469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes    }
179469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes
1800517b282bde8b9a0377dfe5bc4756405a196adb4Chris Banes    void tintDrawable(final int resId, final Drawable drawable) {
181469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes        PorterDuff.Mode tintMode = null;
182469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes        boolean colorAttrSet = false;
183469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes        int colorAttr = 0;
1840517b282bde8b9a0377dfe5bc4756405a196adb4Chris Banes        int alpha = -1;
185469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes
186469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes        if (arrayContains(TINT_COLOR_CONTROL_NORMAL, resId)) {
187469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes            colorAttr = R.attr.colorControlNormal;
188469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes            colorAttrSet = true;
189469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes        } else if (arrayContains(TINT_COLOR_CONTROL_ACTIVATED, resId)) {
190469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes            colorAttr = R.attr.colorControlActivated;
191469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes            colorAttrSet = true;
192469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes        } else if (arrayContains(TINT_COLOR_BACKGROUND_MULTIPLY, resId)) {
193469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes            colorAttr = android.R.attr.colorBackground;
194469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes            colorAttrSet = true;
195469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes            tintMode = PorterDuff.Mode.MULTIPLY;
1960517b282bde8b9a0377dfe5bc4756405a196adb4Chris Banes        } else if (resId == R.drawable.abc_list_divider_mtrl_alpha) {
1970517b282bde8b9a0377dfe5bc4756405a196adb4Chris Banes            colorAttr = android.R.attr.colorForeground;
1980517b282bde8b9a0377dfe5bc4756405a196adb4Chris Banes            colorAttrSet = true;
1990517b282bde8b9a0377dfe5bc4756405a196adb4Chris Banes            alpha = Math.round(0.16f * 255);
200469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes        }
201469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes
202f31fb9d2fd3b5b130f3f5ac121b033546d869231Chris Banes        if (colorAttrSet) {
203469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes            if (tintMode == null) {
204469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes                tintMode = DEFAULT_MODE;
205469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes            }
206f31fb9d2fd3b5b130f3f5ac121b033546d869231Chris Banes            final int color = getThemeAttrColor(colorAttr);
207469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes
208a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banes            tintDrawableUsingColorFilter(drawable, color, tintMode);
209469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes
2100517b282bde8b9a0377dfe5bc4756405a196adb4Chris Banes            if (alpha != -1) {
2110517b282bde8b9a0377dfe5bc4756405a196adb4Chris Banes                drawable.setAlpha(alpha);
2120517b282bde8b9a0377dfe5bc4756405a196adb4Chris Banes            }
2130517b282bde8b9a0377dfe5bc4756405a196adb4Chris Banes
214469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes            if (DEBUG) {
215469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes                Log.d(TAG, "Tinted Drawable ID: " + mResources.getResourceName(resId) +
216469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes                        " with color: #" + Integer.toHexString(color));
217469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes            }
218469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes        }
219469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes    }
220469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes
221469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes    private static boolean arrayContains(int[] array, int value) {
222469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes        for (int id : array) {
223469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes            if (id == value) {
224469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes                return true;
225469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes            }
226469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes        }
227469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes        return false;
228469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes    }
229469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes
230b37a31664b07243ca9e86c8dac58b9be6a417e8cChris Banes    private static boolean isInTintList(int drawableId) {
231b37a31664b07243ca9e86c8dac58b9be6a417e8cChris Banes        return arrayContains(TINT_COLOR_BACKGROUND_MULTIPLY, drawableId) ||
232b37a31664b07243ca9e86c8dac58b9be6a417e8cChris Banes                arrayContains(TINT_COLOR_CONTROL_NORMAL, drawableId) ||
233b37a31664b07243ca9e86c8dac58b9be6a417e8cChris Banes                arrayContains(TINT_COLOR_CONTROL_ACTIVATED, drawableId) ||
234b37a31664b07243ca9e86c8dac58b9be6a417e8cChris Banes                arrayContains(TINT_COLOR_CONTROL_STATE_LIST, drawableId) ||
235b37a31664b07243ca9e86c8dac58b9be6a417e8cChris Banes                arrayContains(CONTAINERS_WITH_TINT_CHILDREN, drawableId);
236b37a31664b07243ca9e86c8dac58b9be6a417e8cChris Banes    }
237b37a31664b07243ca9e86c8dac58b9be6a417e8cChris Banes
238a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banes    ColorStateList getColorStateList(int resId) {
239a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banes        return arrayContains(TINT_COLOR_CONTROL_STATE_LIST, resId)
240a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banes                ? getColorStateListForKnownDrawableId(resId)
241a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banes                : null;
242a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banes    }
243a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banes
244a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banes    private ColorStateList getColorStateListForKnownDrawableId(int resId) {
24590075479814eb758d97b822606b448e1a521c298Chris Banes        // Try the cache first
24690075479814eb758d97b822606b448e1a521c298Chris Banes        ColorStateList colorStateList = mColorStateLists.get(resId);
24790075479814eb758d97b822606b448e1a521c298Chris Banes
24890075479814eb758d97b822606b448e1a521c298Chris Banes        if (colorStateList == null) {
24990075479814eb758d97b822606b448e1a521c298Chris Banes            // ...if the cache did not contain a color state list, try and create
25090075479814eb758d97b822606b448e1a521c298Chris Banes            if (resId == R.drawable.abc_edit_text_material) {
25190075479814eb758d97b822606b448e1a521c298Chris Banes                colorStateList = createEditTextColorStateList();
25290075479814eb758d97b822606b448e1a521c298Chris Banes            } else if (resId == R.drawable.abc_switch_track_mtrl_alpha) {
25390075479814eb758d97b822606b448e1a521c298Chris Banes                colorStateList = createSwitchTrackColorStateList();
25490075479814eb758d97b822606b448e1a521c298Chris Banes            } else if (resId == R.drawable.abc_switch_thumb_material) {
25590075479814eb758d97b822606b448e1a521c298Chris Banes                colorStateList = createSwitchThumbColorStateList();
25616e8d4dd91083260223c007c797e46bc8f631055Chris Banes            } else if (resId == R.drawable.abc_btn_default_mtrl_shape
25716e8d4dd91083260223c007c797e46bc8f631055Chris Banes                    || resId == R.drawable.abc_btn_borderless_material) {
25890075479814eb758d97b822606b448e1a521c298Chris Banes                colorStateList = createButtonColorStateList();
259cdd1b1d70cefeb052c2b506738b396f2f982e519Chris Banes            } else if (resId == R.drawable.abc_spinner_mtrl_am_alpha
260cdd1b1d70cefeb052c2b506738b396f2f982e519Chris Banes                    || resId == R.drawable.abc_spinner_textfield_background_material) {
261cdd1b1d70cefeb052c2b506738b396f2f982e519Chris Banes                colorStateList = createSpinnerColorStateList();
26290075479814eb758d97b822606b448e1a521c298Chris Banes            } else {
26390075479814eb758d97b822606b448e1a521c298Chris Banes                // If we don't have an explicit color state list for this Drawable, use the default
26490075479814eb758d97b822606b448e1a521c298Chris Banes                colorStateList = getDefaultColorStateList();
26590075479814eb758d97b822606b448e1a521c298Chris Banes            }
266a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banes
26790075479814eb758d97b822606b448e1a521c298Chris Banes            // ..and add it to the cache
26890075479814eb758d97b822606b448e1a521c298Chris Banes            mColorStateLists.append(resId, colorStateList);
26990075479814eb758d97b822606b448e1a521c298Chris Banes        }
270a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banes        return colorStateList;
27190075479814eb758d97b822606b448e1a521c298Chris Banes    }
27290075479814eb758d97b822606b448e1a521c298Chris Banes
273469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes    private ColorStateList getDefaultColorStateList() {
274469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes        if (mDefaultColorStateList == null) {
275469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes            /**
276469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes             * Generate the default color state list which uses the colorControl attributes.
277469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes             * Order is important here. The default enabled state needs to go at the bottom.
278469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes             */
279469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes
280469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes            final int colorControlNormal = getThemeAttrColor(R.attr.colorControlNormal);
281469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes            final int colorControlActivated = getThemeAttrColor(R.attr.colorControlActivated);
282469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes
283469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes            final int[][] states = new int[7][];
284469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes            final int[] colors = new int[7];
285469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes            int i = 0;
286469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes
287c497221ee0e2e873f49653ff1a046df2e5276759Chris Banes            // Disabled state
288c497221ee0e2e873f49653ff1a046df2e5276759Chris Banes            states[i] = new int[] { -android.R.attr.state_enabled };
289c497221ee0e2e873f49653ff1a046df2e5276759Chris Banes            colors[i] = getDisabledThemeAttrColor(R.attr.colorControlNormal);
290469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes            i++;
291469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes
292c497221ee0e2e873f49653ff1a046df2e5276759Chris Banes            states[i] = new int[] { android.R.attr.state_focused };
293469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes            colors[i] = colorControlActivated;
294469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes            i++;
295469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes
296c497221ee0e2e873f49653ff1a046df2e5276759Chris Banes            states[i] = new int[] { android.R.attr.state_activated };
297469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes            colors[i] = colorControlActivated;
298469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes            i++;
299469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes
300c497221ee0e2e873f49653ff1a046df2e5276759Chris Banes            states[i] = new int[] { android.R.attr.state_pressed };
301469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes            colors[i] = colorControlActivated;
302469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes            i++;
303469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes
304c497221ee0e2e873f49653ff1a046df2e5276759Chris Banes            states[i] = new int[] { android.R.attr.state_checked };
305469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes            colors[i] = colorControlActivated;
306469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes            i++;
307469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes
308c497221ee0e2e873f49653ff1a046df2e5276759Chris Banes            states[i] = new int[] { android.R.attr.state_selected };
309c497221ee0e2e873f49653ff1a046df2e5276759Chris Banes            colors[i] = colorControlActivated;
310469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes            i++;
311469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes
312c497221ee0e2e873f49653ff1a046df2e5276759Chris Banes            // Default enabled state
313469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes            states[i] = new int[0];
314c497221ee0e2e873f49653ff1a046df2e5276759Chris Banes            colors[i] = colorControlNormal;
315469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes            i++;
316469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes
317469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes            mDefaultColorStateList = new ColorStateList(states, colors);
318469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes        }
319469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes        return mDefaultColorStateList;
320469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes    }
321469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes
32290075479814eb758d97b822606b448e1a521c298Chris Banes    private ColorStateList createSwitchTrackColorStateList() {
32390075479814eb758d97b822606b448e1a521c298Chris Banes        final int[][] states = new int[3][];
32490075479814eb758d97b822606b448e1a521c298Chris Banes        final int[] colors = new int[3];
32590075479814eb758d97b822606b448e1a521c298Chris Banes        int i = 0;
326415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
32790075479814eb758d97b822606b448e1a521c298Chris Banes        // Disabled state
32890075479814eb758d97b822606b448e1a521c298Chris Banes        states[i] = new int[]{-android.R.attr.state_enabled};
32990075479814eb758d97b822606b448e1a521c298Chris Banes        colors[i] = getThemeAttrColor(android.R.attr.colorForeground, 0.1f);
33090075479814eb758d97b822606b448e1a521c298Chris Banes        i++;
331415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
33290075479814eb758d97b822606b448e1a521c298Chris Banes        states[i] = new int[]{android.R.attr.state_checked};
33390075479814eb758d97b822606b448e1a521c298Chris Banes        colors[i] = getThemeAttrColor(R.attr.colorControlActivated, 0.3f);
33490075479814eb758d97b822606b448e1a521c298Chris Banes        i++;
335415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
33690075479814eb758d97b822606b448e1a521c298Chris Banes        // Default enabled state
33790075479814eb758d97b822606b448e1a521c298Chris Banes        states[i] = new int[0];
33890075479814eb758d97b822606b448e1a521c298Chris Banes        colors[i] = getThemeAttrColor(android.R.attr.colorForeground, 0.3f);
33990075479814eb758d97b822606b448e1a521c298Chris Banes        i++;
340415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
34190075479814eb758d97b822606b448e1a521c298Chris Banes        return new ColorStateList(states, colors);
342415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    }
343415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
34490075479814eb758d97b822606b448e1a521c298Chris Banes    private ColorStateList createSwitchThumbColorStateList() {
34590075479814eb758d97b822606b448e1a521c298Chris Banes        final int[][] states = new int[3][];
34690075479814eb758d97b822606b448e1a521c298Chris Banes        final int[] colors = new int[3];
34790075479814eb758d97b822606b448e1a521c298Chris Banes        int i = 0;
348415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
349414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes        final ColorStateList thumbColor = getThemeAttrColorStateList(R.attr.colorSwitchThumbNormal);
350415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
351414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes        if (thumbColor != null && thumbColor.isStateful()) {
352414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes            // If colorSwitchThumbNormal is a valid ColorStateList, extract the default and
353414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes            // disabled colors from it
354415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
355414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes            // Disabled state
356414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes            states[i] = new int[]{-android.R.attr.state_enabled};
357414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes            colors[i] = thumbColor.getColorForState(states[i], 0);
358414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes            i++;
359414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes
360414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes            states[i] = new int[]{android.R.attr.state_checked};
361414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes            colors[i] = getThemeAttrColor(R.attr.colorControlActivated);
362414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes            i++;
363414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes
364414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes            // Default enabled state
365414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes            states[i] = new int[0];
366414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes            colors[i] = thumbColor.getDefaultColor();
367414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes            i++;
368414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes        } else {
369414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes            // Else we'll use an approximation using the default disabled alpha
370414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes
371414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes            // Disabled state
372414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes            states[i] = new int[]{-android.R.attr.state_enabled};
373414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes            colors[i] = getDisabledThemeAttrColor(R.attr.colorSwitchThumbNormal);
374414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes            i++;
375414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes
376414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes            states[i] = new int[]{android.R.attr.state_checked};
377414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes            colors[i] = getThemeAttrColor(R.attr.colorControlActivated);
378414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes            i++;
379414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes
380414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes            // Default enabled state
381414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes            states[i] = new int[0];
382414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes            colors[i] = getThemeAttrColor(R.attr.colorSwitchThumbNormal);
383414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes            i++;
384414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes        }
385415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
38690075479814eb758d97b822606b448e1a521c298Chris Banes        return new ColorStateList(states, colors);
387415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    }
388415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
38990075479814eb758d97b822606b448e1a521c298Chris Banes    private ColorStateList createEditTextColorStateList() {
39090075479814eb758d97b822606b448e1a521c298Chris Banes        final int[][] states = new int[3][];
39190075479814eb758d97b822606b448e1a521c298Chris Banes        final int[] colors = new int[3];
39290075479814eb758d97b822606b448e1a521c298Chris Banes        int i = 0;
393a9585dae398a69dd67797e7ca86f44ffcabd9e86Chris Banes
39490075479814eb758d97b822606b448e1a521c298Chris Banes        // Disabled state
39590075479814eb758d97b822606b448e1a521c298Chris Banes        states[i] = new int[]{-android.R.attr.state_enabled};
39690075479814eb758d97b822606b448e1a521c298Chris Banes        colors[i] = getDisabledThemeAttrColor(R.attr.colorControlNormal);
39790075479814eb758d97b822606b448e1a521c298Chris Banes        i++;
398a9585dae398a69dd67797e7ca86f44ffcabd9e86Chris Banes
39990075479814eb758d97b822606b448e1a521c298Chris Banes        states[i] = new int[]{-android.R.attr.state_pressed, -android.R.attr.state_focused};
40090075479814eb758d97b822606b448e1a521c298Chris Banes        colors[i] = getThemeAttrColor(R.attr.colorControlNormal);
40190075479814eb758d97b822606b448e1a521c298Chris Banes        i++;
402a9585dae398a69dd67797e7ca86f44ffcabd9e86Chris Banes
40390075479814eb758d97b822606b448e1a521c298Chris Banes        // Default enabled state
40490075479814eb758d97b822606b448e1a521c298Chris Banes        states[i] = new int[0];
40590075479814eb758d97b822606b448e1a521c298Chris Banes        colors[i] = getThemeAttrColor(R.attr.colorControlActivated);
40690075479814eb758d97b822606b448e1a521c298Chris Banes        i++;
407a9585dae398a69dd67797e7ca86f44ffcabd9e86Chris Banes
40890075479814eb758d97b822606b448e1a521c298Chris Banes        return new ColorStateList(states, colors);
40990075479814eb758d97b822606b448e1a521c298Chris Banes    }
410a9585dae398a69dd67797e7ca86f44ffcabd9e86Chris Banes
41190075479814eb758d97b822606b448e1a521c298Chris Banes    private ColorStateList createButtonColorStateList() {
41290075479814eb758d97b822606b448e1a521c298Chris Banes        final int[][] states = new int[4][];
41390075479814eb758d97b822606b448e1a521c298Chris Banes        final int[] colors = new int[4];
41490075479814eb758d97b822606b448e1a521c298Chris Banes        int i = 0;
41590075479814eb758d97b822606b448e1a521c298Chris Banes
41690075479814eb758d97b822606b448e1a521c298Chris Banes        // Disabled state
41790075479814eb758d97b822606b448e1a521c298Chris Banes        states[i] = new int[]{-android.R.attr.state_enabled};
41890075479814eb758d97b822606b448e1a521c298Chris Banes        colors[i] = getDisabledThemeAttrColor(R.attr.colorButtonNormal);
41990075479814eb758d97b822606b448e1a521c298Chris Banes        i++;
42090075479814eb758d97b822606b448e1a521c298Chris Banes
42190075479814eb758d97b822606b448e1a521c298Chris Banes        states[i] = new int[]{android.R.attr.state_pressed};
42290075479814eb758d97b822606b448e1a521c298Chris Banes        colors[i] = getThemeAttrColor(R.attr.colorControlHighlight);
42390075479814eb758d97b822606b448e1a521c298Chris Banes        i++;
42490075479814eb758d97b822606b448e1a521c298Chris Banes
42590075479814eb758d97b822606b448e1a521c298Chris Banes        states[i] = new int[]{android.R.attr.state_focused};
42690075479814eb758d97b822606b448e1a521c298Chris Banes        colors[i] = getThemeAttrColor(R.attr.colorControlHighlight);
42790075479814eb758d97b822606b448e1a521c298Chris Banes        i++;
42890075479814eb758d97b822606b448e1a521c298Chris Banes
42990075479814eb758d97b822606b448e1a521c298Chris Banes        // Default enabled state
43090075479814eb758d97b822606b448e1a521c298Chris Banes        states[i] = new int[0];
43190075479814eb758d97b822606b448e1a521c298Chris Banes        colors[i] = getThemeAttrColor(R.attr.colorButtonNormal);
43290075479814eb758d97b822606b448e1a521c298Chris Banes        i++;
43390075479814eb758d97b822606b448e1a521c298Chris Banes
43490075479814eb758d97b822606b448e1a521c298Chris Banes        return new ColorStateList(states, colors);
435a9585dae398a69dd67797e7ca86f44ffcabd9e86Chris Banes    }
436a9585dae398a69dd67797e7ca86f44ffcabd9e86Chris Banes
437cdd1b1d70cefeb052c2b506738b396f2f982e519Chris Banes    private ColorStateList createSpinnerColorStateList() {
438cdd1b1d70cefeb052c2b506738b396f2f982e519Chris Banes        final int[][] states = new int[3][];
439cdd1b1d70cefeb052c2b506738b396f2f982e519Chris Banes        final int[] colors = new int[3];
440cdd1b1d70cefeb052c2b506738b396f2f982e519Chris Banes        int i = 0;
441cdd1b1d70cefeb052c2b506738b396f2f982e519Chris Banes
442cdd1b1d70cefeb052c2b506738b396f2f982e519Chris Banes        // Disabled state
443cdd1b1d70cefeb052c2b506738b396f2f982e519Chris Banes        states[i] = new int[]{-android.R.attr.state_enabled};
444cdd1b1d70cefeb052c2b506738b396f2f982e519Chris Banes        colors[i] = getDisabledThemeAttrColor(R.attr.colorControlNormal);
445cdd1b1d70cefeb052c2b506738b396f2f982e519Chris Banes        i++;
446cdd1b1d70cefeb052c2b506738b396f2f982e519Chris Banes
447cdd1b1d70cefeb052c2b506738b396f2f982e519Chris Banes        states[i] = new int[]{-android.R.attr.state_pressed, -android.R.attr.state_focused};
448cdd1b1d70cefeb052c2b506738b396f2f982e519Chris Banes        colors[i] = getThemeAttrColor(R.attr.colorControlNormal);
449cdd1b1d70cefeb052c2b506738b396f2f982e519Chris Banes        i++;
450cdd1b1d70cefeb052c2b506738b396f2f982e519Chris Banes
451cdd1b1d70cefeb052c2b506738b396f2f982e519Chris Banes        states[i] = new int[0];
452cdd1b1d70cefeb052c2b506738b396f2f982e519Chris Banes        colors[i] = getThemeAttrColor(R.attr.colorControlActivated);
453cdd1b1d70cefeb052c2b506738b396f2f982e519Chris Banes        i++;
454cdd1b1d70cefeb052c2b506738b396f2f982e519Chris Banes
455cdd1b1d70cefeb052c2b506738b396f2f982e519Chris Banes        return new ColorStateList(states, colors);
456cdd1b1d70cefeb052c2b506738b396f2f982e519Chris Banes    }
457cdd1b1d70cefeb052c2b506738b396f2f982e519Chris Banes
45890075479814eb758d97b822606b448e1a521c298Chris Banes    private int getThemeAttrColor(int attr) {
459f31fb9d2fd3b5b130f3f5ac121b033546d869231Chris Banes        if (mContext.getTheme().resolveAttribute(attr, mTypedValue, true)) {
460f31fb9d2fd3b5b130f3f5ac121b033546d869231Chris Banes            if (mTypedValue.type >= TypedValue.TYPE_FIRST_INT
461f31fb9d2fd3b5b130f3f5ac121b033546d869231Chris Banes                    && mTypedValue.type <= TypedValue.TYPE_LAST_INT) {
462f31fb9d2fd3b5b130f3f5ac121b033546d869231Chris Banes                return mTypedValue.data;
463f31fb9d2fd3b5b130f3f5ac121b033546d869231Chris Banes            } else if (mTypedValue.type == TypedValue.TYPE_STRING) {
464f31fb9d2fd3b5b130f3f5ac121b033546d869231Chris Banes                return mResources.getColor(mTypedValue.resourceId);
465f31fb9d2fd3b5b130f3f5ac121b033546d869231Chris Banes            }
466f31fb9d2fd3b5b130f3f5ac121b033546d869231Chris Banes        }
467f31fb9d2fd3b5b130f3f5ac121b033546d869231Chris Banes        return 0;
468469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes    }
469469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes
470414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes    private ColorStateList getThemeAttrColorStateList(int attr) {
471414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes        if (mContext.getTheme().resolveAttribute(attr, mTypedValue, true)) {
472414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes            if (mTypedValue.type == TypedValue.TYPE_STRING) {
473414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes                return mResources.getColorStateList(mTypedValue.resourceId);
474414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes            }
475414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes        }
476414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes        return null;
477414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes    }
478414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes
47990075479814eb758d97b822606b448e1a521c298Chris Banes    private int getThemeAttrColor(int attr, float alpha) {
480f31fb9d2fd3b5b130f3f5ac121b033546d869231Chris Banes        final int color = getThemeAttrColor(attr);
481c497221ee0e2e873f49653ff1a046df2e5276759Chris Banes        final int originalAlpha = Color.alpha(color);
482c497221ee0e2e873f49653ff1a046df2e5276759Chris Banes
483415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        // Return the color, multiplying the original alpha by the disabled value
484415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        return (color & 0x00ffffff) | (Math.round(originalAlpha * alpha) << 24);
485415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    }
486415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
48790075479814eb758d97b822606b448e1a521c298Chris Banes    private int getDisabledThemeAttrColor(int attr) {
488c497221ee0e2e873f49653ff1a046df2e5276759Chris Banes        // Now retrieve the disabledAlpha value from the theme
489c497221ee0e2e873f49653ff1a046df2e5276759Chris Banes        mContext.getTheme().resolveAttribute(android.R.attr.disabledAlpha, mTypedValue, true);
490c497221ee0e2e873f49653ff1a046df2e5276759Chris Banes        final float disabledAlpha = mTypedValue.getFloat();
491c497221ee0e2e873f49653ff1a046df2e5276759Chris Banes
492415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        return getThemeAttrColor(attr, disabledAlpha);
493c497221ee0e2e873f49653ff1a046df2e5276759Chris Banes    }
494c497221ee0e2e873f49653ff1a046df2e5276759Chris Banes
495469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes    private static class ColorFilterLruCache extends LruCache<Integer, PorterDuffColorFilter> {
496469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes
497469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes        public ColorFilterLruCache(int maxSize) {
498469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes            super(maxSize);
499469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes        }
500469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes
501469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes        PorterDuffColorFilter get(int color, PorterDuff.Mode mode) {
502469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes            return get(generateCacheKey(color, mode));
503469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes        }
504469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes
505469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes        PorterDuffColorFilter put(int color, PorterDuff.Mode mode, PorterDuffColorFilter filter) {
506469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes            return put(generateCacheKey(color, mode), filter);
507469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes        }
508469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes
509469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes        private static int generateCacheKey(int color, PorterDuff.Mode mode) {
510469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes            int hashCode = 1;
511469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes            hashCode = 31 * hashCode + color;
512469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes            hashCode = 31 * hashCode + mode.hashCode();
513469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes            return hashCode;
514469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes        }
515469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes    }
516a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banes
517a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banes    public static void tintViewBackground(View view, TintInfo tint) {
518a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banes        final Drawable background = view.getBackground();
519a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banes        if (tint.mTintList != null) {
520a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banes            tintDrawableUsingColorFilter(
521a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banes                    background,
522a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banes                    tint.mTintList.getColorForState(view.getDrawableState(),
523a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banes                            tint.mTintList.getDefaultColor()),
524a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banes                    tint.mTintMode != null ? tint.mTintMode : DEFAULT_MODE);
525a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banes        } else {
526a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banes            background.clearColorFilter();
527a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banes        }
528a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banes    }
529a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banes
530a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banes    private static void tintDrawableUsingColorFilter(Drawable drawable, int color,
531a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banes            PorterDuff.Mode mode) {
532a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banes        // First, lets see if the cache already contains the color filter
533a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banes        PorterDuffColorFilter filter = COLOR_FILTER_CACHE.get(color, mode);
534a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banes
535a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banes        if (filter == null) {
536a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banes            // Cache miss, so create a color filter and add it to the cache
537a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banes            filter = new PorterDuffColorFilter(color, mode);
538a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banes            COLOR_FILTER_CACHE.put(color, mode, filter);
539a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banes        }
540a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banes
541a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banes        drawable.setColorFilter(filter);
542a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banes    }
543469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes}
544