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;
2139cb4c1c59fa156ed28bc8835ef05eeb356ad13cChris Banesimport android.graphics.Color;
22469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banesimport android.graphics.PorterDuff;
23469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banesimport android.graphics.PorterDuffColorFilter;
24469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banesimport android.graphics.drawable.Drawable;
25cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banesimport android.graphics.drawable.LayerDrawable;
26fd1eb27a3700de31507de34fd1bcc51830fe876cChris Banesimport android.os.Build;
27469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banesimport android.support.v4.content.ContextCompat;
28eb0d0c030a15e93f456cc1403fffb909c0ae4e66Chris Banesimport android.support.v4.graphics.ColorUtils;
297e82b99953680915596eaf0eb35927388e574ca8Chris Banesimport android.support.v4.graphics.drawable.DrawableCompat;
30469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banesimport android.support.v4.util.LruCache;
31469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banesimport android.support.v7.appcompat.R;
32469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banesimport android.util.Log;
3390075479814eb758d97b822606b448e1a521c298Chris Banesimport android.util.SparseArray;
34a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banesimport android.view.View;
35469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes
36cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banesimport java.lang.ref.WeakReference;
37cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banesimport java.util.WeakHashMap;
38cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes
392aeb0f4237bca35d7f650c3145354416306d4f7bChris Banesimport static android.support.v7.internal.widget.ThemeUtils.getDisabledThemeAttrColor;
402aeb0f4237bca35d7f650c3145354416306d4f7bChris Banesimport static android.support.v7.internal.widget.ThemeUtils.getThemeAttrColor;
412aeb0f4237bca35d7f650c3145354416306d4f7bChris Banesimport static android.support.v7.internal.widget.ThemeUtils.getThemeAttrColorStateList;
422aeb0f4237bca35d7f650c3145354416306d4f7bChris Banes
43469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes/**
44469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes * @hide
45469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes */
46a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banespublic final class TintManager {
47469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes
482cccf609662389d6a23dbc0711d5fb2e826e8c63Chris Banes    public static final boolean SHOULD_BE_USED = Build.VERSION.SDK_INT < 21;
49fd1eb27a3700de31507de34fd1bcc51830fe876cChris Banes
50cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes    private static final String TAG = "TintManager";
51469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes    private static final boolean DEBUG = false;
52cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes    private static final PorterDuff.Mode DEFAULT_MODE = PorterDuff.Mode.SRC_IN;
53415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
54cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes    private static final WeakHashMap<Context, TintManager> INSTANCE_CACHE = new WeakHashMap<>();
55469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes    private static final ColorFilterLruCache COLOR_FILTER_CACHE = new ColorFilterLruCache(6);
56469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes
57469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes    /**
58469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes     * Drawables which should be tinted with the value of {@code R.attr.colorControlNormal},
59cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes     * using the default mode using a raw color filter.
60cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes     */
61cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes    private static final int[] COLORFILTER_TINT_COLOR_CONTROL_NORMAL = {
62cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes            R.drawable.abc_textfield_search_default_mtrl_alpha,
63cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes            R.drawable.abc_textfield_default_mtrl_alpha,
64cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes            R.drawable.abc_ab_share_pack_mtrl_alpha
65cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes    };
66cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes
67cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes    /**
68cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes     * Drawables which should be tinted with the value of {@code R.attr.colorControlNormal}, using
69cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes     * {@link DrawableCompat}'s tinting functionality.
70469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes     */
71469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes    private static final int[] TINT_COLOR_CONTROL_NORMAL = {
72469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes            R.drawable.abc_ic_ab_back_mtrl_am_alpha,
73469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes            R.drawable.abc_ic_go_search_api_mtrl_alpha,
74469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes            R.drawable.abc_ic_search_api_mtrl_alpha,
75469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes            R.drawable.abc_ic_commit_search_api_mtrl_alpha,
76469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes            R.drawable.abc_ic_clear_mtrl_alpha,
77469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes            R.drawable.abc_ic_menu_share_mtrl_alpha,
78b37a31664b07243ca9e86c8dac58b9be6a417e8cChris Banes            R.drawable.abc_ic_menu_copy_mtrl_am_alpha,
79b37a31664b07243ca9e86c8dac58b9be6a417e8cChris Banes            R.drawable.abc_ic_menu_cut_mtrl_alpha,
80b37a31664b07243ca9e86c8dac58b9be6a417e8cChris Banes            R.drawable.abc_ic_menu_selectall_mtrl_alpha,
81b37a31664b07243ca9e86c8dac58b9be6a417e8cChris Banes            R.drawable.abc_ic_menu_paste_mtrl_am_alpha,
82469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes            R.drawable.abc_ic_menu_moreoverflow_mtrl_alpha,
83cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes            R.drawable.abc_ic_voice_search_api_mtrl_alpha
84469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes    };
85469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes
86469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes    /**
87469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes     * Drawables which should be tinted with the value of {@code R.attr.colorControlActivated},
88cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes     * using a color filter.
89469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes     */
90cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes    private static final int[] COLORFILTER_COLOR_CONTROL_ACTIVATED = {
91469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes            R.drawable.abc_textfield_activated_mtrl_alpha,
9214f8f0dfae09445074dc0f7b5bbe5732d6922da7Chris Banes            R.drawable.abc_textfield_search_activated_mtrl_alpha,
93911642499da7d796aa1e7c19178c3552a590f48bChris Banes            R.drawable.abc_cab_background_top_mtrl_alpha,
94f65da421a61773f1ce03550230dcd6f58cee54e9Chris Banes            R.drawable.abc_text_cursor_material
95469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes    };
96469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes
97469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes    /**
98469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes     * Drawables which should be tinted with the value of {@code android.R.attr.colorBackground},
99cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes     * using the {@link android.graphics.PorterDuff.Mode#MULTIPLY} mode and a color filter.
100469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes     */
101cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes    private static final int[] COLORFILTER_COLOR_BACKGROUND_MULTIPLY = {
1021073132946bb0a53a788949fe4c060f72051cd57Chris Banes            R.drawable.abc_popup_background_mtrl_mult,
10357c6de90985a63358129b99b9f0cd4d6afe887d6Chris Banes            R.drawable.abc_cab_background_internal_bg,
10457c6de90985a63358129b99b9f0cd4d6afe887d6Chris Banes            R.drawable.abc_menu_hardkey_panel_mtrl_mult
105469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes    };
106469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes
107469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes    /**
108469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes     * Drawables which should be tinted using a state list containing values of
109469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes     * {@code R.attr.colorControlNormal} and {@code R.attr.colorControlActivated}
110469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes     */
111469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes    private static final int[] TINT_COLOR_CONTROL_STATE_LIST = {
112469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes            R.drawable.abc_edit_text_material,
113469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes            R.drawable.abc_tab_indicator_material,
114469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes            R.drawable.abc_textfield_search_material,
115415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            R.drawable.abc_spinner_mtrl_am_alpha,
116a9585dae398a69dd67797e7ca86f44ffcabd9e86Chris Banes            R.drawable.abc_spinner_textfield_background_material,
11790075479814eb758d97b822606b448e1a521c298Chris Banes            R.drawable.abc_ratingbar_full_material,
11890075479814eb758d97b822606b448e1a521c298Chris Banes            R.drawable.abc_switch_track_mtrl_alpha,
11990075479814eb758d97b822606b448e1a521c298Chris Banes            R.drawable.abc_switch_thumb_material,
12016e8d4dd91083260223c007c797e46bc8f631055Chris Banes            R.drawable.abc_btn_default_mtrl_shape,
12116e8d4dd91083260223c007c797e46bc8f631055Chris Banes            R.drawable.abc_btn_borderless_material
122469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes    };
123469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes
1241752dfd10e8242d6e017b3828c7d6e94f044691cChris Banes    /**
1251752dfd10e8242d6e017b3828c7d6e94f044691cChris Banes     * Drawables which should be tinted using a state list containing values of
1261752dfd10e8242d6e017b3828c7d6e94f044691cChris Banes     * {@code R.attr.colorControlNormal} and {@code R.attr.colorControlActivated} for the checked
1271752dfd10e8242d6e017b3828c7d6e94f044691cChris Banes     * state.
1281752dfd10e8242d6e017b3828c7d6e94f044691cChris Banes     */
1291752dfd10e8242d6e017b3828c7d6e94f044691cChris Banes    private static final int[] TINT_CHECKABLE_BUTTON_LIST = {
1301752dfd10e8242d6e017b3828c7d6e94f044691cChris Banes            R.drawable.abc_btn_check_material,
1311752dfd10e8242d6e017b3828c7d6e94f044691cChris Banes            R.drawable.abc_btn_radio_material
1321752dfd10e8242d6e017b3828c7d6e94f044691cChris Banes    };
1331752dfd10e8242d6e017b3828c7d6e94f044691cChris Banes
134cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes    private final WeakReference<Context> mContextRef;
135cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes    private SparseArray<ColorStateList> mTintLists;
136469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes    private ColorStateList mDefaultColorStateList;
137469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes
138469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes    /**
139cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes     * A helper method to get a {@link TintManager} and then call {@link #getDrawable(int)}.
140469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes     * This method should not be used routinely.
141469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes     */
142469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes    public static Drawable getDrawable(Context context, int resId) {
143b37a31664b07243ca9e86c8dac58b9be6a417e8cChris Banes        if (isInTintList(resId)) {
144cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes            return TintManager.get(context).getDrawable(resId);
145b37a31664b07243ca9e86c8dac58b9be6a417e8cChris Banes        } else {
146b37a31664b07243ca9e86c8dac58b9be6a417e8cChris Banes            return ContextCompat.getDrawable(context, resId);
147b37a31664b07243ca9e86c8dac58b9be6a417e8cChris Banes        }
148469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes    }
149469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes
150cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes    /**
151cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes     * Get a {@link android.support.v7.internal.widget.TintManager} instance.
152cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes     */
153cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes    public static TintManager get(Context context) {
154cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes        TintManager tm = INSTANCE_CACHE.get(context);
155cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes        if (tm == null) {
156cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes            tm = new TintManager(context);
157cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes            INSTANCE_CACHE.put(context, tm);
158cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes        }
159cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes        return tm;
160911642499da7d796aa1e7c19178c3552a590f48bChris Banes    }
161911642499da7d796aa1e7c19178c3552a590f48bChris Banes
162cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes    private TintManager(Context context) {
163cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes        mContextRef = new WeakReference<>(context);
164469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes    }
165469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes
166469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes    public Drawable getDrawable(int resId) {
1674ab820f4155444d20b37e105873775dd71907eefChris Banes        return getDrawable(resId, false);
1684ab820f4155444d20b37e105873775dd71907eefChris Banes    }
1694ab820f4155444d20b37e105873775dd71907eefChris Banes
1704ab820f4155444d20b37e105873775dd71907eefChris Banes    public Drawable getDrawable(int resId, boolean failIfNotKnown) {
171cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes        final Context context = mContextRef.get();
172cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes        if (context == null) return null;
173cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes
174cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes        Drawable drawable = ContextCompat.getDrawable(context, resId);
175469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes
176469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes        if (drawable != null) {
177fa91294114db5cfa43910d4d921fd06449192420Chris Banes            if (Build.VERSION.SDK_INT >= 8) {
178fa91294114db5cfa43910d4d921fd06449192420Chris Banes                // Mutate can cause NPEs on 2.1
179fa91294114db5cfa43910d4d921fd06449192420Chris Banes                drawable = drawable.mutate();
180fa91294114db5cfa43910d4d921fd06449192420Chris Banes            }
181c7d6de1eb6692c627a02a4ed439a8a4b849ca66cChris Banes
182cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes            final ColorStateList tintList = getTintList(resId);
183cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes            if (tintList != null) {
184cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes                // First wrap the Drawable and set the tint list
185cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes                drawable = DrawableCompat.wrap(drawable);
186cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes                DrawableCompat.setTintList(drawable, tintList);
187a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banes
188cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes                // If there is a blending mode specified for the drawable, use it
189cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes                final PorterDuff.Mode tintMode = getTintMode(resId);
190cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes                if (tintMode != null) {
1917e82b99953680915596eaf0eb35927388e574ca8Chris Banes                    DrawableCompat.setTintMode(drawable, tintMode);
192a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banes                }
193cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes            } else if (resId == R.drawable.abc_cab_background_top_material) {
194cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes                return new LayerDrawable(new Drawable[] {
195cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes                        getDrawable(R.drawable.abc_cab_background_internal_bg),
196cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes                        getDrawable(R.drawable.abc_cab_background_top_mtrl_alpha)
197cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes                });
198469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes            } else {
1994ab820f4155444d20b37e105873775dd71907eefChris Banes                final boolean usedColorFilter = tintDrawableUsingColorFilter(resId, drawable);
2004ab820f4155444d20b37e105873775dd71907eefChris Banes                if (!usedColorFilter && failIfNotKnown) {
2014ab820f4155444d20b37e105873775dd71907eefChris Banes                    // If we didn't tint using a ColorFilter, and we're set to fail if we don't
2024ab820f4155444d20b37e105873775dd71907eefChris Banes                    // know the id, return null
2034ab820f4155444d20b37e105873775dd71907eefChris Banes                    drawable = null;
2044ab820f4155444d20b37e105873775dd71907eefChris Banes                }
205469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes            }
206469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes        }
207469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes        return drawable;
208469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes    }
209469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes
2104ab820f4155444d20b37e105873775dd71907eefChris Banes    public final boolean tintDrawableUsingColorFilter(final int resId, Drawable drawable) {
211cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes        final Context context = mContextRef.get();
2124ab820f4155444d20b37e105873775dd71907eefChris Banes        if (context == null) return false;
213cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes
21439cb4c1c59fa156ed28bc8835ef05eeb356ad13cChris Banes        PorterDuff.Mode tintMode = DEFAULT_MODE;
215469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes        boolean colorAttrSet = false;
216469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes        int colorAttr = 0;
2170517b282bde8b9a0377dfe5bc4756405a196adb4Chris Banes        int alpha = -1;
218469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes
219cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes        if (arrayContains(COLORFILTER_TINT_COLOR_CONTROL_NORMAL, resId)) {
220469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes            colorAttr = R.attr.colorControlNormal;
221469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes            colorAttrSet = true;
222cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes        } else if (arrayContains(COLORFILTER_COLOR_CONTROL_ACTIVATED, resId)) {
223469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes            colorAttr = R.attr.colorControlActivated;
224469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes            colorAttrSet = true;
225cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes        } else if (arrayContains(COLORFILTER_COLOR_BACKGROUND_MULTIPLY, resId)) {
226469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes            colorAttr = android.R.attr.colorBackground;
227469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes            colorAttrSet = true;
228469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes            tintMode = PorterDuff.Mode.MULTIPLY;
2290517b282bde8b9a0377dfe5bc4756405a196adb4Chris Banes        } else if (resId == R.drawable.abc_list_divider_mtrl_alpha) {
2300517b282bde8b9a0377dfe5bc4756405a196adb4Chris Banes            colorAttr = android.R.attr.colorForeground;
2310517b282bde8b9a0377dfe5bc4756405a196adb4Chris Banes            colorAttrSet = true;
2320517b282bde8b9a0377dfe5bc4756405a196adb4Chris Banes            alpha = Math.round(0.16f * 255);
233469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes        }
234469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes
235f31fb9d2fd3b5b130f3f5ac121b033546d869231Chris Banes        if (colorAttrSet) {
236cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes            final int color = getThemeAttrColor(context, colorAttr);
23739cb4c1c59fa156ed28bc8835ef05eeb356ad13cChris Banes            drawable.setColorFilter(getPorterDuffColorFilter(color, tintMode));
238469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes
2390517b282bde8b9a0377dfe5bc4756405a196adb4Chris Banes            if (alpha != -1) {
2400517b282bde8b9a0377dfe5bc4756405a196adb4Chris Banes                drawable.setAlpha(alpha);
2410517b282bde8b9a0377dfe5bc4756405a196adb4Chris Banes            }
2420517b282bde8b9a0377dfe5bc4756405a196adb4Chris Banes
243469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes            if (DEBUG) {
244cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes                Log.d(TAG, "Tinted Drawable: " + context.getResources().getResourceName(resId) +
245469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes                        " with color: #" + Integer.toHexString(color));
246469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes            }
2474ab820f4155444d20b37e105873775dd71907eefChris Banes            return true;
248469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes        }
2494ab820f4155444d20b37e105873775dd71907eefChris Banes        return false;
250469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes    }
251469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes
252469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes    private static boolean arrayContains(int[] array, int value) {
253469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes        for (int id : array) {
254469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes            if (id == value) {
255469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes                return true;
256469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes            }
257469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes        }
258469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes        return false;
259469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes    }
260469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes
261b37a31664b07243ca9e86c8dac58b9be6a417e8cChris Banes    private static boolean isInTintList(int drawableId) {
262cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes        return arrayContains(TINT_COLOR_CONTROL_NORMAL, drawableId) ||
263cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes                arrayContains(COLORFILTER_TINT_COLOR_CONTROL_NORMAL, drawableId) ||
264cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes                arrayContains(COLORFILTER_COLOR_CONTROL_ACTIVATED, drawableId) ||
265b37a31664b07243ca9e86c8dac58b9be6a417e8cChris Banes                arrayContains(TINT_COLOR_CONTROL_STATE_LIST, drawableId) ||
266cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes                arrayContains(COLORFILTER_COLOR_BACKGROUND_MULTIPLY, drawableId) ||
2671752dfd10e8242d6e017b3828c7d6e94f044691cChris Banes                arrayContains(TINT_CHECKABLE_BUTTON_LIST, drawableId) ||
268cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes                drawableId == R.drawable.abc_cab_background_top_material;
269b37a31664b07243ca9e86c8dac58b9be6a417e8cChris Banes    }
270b37a31664b07243ca9e86c8dac58b9be6a417e8cChris Banes
271cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes    final PorterDuff.Mode getTintMode(final int resId) {
272cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes        PorterDuff.Mode mode = null;
273cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes
274cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes        if (resId == R.drawable.abc_switch_thumb_material) {
275cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes            mode = PorterDuff.Mode.MULTIPLY;
276cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes        }
277cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes
278cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes        return mode;
279a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banes    }
280a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banes
2812cccf609662389d6a23dbc0711d5fb2e826e8c63Chris Banes    public final ColorStateList getTintList(int resId) {
282cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes        final Context context = mContextRef.get();
283cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes        if (context == null) return null;
28490075479814eb758d97b822606b448e1a521c298Chris Banes
285cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes        // Try the cache first (if it exists)
286cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes        ColorStateList tint = mTintLists != null ? mTintLists.get(resId) : null;
287cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes
288cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes        if (tint == null) {
289cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes            // ...if the cache did not contain a color state list, try and create one
29090075479814eb758d97b822606b448e1a521c298Chris Banes            if (resId == R.drawable.abc_edit_text_material) {
291cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes                tint = createEditTextColorStateList(context);
29290075479814eb758d97b822606b448e1a521c298Chris Banes            } else if (resId == R.drawable.abc_switch_track_mtrl_alpha) {
293cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes                tint = createSwitchTrackColorStateList(context);
29490075479814eb758d97b822606b448e1a521c298Chris Banes            } else if (resId == R.drawable.abc_switch_thumb_material) {
295cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes                tint = createSwitchThumbColorStateList(context);
29616e8d4dd91083260223c007c797e46bc8f631055Chris Banes            } else if (resId == R.drawable.abc_btn_default_mtrl_shape
29716e8d4dd91083260223c007c797e46bc8f631055Chris Banes                    || resId == R.drawable.abc_btn_borderless_material) {
298b1131c6dfc9affe5751523f235878055cb699960Chris Banes                tint = createDefaultButtonColorStateList(context);
299b1131c6dfc9affe5751523f235878055cb699960Chris Banes            } else if (resId == R.drawable.abc_btn_colored_material) {
300b1131c6dfc9affe5751523f235878055cb699960Chris Banes                tint = createColoredButtonColorStateList(context);
301cdd1b1d70cefeb052c2b506738b396f2f982e519Chris Banes            } else if (resId == R.drawable.abc_spinner_mtrl_am_alpha
302cdd1b1d70cefeb052c2b506738b396f2f982e519Chris Banes                    || resId == R.drawable.abc_spinner_textfield_background_material) {
303cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes                tint = createSpinnerColorStateList(context);
304cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes            } else if (arrayContains(TINT_COLOR_CONTROL_NORMAL, resId)) {
305cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes                tint = getThemeAttrColorStateList(context, R.attr.colorControlNormal);
306cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes            } else if (arrayContains(TINT_COLOR_CONTROL_STATE_LIST, resId)) {
307cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes                tint = getDefaultColorStateList(context);
3081752dfd10e8242d6e017b3828c7d6e94f044691cChris Banes            } else if (arrayContains(TINT_CHECKABLE_BUTTON_LIST, resId)) {
3091752dfd10e8242d6e017b3828c7d6e94f044691cChris Banes                tint = createCheckableButtonColorStateList(context);
31090075479814eb758d97b822606b448e1a521c298Chris Banes            }
311a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banes
312cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes            if (tint != null) {
313cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes                if (mTintLists == null) {
314cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes                    // If our tint list cache hasn't been set up yet, create it
315cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes                    mTintLists = new SparseArray<>();
316cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes                }
317cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes                // Add any newly created ColorStateList to the cache
318cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes                mTintLists.append(resId, tint);
319cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes            }
32090075479814eb758d97b822606b448e1a521c298Chris Banes        }
321cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes        return tint;
32290075479814eb758d97b822606b448e1a521c298Chris Banes    }
32390075479814eb758d97b822606b448e1a521c298Chris Banes
324cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes    private ColorStateList getDefaultColorStateList(Context context) {
325469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes        if (mDefaultColorStateList == null) {
326469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes            /**
327469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes             * Generate the default color state list which uses the colorControl attributes.
328469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes             * Order is important here. The default enabled state needs to go at the bottom.
329469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes             */
330469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes
331cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes            final int colorControlNormal = getThemeAttrColor(context, R.attr.colorControlNormal);
332cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes            final int colorControlActivated = getThemeAttrColor(context,
3332aeb0f4237bca35d7f650c3145354416306d4f7bChris Banes                    R.attr.colorControlActivated);
334469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes
335469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes            final int[][] states = new int[7][];
336469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes            final int[] colors = new int[7];
337469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes            int i = 0;
338469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes
339c497221ee0e2e873f49653ff1a046df2e5276759Chris Banes            // Disabled state
340bb1a62b3eed874aa47ea4763ac972902c77e988fChris Banes            states[i] = ThemeUtils.DISABLED_STATE_SET;
341cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes            colors[i] = getDisabledThemeAttrColor(context, R.attr.colorControlNormal);
342469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes            i++;
343469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes
344bb1a62b3eed874aa47ea4763ac972902c77e988fChris Banes            states[i] = ThemeUtils.FOCUSED_STATE_SET;
345469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes            colors[i] = colorControlActivated;
346469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes            i++;
347469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes
348bb1a62b3eed874aa47ea4763ac972902c77e988fChris Banes            states[i] = ThemeUtils.ACTIVATED_STATE_SET;
349469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes            colors[i] = colorControlActivated;
350469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes            i++;
351469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes
352bb1a62b3eed874aa47ea4763ac972902c77e988fChris Banes            states[i] = ThemeUtils.PRESSED_STATE_SET;
353469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes            colors[i] = colorControlActivated;
354469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes            i++;
355469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes
356bb1a62b3eed874aa47ea4763ac972902c77e988fChris Banes            states[i] = ThemeUtils.CHECKED_STATE_SET;
357469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes            colors[i] = colorControlActivated;
358469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes            i++;
359469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes
360bb1a62b3eed874aa47ea4763ac972902c77e988fChris Banes            states[i] = ThemeUtils.SELECTED_STATE_SET;
361c497221ee0e2e873f49653ff1a046df2e5276759Chris Banes            colors[i] = colorControlActivated;
362469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes            i++;
363469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes
364c497221ee0e2e873f49653ff1a046df2e5276759Chris Banes            // Default enabled state
365bb1a62b3eed874aa47ea4763ac972902c77e988fChris Banes            states[i] = ThemeUtils.EMPTY_STATE_SET;
366c497221ee0e2e873f49653ff1a046df2e5276759Chris Banes            colors[i] = colorControlNormal;
367469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes            i++;
368469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes
369469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes            mDefaultColorStateList = new ColorStateList(states, colors);
370469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes        }
371469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes        return mDefaultColorStateList;
372469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes    }
373469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes
3741752dfd10e8242d6e017b3828c7d6e94f044691cChris Banes    private ColorStateList createCheckableButtonColorStateList(Context context) {
3751752dfd10e8242d6e017b3828c7d6e94f044691cChris Banes        final int[][] states = new int[3][];
3761752dfd10e8242d6e017b3828c7d6e94f044691cChris Banes        final int[] colors = new int[3];
3771752dfd10e8242d6e017b3828c7d6e94f044691cChris Banes        int i = 0;
3781752dfd10e8242d6e017b3828c7d6e94f044691cChris Banes
3791752dfd10e8242d6e017b3828c7d6e94f044691cChris Banes        // Disabled state
3801752dfd10e8242d6e017b3828c7d6e94f044691cChris Banes        states[i] = ThemeUtils.DISABLED_STATE_SET;
3811752dfd10e8242d6e017b3828c7d6e94f044691cChris Banes        colors[i] = getDisabledThemeAttrColor(context, R.attr.colorControlNormal);
3821752dfd10e8242d6e017b3828c7d6e94f044691cChris Banes        i++;
3831752dfd10e8242d6e017b3828c7d6e94f044691cChris Banes
3841752dfd10e8242d6e017b3828c7d6e94f044691cChris Banes        states[i] = ThemeUtils.CHECKED_STATE_SET;
3851752dfd10e8242d6e017b3828c7d6e94f044691cChris Banes        colors[i] = getThemeAttrColor(context, R.attr.colorControlActivated);
3861752dfd10e8242d6e017b3828c7d6e94f044691cChris Banes        i++;
3871752dfd10e8242d6e017b3828c7d6e94f044691cChris Banes
3881752dfd10e8242d6e017b3828c7d6e94f044691cChris Banes        // Default enabled state
3891752dfd10e8242d6e017b3828c7d6e94f044691cChris Banes        states[i] = ThemeUtils.EMPTY_STATE_SET;
3901752dfd10e8242d6e017b3828c7d6e94f044691cChris Banes        colors[i] = getThemeAttrColor(context, R.attr.colorControlNormal);
3911752dfd10e8242d6e017b3828c7d6e94f044691cChris Banes        i++;
3921752dfd10e8242d6e017b3828c7d6e94f044691cChris Banes
3931752dfd10e8242d6e017b3828c7d6e94f044691cChris Banes        return new ColorStateList(states, colors);
3941752dfd10e8242d6e017b3828c7d6e94f044691cChris Banes    }
3951752dfd10e8242d6e017b3828c7d6e94f044691cChris Banes
396cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes    private ColorStateList createSwitchTrackColorStateList(Context context) {
39790075479814eb758d97b822606b448e1a521c298Chris Banes        final int[][] states = new int[3][];
39890075479814eb758d97b822606b448e1a521c298Chris Banes        final int[] colors = new int[3];
39990075479814eb758d97b822606b448e1a521c298Chris Banes        int i = 0;
400415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
40190075479814eb758d97b822606b448e1a521c298Chris Banes        // Disabled state
402bb1a62b3eed874aa47ea4763ac972902c77e988fChris Banes        states[i] = ThemeUtils.DISABLED_STATE_SET;
403cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes        colors[i] = getThemeAttrColor(context, android.R.attr.colorForeground, 0.1f);
40490075479814eb758d97b822606b448e1a521c298Chris Banes        i++;
405415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
406bb1a62b3eed874aa47ea4763ac972902c77e988fChris Banes        states[i] = ThemeUtils.CHECKED_STATE_SET;
407cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes        colors[i] = getThemeAttrColor(context, R.attr.colorControlActivated, 0.3f);
40890075479814eb758d97b822606b448e1a521c298Chris Banes        i++;
409415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
41090075479814eb758d97b822606b448e1a521c298Chris Banes        // Default enabled state
411bb1a62b3eed874aa47ea4763ac972902c77e988fChris Banes        states[i] = ThemeUtils.EMPTY_STATE_SET;
412cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes        colors[i] = getThemeAttrColor(context, android.R.attr.colorForeground, 0.3f);
41390075479814eb758d97b822606b448e1a521c298Chris Banes        i++;
414415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
41590075479814eb758d97b822606b448e1a521c298Chris Banes        return new ColorStateList(states, colors);
416415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    }
417415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
418cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes    private ColorStateList createSwitchThumbColorStateList(Context context) {
41990075479814eb758d97b822606b448e1a521c298Chris Banes        final int[][] states = new int[3][];
42090075479814eb758d97b822606b448e1a521c298Chris Banes        final int[] colors = new int[3];
42190075479814eb758d97b822606b448e1a521c298Chris Banes        int i = 0;
422415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
423cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes        final ColorStateList thumbColor = getThemeAttrColorStateList(context,
4242aeb0f4237bca35d7f650c3145354416306d4f7bChris Banes                R.attr.colorSwitchThumbNormal);
425415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
426414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes        if (thumbColor != null && thumbColor.isStateful()) {
427414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes            // If colorSwitchThumbNormal is a valid ColorStateList, extract the default and
428414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes            // disabled colors from it
429415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
430414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes            // Disabled state
431bb1a62b3eed874aa47ea4763ac972902c77e988fChris Banes            states[i] = ThemeUtils.DISABLED_STATE_SET;
432414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes            colors[i] = thumbColor.getColorForState(states[i], 0);
433414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes            i++;
434414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes
435bb1a62b3eed874aa47ea4763ac972902c77e988fChris Banes            states[i] = ThemeUtils.CHECKED_STATE_SET;
436cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes            colors[i] = getThemeAttrColor(context, R.attr.colorControlActivated);
437414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes            i++;
438414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes
439414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes            // Default enabled state
440bb1a62b3eed874aa47ea4763ac972902c77e988fChris Banes            states[i] = ThemeUtils.EMPTY_STATE_SET;
441414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes            colors[i] = thumbColor.getDefaultColor();
442414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes            i++;
443414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes        } else {
444414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes            // Else we'll use an approximation using the default disabled alpha
445414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes
446414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes            // Disabled state
447bb1a62b3eed874aa47ea4763ac972902c77e988fChris Banes            states[i] = ThemeUtils.DISABLED_STATE_SET;
448cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes            colors[i] = getDisabledThemeAttrColor(context, R.attr.colorSwitchThumbNormal);
449414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes            i++;
450414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes
451bb1a62b3eed874aa47ea4763ac972902c77e988fChris Banes            states[i] = ThemeUtils.CHECKED_STATE_SET;
452cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes            colors[i] = getThemeAttrColor(context, R.attr.colorControlActivated);
453414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes            i++;
454414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes
455414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes            // Default enabled state
456bb1a62b3eed874aa47ea4763ac972902c77e988fChris Banes            states[i] = ThemeUtils.EMPTY_STATE_SET;
457cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes            colors[i] = getThemeAttrColor(context, R.attr.colorSwitchThumbNormal);
458414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes            i++;
459414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes        }
460415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
46190075479814eb758d97b822606b448e1a521c298Chris Banes        return new ColorStateList(states, colors);
462415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    }
463415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
464cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes    private ColorStateList createEditTextColorStateList(Context context) {
46590075479814eb758d97b822606b448e1a521c298Chris Banes        final int[][] states = new int[3][];
46690075479814eb758d97b822606b448e1a521c298Chris Banes        final int[] colors = new int[3];
46790075479814eb758d97b822606b448e1a521c298Chris Banes        int i = 0;
468a9585dae398a69dd67797e7ca86f44ffcabd9e86Chris Banes
46990075479814eb758d97b822606b448e1a521c298Chris Banes        // Disabled state
470bb1a62b3eed874aa47ea4763ac972902c77e988fChris Banes        states[i] = ThemeUtils.DISABLED_STATE_SET;
471cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes        colors[i] = getDisabledThemeAttrColor(context, R.attr.colorControlNormal);
47290075479814eb758d97b822606b448e1a521c298Chris Banes        i++;
473a9585dae398a69dd67797e7ca86f44ffcabd9e86Chris Banes
474bb1a62b3eed874aa47ea4763ac972902c77e988fChris Banes        states[i] = ThemeUtils.NOT_PRESSED_OR_FOCUSED_STATE_SET;
475cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes        colors[i] = getThemeAttrColor(context, R.attr.colorControlNormal);
47690075479814eb758d97b822606b448e1a521c298Chris Banes        i++;
477a9585dae398a69dd67797e7ca86f44ffcabd9e86Chris Banes
47890075479814eb758d97b822606b448e1a521c298Chris Banes        // Default enabled state
479bb1a62b3eed874aa47ea4763ac972902c77e988fChris Banes        states[i] = ThemeUtils.EMPTY_STATE_SET;
480cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes        colors[i] = getThemeAttrColor(context, R.attr.colorControlActivated);
48190075479814eb758d97b822606b448e1a521c298Chris Banes        i++;
482a9585dae398a69dd67797e7ca86f44ffcabd9e86Chris Banes
48390075479814eb758d97b822606b448e1a521c298Chris Banes        return new ColorStateList(states, colors);
48490075479814eb758d97b822606b448e1a521c298Chris Banes    }
485a9585dae398a69dd67797e7ca86f44ffcabd9e86Chris Banes
486b1131c6dfc9affe5751523f235878055cb699960Chris Banes    private ColorStateList createDefaultButtonColorStateList(Context context) {
487b1131c6dfc9affe5751523f235878055cb699960Chris Banes        return createButtonColorStateList(context, R.attr.colorButtonNormal);
488b1131c6dfc9affe5751523f235878055cb699960Chris Banes    }
489b1131c6dfc9affe5751523f235878055cb699960Chris Banes
490b1131c6dfc9affe5751523f235878055cb699960Chris Banes    private ColorStateList createColoredButtonColorStateList(Context context) {
491b1131c6dfc9affe5751523f235878055cb699960Chris Banes        return createButtonColorStateList(context, R.attr.colorAccent);
492b1131c6dfc9affe5751523f235878055cb699960Chris Banes    }
493b1131c6dfc9affe5751523f235878055cb699960Chris Banes
494b1131c6dfc9affe5751523f235878055cb699960Chris Banes    private ColorStateList createButtonColorStateList(Context context, int baseColorAttr) {
49590075479814eb758d97b822606b448e1a521c298Chris Banes        final int[][] states = new int[4][];
49690075479814eb758d97b822606b448e1a521c298Chris Banes        final int[] colors = new int[4];
49790075479814eb758d97b822606b448e1a521c298Chris Banes        int i = 0;
49890075479814eb758d97b822606b448e1a521c298Chris Banes
499b1131c6dfc9affe5751523f235878055cb699960Chris Banes        final int baseColor = getThemeAttrColor(context, baseColorAttr);
500eb0d0c030a15e93f456cc1403fffb909c0ae4e66Chris Banes        final int colorControlHighlight = getThemeAttrColor(context, R.attr.colorControlHighlight);
501eb0d0c030a15e93f456cc1403fffb909c0ae4e66Chris Banes
50290075479814eb758d97b822606b448e1a521c298Chris Banes        // Disabled state
503bb1a62b3eed874aa47ea4763ac972902c77e988fChris Banes        states[i] = ThemeUtils.DISABLED_STATE_SET;
504cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes        colors[i] = getDisabledThemeAttrColor(context, R.attr.colorButtonNormal);
50590075479814eb758d97b822606b448e1a521c298Chris Banes        i++;
50690075479814eb758d97b822606b448e1a521c298Chris Banes
507bb1a62b3eed874aa47ea4763ac972902c77e988fChris Banes        states[i] = ThemeUtils.PRESSED_STATE_SET;
508b1131c6dfc9affe5751523f235878055cb699960Chris Banes        colors[i] = ColorUtils.compositeColors(colorControlHighlight, baseColor);
50990075479814eb758d97b822606b448e1a521c298Chris Banes        i++;
51090075479814eb758d97b822606b448e1a521c298Chris Banes
511bb1a62b3eed874aa47ea4763ac972902c77e988fChris Banes        states[i] = ThemeUtils.FOCUSED_STATE_SET;
512b1131c6dfc9affe5751523f235878055cb699960Chris Banes        colors[i] = ColorUtils.compositeColors(colorControlHighlight, baseColor);
51390075479814eb758d97b822606b448e1a521c298Chris Banes        i++;
51490075479814eb758d97b822606b448e1a521c298Chris Banes
51590075479814eb758d97b822606b448e1a521c298Chris Banes        // Default enabled state
516bb1a62b3eed874aa47ea4763ac972902c77e988fChris Banes        states[i] = ThemeUtils.EMPTY_STATE_SET;
517b1131c6dfc9affe5751523f235878055cb699960Chris Banes        colors[i] = baseColor;
51890075479814eb758d97b822606b448e1a521c298Chris Banes        i++;
51990075479814eb758d97b822606b448e1a521c298Chris Banes
52090075479814eb758d97b822606b448e1a521c298Chris Banes        return new ColorStateList(states, colors);
521a9585dae398a69dd67797e7ca86f44ffcabd9e86Chris Banes    }
522a9585dae398a69dd67797e7ca86f44ffcabd9e86Chris Banes
523cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes    private ColorStateList createSpinnerColorStateList(Context context) {
524cdd1b1d70cefeb052c2b506738b396f2f982e519Chris Banes        final int[][] states = new int[3][];
525cdd1b1d70cefeb052c2b506738b396f2f982e519Chris Banes        final int[] colors = new int[3];
526cdd1b1d70cefeb052c2b506738b396f2f982e519Chris Banes        int i = 0;
527cdd1b1d70cefeb052c2b506738b396f2f982e519Chris Banes
528cdd1b1d70cefeb052c2b506738b396f2f982e519Chris Banes        // Disabled state
529bb1a62b3eed874aa47ea4763ac972902c77e988fChris Banes        states[i] = ThemeUtils.DISABLED_STATE_SET;
530cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes        colors[i] = getDisabledThemeAttrColor(context, R.attr.colorControlNormal);
531cdd1b1d70cefeb052c2b506738b396f2f982e519Chris Banes        i++;
532cdd1b1d70cefeb052c2b506738b396f2f982e519Chris Banes
533bb1a62b3eed874aa47ea4763ac972902c77e988fChris Banes        states[i] = ThemeUtils.NOT_PRESSED_OR_FOCUSED_STATE_SET;
534cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes        colors[i] = getThemeAttrColor(context, R.attr.colorControlNormal);
535cdd1b1d70cefeb052c2b506738b396f2f982e519Chris Banes        i++;
536cdd1b1d70cefeb052c2b506738b396f2f982e519Chris Banes
537bb1a62b3eed874aa47ea4763ac972902c77e988fChris Banes        states[i] = ThemeUtils.EMPTY_STATE_SET;
538cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes        colors[i] = getThemeAttrColor(context, R.attr.colorControlActivated);
539cdd1b1d70cefeb052c2b506738b396f2f982e519Chris Banes        i++;
540cdd1b1d70cefeb052c2b506738b396f2f982e519Chris Banes
541cdd1b1d70cefeb052c2b506738b396f2f982e519Chris Banes        return new ColorStateList(states, colors);
542cdd1b1d70cefeb052c2b506738b396f2f982e519Chris Banes    }
543cdd1b1d70cefeb052c2b506738b396f2f982e519Chris Banes
544469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes    private static class ColorFilterLruCache extends LruCache<Integer, PorterDuffColorFilter> {
545469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes
546469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes        public ColorFilterLruCache(int maxSize) {
547469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes            super(maxSize);
548469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes        }
549469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes
550469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes        PorterDuffColorFilter get(int color, PorterDuff.Mode mode) {
551469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes            return get(generateCacheKey(color, mode));
552469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes        }
553469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes
554469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes        PorterDuffColorFilter put(int color, PorterDuff.Mode mode, PorterDuffColorFilter filter) {
555469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes            return put(generateCacheKey(color, mode), filter);
556469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes        }
557469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes
558469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes        private static int generateCacheKey(int color, PorterDuff.Mode mode) {
559469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes            int hashCode = 1;
560469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes            hashCode = 31 * hashCode + color;
561469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes            hashCode = 31 * hashCode + mode.hashCode();
562469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes            return hashCode;
563469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes        }
564469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes    }
565a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banes
566a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banes    public static void tintViewBackground(View view, TintInfo tint) {
567a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banes        final Drawable background = view.getBackground();
56839cb4c1c59fa156ed28bc8835ef05eeb356ad13cChris Banes        if (tint.mHasTintList || tint.mHasTintMode) {
56939cb4c1c59fa156ed28bc8835ef05eeb356ad13cChris Banes            background.setColorFilter(createTintFilter(
57039cb4c1c59fa156ed28bc8835ef05eeb356ad13cChris Banes                    tint.mHasTintList ? tint.mTintList : null,
57139cb4c1c59fa156ed28bc8835ef05eeb356ad13cChris Banes                    tint.mHasTintMode ? tint.mTintMode : DEFAULT_MODE,
57239cb4c1c59fa156ed28bc8835ef05eeb356ad13cChris Banes                    view.getDrawableState()));
573a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banes        } else {
574a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banes            background.clearColorFilter();
575a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banes        }
576eb0d0c030a15e93f456cc1403fffb909c0ae4e66Chris Banes
577eb0d0c030a15e93f456cc1403fffb909c0ae4e66Chris Banes        if (Build.VERSION.SDK_INT <= 10) {
578eb0d0c030a15e93f456cc1403fffb909c0ae4e66Chris Banes            // On Gingerbread, GradientDrawable does not invalidate itself when it's ColorFilter
579eb0d0c030a15e93f456cc1403fffb909c0ae4e66Chris Banes            // has changed, so we need to force an invalidation
580eb0d0c030a15e93f456cc1403fffb909c0ae4e66Chris Banes            view.invalidate();
581eb0d0c030a15e93f456cc1403fffb909c0ae4e66Chris Banes        }
582a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banes    }
583a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banes
58439cb4c1c59fa156ed28bc8835ef05eeb356ad13cChris Banes    private static PorterDuffColorFilter createTintFilter(ColorStateList tint,
58539cb4c1c59fa156ed28bc8835ef05eeb356ad13cChris Banes            PorterDuff.Mode tintMode, final int[] state) {
58639cb4c1c59fa156ed28bc8835ef05eeb356ad13cChris Banes        if (tint == null || tintMode == null) {
58739cb4c1c59fa156ed28bc8835ef05eeb356ad13cChris Banes            return null;
588cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes        }
58939cb4c1c59fa156ed28bc8835ef05eeb356ad13cChris Banes        final int color = tint.getColorForState(state, Color.TRANSPARENT);
59039cb4c1c59fa156ed28bc8835ef05eeb356ad13cChris Banes        return getPorterDuffColorFilter(color, tintMode);
59139cb4c1c59fa156ed28bc8835ef05eeb356ad13cChris Banes    }
592cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes
59339cb4c1c59fa156ed28bc8835ef05eeb356ad13cChris Banes    private static PorterDuffColorFilter getPorterDuffColorFilter(int color, PorterDuff.Mode mode) {
594a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banes        // First, lets see if the cache already contains the color filter
595a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banes        PorterDuffColorFilter filter = COLOR_FILTER_CACHE.get(color, mode);
596a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banes
597a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banes        if (filter == null) {
598a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banes            // Cache miss, so create a color filter and add it to the cache
599a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banes            filter = new PorterDuffColorFilter(color, mode);
600a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banes            COLOR_FILTER_CACHE.put(color, mode, filter);
601a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banes        }
602a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banes
60339cb4c1c59fa156ed28bc8835ef05eeb356ad13cChris Banes        return filter;
604a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banes    }
605469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes}
606