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
198e10080c914d1ad0784394fa3026b85535535847Aurimas Liutikasimport static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
204c99f0e29b0926d8e5de44b7e3980d47f052f04cChris Banesimport static android.support.v4.graphics.ColorUtils.compositeColors;
214c99f0e29b0926d8e5de44b7e3980d47f052f04cChris Banesimport static android.support.v7.content.res.AppCompatResources.getColorStateList;
224c99f0e29b0926d8e5de44b7e3980d47f052f04cChris Banesimport static android.support.v7.widget.ThemeUtils.getDisabledThemeAttrColor;
234c99f0e29b0926d8e5de44b7e3980d47f052f04cChris Banesimport static android.support.v7.widget.ThemeUtils.getThemeAttrColor;
244c99f0e29b0926d8e5de44b7e3980d47f052f04cChris Banesimport static android.support.v7.widget.ThemeUtils.getThemeAttrColorStateList;
25e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes
26469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banesimport android.content.Context;
27469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banesimport android.content.res.ColorStateList;
28e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banesimport android.content.res.Resources;
2939cb4c1c59fa156ed28bc8835ef05eeb356ad13cChris Banesimport android.graphics.Color;
30469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banesimport android.graphics.PorterDuff;
31469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banesimport android.graphics.PorterDuffColorFilter;
32469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banesimport android.graphics.drawable.Drawable;
339f63864c84e3d48841d553521468c7a27189c4f0Chris Banesimport android.graphics.drawable.Drawable.ConstantState;
34cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banesimport android.graphics.drawable.LayerDrawable;
35fd1eb27a3700de31507de34fd1bcc51830fe876cChris Banesimport android.os.Build;
36a1a9092aba48f3ca6400ff53f5f4734752180e81Chris Banesimport android.support.annotation.ColorInt;
377e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banesimport android.support.annotation.DrawableRes;
387e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banesimport android.support.annotation.NonNull;
397e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banesimport android.support.annotation.Nullable;
408f886fe8c7e23fe6ccb8734167c960c2ed3429c3Alan Viveretteimport android.support.annotation.RequiresApi;
41c39d9c75590eca86a5e7e32a8824ba04a0d42e9bAlan Viveretteimport android.support.annotation.RestrictTo;
42bd3494de1d97206f366f52641a14b21eb06a7022Chris Banesimport android.support.graphics.drawable.AnimatedVectorDrawableCompat;
43e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banesimport android.support.graphics.drawable.VectorDrawableCompat;
44469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banesimport android.support.v4.content.ContextCompat;
457e82b99953680915596eaf0eb35927388e574ca8Chris Banesimport android.support.v4.graphics.drawable.DrawableCompat;
46e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banesimport android.support.v4.util.ArrayMap;
479f63864c84e3d48841d553521468c7a27189c4f0Chris Banesimport android.support.v4.util.LongSparseArray;
48469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banesimport android.support.v4.util.LruCache;
494ec7a936d40164b0f001b9ea860fa7f29754b228Andrei Stingaceanuimport android.support.v4.util.SparseArrayCompat;
50469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banesimport android.support.v7.appcompat.R;
51e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banesimport android.util.AttributeSet;
52469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banesimport android.util.Log;
53e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banesimport android.util.TypedValue;
54e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banesimport android.util.Xml;
55469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes
564c99f0e29b0926d8e5de44b7e3980d47f052f04cChris Banesimport org.xmlpull.v1.XmlPullParser;
574c99f0e29b0926d8e5de44b7e3980d47f052f04cChris Banesimport org.xmlpull.v1.XmlPullParserException;
584c99f0e29b0926d8e5de44b7e3980d47f052f04cChris Banes
599f63864c84e3d48841d553521468c7a27189c4f0Chris Banesimport java.lang.ref.WeakReference;
60cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banesimport java.util.WeakHashMap;
61cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes
62469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes/**
63469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes * @hide
64469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes */
658e10080c914d1ad0784394fa3026b85535535847Aurimas Liutikas@RestrictTo(LIBRARY_GROUP)
667e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banespublic final class AppCompatDrawableManager {
677e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes
689f63864c84e3d48841d553521468c7a27189c4f0Chris Banes    private interface InflateDelegate {
69bd3494de1d97206f366f52641a14b21eb06a7022Chris Banes        Drawable createFromXmlInner(@NonNull Context context, @NonNull XmlPullParser parser,
70e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes                @NonNull AttributeSet attrs, @Nullable Resources.Theme theme);
717e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes    }
72fd1eb27a3700de31507de34fd1bcc51830fe876cChris Banes
739f63864c84e3d48841d553521468c7a27189c4f0Chris Banes    private static final String TAG = "AppCompatDrawableManager";
74469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes    private static final boolean DEBUG = false;
75cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes    private static final PorterDuff.Mode DEFAULT_MODE = PorterDuff.Mode.SRC_IN;
76e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes    private static final String SKIP_DRAWABLE_TAG = "appcompat_skip_skip";
77415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
78ba13a44cd6db80ec983b1ed678b7dbeab04056a1Chris Banes    private static final String PLATFORM_VD_CLAZZ = "android.graphics.drawable.VectorDrawable";
79ba13a44cd6db80ec983b1ed678b7dbeab04056a1Chris Banes
807e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes    private static AppCompatDrawableManager INSTANCE;
817e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes
827e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes    public static AppCompatDrawableManager get() {
837e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes        if (INSTANCE == null) {
847e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes            INSTANCE = new AppCompatDrawableManager();
85bd3494de1d97206f366f52641a14b21eb06a7022Chris Banes            installDefaultInflateDelegates(INSTANCE);
86bd3494de1d97206f366f52641a14b21eb06a7022Chris Banes        }
87bd3494de1d97206f366f52641a14b21eb06a7022Chris Banes        return INSTANCE;
88bd3494de1d97206f366f52641a14b21eb06a7022Chris Banes    }
89bd3494de1d97206f366f52641a14b21eb06a7022Chris Banes
90bd3494de1d97206f366f52641a14b21eb06a7022Chris Banes    private static void installDefaultInflateDelegates(@NonNull AppCompatDrawableManager manager) {
91c59ac731f7d38ee41d0aba567a9d3b77b40df628Teng-Hui Zhu        // This sdk version check will affect src:appCompat code path.
9295276d3d3d07337500b6255d6a6ae031591784f3Teng-Hui Zhu        // Although VectorDrawable exists in Android framework from Lollipop, AppCompat will use the
9395276d3d3d07337500b6255d6a6ae031591784f3Teng-Hui Zhu        // VectorDrawableCompat before Nougat to utilize the bug fixes in VectorDrawableCompat.
948f886fe8c7e23fe6ccb8734167c960c2ed3429c3Alan Viverette        if (Build.VERSION.SDK_INT < 24) {
95bd3494de1d97206f366f52641a14b21eb06a7022Chris Banes            manager.addDelegate("vector", new VdcInflateDelegate());
968f886fe8c7e23fe6ccb8734167c960c2ed3429c3Alan Viverette            if (Build.VERSION.SDK_INT >= 11) {
97bd3494de1d97206f366f52641a14b21eb06a7022Chris Banes                // AnimatedVectorDrawableCompat only works on API v11+
98bd3494de1d97206f366f52641a14b21eb06a7022Chris Banes                manager.addDelegate("animated-vector", new AvdcInflateDelegate());
999f63864c84e3d48841d553521468c7a27189c4f0Chris Banes            }
1007e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes        }
1017e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes    }
1027e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes
103469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes    private static final ColorFilterLruCache COLOR_FILTER_CACHE = new ColorFilterLruCache(6);
104469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes
105469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes    /**
106469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes     * Drawables which should be tinted with the value of {@code R.attr.colorControlNormal},
107cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes     * using the default mode using a raw color filter.
108cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes     */
109cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes    private static final int[] COLORFILTER_TINT_COLOR_CONTROL_NORMAL = {
110cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes            R.drawable.abc_textfield_search_default_mtrl_alpha,
111cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes            R.drawable.abc_textfield_default_mtrl_alpha,
112cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes            R.drawable.abc_ab_share_pack_mtrl_alpha
113cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes    };
114cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes
115cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes    /**
116cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes     * Drawables which should be tinted with the value of {@code R.attr.colorControlNormal}, using
117cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes     * {@link DrawableCompat}'s tinting functionality.
118469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes     */
119469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes    private static final int[] TINT_COLOR_CONTROL_NORMAL = {
120469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes            R.drawable.abc_ic_commit_search_api_mtrl_alpha,
121a59882b82ecd74c79e05538a0962646d003d4c0eChris Banes            R.drawable.abc_seekbar_tick_mark_material,
122a59882b82ecd74c79e05538a0962646d003d4c0eChris Banes            R.drawable.abc_ic_menu_share_mtrl_alpha,
123a59882b82ecd74c79e05538a0962646d003d4c0eChris Banes            R.drawable.abc_ic_menu_copy_mtrl_am_alpha,
124a59882b82ecd74c79e05538a0962646d003d4c0eChris Banes            R.drawable.abc_ic_menu_cut_mtrl_alpha,
125a59882b82ecd74c79e05538a0962646d003d4c0eChris Banes            R.drawable.abc_ic_menu_selectall_mtrl_alpha,
126a59882b82ecd74c79e05538a0962646d003d4c0eChris Banes            R.drawable.abc_ic_menu_paste_mtrl_am_alpha
127469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes    };
128469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes
129469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes    /**
130469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes     * Drawables which should be tinted with the value of {@code R.attr.colorControlActivated},
131cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes     * using a color filter.
132469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes     */
133cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes    private static final int[] COLORFILTER_COLOR_CONTROL_ACTIVATED = {
134469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes            R.drawable.abc_textfield_activated_mtrl_alpha,
13514f8f0dfae09445074dc0f7b5bbe5732d6922da7Chris Banes            R.drawable.abc_textfield_search_activated_mtrl_alpha,
136911642499da7d796aa1e7c19178c3552a590f48bChris Banes            R.drawable.abc_cab_background_top_mtrl_alpha,
13770d10816465bf5844901adab283ae7ab3e041396Chris Banes            R.drawable.abc_text_cursor_material,
1380b8e0916ede7eb212e7d906956c5137a9b9dfe65Chris Banes            R.drawable.abc_text_select_handle_left_mtrl_dark,
1390b8e0916ede7eb212e7d906956c5137a9b9dfe65Chris Banes            R.drawable.abc_text_select_handle_middle_mtrl_dark,
1400b8e0916ede7eb212e7d906956c5137a9b9dfe65Chris Banes            R.drawable.abc_text_select_handle_right_mtrl_dark,
1410b8e0916ede7eb212e7d906956c5137a9b9dfe65Chris Banes            R.drawable.abc_text_select_handle_left_mtrl_light,
1420b8e0916ede7eb212e7d906956c5137a9b9dfe65Chris Banes            R.drawable.abc_text_select_handle_middle_mtrl_light,
1430b8e0916ede7eb212e7d906956c5137a9b9dfe65Chris Banes            R.drawable.abc_text_select_handle_right_mtrl_light
144469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes    };
145469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes
146469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes    /**
147469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes     * Drawables which should be tinted with the value of {@code android.R.attr.colorBackground},
148cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes     * using the {@link android.graphics.PorterDuff.Mode#MULTIPLY} mode and a color filter.
149469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes     */
150cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes    private static final int[] COLORFILTER_COLOR_BACKGROUND_MULTIPLY = {
1511073132946bb0a53a788949fe4c060f72051cd57Chris Banes            R.drawable.abc_popup_background_mtrl_mult,
15257c6de90985a63358129b99b9f0cd4d6afe887d6Chris Banes            R.drawable.abc_cab_background_internal_bg,
15357c6de90985a63358129b99b9f0cd4d6afe887d6Chris Banes            R.drawable.abc_menu_hardkey_panel_mtrl_mult
154469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes    };
155469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes
156469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes    /**
157469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes     * Drawables which should be tinted using a state list containing values of
158469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes     * {@code R.attr.colorControlNormal} and {@code R.attr.colorControlActivated}
159469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes     */
160469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes    private static final int[] TINT_COLOR_CONTROL_STATE_LIST = {
161469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes            R.drawable.abc_tab_indicator_material,
16274433e220541f8236f4e01f658a96854b29acfd1Chris Banes            R.drawable.abc_textfield_search_material
163469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes    };
164469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes
1651752dfd10e8242d6e017b3828c7d6e94f044691cChris Banes    /**
1661752dfd10e8242d6e017b3828c7d6e94f044691cChris Banes     * Drawables which should be tinted using a state list containing values of
1671752dfd10e8242d6e017b3828c7d6e94f044691cChris Banes     * {@code R.attr.colorControlNormal} and {@code R.attr.colorControlActivated} for the checked
1681752dfd10e8242d6e017b3828c7d6e94f044691cChris Banes     * state.
1691752dfd10e8242d6e017b3828c7d6e94f044691cChris Banes     */
1701752dfd10e8242d6e017b3828c7d6e94f044691cChris Banes    private static final int[] TINT_CHECKABLE_BUTTON_LIST = {
1711752dfd10e8242d6e017b3828c7d6e94f044691cChris Banes            R.drawable.abc_btn_check_material,
1721752dfd10e8242d6e017b3828c7d6e94f044691cChris Banes            R.drawable.abc_btn_radio_material
1731752dfd10e8242d6e017b3828c7d6e94f044691cChris Banes    };
1741752dfd10e8242d6e017b3828c7d6e94f044691cChris Banes
1754ec7a936d40164b0f001b9ea860fa7f29754b228Andrei Stingaceanu    private WeakHashMap<Context, SparseArrayCompat<ColorStateList>> mTintLists;
176e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes    private ArrayMap<String, InflateDelegate> mDelegates;
1774ec7a936d40164b0f001b9ea860fa7f29754b228Andrei Stingaceanu    private SparseArrayCompat<String> mKnownDrawableIdTags;
178e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes
17960cc94cdf2f9dfd10f2fe0b3fa0fa438d51df271Chris Banes    private final Object mDrawableCacheLock = new Object();
1809f63864c84e3d48841d553521468c7a27189c4f0Chris Banes    private final WeakHashMap<Context, LongSparseArray<WeakReference<Drawable.ConstantState>>>
18160cc94cdf2f9dfd10f2fe0b3fa0fa438d51df271Chris Banes            mDrawableCaches = new WeakHashMap<>(0);
1829f63864c84e3d48841d553521468c7a27189c4f0Chris Banes
183e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes    private TypedValue mTypedValue;
184469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes
185ba13a44cd6db80ec983b1ed678b7dbeab04056a1Chris Banes    private boolean mHasCheckedVectorDrawableSetup;
186ba13a44cd6db80ec983b1ed678b7dbeab04056a1Chris Banes
1877e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes    public Drawable getDrawable(@NonNull Context context, @DrawableRes int resId) {
1887e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes        return getDrawable(context, resId, false);
189469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes    }
190469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes
1914c99f0e29b0926d8e5de44b7e3980d47f052f04cChris Banes    Drawable getDrawable(@NonNull Context context, @DrawableRes int resId,
1927e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes            boolean failIfNotKnown) {
193eb648620b291eabbb5523f649baef5abb2e4687bAlan Viverette        checkVectorDrawableSetup(context);
194eb648620b291eabbb5523f649baef5abb2e4687bAlan Viverette
195e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes        Drawable drawable = loadDrawableFromDelegates(context, resId);
196e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes        if (drawable == null) {
19760cc94cdf2f9dfd10f2fe0b3fa0fa438d51df271Chris Banes            drawable = createDrawableIfNeeded(context, resId);
19860cc94cdf2f9dfd10f2fe0b3fa0fa438d51df271Chris Banes        }
19960cc94cdf2f9dfd10f2fe0b3fa0fa438d51df271Chris Banes        if (drawable == null) {
200e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes            drawable = ContextCompat.getDrawable(context, resId);
201cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes        }
20260cc94cdf2f9dfd10f2fe0b3fa0fa438d51df271Chris Banes
203e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes        if (drawable != null) {
204aaa85b7d563d27fdf10048dd619a317451477ad5Chris Banes            // Tint it if needed
205aaa85b7d563d27fdf10048dd619a317451477ad5Chris Banes            drawable = tintDrawable(context, resId, failIfNotKnown, drawable);
206cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes        }
207aaa85b7d563d27fdf10048dd619a317451477ad5Chris Banes        if (drawable != null) {
208aaa85b7d563d27fdf10048dd619a317451477ad5Chris Banes            // See if we need to 'fix' the drawable
209aaa85b7d563d27fdf10048dd619a317451477ad5Chris Banes            DrawableUtils.fixDrawable(drawable);
210aaa85b7d563d27fdf10048dd619a317451477ad5Chris Banes        }
211aaa85b7d563d27fdf10048dd619a317451477ad5Chris Banes        return drawable;
212e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes    }
213cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes
214dfdb7ac7893845f052e27c8c705b0708524b6a91Chris Banes    public void onConfigurationChanged(@NonNull Context context) {
215dfdb7ac7893845f052e27c8c705b0708524b6a91Chris Banes        synchronized (mDrawableCacheLock) {
216dfdb7ac7893845f052e27c8c705b0708524b6a91Chris Banes            LongSparseArray<WeakReference<ConstantState>> cache = mDrawableCaches.get(context);
217dfdb7ac7893845f052e27c8c705b0708524b6a91Chris Banes            if (cache != null) {
218dfdb7ac7893845f052e27c8c705b0708524b6a91Chris Banes                // Crude, but we'll just clear the cache when the configuration changes
219dfdb7ac7893845f052e27c8c705b0708524b6a91Chris Banes                cache.clear();
220dfdb7ac7893845f052e27c8c705b0708524b6a91Chris Banes            }
221dfdb7ac7893845f052e27c8c705b0708524b6a91Chris Banes        }
222dfdb7ac7893845f052e27c8c705b0708524b6a91Chris Banes    }
223dfdb7ac7893845f052e27c8c705b0708524b6a91Chris Banes
22460cc94cdf2f9dfd10f2fe0b3fa0fa438d51df271Chris Banes    private static long createCacheKey(TypedValue tv) {
22560cc94cdf2f9dfd10f2fe0b3fa0fa438d51df271Chris Banes        return (((long) tv.assetCookie) << 32) | tv.data;
22660cc94cdf2f9dfd10f2fe0b3fa0fa438d51df271Chris Banes    }
22760cc94cdf2f9dfd10f2fe0b3fa0fa438d51df271Chris Banes
22860cc94cdf2f9dfd10f2fe0b3fa0fa438d51df271Chris Banes    private Drawable createDrawableIfNeeded(@NonNull Context context,
22960cc94cdf2f9dfd10f2fe0b3fa0fa438d51df271Chris Banes            @DrawableRes final int resId) {
23060cc94cdf2f9dfd10f2fe0b3fa0fa438d51df271Chris Banes        if (mTypedValue == null) {
23160cc94cdf2f9dfd10f2fe0b3fa0fa438d51df271Chris Banes            mTypedValue = new TypedValue();
23260cc94cdf2f9dfd10f2fe0b3fa0fa438d51df271Chris Banes        }
23360cc94cdf2f9dfd10f2fe0b3fa0fa438d51df271Chris Banes        final TypedValue tv = mTypedValue;
23460cc94cdf2f9dfd10f2fe0b3fa0fa438d51df271Chris Banes        context.getResources().getValue(resId, tv, true);
23560cc94cdf2f9dfd10f2fe0b3fa0fa438d51df271Chris Banes        final long key = createCacheKey(tv);
23660cc94cdf2f9dfd10f2fe0b3fa0fa438d51df271Chris Banes
23760cc94cdf2f9dfd10f2fe0b3fa0fa438d51df271Chris Banes        Drawable dr = getCachedDrawable(context, key);
23860cc94cdf2f9dfd10f2fe0b3fa0fa438d51df271Chris Banes        if (dr != null) {
23960cc94cdf2f9dfd10f2fe0b3fa0fa438d51df271Chris Banes            // If we got a cached drawable, return it
24060cc94cdf2f9dfd10f2fe0b3fa0fa438d51df271Chris Banes            return dr;
24160cc94cdf2f9dfd10f2fe0b3fa0fa438d51df271Chris Banes        }
24260cc94cdf2f9dfd10f2fe0b3fa0fa438d51df271Chris Banes
24360cc94cdf2f9dfd10f2fe0b3fa0fa438d51df271Chris Banes        // Else we need to try and create one...
24460cc94cdf2f9dfd10f2fe0b3fa0fa438d51df271Chris Banes        if (resId == R.drawable.abc_cab_background_top_material) {
24560cc94cdf2f9dfd10f2fe0b3fa0fa438d51df271Chris Banes            dr = new LayerDrawable(new Drawable[]{
24660cc94cdf2f9dfd10f2fe0b3fa0fa438d51df271Chris Banes                    getDrawable(context, R.drawable.abc_cab_background_internal_bg),
24760cc94cdf2f9dfd10f2fe0b3fa0fa438d51df271Chris Banes                    getDrawable(context, R.drawable.abc_cab_background_top_mtrl_alpha)
24860cc94cdf2f9dfd10f2fe0b3fa0fa438d51df271Chris Banes            });
24960cc94cdf2f9dfd10f2fe0b3fa0fa438d51df271Chris Banes        }
25060cc94cdf2f9dfd10f2fe0b3fa0fa438d51df271Chris Banes
25160cc94cdf2f9dfd10f2fe0b3fa0fa438d51df271Chris Banes        if (dr != null) {
25260cc94cdf2f9dfd10f2fe0b3fa0fa438d51df271Chris Banes            dr.setChangingConfigurations(tv.changingConfigurations);
25360cc94cdf2f9dfd10f2fe0b3fa0fa438d51df271Chris Banes            // If we reached here then we created a new drawable, add it to the cache
25460cc94cdf2f9dfd10f2fe0b3fa0fa438d51df271Chris Banes            addDrawableToCache(context, key, dr);
25560cc94cdf2f9dfd10f2fe0b3fa0fa438d51df271Chris Banes        }
25660cc94cdf2f9dfd10f2fe0b3fa0fa438d51df271Chris Banes
25760cc94cdf2f9dfd10f2fe0b3fa0fa438d51df271Chris Banes        return dr;
25860cc94cdf2f9dfd10f2fe0b3fa0fa438d51df271Chris Banes    }
25960cc94cdf2f9dfd10f2fe0b3fa0fa438d51df271Chris Banes
260e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes    private Drawable tintDrawable(@NonNull Context context, @DrawableRes int resId,
261e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes            boolean failIfNotKnown, @NonNull Drawable drawable) {
262e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes        final ColorStateList tintList = getTintList(context, resId);
263e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes        if (tintList != null) {
264e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes            // First mutate the Drawable, then wrap it and set the tint list
265aaa85b7d563d27fdf10048dd619a317451477ad5Chris Banes            if (DrawableUtils.canSafelyMutateDrawable(drawable)) {
266e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes                drawable = drawable.mutate();
267e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes            }
268e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes            drawable = DrawableCompat.wrap(drawable);
269e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes            DrawableCompat.setTintList(drawable, tintList);
270469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes
271e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes            // If there is a blending mode specified for the drawable, use it
272e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes            final PorterDuff.Mode tintMode = getTintMode(resId);
273e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes            if (tintMode != null) {
274e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes                DrawableCompat.setTintMode(drawable, tintMode);
275e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes            }
276e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes        } else if (resId == R.drawable.abc_seekbar_track_material) {
277e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes            LayerDrawable ld = (LayerDrawable) drawable;
278e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes            setPorterDuffColorFilter(ld.findDrawableByLayerId(android.R.id.background),
279e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes                    getThemeAttrColor(context, R.attr.colorControlNormal), DEFAULT_MODE);
280e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes            setPorterDuffColorFilter(ld.findDrawableByLayerId(android.R.id.secondaryProgress),
281e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes                    getThemeAttrColor(context, R.attr.colorControlNormal), DEFAULT_MODE);
282e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes            setPorterDuffColorFilter(ld.findDrawableByLayerId(android.R.id.progress),
283e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes                    getThemeAttrColor(context, R.attr.colorControlActivated), DEFAULT_MODE);
28474433e220541f8236f4e01f658a96854b29acfd1Chris Banes        } else if (resId == R.drawable.abc_ratingbar_material
28574433e220541f8236f4e01f658a96854b29acfd1Chris Banes                || resId == R.drawable.abc_ratingbar_indicator_material
286e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes                || resId == R.drawable.abc_ratingbar_small_material) {
287e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes            LayerDrawable ld = (LayerDrawable) drawable;
288e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes            setPorterDuffColorFilter(ld.findDrawableByLayerId(android.R.id.background),
289e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes                    getDisabledThemeAttrColor(context, R.attr.colorControlNormal),
290e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes                    DEFAULT_MODE);
291e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes            setPorterDuffColorFilter(ld.findDrawableByLayerId(android.R.id.secondaryProgress),
292e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes                    getThemeAttrColor(context, R.attr.colorControlActivated), DEFAULT_MODE);
293e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes            setPorterDuffColorFilter(ld.findDrawableByLayerId(android.R.id.progress),
294e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes                    getThemeAttrColor(context, R.attr.colorControlActivated), DEFAULT_MODE);
295e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes        } else {
296e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes            final boolean tinted = tintDrawableUsingColorFilter(context, resId, drawable);
297e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes            if (!tinted && failIfNotKnown) {
298e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes                // If we didn't tint using a ColorFilter, and we're set to fail if we don't
299e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes                // know the id, return null
300e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes                drawable = null;
301e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes            }
302e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes        }
303e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes        return drawable;
304e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes    }
305a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banes
306e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes    private Drawable loadDrawableFromDelegates(@NonNull Context context, @DrawableRes int resId) {
307e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes        if (mDelegates != null && !mDelegates.isEmpty()) {
308e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes            if (mKnownDrawableIdTags != null) {
3099f63864c84e3d48841d553521468c7a27189c4f0Chris Banes                final String cachedTagName = mKnownDrawableIdTags.get(resId);
310e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes                if (SKIP_DRAWABLE_TAG.equals(cachedTagName)
311e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes                        || (cachedTagName != null && mDelegates.get(cachedTagName) == null)) {
312e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes                    // If we don't have a delegate for the drawable tag, or we've been set to
313e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes                    // skip it, fail fast and return null
314e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes                    if (DEBUG) {
3159f63864c84e3d48841d553521468c7a27189c4f0Chris Banes                        Log.d(TAG, "[loadDrawableFromDelegates] Skipping drawable: "
316e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes                                + context.getResources().getResourceName(resId));
317e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes                    }
318e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes                    return null;
319a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banes                }
320469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes            } else {
321e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes                // Create an id cache as we'll need one later
3224ec7a936d40164b0f001b9ea860fa7f29754b228Andrei Stingaceanu                mKnownDrawableIdTags = new SparseArrayCompat<>();
323e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes            }
324e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes
325e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes            if (mTypedValue == null) {
326e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes                mTypedValue = new TypedValue();
327e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes            }
328e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes            final TypedValue tv = mTypedValue;
329e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes            final Resources res = context.getResources();
330e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes            res.getValue(resId, tv, true);
331e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes
33260cc94cdf2f9dfd10f2fe0b3fa0fa438d51df271Chris Banes            final long key = createCacheKey(tv);
3339f63864c84e3d48841d553521468c7a27189c4f0Chris Banes
33460cc94cdf2f9dfd10f2fe0b3fa0fa438d51df271Chris Banes            Drawable dr = getCachedDrawable(context, key);
3359f63864c84e3d48841d553521468c7a27189c4f0Chris Banes            if (dr != null) {
3369f63864c84e3d48841d553521468c7a27189c4f0Chris Banes                if (DEBUG) {
3379f63864c84e3d48841d553521468c7a27189c4f0Chris Banes                    Log.i(TAG, "[loadDrawableFromDelegates] Returning cached drawable: " +
3389f63864c84e3d48841d553521468c7a27189c4f0Chris Banes                            context.getResources().getResourceName(resId));
3399f63864c84e3d48841d553521468c7a27189c4f0Chris Banes                }
3409f63864c84e3d48841d553521468c7a27189c4f0Chris Banes                // We have a cached drawable, return it!
3419f63864c84e3d48841d553521468c7a27189c4f0Chris Banes                return dr;
3429f63864c84e3d48841d553521468c7a27189c4f0Chris Banes            }
3439f63864c84e3d48841d553521468c7a27189c4f0Chris Banes
344e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes            if (tv.string != null && tv.string.toString().endsWith(".xml")) {
345e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes                // If the resource is an XML file, let's try and parse it
346e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes                try {
347e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes                    final XmlPullParser parser = res.getXml(resId);
348e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes                    final AttributeSet attrs = Xml.asAttributeSet(parser);
349e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes                    int type;
350e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes                    while ((type = parser.next()) != XmlPullParser.START_TAG &&
351e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes                            type != XmlPullParser.END_DOCUMENT) {
352e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes                        // Empty loop
353e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes                    }
354e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes                    if (type != XmlPullParser.START_TAG) {
355e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes                        throw new XmlPullParserException("No start tag found");
356e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes                    }
357e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes
358e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes                    final String tagName = parser.getName();
3599f63864c84e3d48841d553521468c7a27189c4f0Chris Banes                    // Add the tag name to the cache
3609f63864c84e3d48841d553521468c7a27189c4f0Chris Banes                    mKnownDrawableIdTags.append(resId, tagName);
361e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes
362e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes                    // Now try and find a delegate for the tag name and inflate if found
363e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes                    final InflateDelegate delegate = mDelegates.get(tagName);
364e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes                    if (delegate != null) {
365bd3494de1d97206f366f52641a14b21eb06a7022Chris Banes                        dr = delegate.createFromXmlInner(context, parser, attrs,
366bd3494de1d97206f366f52641a14b21eb06a7022Chris Banes                                context.getTheme());
3679f63864c84e3d48841d553521468c7a27189c4f0Chris Banes                    }
3689f63864c84e3d48841d553521468c7a27189c4f0Chris Banes                    if (dr != null) {
3699f63864c84e3d48841d553521468c7a27189c4f0Chris Banes                        // Add it to the drawable cache
3709f63864c84e3d48841d553521468c7a27189c4f0Chris Banes                        dr.setChangingConfigurations(tv.changingConfigurations);
37160cc94cdf2f9dfd10f2fe0b3fa0fa438d51df271Chris Banes                        if (addDrawableToCache(context, key, dr) && DEBUG) {
3729f63864c84e3d48841d553521468c7a27189c4f0Chris Banes                            Log.i(TAG, "[loadDrawableFromDelegates] Saved drawable to cache: " +
3739f63864c84e3d48841d553521468c7a27189c4f0Chris Banes                                    context.getResources().getResourceName(resId));
3749f63864c84e3d48841d553521468c7a27189c4f0Chris Banes                        }
375e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes                    }
376e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes                } catch (Exception e) {
377e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes                    Log.e(TAG, "Exception while inflating drawable", e);
3784ab820f4155444d20b37e105873775dd71907eefChris Banes                }
379469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes            }
3809f63864c84e3d48841d553521468c7a27189c4f0Chris Banes            if (dr == null) {
3819f63864c84e3d48841d553521468c7a27189c4f0Chris Banes                // If we reach here then the delegate inflation of the resource failed. Mark it as
3829f63864c84e3d48841d553521468c7a27189c4f0Chris Banes                // bad so we skip the id next time
3839f63864c84e3d48841d553521468c7a27189c4f0Chris Banes                mKnownDrawableIdTags.append(resId, SKIP_DRAWABLE_TAG);
3849f63864c84e3d48841d553521468c7a27189c4f0Chris Banes            }
3859f63864c84e3d48841d553521468c7a27189c4f0Chris Banes            return dr;
386469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes        }
387e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes
388e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes        return null;
389469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes    }
390469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes
39160cc94cdf2f9dfd10f2fe0b3fa0fa438d51df271Chris Banes    private Drawable getCachedDrawable(@NonNull final Context context, final long key) {
39260cc94cdf2f9dfd10f2fe0b3fa0fa438d51df271Chris Banes        synchronized (mDrawableCacheLock) {
3939f63864c84e3d48841d553521468c7a27189c4f0Chris Banes            final LongSparseArray<WeakReference<ConstantState>> cache
39460cc94cdf2f9dfd10f2fe0b3fa0fa438d51df271Chris Banes                    = mDrawableCaches.get(context);
3959f63864c84e3d48841d553521468c7a27189c4f0Chris Banes            if (cache == null) {
3969f63864c84e3d48841d553521468c7a27189c4f0Chris Banes                return null;
3979f63864c84e3d48841d553521468c7a27189c4f0Chris Banes            }
3989f63864c84e3d48841d553521468c7a27189c4f0Chris Banes
3999f63864c84e3d48841d553521468c7a27189c4f0Chris Banes            final WeakReference<ConstantState> wr = cache.get(key);
4009f63864c84e3d48841d553521468c7a27189c4f0Chris Banes            if (wr != null) {
4019f63864c84e3d48841d553521468c7a27189c4f0Chris Banes                // We have the key, and the secret
4029f63864c84e3d48841d553521468c7a27189c4f0Chris Banes                ConstantState entry = wr.get();
4039f63864c84e3d48841d553521468c7a27189c4f0Chris Banes                if (entry != null) {
4049f63864c84e3d48841d553521468c7a27189c4f0Chris Banes                    return entry.newDrawable(context.getResources());
4059f63864c84e3d48841d553521468c7a27189c4f0Chris Banes                } else {
4069f63864c84e3d48841d553521468c7a27189c4f0Chris Banes                    // Our entry has been purged
4079f63864c84e3d48841d553521468c7a27189c4f0Chris Banes                    cache.delete(key);
4089f63864c84e3d48841d553521468c7a27189c4f0Chris Banes                }
4099f63864c84e3d48841d553521468c7a27189c4f0Chris Banes            }
4109f63864c84e3d48841d553521468c7a27189c4f0Chris Banes        }
4119f63864c84e3d48841d553521468c7a27189c4f0Chris Banes        return null;
4129f63864c84e3d48841d553521468c7a27189c4f0Chris Banes    }
4139f63864c84e3d48841d553521468c7a27189c4f0Chris Banes
41460cc94cdf2f9dfd10f2fe0b3fa0fa438d51df271Chris Banes    private boolean addDrawableToCache(@NonNull final Context context, final long key,
4159f63864c84e3d48841d553521468c7a27189c4f0Chris Banes            @NonNull final Drawable drawable) {
4169f63864c84e3d48841d553521468c7a27189c4f0Chris Banes        final ConstantState cs = drawable.getConstantState();
4179f63864c84e3d48841d553521468c7a27189c4f0Chris Banes        if (cs != null) {
41860cc94cdf2f9dfd10f2fe0b3fa0fa438d51df271Chris Banes            synchronized (mDrawableCacheLock) {
41960cc94cdf2f9dfd10f2fe0b3fa0fa438d51df271Chris Banes                LongSparseArray<WeakReference<ConstantState>> cache = mDrawableCaches.get(context);
4209f63864c84e3d48841d553521468c7a27189c4f0Chris Banes                if (cache == null) {
4219f63864c84e3d48841d553521468c7a27189c4f0Chris Banes                    cache = new LongSparseArray<>();
42260cc94cdf2f9dfd10f2fe0b3fa0fa438d51df271Chris Banes                    mDrawableCaches.put(context, cache);
4239f63864c84e3d48841d553521468c7a27189c4f0Chris Banes                }
4244ec7a936d40164b0f001b9ea860fa7f29754b228Andrei Stingaceanu                cache.put(key, new WeakReference<>(cs));
4259f63864c84e3d48841d553521468c7a27189c4f0Chris Banes            }
4269f63864c84e3d48841d553521468c7a27189c4f0Chris Banes            return true;
4279f63864c84e3d48841d553521468c7a27189c4f0Chris Banes        }
4289f63864c84e3d48841d553521468c7a27189c4f0Chris Banes        return false;
4299f63864c84e3d48841d553521468c7a27189c4f0Chris Banes    }
4309f63864c84e3d48841d553521468c7a27189c4f0Chris Banes
4314c99f0e29b0926d8e5de44b7e3980d47f052f04cChris Banes    Drawable onDrawableLoadedFromResources(@NonNull Context context,
4325fa121a1f51114f3a2f6d705139e83c0e0aea610Chris Banes            @NonNull VectorEnabledTintResources resources, @DrawableRes final int resId) {
4335fa121a1f51114f3a2f6d705139e83c0e0aea610Chris Banes        Drawable drawable = loadDrawableFromDelegates(context, resId);
4345fa121a1f51114f3a2f6d705139e83c0e0aea610Chris Banes        if (drawable == null) {
4355fa121a1f51114f3a2f6d705139e83c0e0aea610Chris Banes            drawable = resources.superGetDrawable(resId);
4365fa121a1f51114f3a2f6d705139e83c0e0aea610Chris Banes        }
4375fa121a1f51114f3a2f6d705139e83c0e0aea610Chris Banes        if (drawable != null) {
4385fa121a1f51114f3a2f6d705139e83c0e0aea610Chris Banes            return tintDrawable(context, resId, false, drawable);
4395fa121a1f51114f3a2f6d705139e83c0e0aea610Chris Banes        }
4405fa121a1f51114f3a2f6d705139e83c0e0aea610Chris Banes        return null;
4415fa121a1f51114f3a2f6d705139e83c0e0aea610Chris Banes    }
4425fa121a1f51114f3a2f6d705139e83c0e0aea610Chris Banes
44360cc94cdf2f9dfd10f2fe0b3fa0fa438d51df271Chris Banes    static boolean tintDrawableUsingColorFilter(@NonNull Context context,
4447e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes            @DrawableRes final int resId, @NonNull Drawable drawable) {
44539cb4c1c59fa156ed28bc8835ef05eeb356ad13cChris Banes        PorterDuff.Mode tintMode = DEFAULT_MODE;
446469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes        boolean colorAttrSet = false;
447469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes        int colorAttr = 0;
4480517b282bde8b9a0377dfe5bc4756405a196adb4Chris Banes        int alpha = -1;
449469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes
450cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes        if (arrayContains(COLORFILTER_TINT_COLOR_CONTROL_NORMAL, resId)) {
451469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes            colorAttr = R.attr.colorControlNormal;
452469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes            colorAttrSet = true;
453cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes        } else if (arrayContains(COLORFILTER_COLOR_CONTROL_ACTIVATED, resId)) {
454469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes            colorAttr = R.attr.colorControlActivated;
455469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes            colorAttrSet = true;
456cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes        } else if (arrayContains(COLORFILTER_COLOR_BACKGROUND_MULTIPLY, resId)) {
457469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes            colorAttr = android.R.attr.colorBackground;
458469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes            colorAttrSet = true;
459469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes            tintMode = PorterDuff.Mode.MULTIPLY;
4600517b282bde8b9a0377dfe5bc4756405a196adb4Chris Banes        } else if (resId == R.drawable.abc_list_divider_mtrl_alpha) {
4610517b282bde8b9a0377dfe5bc4756405a196adb4Chris Banes            colorAttr = android.R.attr.colorForeground;
4620517b282bde8b9a0377dfe5bc4756405a196adb4Chris Banes            colorAttrSet = true;
4630517b282bde8b9a0377dfe5bc4756405a196adb4Chris Banes            alpha = Math.round(0.16f * 255);
464593d8bcc82c01a559f737dfcbc6e806005300a64Chris Banes        } else if (resId == R.drawable.abc_dialog_material_background) {
465593d8bcc82c01a559f737dfcbc6e806005300a64Chris Banes            colorAttr = android.R.attr.colorBackground;
466593d8bcc82c01a559f737dfcbc6e806005300a64Chris Banes            colorAttrSet = true;
467469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes        }
468469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes
469f31fb9d2fd3b5b130f3f5ac121b033546d869231Chris Banes        if (colorAttrSet) {
470aaa85b7d563d27fdf10048dd619a317451477ad5Chris Banes            if (DrawableUtils.canSafelyMutateDrawable(drawable)) {
471d20d889e7abef50efbaf6e975100a8fb73409b13Chris Banes                drawable = drawable.mutate();
472d20d889e7abef50efbaf6e975100a8fb73409b13Chris Banes            }
473d20d889e7abef50efbaf6e975100a8fb73409b13Chris Banes
474cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes            final int color = getThemeAttrColor(context, colorAttr);
47539cb4c1c59fa156ed28bc8835ef05eeb356ad13cChris Banes            drawable.setColorFilter(getPorterDuffColorFilter(color, tintMode));
476469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes
4770517b282bde8b9a0377dfe5bc4756405a196adb4Chris Banes            if (alpha != -1) {
4780517b282bde8b9a0377dfe5bc4756405a196adb4Chris Banes                drawable.setAlpha(alpha);
4790517b282bde8b9a0377dfe5bc4756405a196adb4Chris Banes            }
4800517b282bde8b9a0377dfe5bc4756405a196adb4Chris Banes
481469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes            if (DEBUG) {
4829f63864c84e3d48841d553521468c7a27189c4f0Chris Banes                Log.d(TAG, "[tintDrawableUsingColorFilter] Tinted "
4839f63864c84e3d48841d553521468c7a27189c4f0Chris Banes                        + context.getResources().getResourceName(resId) +
484469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes                        " with color: #" + Integer.toHexString(color));
485469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes            }
4864ab820f4155444d20b37e105873775dd71907eefChris Banes            return true;
487469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes        }
4884ab820f4155444d20b37e105873775dd71907eefChris Banes        return false;
489469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes    }
490469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes
4919f63864c84e3d48841d553521468c7a27189c4f0Chris Banes    private void addDelegate(@NonNull String tagName, @NonNull InflateDelegate delegate) {
4927e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes        if (mDelegates == null) {
493e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes            mDelegates = new ArrayMap<>();
4947e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes        }
495e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes        mDelegates.put(tagName, delegate);
4967e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes    }
4977e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes
4989f63864c84e3d48841d553521468c7a27189c4f0Chris Banes    private void removeDelegate(@NonNull String tagName, @NonNull InflateDelegate delegate) {
499e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes        if (mDelegates != null && mDelegates.get(tagName) == delegate) {
500e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes            mDelegates.remove(tagName);
5017e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes        }
5027e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes    }
5037e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes
504469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes    private static boolean arrayContains(int[] array, int value) {
505469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes        for (int id : array) {
506469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes            if (id == value) {
507469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes                return true;
508469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes            }
509469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes        }
510469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes        return false;
511469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes    }
512469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes
5134c99f0e29b0926d8e5de44b7e3980d47f052f04cChris Banes    static PorterDuff.Mode getTintMode(final int resId) {
514cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes        PorterDuff.Mode mode = null;
515cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes
516cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes        if (resId == R.drawable.abc_switch_thumb_material) {
517cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes            mode = PorterDuff.Mode.MULTIPLY;
518cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes        }
519cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes
520cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes        return mode;
521a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banes    }
522a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banes
5234c99f0e29b0926d8e5de44b7e3980d47f052f04cChris Banes    ColorStateList getTintList(@NonNull Context context, @DrawableRes int resId) {
524cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes        // Try the cache first (if it exists)
525f39fda5f5a43ca50ea942da561cbe4eb9a93be8aChris Banes        ColorStateList tint = getTintListFromCache(context, resId);
526cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes
527cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes        if (tint == null) {
528cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes            // ...if the cache did not contain a color state list, try and create one
52990075479814eb758d97b822606b448e1a521c298Chris Banes            if (resId == R.drawable.abc_edit_text_material) {
5309d5f84f33353a42e837c6b465412d1a6f2fc6eaaChris Banes                tint = getColorStateList(context, R.color.abc_tint_edittext);
53190075479814eb758d97b822606b448e1a521c298Chris Banes            } else if (resId == R.drawable.abc_switch_track_mtrl_alpha) {
5329d5f84f33353a42e837c6b465412d1a6f2fc6eaaChris Banes                tint = getColorStateList(context, R.color.abc_tint_switch_track);
53390075479814eb758d97b822606b448e1a521c298Chris Banes            } else if (resId == R.drawable.abc_switch_thumb_material) {
534613689efde3186aa87a2d541ba8ba72402f090e2Kirill Grouchnikov                tint = createSwitchThumbColorStateList(context);
535a1a9092aba48f3ca6400ff53f5f4734752180e81Chris Banes            } else if (resId == R.drawable.abc_btn_default_mtrl_shape) {
536f39fda5f5a43ca50ea942da561cbe4eb9a93be8aChris Banes                tint = createDefaultButtonColorStateList(context);
537a1a9092aba48f3ca6400ff53f5f4734752180e81Chris Banes            } else if (resId == R.drawable.abc_btn_borderless_material) {
538f39fda5f5a43ca50ea942da561cbe4eb9a93be8aChris Banes                tint = createBorderlessButtonColorStateList(context);
539b1131c6dfc9affe5751523f235878055cb699960Chris Banes            } else if (resId == R.drawable.abc_btn_colored_material) {
540f39fda5f5a43ca50ea942da561cbe4eb9a93be8aChris Banes                tint = createColoredButtonColorStateList(context);
541cdd1b1d70cefeb052c2b506738b396f2f982e519Chris Banes            } else if (resId == R.drawable.abc_spinner_mtrl_am_alpha
542cdd1b1d70cefeb052c2b506738b396f2f982e519Chris Banes                    || resId == R.drawable.abc_spinner_textfield_background_material) {
5439d5f84f33353a42e837c6b465412d1a6f2fc6eaaChris Banes                tint = getColorStateList(context, R.color.abc_tint_spinner);
544cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes            } else if (arrayContains(TINT_COLOR_CONTROL_NORMAL, resId)) {
545cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes                tint = getThemeAttrColorStateList(context, R.attr.colorControlNormal);
546cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes            } else if (arrayContains(TINT_COLOR_CONTROL_STATE_LIST, resId)) {
5479d5f84f33353a42e837c6b465412d1a6f2fc6eaaChris Banes                tint = getColorStateList(context, R.color.abc_tint_default);
5481752dfd10e8242d6e017b3828c7d6e94f044691cChris Banes            } else if (arrayContains(TINT_CHECKABLE_BUTTON_LIST, resId)) {
5499d5f84f33353a42e837c6b465412d1a6f2fc6eaaChris Banes                tint = getColorStateList(context, R.color.abc_tint_btn_checkable);
55010e2dbc1ad1c01d2824d921a8b0f070859d6f146Chris Banes            } else if (resId == R.drawable.abc_seekbar_thumb_material) {
5519d5f84f33353a42e837c6b465412d1a6f2fc6eaaChris Banes                tint = getColorStateList(context, R.color.abc_tint_seek_thumb);
55290075479814eb758d97b822606b448e1a521c298Chris Banes            }
553a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banes
554f39fda5f5a43ca50ea942da561cbe4eb9a93be8aChris Banes            if (tint != null) {
5557e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes                addTintListToCache(context, resId, tint);
556cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes            }
55790075479814eb758d97b822606b448e1a521c298Chris Banes        }
558cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes        return tint;
55990075479814eb758d97b822606b448e1a521c298Chris Banes    }
56090075479814eb758d97b822606b448e1a521c298Chris Banes
5617e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes    private ColorStateList getTintListFromCache(@NonNull Context context, @DrawableRes int resId) {
5627e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes        if (mTintLists != null) {
5634ec7a936d40164b0f001b9ea860fa7f29754b228Andrei Stingaceanu            final SparseArrayCompat<ColorStateList> tints = mTintLists.get(context);
5647e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes            return tints != null ? tints.get(resId) : null;
5657e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes        }
5667e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes        return null;
5677e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes    }
568469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes
5697e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes    private void addTintListToCache(@NonNull Context context, @DrawableRes int resId,
5707e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes            @NonNull ColorStateList tintList) {
5717e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes        if (mTintLists == null) {
5727e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes            mTintLists = new WeakHashMap<>();
5737e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes        }
5744ec7a936d40164b0f001b9ea860fa7f29754b228Andrei Stingaceanu        SparseArrayCompat<ColorStateList> themeTints = mTintLists.get(context);
5757e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes        if (themeTints == null) {
5764ec7a936d40164b0f001b9ea860fa7f29754b228Andrei Stingaceanu            themeTints = new SparseArrayCompat<>();
5777e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes            mTintLists.put(context, themeTints);
5787e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes        }
5797e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes        themeTints.append(resId, tintList);
5807e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes    }
581469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes
582f39fda5f5a43ca50ea942da561cbe4eb9a93be8aChris Banes    private ColorStateList createDefaultButtonColorStateList(@NonNull Context context) {
583a1a9092aba48f3ca6400ff53f5f4734752180e81Chris Banes        return createButtonColorStateList(context,
584f39fda5f5a43ca50ea942da561cbe4eb9a93be8aChris Banes                getThemeAttrColor(context, R.attr.colorButtonNormal));
585a1a9092aba48f3ca6400ff53f5f4734752180e81Chris Banes    }
586a1a9092aba48f3ca6400ff53f5f4734752180e81Chris Banes
587f39fda5f5a43ca50ea942da561cbe4eb9a93be8aChris Banes    private ColorStateList createBorderlessButtonColorStateList(@NonNull Context context) {
588c3fa0c344f27c192cf69c5f608d5dc7073a4dfb8Chris Banes        // We ignore the custom tint for borderless buttons
589f39fda5f5a43ca50ea942da561cbe4eb9a93be8aChris Banes        return createButtonColorStateList(context, Color.TRANSPARENT);
590b1131c6dfc9affe5751523f235878055cb699960Chris Banes    }
591b1131c6dfc9affe5751523f235878055cb699960Chris Banes
592f39fda5f5a43ca50ea942da561cbe4eb9a93be8aChris Banes    private ColorStateList createColoredButtonColorStateList(@NonNull Context context) {
593c3fa0c344f27c192cf69c5f608d5dc7073a4dfb8Chris Banes        return createButtonColorStateList(context,
594f39fda5f5a43ca50ea942da561cbe4eb9a93be8aChris Banes                getThemeAttrColor(context, R.attr.colorAccent));
595b1131c6dfc9affe5751523f235878055cb699960Chris Banes    }
596b1131c6dfc9affe5751523f235878055cb699960Chris Banes
597c3fa0c344f27c192cf69c5f608d5dc7073a4dfb8Chris Banes    private ColorStateList createButtonColorStateList(@NonNull final Context context,
598f39fda5f5a43ca50ea942da561cbe4eb9a93be8aChris Banes            @ColorInt final int baseColor) {
59990075479814eb758d97b822606b448e1a521c298Chris Banes        final int[][] states = new int[4][];
60090075479814eb758d97b822606b448e1a521c298Chris Banes        final int[] colors = new int[4];
60190075479814eb758d97b822606b448e1a521c298Chris Banes        int i = 0;
60290075479814eb758d97b822606b448e1a521c298Chris Banes
603eb0d0c030a15e93f456cc1403fffb909c0ae4e66Chris Banes        final int colorControlHighlight = getThemeAttrColor(context, R.attr.colorControlHighlight);
604c3fa0c344f27c192cf69c5f608d5dc7073a4dfb8Chris Banes        final int disabledColor = getDisabledThemeAttrColor(context, R.attr.colorButtonNormal);
605eb0d0c030a15e93f456cc1403fffb909c0ae4e66Chris Banes
60690075479814eb758d97b822606b448e1a521c298Chris Banes        // Disabled state
607bb1a62b3eed874aa47ea4763ac972902c77e988fChris Banes        states[i] = ThemeUtils.DISABLED_STATE_SET;
608f39fda5f5a43ca50ea942da561cbe4eb9a93be8aChris Banes        colors[i] = disabledColor;
60990075479814eb758d97b822606b448e1a521c298Chris Banes        i++;
61090075479814eb758d97b822606b448e1a521c298Chris Banes
611bb1a62b3eed874aa47ea4763ac972902c77e988fChris Banes        states[i] = ThemeUtils.PRESSED_STATE_SET;
612f39fda5f5a43ca50ea942da561cbe4eb9a93be8aChris Banes        colors[i] = compositeColors(colorControlHighlight, baseColor);
61390075479814eb758d97b822606b448e1a521c298Chris Banes        i++;
61490075479814eb758d97b822606b448e1a521c298Chris Banes
615bb1a62b3eed874aa47ea4763ac972902c77e988fChris Banes        states[i] = ThemeUtils.FOCUSED_STATE_SET;
616f39fda5f5a43ca50ea942da561cbe4eb9a93be8aChris Banes        colors[i] = compositeColors(colorControlHighlight, baseColor);
61790075479814eb758d97b822606b448e1a521c298Chris Banes        i++;
61890075479814eb758d97b822606b448e1a521c298Chris Banes
61990075479814eb758d97b822606b448e1a521c298Chris Banes        // Default enabled state
620bb1a62b3eed874aa47ea4763ac972902c77e988fChris Banes        states[i] = ThemeUtils.EMPTY_STATE_SET;
621f39fda5f5a43ca50ea942da561cbe4eb9a93be8aChris Banes        colors[i] = baseColor;
62290075479814eb758d97b822606b448e1a521c298Chris Banes        i++;
62390075479814eb758d97b822606b448e1a521c298Chris Banes
62490075479814eb758d97b822606b448e1a521c298Chris Banes        return new ColorStateList(states, colors);
625a9585dae398a69dd67797e7ca86f44ffcabd9e86Chris Banes    }
626a9585dae398a69dd67797e7ca86f44ffcabd9e86Chris Banes
627613689efde3186aa87a2d541ba8ba72402f090e2Kirill Grouchnikov    private ColorStateList createSwitchThumbColorStateList(Context context) {
628613689efde3186aa87a2d541ba8ba72402f090e2Kirill Grouchnikov        final int[][] states = new int[3][];
629613689efde3186aa87a2d541ba8ba72402f090e2Kirill Grouchnikov        final int[] colors = new int[3];
630613689efde3186aa87a2d541ba8ba72402f090e2Kirill Grouchnikov        int i = 0;
631613689efde3186aa87a2d541ba8ba72402f090e2Kirill Grouchnikov
632613689efde3186aa87a2d541ba8ba72402f090e2Kirill Grouchnikov        final ColorStateList thumbColor = getThemeAttrColorStateList(context,
633613689efde3186aa87a2d541ba8ba72402f090e2Kirill Grouchnikov                R.attr.colorSwitchThumbNormal);
634613689efde3186aa87a2d541ba8ba72402f090e2Kirill Grouchnikov
635613689efde3186aa87a2d541ba8ba72402f090e2Kirill Grouchnikov        if (thumbColor != null && thumbColor.isStateful()) {
636613689efde3186aa87a2d541ba8ba72402f090e2Kirill Grouchnikov            // If colorSwitchThumbNormal is a valid ColorStateList, extract the default and
637613689efde3186aa87a2d541ba8ba72402f090e2Kirill Grouchnikov            // disabled colors from it
638613689efde3186aa87a2d541ba8ba72402f090e2Kirill Grouchnikov
639613689efde3186aa87a2d541ba8ba72402f090e2Kirill Grouchnikov            // Disabled state
640613689efde3186aa87a2d541ba8ba72402f090e2Kirill Grouchnikov            states[i] = ThemeUtils.DISABLED_STATE_SET;
641613689efde3186aa87a2d541ba8ba72402f090e2Kirill Grouchnikov            colors[i] = thumbColor.getColorForState(states[i], 0);
642613689efde3186aa87a2d541ba8ba72402f090e2Kirill Grouchnikov            i++;
643613689efde3186aa87a2d541ba8ba72402f090e2Kirill Grouchnikov
644613689efde3186aa87a2d541ba8ba72402f090e2Kirill Grouchnikov            states[i] = ThemeUtils.CHECKED_STATE_SET;
645613689efde3186aa87a2d541ba8ba72402f090e2Kirill Grouchnikov            colors[i] = getThemeAttrColor(context, R.attr.colorControlActivated);
646613689efde3186aa87a2d541ba8ba72402f090e2Kirill Grouchnikov            i++;
647613689efde3186aa87a2d541ba8ba72402f090e2Kirill Grouchnikov
648613689efde3186aa87a2d541ba8ba72402f090e2Kirill Grouchnikov            // Default enabled state
649613689efde3186aa87a2d541ba8ba72402f090e2Kirill Grouchnikov            states[i] = ThemeUtils.EMPTY_STATE_SET;
650613689efde3186aa87a2d541ba8ba72402f090e2Kirill Grouchnikov            colors[i] = thumbColor.getDefaultColor();
651613689efde3186aa87a2d541ba8ba72402f090e2Kirill Grouchnikov            i++;
652613689efde3186aa87a2d541ba8ba72402f090e2Kirill Grouchnikov        } else {
653613689efde3186aa87a2d541ba8ba72402f090e2Kirill Grouchnikov            // Else we'll use an approximation using the default disabled alpha
654613689efde3186aa87a2d541ba8ba72402f090e2Kirill Grouchnikov
655613689efde3186aa87a2d541ba8ba72402f090e2Kirill Grouchnikov            // Disabled state
656613689efde3186aa87a2d541ba8ba72402f090e2Kirill Grouchnikov            states[i] = ThemeUtils.DISABLED_STATE_SET;
657613689efde3186aa87a2d541ba8ba72402f090e2Kirill Grouchnikov            colors[i] = getDisabledThemeAttrColor(context, R.attr.colorSwitchThumbNormal);
658613689efde3186aa87a2d541ba8ba72402f090e2Kirill Grouchnikov            i++;
659613689efde3186aa87a2d541ba8ba72402f090e2Kirill Grouchnikov
660613689efde3186aa87a2d541ba8ba72402f090e2Kirill Grouchnikov            states[i] = ThemeUtils.CHECKED_STATE_SET;
661613689efde3186aa87a2d541ba8ba72402f090e2Kirill Grouchnikov            colors[i] = getThemeAttrColor(context, R.attr.colorControlActivated);
662613689efde3186aa87a2d541ba8ba72402f090e2Kirill Grouchnikov            i++;
663613689efde3186aa87a2d541ba8ba72402f090e2Kirill Grouchnikov
664613689efde3186aa87a2d541ba8ba72402f090e2Kirill Grouchnikov            // Default enabled state
665613689efde3186aa87a2d541ba8ba72402f090e2Kirill Grouchnikov            states[i] = ThemeUtils.EMPTY_STATE_SET;
666613689efde3186aa87a2d541ba8ba72402f090e2Kirill Grouchnikov            colors[i] = getThemeAttrColor(context, R.attr.colorSwitchThumbNormal);
667613689efde3186aa87a2d541ba8ba72402f090e2Kirill Grouchnikov            i++;
668613689efde3186aa87a2d541ba8ba72402f090e2Kirill Grouchnikov        }
669613689efde3186aa87a2d541ba8ba72402f090e2Kirill Grouchnikov
670613689efde3186aa87a2d541ba8ba72402f090e2Kirill Grouchnikov        return new ColorStateList(states, colors);
671613689efde3186aa87a2d541ba8ba72402f090e2Kirill Grouchnikov    }
672613689efde3186aa87a2d541ba8ba72402f090e2Kirill Grouchnikov
673469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes    private static class ColorFilterLruCache extends LruCache<Integer, PorterDuffColorFilter> {
674469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes
675469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes        public ColorFilterLruCache(int maxSize) {
676469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes            super(maxSize);
677469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes        }
678469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes
679469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes        PorterDuffColorFilter get(int color, PorterDuff.Mode mode) {
680469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes            return get(generateCacheKey(color, mode));
681469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes        }
682469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes
683469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes        PorterDuffColorFilter put(int color, PorterDuff.Mode mode, PorterDuffColorFilter filter) {
684469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes            return put(generateCacheKey(color, mode), filter);
685469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes        }
686469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes
687469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes        private static int generateCacheKey(int color, PorterDuff.Mode mode) {
688469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes            int hashCode = 1;
689469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes            hashCode = 31 * hashCode + color;
690469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes            hashCode = 31 * hashCode + mode.hashCode();
691469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes            return hashCode;
692469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes        }
693469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes    }
694a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banes
6954c99f0e29b0926d8e5de44b7e3980d47f052f04cChris Banes    static void tintDrawable(Drawable drawable, TintInfo tint, int[] state) {
696aaa85b7d563d27fdf10048dd619a317451477ad5Chris Banes        if (DrawableUtils.canSafelyMutateDrawable(drawable)
697aaa85b7d563d27fdf10048dd619a317451477ad5Chris Banes                && drawable.mutate() != drawable) {
69844eb323d09e9bc3baca24ef4a8595aafb7ed2652Chris Banes            Log.d(TAG, "Mutated drawable is not the same instance as the input.");
69944eb323d09e9bc3baca24ef4a8595aafb7ed2652Chris Banes            return;
70044eb323d09e9bc3baca24ef4a8595aafb7ed2652Chris Banes        }
701eb0d0c030a15e93f456cc1403fffb909c0ae4e66Chris Banes
70244eb323d09e9bc3baca24ef4a8595aafb7ed2652Chris Banes        if (tint.mHasTintList || tint.mHasTintMode) {
70344eb323d09e9bc3baca24ef4a8595aafb7ed2652Chris Banes            drawable.setColorFilter(createTintFilter(
70444eb323d09e9bc3baca24ef4a8595aafb7ed2652Chris Banes                    tint.mHasTintList ? tint.mTintList : null,
70544eb323d09e9bc3baca24ef4a8595aafb7ed2652Chris Banes                    tint.mHasTintMode ? tint.mTintMode : DEFAULT_MODE,
70644eb323d09e9bc3baca24ef4a8595aafb7ed2652Chris Banes                    state));
70744eb323d09e9bc3baca24ef4a8595aafb7ed2652Chris Banes        } else {
70844eb323d09e9bc3baca24ef4a8595aafb7ed2652Chris Banes            drawable.clearColorFilter();
70944eb323d09e9bc3baca24ef4a8595aafb7ed2652Chris Banes        }
71044eb323d09e9bc3baca24ef4a8595aafb7ed2652Chris Banes
7117797b9f22c8c404309b778a0966266d2b1a84915Chris Banes        if (Build.VERSION.SDK_INT <= 23) {
7127797b9f22c8c404309b778a0966266d2b1a84915Chris Banes            // Pre-v23 there is no guarantee that a state change will invoke an invalidation,
7137797b9f22c8c404309b778a0966266d2b1a84915Chris Banes            // so we force it ourselves
71444eb323d09e9bc3baca24ef4a8595aafb7ed2652Chris Banes            drawable.invalidateSelf();
71544eb323d09e9bc3baca24ef4a8595aafb7ed2652Chris Banes        }
71644eb323d09e9bc3baca24ef4a8595aafb7ed2652Chris Banes    }
71744eb323d09e9bc3baca24ef4a8595aafb7ed2652Chris Banes
71839cb4c1c59fa156ed28bc8835ef05eeb356ad13cChris Banes    private static PorterDuffColorFilter createTintFilter(ColorStateList tint,
71939cb4c1c59fa156ed28bc8835ef05eeb356ad13cChris Banes            PorterDuff.Mode tintMode, final int[] state) {
72039cb4c1c59fa156ed28bc8835ef05eeb356ad13cChris Banes        if (tint == null || tintMode == null) {
72139cb4c1c59fa156ed28bc8835ef05eeb356ad13cChris Banes            return null;
722cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes        }
72339cb4c1c59fa156ed28bc8835ef05eeb356ad13cChris Banes        final int color = tint.getColorForState(state, Color.TRANSPARENT);
72439cb4c1c59fa156ed28bc8835ef05eeb356ad13cChris Banes        return getPorterDuffColorFilter(color, tintMode);
72539cb4c1c59fa156ed28bc8835ef05eeb356ad13cChris Banes    }
726cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes
727fe1cbed21122206b7a4af97790ade439d49421d8Chris Banes    public static PorterDuffColorFilter getPorterDuffColorFilter(int color, PorterDuff.Mode mode) {
728a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banes        // First, lets see if the cache already contains the color filter
729a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banes        PorterDuffColorFilter filter = COLOR_FILTER_CACHE.get(color, mode);
730a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banes
731a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banes        if (filter == null) {
732a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banes            // Cache miss, so create a color filter and add it to the cache
733a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banes            filter = new PorterDuffColorFilter(color, mode);
734a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banes            COLOR_FILTER_CACHE.put(color, mode, filter);
735a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banes        }
736a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banes
73739cb4c1c59fa156ed28bc8835ef05eeb356ad13cChris Banes        return filter;
738a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banes    }
73910e2dbc1ad1c01d2824d921a8b0f070859d6f146Chris Banes
74010e2dbc1ad1c01d2824d921a8b0f070859d6f146Chris Banes    private static void setPorterDuffColorFilter(Drawable d, int color, PorterDuff.Mode mode) {
741aaa85b7d563d27fdf10048dd619a317451477ad5Chris Banes        if (DrawableUtils.canSafelyMutateDrawable(d)) {
742d20d889e7abef50efbaf6e975100a8fb73409b13Chris Banes            d = d.mutate();
743d20d889e7abef50efbaf6e975100a8fb73409b13Chris Banes        }
74410e2dbc1ad1c01d2824d921a8b0f070859d6f146Chris Banes        d.setColorFilter(getPorterDuffColorFilter(color, mode == null ? DEFAULT_MODE : mode));
74510e2dbc1ad1c01d2824d921a8b0f070859d6f146Chris Banes    }
7469f63864c84e3d48841d553521468c7a27189c4f0Chris Banes
747eb648620b291eabbb5523f649baef5abb2e4687bAlan Viverette    private void checkVectorDrawableSetup(@NonNull Context context) {
7489ccbe0f4fa88c4ec959e1bdfaaa42caedf6886ccTeng-Hui Zhu        if (mHasCheckedVectorDrawableSetup) {
749eb648620b291eabbb5523f649baef5abb2e4687bAlan Viverette            // We've already checked so return now...
750eb648620b291eabbb5523f649baef5abb2e4687bAlan Viverette            return;
751eb648620b291eabbb5523f649baef5abb2e4687bAlan Viverette        }
752eb648620b291eabbb5523f649baef5abb2e4687bAlan Viverette        // Here we will check that a known Vector drawable resource inside AppCompat can be
7533a5aeed1e19247ea088691186500877b4e3f7d69Chris Banes        // correctly decoded
754dd61be0d0da04cc63c9c41c1b82348c20b2bf1cdYuichi Araki        mHasCheckedVectorDrawableSetup = true;
7553a5aeed1e19247ea088691186500877b4e3f7d69Chris Banes        final Drawable d = getDrawable(context, R.drawable.abc_vector_test);
756dd61be0d0da04cc63c9c41c1b82348c20b2bf1cdYuichi Araki        if (d == null || !isVectorDrawable(d)) {
757dd61be0d0da04cc63c9c41c1b82348c20b2bf1cdYuichi Araki            mHasCheckedVectorDrawableSetup = false;
758eb648620b291eabbb5523f649baef5abb2e4687bAlan Viverette            throw new IllegalStateException("This app has been built with an incorrect "
759eb648620b291eabbb5523f649baef5abb2e4687bAlan Viverette                    + "configuration. Please configure your build for VectorDrawableCompat.");
760eb648620b291eabbb5523f649baef5abb2e4687bAlan Viverette        }
761eb648620b291eabbb5523f649baef5abb2e4687bAlan Viverette    }
762eb648620b291eabbb5523f649baef5abb2e4687bAlan Viverette
763ba13a44cd6db80ec983b1ed678b7dbeab04056a1Chris Banes    private static boolean isVectorDrawable(@NonNull Drawable d) {
764ba13a44cd6db80ec983b1ed678b7dbeab04056a1Chris Banes        return d instanceof VectorDrawableCompat
765ba13a44cd6db80ec983b1ed678b7dbeab04056a1Chris Banes                || PLATFORM_VD_CLAZZ.equals(d.getClass().getName());
766ba13a44cd6db80ec983b1ed678b7dbeab04056a1Chris Banes    }
767ba13a44cd6db80ec983b1ed678b7dbeab04056a1Chris Banes
7689f63864c84e3d48841d553521468c7a27189c4f0Chris Banes    private static class VdcInflateDelegate implements InflateDelegate {
7692c1bad7ecd5879bf0f29ce2ce1bc5bd67a3f4682Aurimas Liutikas        VdcInflateDelegate() {
7702c1bad7ecd5879bf0f29ce2ce1bc5bd67a3f4682Aurimas Liutikas        }
7712c1bad7ecd5879bf0f29ce2ce1bc5bd67a3f4682Aurimas Liutikas
7729f63864c84e3d48841d553521468c7a27189c4f0Chris Banes        @Override
773bd3494de1d97206f366f52641a14b21eb06a7022Chris Banes        public Drawable createFromXmlInner(@NonNull Context context, @NonNull XmlPullParser parser,
7749f63864c84e3d48841d553521468c7a27189c4f0Chris Banes                @NonNull AttributeSet attrs, @Nullable Resources.Theme theme) {
7759f63864c84e3d48841d553521468c7a27189c4f0Chris Banes            try {
776bd3494de1d97206f366f52641a14b21eb06a7022Chris Banes                return VectorDrawableCompat
777bd3494de1d97206f366f52641a14b21eb06a7022Chris Banes                        .createFromXmlInner(context.getResources(), parser, attrs, theme);
7789f63864c84e3d48841d553521468c7a27189c4f0Chris Banes            } catch (Exception e) {
7799f63864c84e3d48841d553521468c7a27189c4f0Chris Banes                Log.e("VdcInflateDelegate", "Exception while inflating <vector>", e);
7809f63864c84e3d48841d553521468c7a27189c4f0Chris Banes                return null;
7819f63864c84e3d48841d553521468c7a27189c4f0Chris Banes            }
7829f63864c84e3d48841d553521468c7a27189c4f0Chris Banes        }
7839f63864c84e3d48841d553521468c7a27189c4f0Chris Banes    }
784bd3494de1d97206f366f52641a14b21eb06a7022Chris Banes
7858f886fe8c7e23fe6ccb8734167c960c2ed3429c3Alan Viverette    @RequiresApi(11)
786bd3494de1d97206f366f52641a14b21eb06a7022Chris Banes    private static class AvdcInflateDelegate implements InflateDelegate {
7872c1bad7ecd5879bf0f29ce2ce1bc5bd67a3f4682Aurimas Liutikas        AvdcInflateDelegate() {
7882c1bad7ecd5879bf0f29ce2ce1bc5bd67a3f4682Aurimas Liutikas        }
7892c1bad7ecd5879bf0f29ce2ce1bc5bd67a3f4682Aurimas Liutikas
790bd3494de1d97206f366f52641a14b21eb06a7022Chris Banes        @Override
791bd3494de1d97206f366f52641a14b21eb06a7022Chris Banes        public Drawable createFromXmlInner(@NonNull Context context, @NonNull XmlPullParser parser,
792bd3494de1d97206f366f52641a14b21eb06a7022Chris Banes                @NonNull AttributeSet attrs, @Nullable Resources.Theme theme) {
793bd3494de1d97206f366f52641a14b21eb06a7022Chris Banes            try {
794bd3494de1d97206f366f52641a14b21eb06a7022Chris Banes                return AnimatedVectorDrawableCompat
795bd3494de1d97206f366f52641a14b21eb06a7022Chris Banes                        .createFromXmlInner(context, context.getResources(), parser, attrs, theme);
796bd3494de1d97206f366f52641a14b21eb06a7022Chris Banes            } catch (Exception e) {
797bd3494de1d97206f366f52641a14b21eb06a7022Chris Banes                Log.e("AvdcInflateDelegate", "Exception while inflating <animated-vector>", e);
798bd3494de1d97206f366f52641a14b21eb06a7022Chris Banes                return null;
799bd3494de1d97206f366f52641a14b21eb06a7022Chris Banes            }
800bd3494de1d97206f366f52641a14b21eb06a7022Chris Banes        }
801bd3494de1d97206f366f52641a14b21eb06a7022Chris Banes    }
802469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes}
803