AppCompatDrawableManager.java revision d20d889e7abef50efbaf6e975100a8fb73409b13
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
1766698bb15ba0f873aa1c2290cc50d6bb839a474aChris Banespackage android.support.v7.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;
2544eb323d09e9bc3baca24ef4a8595aafb7ed2652Chris Banesimport android.graphics.drawable.DrawableContainer;
26d20d889e7abef50efbaf6e975100a8fb73409b13Chris Banesimport android.graphics.drawable.GradientDrawable;
2744eb323d09e9bc3baca24ef4a8595aafb7ed2652Chris Banesimport android.graphics.drawable.InsetDrawable;
28cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banesimport android.graphics.drawable.LayerDrawable;
29d57359e205b2c04a4f0f0ecf9dcb8d6086e75663Chris Banesimport android.graphics.drawable.StateListDrawable;
30fd1eb27a3700de31507de34fd1bcc51830fe876cChris Banesimport android.os.Build;
317e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banesimport android.support.annotation.DrawableRes;
327e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banesimport android.support.annotation.NonNull;
337e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banesimport android.support.annotation.Nullable;
34469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banesimport android.support.v4.content.ContextCompat;
35eb0d0c030a15e93f456cc1403fffb909c0ae4e66Chris Banesimport android.support.v4.graphics.ColorUtils;
367e82b99953680915596eaf0eb35927388e574ca8Chris Banesimport android.support.v4.graphics.drawable.DrawableCompat;
37469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banesimport android.support.v4.util.LruCache;
38469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banesimport android.support.v7.appcompat.R;
39469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banesimport android.util.Log;
4090075479814eb758d97b822606b448e1a521c298Chris Banesimport android.util.SparseArray;
41469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes
427e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banesimport java.util.ArrayList;
43cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banesimport java.util.WeakHashMap;
44cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes
4566698bb15ba0f873aa1c2290cc50d6bb839a474aChris Banesimport static android.support.v7.widget.ThemeUtils.getDisabledThemeAttrColor;
4666698bb15ba0f873aa1c2290cc50d6bb839a474aChris Banesimport static android.support.v7.widget.ThemeUtils.getThemeAttrColor;
4766698bb15ba0f873aa1c2290cc50d6bb839a474aChris Banesimport static android.support.v7.widget.ThemeUtils.getThemeAttrColorStateList;
482aeb0f4237bca35d7f650c3145354416306d4f7bChris Banes
49469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes/**
50469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes * @hide
51469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes */
527e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banespublic final class AppCompatDrawableManager {
537e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes
547e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes    public interface InflateDelegate {
557e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes        /**
567e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes         * Allows custom inflation of a drawable resource.
577e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes         *
587e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes         * @param context Context to inflate/create with
597e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes         * @param resId Resource ID of the drawable
607e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes         * @return the created drawable, or {@code null} to leave inflation to
617e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes         * AppCompatDrawableManager.
627e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes         */
637e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes        @Nullable
647e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes        Drawable onInflateDrawable(@NonNull Context context, @DrawableRes int resId);
657e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes    }
66fd1eb27a3700de31507de34fd1bcc51830fe876cChris Banes
67cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes    private static final String TAG = "TintManager";
68469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes    private static final boolean DEBUG = false;
69cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes    private static final PorterDuff.Mode DEFAULT_MODE = PorterDuff.Mode.SRC_IN;
70415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
717e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes    private static AppCompatDrawableManager INSTANCE;
727e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes
737e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes    public static AppCompatDrawableManager get() {
747e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes        if (INSTANCE == null) {
757e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes            INSTANCE = new AppCompatDrawableManager();
767e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes        }
777e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes        return INSTANCE;
787e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes    }
797e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes
80469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes    private static final ColorFilterLruCache COLOR_FILTER_CACHE = new ColorFilterLruCache(6);
81469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes
82469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes    /**
83469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes     * Drawables which should be tinted with the value of {@code R.attr.colorControlNormal},
84cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes     * using the default mode using a raw color filter.
85cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes     */
86cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes    private static final int[] COLORFILTER_TINT_COLOR_CONTROL_NORMAL = {
87cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes            R.drawable.abc_textfield_search_default_mtrl_alpha,
88cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes            R.drawable.abc_textfield_default_mtrl_alpha,
89cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes            R.drawable.abc_ab_share_pack_mtrl_alpha
90cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes    };
91cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes
92cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes    /**
93cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes     * Drawables which should be tinted with the value of {@code R.attr.colorControlNormal}, using
94cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes     * {@link DrawableCompat}'s tinting functionality.
95469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes     */
96469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes    private static final int[] TINT_COLOR_CONTROL_NORMAL = {
97469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes            R.drawable.abc_ic_ab_back_mtrl_am_alpha,
98469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes            R.drawable.abc_ic_go_search_api_mtrl_alpha,
99469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes            R.drawable.abc_ic_search_api_mtrl_alpha,
100469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes            R.drawable.abc_ic_commit_search_api_mtrl_alpha,
101469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes            R.drawable.abc_ic_clear_mtrl_alpha,
102469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes            R.drawable.abc_ic_menu_share_mtrl_alpha,
103b37a31664b07243ca9e86c8dac58b9be6a417e8cChris Banes            R.drawable.abc_ic_menu_copy_mtrl_am_alpha,
104b37a31664b07243ca9e86c8dac58b9be6a417e8cChris Banes            R.drawable.abc_ic_menu_cut_mtrl_alpha,
105b37a31664b07243ca9e86c8dac58b9be6a417e8cChris Banes            R.drawable.abc_ic_menu_selectall_mtrl_alpha,
106b37a31664b07243ca9e86c8dac58b9be6a417e8cChris Banes            R.drawable.abc_ic_menu_paste_mtrl_am_alpha,
107469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes            R.drawable.abc_ic_menu_moreoverflow_mtrl_alpha,
108cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes            R.drawable.abc_ic_voice_search_api_mtrl_alpha
109469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes    };
110469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes
111469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes    /**
112469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes     * Drawables which should be tinted with the value of {@code R.attr.colorControlActivated},
113cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes     * using a color filter.
114469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes     */
115cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes    private static final int[] COLORFILTER_COLOR_CONTROL_ACTIVATED = {
116469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes            R.drawable.abc_textfield_activated_mtrl_alpha,
11714f8f0dfae09445074dc0f7b5bbe5732d6922da7Chris Banes            R.drawable.abc_textfield_search_activated_mtrl_alpha,
118911642499da7d796aa1e7c19178c3552a590f48bChris Banes            R.drawable.abc_cab_background_top_mtrl_alpha,
119f65da421a61773f1ce03550230dcd6f58cee54e9Chris Banes            R.drawable.abc_text_cursor_material
120469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes    };
121469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes
122469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes    /**
123469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes     * Drawables which should be tinted with the value of {@code android.R.attr.colorBackground},
124cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes     * using the {@link android.graphics.PorterDuff.Mode#MULTIPLY} mode and a color filter.
125469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes     */
126cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes    private static final int[] COLORFILTER_COLOR_BACKGROUND_MULTIPLY = {
1271073132946bb0a53a788949fe4c060f72051cd57Chris Banes            R.drawable.abc_popup_background_mtrl_mult,
12857c6de90985a63358129b99b9f0cd4d6afe887d6Chris Banes            R.drawable.abc_cab_background_internal_bg,
12957c6de90985a63358129b99b9f0cd4d6afe887d6Chris Banes            R.drawable.abc_menu_hardkey_panel_mtrl_mult
130469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes    };
131469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes
132469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes    /**
133469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes     * Drawables which should be tinted using a state list containing values of
134469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes     * {@code R.attr.colorControlNormal} and {@code R.attr.colorControlActivated}
135469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes     */
136469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes    private static final int[] TINT_COLOR_CONTROL_STATE_LIST = {
137469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes            R.drawable.abc_edit_text_material,
138469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes            R.drawable.abc_tab_indicator_material,
139469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes            R.drawable.abc_textfield_search_material,
140415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            R.drawable.abc_spinner_mtrl_am_alpha,
141a9585dae398a69dd67797e7ca86f44ffcabd9e86Chris Banes            R.drawable.abc_spinner_textfield_background_material,
14290075479814eb758d97b822606b448e1a521c298Chris Banes            R.drawable.abc_ratingbar_full_material,
14390075479814eb758d97b822606b448e1a521c298Chris Banes            R.drawable.abc_switch_track_mtrl_alpha,
14490075479814eb758d97b822606b448e1a521c298Chris Banes            R.drawable.abc_switch_thumb_material,
14516e8d4dd91083260223c007c797e46bc8f631055Chris Banes            R.drawable.abc_btn_default_mtrl_shape,
14616e8d4dd91083260223c007c797e46bc8f631055Chris Banes            R.drawable.abc_btn_borderless_material
147469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes    };
148469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes
1491752dfd10e8242d6e017b3828c7d6e94f044691cChris Banes    /**
1501752dfd10e8242d6e017b3828c7d6e94f044691cChris Banes     * Drawables which should be tinted using a state list containing values of
1511752dfd10e8242d6e017b3828c7d6e94f044691cChris Banes     * {@code R.attr.colorControlNormal} and {@code R.attr.colorControlActivated} for the checked
1521752dfd10e8242d6e017b3828c7d6e94f044691cChris Banes     * state.
1531752dfd10e8242d6e017b3828c7d6e94f044691cChris Banes     */
1541752dfd10e8242d6e017b3828c7d6e94f044691cChris Banes    private static final int[] TINT_CHECKABLE_BUTTON_LIST = {
1551752dfd10e8242d6e017b3828c7d6e94f044691cChris Banes            R.drawable.abc_btn_check_material,
1561752dfd10e8242d6e017b3828c7d6e94f044691cChris Banes            R.drawable.abc_btn_radio_material
1571752dfd10e8242d6e017b3828c7d6e94f044691cChris Banes    };
1581752dfd10e8242d6e017b3828c7d6e94f044691cChris Banes
1597e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes    private WeakHashMap<Context, SparseArray<ColorStateList>> mTintLists;
1607e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes    private ArrayList<InflateDelegate> mDelegates;
161469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes
1627e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes    public Drawable getDrawable(@NonNull Context context, @DrawableRes int resId) {
1637e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes        return getDrawable(context, resId, false);
164469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes    }
165469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes
1667e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes    public Drawable getDrawable(@NonNull Context context, @DrawableRes int resId,
1677e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes            boolean failIfNotKnown) {
1687e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes        // Let the InflateDelegates have a go first
1697e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes        if (mDelegates != null) {
1707e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes            for (int i = 0, count = mDelegates.size(); i < count; i++) {
1717e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes                final InflateDelegate delegate = mDelegates.get(i);
1727e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes                final Drawable result = delegate.onInflateDrawable(context, resId);
1737e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes                if (result != null) {
1747e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes                    return result;
1757e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes                }
1767e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes            }
177cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes        }
178cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes
1797e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes        // The delegates failed so we'll carry on
180cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes        Drawable drawable = ContextCompat.getDrawable(context, resId);
181469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes
182469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes        if (drawable != null) {
1837e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes            final ColorStateList tintList = getTintList(context, resId);
184cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes            if (tintList != null) {
185d20d889e7abef50efbaf6e975100a8fb73409b13Chris Banes                // First mutate the Drawable, then wrap it and set the tint list
186d20d889e7abef50efbaf6e975100a8fb73409b13Chris Banes                if (shouldMutateDrawable(drawable)) {
187d20d889e7abef50efbaf6e975100a8fb73409b13Chris Banes                    drawable = drawable.mutate();
188d20d889e7abef50efbaf6e975100a8fb73409b13Chris Banes                }
189cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes                drawable = DrawableCompat.wrap(drawable);
190cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes                DrawableCompat.setTintList(drawable, tintList);
191a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banes
192cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes                // If there is a blending mode specified for the drawable, use it
193cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes                final PorterDuff.Mode tintMode = getTintMode(resId);
194cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes                if (tintMode != null) {
1957e82b99953680915596eaf0eb35927388e574ca8Chris Banes                    DrawableCompat.setTintMode(drawable, tintMode);
196a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banes                }
197cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes            } else if (resId == R.drawable.abc_cab_background_top_material) {
19810e2dbc1ad1c01d2824d921a8b0f070859d6f146Chris Banes                return new LayerDrawable(new Drawable[]{
1997e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes                        getDrawable(context, R.drawable.abc_cab_background_internal_bg),
2007e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes                        getDrawable(context, R.drawable.abc_cab_background_top_mtrl_alpha)
201cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes                });
20210e2dbc1ad1c01d2824d921a8b0f070859d6f146Chris Banes            } else if (resId == R.drawable.abc_seekbar_track_material) {
20310e2dbc1ad1c01d2824d921a8b0f070859d6f146Chris Banes                LayerDrawable ld = (LayerDrawable) drawable;
20410e2dbc1ad1c01d2824d921a8b0f070859d6f146Chris Banes                setPorterDuffColorFilter(ld.findDrawableByLayerId(android.R.id.background),
20510e2dbc1ad1c01d2824d921a8b0f070859d6f146Chris Banes                        getThemeAttrColor(context, R.attr.colorControlNormal), DEFAULT_MODE);
20610e2dbc1ad1c01d2824d921a8b0f070859d6f146Chris Banes                setPorterDuffColorFilter(ld.findDrawableByLayerId(android.R.id.secondaryProgress),
20710e2dbc1ad1c01d2824d921a8b0f070859d6f146Chris Banes                        getThemeAttrColor(context, R.attr.colorControlNormal), DEFAULT_MODE);
20810e2dbc1ad1c01d2824d921a8b0f070859d6f146Chris Banes                setPorterDuffColorFilter(ld.findDrawableByLayerId(android.R.id.progress),
20910e2dbc1ad1c01d2824d921a8b0f070859d6f146Chris Banes                        getThemeAttrColor(context, R.attr.colorControlActivated), DEFAULT_MODE);
2107c8ba0f0cd06a684132260d36840658be317d4c7Chris Banes            } else if (resId == R.drawable.abc_ratingbar_indicator_material
2117c8ba0f0cd06a684132260d36840658be317d4c7Chris Banes                    || resId == R.drawable.abc_ratingbar_small_material) {
2127c8ba0f0cd06a684132260d36840658be317d4c7Chris Banes                LayerDrawable ld = (LayerDrawable) drawable;
2137c8ba0f0cd06a684132260d36840658be317d4c7Chris Banes                setPorterDuffColorFilter(ld.findDrawableByLayerId(android.R.id.background),
2147c8ba0f0cd06a684132260d36840658be317d4c7Chris Banes                        getDisabledThemeAttrColor(context, R.attr.colorControlNormal),
2157c8ba0f0cd06a684132260d36840658be317d4c7Chris Banes                        DEFAULT_MODE);
2167c8ba0f0cd06a684132260d36840658be317d4c7Chris Banes                setPorterDuffColorFilter(ld.findDrawableByLayerId(android.R.id.secondaryProgress),
2177c8ba0f0cd06a684132260d36840658be317d4c7Chris Banes                        getThemeAttrColor(context, R.attr.colorControlActivated), DEFAULT_MODE);
2187c8ba0f0cd06a684132260d36840658be317d4c7Chris Banes                setPorterDuffColorFilter(ld.findDrawableByLayerId(android.R.id.progress),
2197c8ba0f0cd06a684132260d36840658be317d4c7Chris Banes                        getThemeAttrColor(context, R.attr.colorControlActivated), DEFAULT_MODE);
220469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes            } else {
2217e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes                final boolean tinted = tintDrawableUsingColorFilter(context, resId, drawable);
2227e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes                if (!tinted && failIfNotKnown) {
2234ab820f4155444d20b37e105873775dd71907eefChris Banes                    // If we didn't tint using a ColorFilter, and we're set to fail if we don't
2244ab820f4155444d20b37e105873775dd71907eefChris Banes                    // know the id, return null
2254ab820f4155444d20b37e105873775dd71907eefChris Banes                    drawable = null;
2264ab820f4155444d20b37e105873775dd71907eefChris Banes                }
227469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes            }
228469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes        }
229469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes        return drawable;
230469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes    }
231469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes
2327e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes    public final boolean tintDrawableUsingColorFilter(@NonNull Context context,
2337e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes            @DrawableRes final int resId, @NonNull Drawable drawable) {
23439cb4c1c59fa156ed28bc8835ef05eeb356ad13cChris Banes        PorterDuff.Mode tintMode = DEFAULT_MODE;
235469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes        boolean colorAttrSet = false;
236469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes        int colorAttr = 0;
2370517b282bde8b9a0377dfe5bc4756405a196adb4Chris Banes        int alpha = -1;
238469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes
239cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes        if (arrayContains(COLORFILTER_TINT_COLOR_CONTROL_NORMAL, resId)) {
240469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes            colorAttr = R.attr.colorControlNormal;
241469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes            colorAttrSet = true;
242cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes        } else if (arrayContains(COLORFILTER_COLOR_CONTROL_ACTIVATED, resId)) {
243469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes            colorAttr = R.attr.colorControlActivated;
244469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes            colorAttrSet = true;
245cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes        } else if (arrayContains(COLORFILTER_COLOR_BACKGROUND_MULTIPLY, resId)) {
246469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes            colorAttr = android.R.attr.colorBackground;
247469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes            colorAttrSet = true;
248469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes            tintMode = PorterDuff.Mode.MULTIPLY;
2490517b282bde8b9a0377dfe5bc4756405a196adb4Chris Banes        } else if (resId == R.drawable.abc_list_divider_mtrl_alpha) {
2500517b282bde8b9a0377dfe5bc4756405a196adb4Chris Banes            colorAttr = android.R.attr.colorForeground;
2510517b282bde8b9a0377dfe5bc4756405a196adb4Chris Banes            colorAttrSet = true;
2520517b282bde8b9a0377dfe5bc4756405a196adb4Chris Banes            alpha = Math.round(0.16f * 255);
253469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes        }
254469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes
255f31fb9d2fd3b5b130f3f5ac121b033546d869231Chris Banes        if (colorAttrSet) {
256d20d889e7abef50efbaf6e975100a8fb73409b13Chris Banes            if (shouldMutateDrawable(drawable)) {
257d20d889e7abef50efbaf6e975100a8fb73409b13Chris Banes                drawable = drawable.mutate();
258d20d889e7abef50efbaf6e975100a8fb73409b13Chris Banes            }
259d20d889e7abef50efbaf6e975100a8fb73409b13Chris Banes
260cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes            final int color = getThemeAttrColor(context, colorAttr);
26139cb4c1c59fa156ed28bc8835ef05eeb356ad13cChris Banes            drawable.setColorFilter(getPorterDuffColorFilter(color, tintMode));
262469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes
2630517b282bde8b9a0377dfe5bc4756405a196adb4Chris Banes            if (alpha != -1) {
2640517b282bde8b9a0377dfe5bc4756405a196adb4Chris Banes                drawable.setAlpha(alpha);
2650517b282bde8b9a0377dfe5bc4756405a196adb4Chris Banes            }
2660517b282bde8b9a0377dfe5bc4756405a196adb4Chris Banes
267469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes            if (DEBUG) {
268cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes                Log.d(TAG, "Tinted Drawable: " + context.getResources().getResourceName(resId) +
269469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes                        " with color: #" + Integer.toHexString(color));
270469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes            }
2714ab820f4155444d20b37e105873775dd71907eefChris Banes            return true;
272469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes        }
2734ab820f4155444d20b37e105873775dd71907eefChris Banes        return false;
274469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes    }
275469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes
2767e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes    public void addDelegate(@NonNull InflateDelegate delegate) {
2777e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes        if (mDelegates == null) {
2787e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes            mDelegates = new ArrayList<>();
2797e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes        }
2807e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes        if (!mDelegates.contains(delegate)) {
2817e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes            mDelegates.add(delegate);
2827e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes        }
2837e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes    }
2847e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes
2857e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes    public void removeDelegate(@NonNull InflateDelegate delegate) {
2867e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes        if (mDelegates != null) {
2877e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes            mDelegates.remove(delegate);
2887e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes        }
2897e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes    }
2907e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes
291469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes    private static boolean arrayContains(int[] array, int value) {
292469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes        for (int id : array) {
293469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes            if (id == value) {
294469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes                return true;
295469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes            }
296469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes        }
297469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes        return false;
298469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes    }
299469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes
300cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes    final PorterDuff.Mode getTintMode(final int resId) {
301cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes        PorterDuff.Mode mode = null;
302cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes
303cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes        if (resId == R.drawable.abc_switch_thumb_material) {
304cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes            mode = PorterDuff.Mode.MULTIPLY;
305cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes        }
306cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes
307cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes        return mode;
308a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banes    }
309a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banes
3107e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes    public final ColorStateList getTintList(@NonNull Context context, @DrawableRes int resId) {
311cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes        // Try the cache first (if it exists)
3127e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes        ColorStateList tint = getTintListFromCache(context, resId);
313cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes
314cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes        if (tint == null) {
315cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes            // ...if the cache did not contain a color state list, try and create one
31690075479814eb758d97b822606b448e1a521c298Chris Banes            if (resId == R.drawable.abc_edit_text_material) {
317cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes                tint = createEditTextColorStateList(context);
31890075479814eb758d97b822606b448e1a521c298Chris Banes            } else if (resId == R.drawable.abc_switch_track_mtrl_alpha) {
319cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes                tint = createSwitchTrackColorStateList(context);
32090075479814eb758d97b822606b448e1a521c298Chris Banes            } else if (resId == R.drawable.abc_switch_thumb_material) {
321cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes                tint = createSwitchThumbColorStateList(context);
32216e8d4dd91083260223c007c797e46bc8f631055Chris Banes            } else if (resId == R.drawable.abc_btn_default_mtrl_shape
32378e25bf358f609c55530f95e2a36abe84e80b41fChris Banes                    || resId == R.drawable.abc_btn_borderless_material) {
324b1131c6dfc9affe5751523f235878055cb699960Chris Banes                tint = createDefaultButtonColorStateList(context);
325b1131c6dfc9affe5751523f235878055cb699960Chris Banes            } else if (resId == R.drawable.abc_btn_colored_material) {
326b1131c6dfc9affe5751523f235878055cb699960Chris Banes                tint = createColoredButtonColorStateList(context);
327cdd1b1d70cefeb052c2b506738b396f2f982e519Chris Banes            } else if (resId == R.drawable.abc_spinner_mtrl_am_alpha
328cdd1b1d70cefeb052c2b506738b396f2f982e519Chris Banes                    || resId == R.drawable.abc_spinner_textfield_background_material) {
329cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes                tint = createSpinnerColorStateList(context);
330cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes            } else if (arrayContains(TINT_COLOR_CONTROL_NORMAL, resId)) {
331cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes                tint = getThemeAttrColorStateList(context, R.attr.colorControlNormal);
332cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes            } else if (arrayContains(TINT_COLOR_CONTROL_STATE_LIST, resId)) {
3337e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes                tint = createDefaultColorStateList(context);
3341752dfd10e8242d6e017b3828c7d6e94f044691cChris Banes            } else if (arrayContains(TINT_CHECKABLE_BUTTON_LIST, resId)) {
3351752dfd10e8242d6e017b3828c7d6e94f044691cChris Banes                tint = createCheckableButtonColorStateList(context);
33610e2dbc1ad1c01d2824d921a8b0f070859d6f146Chris Banes            } else if (resId == R.drawable.abc_seekbar_thumb_material) {
33710e2dbc1ad1c01d2824d921a8b0f070859d6f146Chris Banes                tint = createSeekbarThumbColorStateList(context);
33890075479814eb758d97b822606b448e1a521c298Chris Banes            }
339a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banes
340cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes            if (tint != null) {
3417e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes                addTintListToCache(context, resId, tint);
342cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes            }
34390075479814eb758d97b822606b448e1a521c298Chris Banes        }
344cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes        return tint;
34590075479814eb758d97b822606b448e1a521c298Chris Banes    }
34690075479814eb758d97b822606b448e1a521c298Chris Banes
3477e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes    private ColorStateList getTintListFromCache(@NonNull Context context, @DrawableRes int resId) {
3487e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes        if (mTintLists != null) {
3497e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes            final SparseArray<ColorStateList> tints = mTintLists.get(context);
3507e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes            return tints != null ? tints.get(resId) : null;
3517e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes        }
3527e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes        return null;
3537e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes    }
354469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes
3557e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes    private void addTintListToCache(@NonNull Context context, @DrawableRes int resId,
3567e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes            @NonNull ColorStateList tintList) {
3577e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes        if (mTintLists == null) {
3587e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes            mTintLists = new WeakHashMap<>();
3597e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes        }
3607e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes        SparseArray<ColorStateList> themeTints = mTintLists.get(context);
3617e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes        if (themeTints == null) {
3627e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes            themeTints = new SparseArray<>();
3637e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes            mTintLists.put(context, themeTints);
3647e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes        }
3657e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes        themeTints.append(resId, tintList);
3667e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes    }
367469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes
3687e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes    private ColorStateList createDefaultColorStateList(Context context) {
3697e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes        /**
3707e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes         * Generate the default color state list which uses the colorControl attributes.
3717e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes         * Order is important here. The default enabled state needs to go at the bottom.
3727e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes         */
373469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes
3747e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes        final int colorControlNormal = getThemeAttrColor(context, R.attr.colorControlNormal);
37566698bb15ba0f873aa1c2290cc50d6bb839a474aChris Banes        final int colorControlActivated = getThemeAttrColor(context, R.attr.colorControlActivated);
376469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes
3777e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes        final int[][] states = new int[7][];
3787e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes        final int[] colors = new int[7];
3797e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes        int i = 0;
380469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes
3817e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes        // Disabled state
3827e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes        states[i] = ThemeUtils.DISABLED_STATE_SET;
3837e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes        colors[i] = getDisabledThemeAttrColor(context, R.attr.colorControlNormal);
3847e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes        i++;
385469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes
3867e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes        states[i] = ThemeUtils.FOCUSED_STATE_SET;
3877e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes        colors[i] = colorControlActivated;
3887e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes        i++;
389469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes
3907e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes        states[i] = ThemeUtils.ACTIVATED_STATE_SET;
3917e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes        colors[i] = colorControlActivated;
3927e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes        i++;
393469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes
3947e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes        states[i] = ThemeUtils.PRESSED_STATE_SET;
3957e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes        colors[i] = colorControlActivated;
3967e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes        i++;
397469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes
3987e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes        states[i] = ThemeUtils.CHECKED_STATE_SET;
3997e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes        colors[i] = colorControlActivated;
4007e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes        i++;
401469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes
4027e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes        states[i] = ThemeUtils.SELECTED_STATE_SET;
4037e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes        colors[i] = colorControlActivated;
4047e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes        i++;
4057e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes
4067e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes        // Default enabled state
4077e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes        states[i] = ThemeUtils.EMPTY_STATE_SET;
4087e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes        colors[i] = colorControlNormal;
4097e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes        i++;
4107e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes
4117e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes        return new ColorStateList(states, colors);
412469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes    }
413469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes
4141752dfd10e8242d6e017b3828c7d6e94f044691cChris Banes    private ColorStateList createCheckableButtonColorStateList(Context context) {
4151752dfd10e8242d6e017b3828c7d6e94f044691cChris Banes        final int[][] states = new int[3][];
4161752dfd10e8242d6e017b3828c7d6e94f044691cChris Banes        final int[] colors = new int[3];
4171752dfd10e8242d6e017b3828c7d6e94f044691cChris Banes        int i = 0;
4181752dfd10e8242d6e017b3828c7d6e94f044691cChris Banes
4191752dfd10e8242d6e017b3828c7d6e94f044691cChris Banes        // Disabled state
4201752dfd10e8242d6e017b3828c7d6e94f044691cChris Banes        states[i] = ThemeUtils.DISABLED_STATE_SET;
4211752dfd10e8242d6e017b3828c7d6e94f044691cChris Banes        colors[i] = getDisabledThemeAttrColor(context, R.attr.colorControlNormal);
4221752dfd10e8242d6e017b3828c7d6e94f044691cChris Banes        i++;
4231752dfd10e8242d6e017b3828c7d6e94f044691cChris Banes
4241752dfd10e8242d6e017b3828c7d6e94f044691cChris Banes        states[i] = ThemeUtils.CHECKED_STATE_SET;
4251752dfd10e8242d6e017b3828c7d6e94f044691cChris Banes        colors[i] = getThemeAttrColor(context, R.attr.colorControlActivated);
4261752dfd10e8242d6e017b3828c7d6e94f044691cChris Banes        i++;
4271752dfd10e8242d6e017b3828c7d6e94f044691cChris Banes
4281752dfd10e8242d6e017b3828c7d6e94f044691cChris Banes        // Default enabled state
4291752dfd10e8242d6e017b3828c7d6e94f044691cChris Banes        states[i] = ThemeUtils.EMPTY_STATE_SET;
4301752dfd10e8242d6e017b3828c7d6e94f044691cChris Banes        colors[i] = getThemeAttrColor(context, R.attr.colorControlNormal);
4311752dfd10e8242d6e017b3828c7d6e94f044691cChris Banes        i++;
4321752dfd10e8242d6e017b3828c7d6e94f044691cChris Banes
4331752dfd10e8242d6e017b3828c7d6e94f044691cChris Banes        return new ColorStateList(states, colors);
4341752dfd10e8242d6e017b3828c7d6e94f044691cChris Banes    }
4351752dfd10e8242d6e017b3828c7d6e94f044691cChris Banes
436cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes    private ColorStateList createSwitchTrackColorStateList(Context context) {
43790075479814eb758d97b822606b448e1a521c298Chris Banes        final int[][] states = new int[3][];
43890075479814eb758d97b822606b448e1a521c298Chris Banes        final int[] colors = new int[3];
43990075479814eb758d97b822606b448e1a521c298Chris Banes        int i = 0;
440415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
44190075479814eb758d97b822606b448e1a521c298Chris Banes        // Disabled state
442bb1a62b3eed874aa47ea4763ac972902c77e988fChris Banes        states[i] = ThemeUtils.DISABLED_STATE_SET;
443cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes        colors[i] = getThemeAttrColor(context, android.R.attr.colorForeground, 0.1f);
44490075479814eb758d97b822606b448e1a521c298Chris Banes        i++;
445415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
446bb1a62b3eed874aa47ea4763ac972902c77e988fChris Banes        states[i] = ThemeUtils.CHECKED_STATE_SET;
447cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes        colors[i] = getThemeAttrColor(context, R.attr.colorControlActivated, 0.3f);
44890075479814eb758d97b822606b448e1a521c298Chris Banes        i++;
449415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
45090075479814eb758d97b822606b448e1a521c298Chris Banes        // Default enabled state
451bb1a62b3eed874aa47ea4763ac972902c77e988fChris Banes        states[i] = ThemeUtils.EMPTY_STATE_SET;
452cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes        colors[i] = getThemeAttrColor(context, android.R.attr.colorForeground, 0.3f);
45390075479814eb758d97b822606b448e1a521c298Chris Banes        i++;
454415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
45590075479814eb758d97b822606b448e1a521c298Chris Banes        return new ColorStateList(states, colors);
456415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    }
457415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
458cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes    private ColorStateList createSwitchThumbColorStateList(Context context) {
45990075479814eb758d97b822606b448e1a521c298Chris Banes        final int[][] states = new int[3][];
46090075479814eb758d97b822606b448e1a521c298Chris Banes        final int[] colors = new int[3];
46190075479814eb758d97b822606b448e1a521c298Chris Banes        int i = 0;
462415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
463cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes        final ColorStateList thumbColor = getThemeAttrColorStateList(context,
4642aeb0f4237bca35d7f650c3145354416306d4f7bChris Banes                R.attr.colorSwitchThumbNormal);
465415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
466414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes        if (thumbColor != null && thumbColor.isStateful()) {
467414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes            // If colorSwitchThumbNormal is a valid ColorStateList, extract the default and
468414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes            // disabled colors from it
469415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
470414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes            // Disabled state
471bb1a62b3eed874aa47ea4763ac972902c77e988fChris Banes            states[i] = ThemeUtils.DISABLED_STATE_SET;
472414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes            colors[i] = thumbColor.getColorForState(states[i], 0);
473414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes            i++;
474414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes
475bb1a62b3eed874aa47ea4763ac972902c77e988fChris Banes            states[i] = ThemeUtils.CHECKED_STATE_SET;
476cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes            colors[i] = getThemeAttrColor(context, R.attr.colorControlActivated);
477414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes            i++;
478414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes
479414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes            // Default enabled state
480bb1a62b3eed874aa47ea4763ac972902c77e988fChris Banes            states[i] = ThemeUtils.EMPTY_STATE_SET;
481414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes            colors[i] = thumbColor.getDefaultColor();
482414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes            i++;
483414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes        } else {
484414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes            // Else we'll use an approximation using the default disabled alpha
485414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes
486414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes            // Disabled state
487bb1a62b3eed874aa47ea4763ac972902c77e988fChris Banes            states[i] = ThemeUtils.DISABLED_STATE_SET;
488cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes            colors[i] = getDisabledThemeAttrColor(context, R.attr.colorSwitchThumbNormal);
489414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes            i++;
490414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes
491bb1a62b3eed874aa47ea4763ac972902c77e988fChris Banes            states[i] = ThemeUtils.CHECKED_STATE_SET;
492cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes            colors[i] = getThemeAttrColor(context, R.attr.colorControlActivated);
493414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes            i++;
494414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes
495414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes            // Default enabled state
496bb1a62b3eed874aa47ea4763ac972902c77e988fChris Banes            states[i] = ThemeUtils.EMPTY_STATE_SET;
497cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes            colors[i] = getThemeAttrColor(context, R.attr.colorSwitchThumbNormal);
498414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes            i++;
499414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes        }
500415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
50190075479814eb758d97b822606b448e1a521c298Chris Banes        return new ColorStateList(states, colors);
502415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    }
503415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
504cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes    private ColorStateList createEditTextColorStateList(Context context) {
50590075479814eb758d97b822606b448e1a521c298Chris Banes        final int[][] states = new int[3][];
50690075479814eb758d97b822606b448e1a521c298Chris Banes        final int[] colors = new int[3];
50790075479814eb758d97b822606b448e1a521c298Chris Banes        int i = 0;
508a9585dae398a69dd67797e7ca86f44ffcabd9e86Chris Banes
50990075479814eb758d97b822606b448e1a521c298Chris Banes        // Disabled state
510bb1a62b3eed874aa47ea4763ac972902c77e988fChris Banes        states[i] = ThemeUtils.DISABLED_STATE_SET;
511cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes        colors[i] = getDisabledThemeAttrColor(context, R.attr.colorControlNormal);
51290075479814eb758d97b822606b448e1a521c298Chris Banes        i++;
513a9585dae398a69dd67797e7ca86f44ffcabd9e86Chris Banes
514bb1a62b3eed874aa47ea4763ac972902c77e988fChris Banes        states[i] = ThemeUtils.NOT_PRESSED_OR_FOCUSED_STATE_SET;
515cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes        colors[i] = getThemeAttrColor(context, R.attr.colorControlNormal);
51690075479814eb758d97b822606b448e1a521c298Chris Banes        i++;
517a9585dae398a69dd67797e7ca86f44ffcabd9e86Chris Banes
51890075479814eb758d97b822606b448e1a521c298Chris Banes        // Default enabled state
519bb1a62b3eed874aa47ea4763ac972902c77e988fChris Banes        states[i] = ThemeUtils.EMPTY_STATE_SET;
520cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes        colors[i] = getThemeAttrColor(context, R.attr.colorControlActivated);
52190075479814eb758d97b822606b448e1a521c298Chris Banes        i++;
522a9585dae398a69dd67797e7ca86f44ffcabd9e86Chris Banes
52390075479814eb758d97b822606b448e1a521c298Chris Banes        return new ColorStateList(states, colors);
52490075479814eb758d97b822606b448e1a521c298Chris Banes    }
525a9585dae398a69dd67797e7ca86f44ffcabd9e86Chris Banes
526b1131c6dfc9affe5751523f235878055cb699960Chris Banes    private ColorStateList createDefaultButtonColorStateList(Context context) {
527b1131c6dfc9affe5751523f235878055cb699960Chris Banes        return createButtonColorStateList(context, R.attr.colorButtonNormal);
528b1131c6dfc9affe5751523f235878055cb699960Chris Banes    }
529b1131c6dfc9affe5751523f235878055cb699960Chris Banes
530b1131c6dfc9affe5751523f235878055cb699960Chris Banes    private ColorStateList createColoredButtonColorStateList(Context context) {
531b1131c6dfc9affe5751523f235878055cb699960Chris Banes        return createButtonColorStateList(context, R.attr.colorAccent);
532b1131c6dfc9affe5751523f235878055cb699960Chris Banes    }
533b1131c6dfc9affe5751523f235878055cb699960Chris Banes
534b1131c6dfc9affe5751523f235878055cb699960Chris Banes    private ColorStateList createButtonColorStateList(Context context, int baseColorAttr) {
53590075479814eb758d97b822606b448e1a521c298Chris Banes        final int[][] states = new int[4][];
53690075479814eb758d97b822606b448e1a521c298Chris Banes        final int[] colors = new int[4];
53790075479814eb758d97b822606b448e1a521c298Chris Banes        int i = 0;
53890075479814eb758d97b822606b448e1a521c298Chris Banes
539b1131c6dfc9affe5751523f235878055cb699960Chris Banes        final int baseColor = getThemeAttrColor(context, baseColorAttr);
540eb0d0c030a15e93f456cc1403fffb909c0ae4e66Chris Banes        final int colorControlHighlight = getThemeAttrColor(context, R.attr.colorControlHighlight);
541eb0d0c030a15e93f456cc1403fffb909c0ae4e66Chris Banes
54290075479814eb758d97b822606b448e1a521c298Chris Banes        // Disabled state
543bb1a62b3eed874aa47ea4763ac972902c77e988fChris Banes        states[i] = ThemeUtils.DISABLED_STATE_SET;
544cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes        colors[i] = getDisabledThemeAttrColor(context, R.attr.colorButtonNormal);
54590075479814eb758d97b822606b448e1a521c298Chris Banes        i++;
54690075479814eb758d97b822606b448e1a521c298Chris Banes
547bb1a62b3eed874aa47ea4763ac972902c77e988fChris Banes        states[i] = ThemeUtils.PRESSED_STATE_SET;
548b1131c6dfc9affe5751523f235878055cb699960Chris Banes        colors[i] = ColorUtils.compositeColors(colorControlHighlight, baseColor);
54990075479814eb758d97b822606b448e1a521c298Chris Banes        i++;
55090075479814eb758d97b822606b448e1a521c298Chris Banes
551bb1a62b3eed874aa47ea4763ac972902c77e988fChris Banes        states[i] = ThemeUtils.FOCUSED_STATE_SET;
552b1131c6dfc9affe5751523f235878055cb699960Chris Banes        colors[i] = ColorUtils.compositeColors(colorControlHighlight, baseColor);
55390075479814eb758d97b822606b448e1a521c298Chris Banes        i++;
55490075479814eb758d97b822606b448e1a521c298Chris Banes
55590075479814eb758d97b822606b448e1a521c298Chris Banes        // Default enabled state
556bb1a62b3eed874aa47ea4763ac972902c77e988fChris Banes        states[i] = ThemeUtils.EMPTY_STATE_SET;
557b1131c6dfc9affe5751523f235878055cb699960Chris Banes        colors[i] = baseColor;
55890075479814eb758d97b822606b448e1a521c298Chris Banes        i++;
55990075479814eb758d97b822606b448e1a521c298Chris Banes
56090075479814eb758d97b822606b448e1a521c298Chris Banes        return new ColorStateList(states, colors);
561a9585dae398a69dd67797e7ca86f44ffcabd9e86Chris Banes    }
562a9585dae398a69dd67797e7ca86f44ffcabd9e86Chris Banes
563cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes    private ColorStateList createSpinnerColorStateList(Context context) {
564cdd1b1d70cefeb052c2b506738b396f2f982e519Chris Banes        final int[][] states = new int[3][];
565cdd1b1d70cefeb052c2b506738b396f2f982e519Chris Banes        final int[] colors = new int[3];
566cdd1b1d70cefeb052c2b506738b396f2f982e519Chris Banes        int i = 0;
567cdd1b1d70cefeb052c2b506738b396f2f982e519Chris Banes
568cdd1b1d70cefeb052c2b506738b396f2f982e519Chris Banes        // Disabled state
569bb1a62b3eed874aa47ea4763ac972902c77e988fChris Banes        states[i] = ThemeUtils.DISABLED_STATE_SET;
570cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes        colors[i] = getDisabledThemeAttrColor(context, R.attr.colorControlNormal);
571cdd1b1d70cefeb052c2b506738b396f2f982e519Chris Banes        i++;
572cdd1b1d70cefeb052c2b506738b396f2f982e519Chris Banes
573bb1a62b3eed874aa47ea4763ac972902c77e988fChris Banes        states[i] = ThemeUtils.NOT_PRESSED_OR_FOCUSED_STATE_SET;
574cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes        colors[i] = getThemeAttrColor(context, R.attr.colorControlNormal);
575cdd1b1d70cefeb052c2b506738b396f2f982e519Chris Banes        i++;
576cdd1b1d70cefeb052c2b506738b396f2f982e519Chris Banes
577bb1a62b3eed874aa47ea4763ac972902c77e988fChris Banes        states[i] = ThemeUtils.EMPTY_STATE_SET;
578cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes        colors[i] = getThemeAttrColor(context, R.attr.colorControlActivated);
579cdd1b1d70cefeb052c2b506738b396f2f982e519Chris Banes        i++;
580cdd1b1d70cefeb052c2b506738b396f2f982e519Chris Banes
581cdd1b1d70cefeb052c2b506738b396f2f982e519Chris Banes        return new ColorStateList(states, colors);
582cdd1b1d70cefeb052c2b506738b396f2f982e519Chris Banes    }
583cdd1b1d70cefeb052c2b506738b396f2f982e519Chris Banes
58410e2dbc1ad1c01d2824d921a8b0f070859d6f146Chris Banes    private ColorStateList createSeekbarThumbColorStateList(Context context) {
58510e2dbc1ad1c01d2824d921a8b0f070859d6f146Chris Banes        final int[][] states = new int[2][];
58610e2dbc1ad1c01d2824d921a8b0f070859d6f146Chris Banes        final int[] colors = new int[2];
58710e2dbc1ad1c01d2824d921a8b0f070859d6f146Chris Banes        int i = 0;
58810e2dbc1ad1c01d2824d921a8b0f070859d6f146Chris Banes
58910e2dbc1ad1c01d2824d921a8b0f070859d6f146Chris Banes        // Disabled state
59010e2dbc1ad1c01d2824d921a8b0f070859d6f146Chris Banes        states[i] = ThemeUtils.DISABLED_STATE_SET;
59110e2dbc1ad1c01d2824d921a8b0f070859d6f146Chris Banes        colors[i] = getDisabledThemeAttrColor(context, R.attr.colorControlActivated);
59210e2dbc1ad1c01d2824d921a8b0f070859d6f146Chris Banes        i++;
59310e2dbc1ad1c01d2824d921a8b0f070859d6f146Chris Banes
59410e2dbc1ad1c01d2824d921a8b0f070859d6f146Chris Banes        states[i] = ThemeUtils.EMPTY_STATE_SET;
59510e2dbc1ad1c01d2824d921a8b0f070859d6f146Chris Banes        colors[i] = getThemeAttrColor(context, R.attr.colorControlActivated);
59610e2dbc1ad1c01d2824d921a8b0f070859d6f146Chris Banes        i++;
59710e2dbc1ad1c01d2824d921a8b0f070859d6f146Chris Banes
59810e2dbc1ad1c01d2824d921a8b0f070859d6f146Chris Banes        return new ColorStateList(states, colors);
59910e2dbc1ad1c01d2824d921a8b0f070859d6f146Chris Banes    }
60010e2dbc1ad1c01d2824d921a8b0f070859d6f146Chris Banes
601469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes    private static class ColorFilterLruCache extends LruCache<Integer, PorterDuffColorFilter> {
602469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes
603469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes        public ColorFilterLruCache(int maxSize) {
604469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes            super(maxSize);
605469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes        }
606469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes
607469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes        PorterDuffColorFilter get(int color, PorterDuff.Mode mode) {
608469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes            return get(generateCacheKey(color, mode));
609469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes        }
610469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes
611469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes        PorterDuffColorFilter put(int color, PorterDuff.Mode mode, PorterDuffColorFilter filter) {
612469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes            return put(generateCacheKey(color, mode), filter);
613469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes        }
614469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes
615469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes        private static int generateCacheKey(int color, PorterDuff.Mode mode) {
616469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes            int hashCode = 1;
617469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes            hashCode = 31 * hashCode + color;
618469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes            hashCode = 31 * hashCode + mode.hashCode();
619469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes            return hashCode;
620469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes        }
621469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes    }
622a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banes
623f7b73431b366b76bcf58536b7b1086489e4683b2Chris Banes    public static void tintDrawable(Drawable drawable, TintInfo tint, int[] state) {
624d20d889e7abef50efbaf6e975100a8fb73409b13Chris Banes        if (shouldMutateDrawable(drawable) && drawable.mutate() != drawable) {
62544eb323d09e9bc3baca24ef4a8595aafb7ed2652Chris Banes            Log.d(TAG, "Mutated drawable is not the same instance as the input.");
62644eb323d09e9bc3baca24ef4a8595aafb7ed2652Chris Banes            return;
62744eb323d09e9bc3baca24ef4a8595aafb7ed2652Chris Banes        }
628eb0d0c030a15e93f456cc1403fffb909c0ae4e66Chris Banes
62944eb323d09e9bc3baca24ef4a8595aafb7ed2652Chris Banes        if (tint.mHasTintList || tint.mHasTintMode) {
63044eb323d09e9bc3baca24ef4a8595aafb7ed2652Chris Banes            drawable.setColorFilter(createTintFilter(
63144eb323d09e9bc3baca24ef4a8595aafb7ed2652Chris Banes                    tint.mHasTintList ? tint.mTintList : null,
63244eb323d09e9bc3baca24ef4a8595aafb7ed2652Chris Banes                    tint.mHasTintMode ? tint.mTintMode : DEFAULT_MODE,
63344eb323d09e9bc3baca24ef4a8595aafb7ed2652Chris Banes                    state));
63444eb323d09e9bc3baca24ef4a8595aafb7ed2652Chris Banes        } else {
63544eb323d09e9bc3baca24ef4a8595aafb7ed2652Chris Banes            drawable.clearColorFilter();
63644eb323d09e9bc3baca24ef4a8595aafb7ed2652Chris Banes        }
63744eb323d09e9bc3baca24ef4a8595aafb7ed2652Chris Banes
6387797b9f22c8c404309b778a0966266d2b1a84915Chris Banes        if (Build.VERSION.SDK_INT <= 23) {
6397797b9f22c8c404309b778a0966266d2b1a84915Chris Banes            // Pre-v23 there is no guarantee that a state change will invoke an invalidation,
6407797b9f22c8c404309b778a0966266d2b1a84915Chris Banes            // so we force it ourselves
64144eb323d09e9bc3baca24ef4a8595aafb7ed2652Chris Banes            drawable.invalidateSelf();
64244eb323d09e9bc3baca24ef4a8595aafb7ed2652Chris Banes        }
64344eb323d09e9bc3baca24ef4a8595aafb7ed2652Chris Banes    }
64444eb323d09e9bc3baca24ef4a8595aafb7ed2652Chris Banes
645d20d889e7abef50efbaf6e975100a8fb73409b13Chris Banes    private static boolean shouldMutateDrawable(@NonNull Drawable drawable) {
64644eb323d09e9bc3baca24ef4a8595aafb7ed2652Chris Banes        if (drawable instanceof LayerDrawable) {
64744eb323d09e9bc3baca24ef4a8595aafb7ed2652Chris Banes            return Build.VERSION.SDK_INT >= 16;
64844eb323d09e9bc3baca24ef4a8595aafb7ed2652Chris Banes        } else if (drawable instanceof InsetDrawable) {
64944eb323d09e9bc3baca24ef4a8595aafb7ed2652Chris Banes            return Build.VERSION.SDK_INT >= 14;
650d57359e205b2c04a4f0f0ecf9dcb8d6086e75663Chris Banes        } else if (drawable instanceof StateListDrawable) {
651d57359e205b2c04a4f0f0ecf9dcb8d6086e75663Chris Banes            // StateListDrawable has a bug in mutate() on API 7
652d57359e205b2c04a4f0f0ecf9dcb8d6086e75663Chris Banes            return Build.VERSION.SDK_INT >= 8;
653d20d889e7abef50efbaf6e975100a8fb73409b13Chris Banes        } else if (drawable instanceof GradientDrawable) {
654d20d889e7abef50efbaf6e975100a8fb73409b13Chris Banes            // GradientDrawable has a bug pre-ICS which results in mutate() resulting
655d20d889e7abef50efbaf6e975100a8fb73409b13Chris Banes            // in loss of color
656d20d889e7abef50efbaf6e975100a8fb73409b13Chris Banes            return Build.VERSION.SDK_INT >= 14;
65744eb323d09e9bc3baca24ef4a8595aafb7ed2652Chris Banes        } else if (drawable instanceof DrawableContainer) {
65844eb323d09e9bc3baca24ef4a8595aafb7ed2652Chris Banes            // If we have a DrawableContainer, let's traverse it's child array
65944eb323d09e9bc3baca24ef4a8595aafb7ed2652Chris Banes            final Drawable.ConstantState state = drawable.getConstantState();
66044eb323d09e9bc3baca24ef4a8595aafb7ed2652Chris Banes            if (state instanceof DrawableContainer.DrawableContainerState) {
66144eb323d09e9bc3baca24ef4a8595aafb7ed2652Chris Banes                final DrawableContainer.DrawableContainerState containerState =
66244eb323d09e9bc3baca24ef4a8595aafb7ed2652Chris Banes                        (DrawableContainer.DrawableContainerState) state;
66344eb323d09e9bc3baca24ef4a8595aafb7ed2652Chris Banes                for (Drawable child : containerState.getChildren()) {
664d20d889e7abef50efbaf6e975100a8fb73409b13Chris Banes                    if (!shouldMutateDrawable(child)) {
66544eb323d09e9bc3baca24ef4a8595aafb7ed2652Chris Banes                        return false;
66644eb323d09e9bc3baca24ef4a8595aafb7ed2652Chris Banes                    }
66744eb323d09e9bc3baca24ef4a8595aafb7ed2652Chris Banes                }
668f7b73431b366b76bcf58536b7b1086489e4683b2Chris Banes            }
669eb0d0c030a15e93f456cc1403fffb909c0ae4e66Chris Banes        }
67044eb323d09e9bc3baca24ef4a8595aafb7ed2652Chris Banes        return true;
671a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banes    }
672a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banes
67339cb4c1c59fa156ed28bc8835ef05eeb356ad13cChris Banes    private static PorterDuffColorFilter createTintFilter(ColorStateList tint,
67439cb4c1c59fa156ed28bc8835ef05eeb356ad13cChris Banes            PorterDuff.Mode tintMode, final int[] state) {
67539cb4c1c59fa156ed28bc8835ef05eeb356ad13cChris Banes        if (tint == null || tintMode == null) {
67639cb4c1c59fa156ed28bc8835ef05eeb356ad13cChris Banes            return null;
677cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes        }
67839cb4c1c59fa156ed28bc8835ef05eeb356ad13cChris Banes        final int color = tint.getColorForState(state, Color.TRANSPARENT);
67939cb4c1c59fa156ed28bc8835ef05eeb356ad13cChris Banes        return getPorterDuffColorFilter(color, tintMode);
68039cb4c1c59fa156ed28bc8835ef05eeb356ad13cChris Banes    }
681cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes
682fe1cbed21122206b7a4af97790ade439d49421d8Chris Banes    public static PorterDuffColorFilter getPorterDuffColorFilter(int color, PorterDuff.Mode mode) {
683a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banes        // First, lets see if the cache already contains the color filter
684a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banes        PorterDuffColorFilter filter = COLOR_FILTER_CACHE.get(color, mode);
685a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banes
686a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banes        if (filter == null) {
687a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banes            // Cache miss, so create a color filter and add it to the cache
688a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banes            filter = new PorterDuffColorFilter(color, mode);
689a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banes            COLOR_FILTER_CACHE.put(color, mode, filter);
690a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banes        }
691a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banes
69239cb4c1c59fa156ed28bc8835ef05eeb356ad13cChris Banes        return filter;
693a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banes    }
69410e2dbc1ad1c01d2824d921a8b0f070859d6f146Chris Banes
69510e2dbc1ad1c01d2824d921a8b0f070859d6f146Chris Banes    private static void setPorterDuffColorFilter(Drawable d, int color, PorterDuff.Mode mode) {
696d20d889e7abef50efbaf6e975100a8fb73409b13Chris Banes        if (shouldMutateDrawable(d)) {
697d20d889e7abef50efbaf6e975100a8fb73409b13Chris Banes            d = d.mutate();
698d20d889e7abef50efbaf6e975100a8fb73409b13Chris Banes        }
69910e2dbc1ad1c01d2824d921a8b0f070859d6f146Chris Banes        d.setColorFilter(getPorterDuffColorFilter(color, mode == null ? DEFAULT_MODE : mode));
70010e2dbc1ad1c01d2824d921a8b0f070859d6f146Chris Banes    }
701469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes}
702