AppCompatDrawableManager.java revision 8f886fe8c7e23fe6ccb8734167c960c2ed3429c3
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 19c39d9c75590eca86a5e7e32a8824ba04a0d42e9bAlan Viveretteimport static android.support.annotation.RestrictTo.Scope.GROUP_ID; 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 268f886fe8c7e23fe6ccb8734167c960c2ed3429c3Alan Viveretteimport android.annotation.TargetApi; 27469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banesimport android.content.Context; 28469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banesimport android.content.res.ColorStateList; 29e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banesimport android.content.res.Resources; 3039cb4c1c59fa156ed28bc8835ef05eeb356ad13cChris Banesimport android.graphics.Color; 31469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banesimport android.graphics.PorterDuff; 32469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banesimport android.graphics.PorterDuffColorFilter; 33469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banesimport android.graphics.drawable.Drawable; 349f63864c84e3d48841d553521468c7a27189c4f0Chris Banesimport android.graphics.drawable.Drawable.ConstantState; 35cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banesimport android.graphics.drawable.LayerDrawable; 36fd1eb27a3700de31507de34fd1bcc51830fe876cChris Banesimport android.os.Build; 37a1a9092aba48f3ca6400ff53f5f4734752180e81Chris Banesimport android.support.annotation.ColorInt; 387e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banesimport android.support.annotation.DrawableRes; 397e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banesimport android.support.annotation.NonNull; 407e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banesimport android.support.annotation.Nullable; 418f886fe8c7e23fe6ccb8734167c960c2ed3429c3Alan Viveretteimport android.support.annotation.RequiresApi; 42c39d9c75590eca86a5e7e32a8824ba04a0d42e9bAlan Viveretteimport android.support.annotation.RestrictTo; 43bd3494de1d97206f366f52641a14b21eb06a7022Chris Banesimport android.support.graphics.drawable.AnimatedVectorDrawableCompat; 44e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banesimport android.support.graphics.drawable.VectorDrawableCompat; 45469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banesimport android.support.v4.content.ContextCompat; 467e82b99953680915596eaf0eb35927388e574ca8Chris Banesimport android.support.v4.graphics.drawable.DrawableCompat; 47e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banesimport android.support.v4.util.ArrayMap; 489f63864c84e3d48841d553521468c7a27189c4f0Chris Banesimport android.support.v4.util.LongSparseArray; 49469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banesimport android.support.v4.util.LruCache; 50469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banesimport android.support.v7.appcompat.R; 51e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banesimport android.util.AttributeSet; 52469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banesimport android.util.Log; 5390075479814eb758d97b822606b448e1a521c298Chris Banesimport android.util.SparseArray; 54e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banesimport android.util.TypedValue; 55e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banesimport android.util.Xml; 56469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes 574c99f0e29b0926d8e5de44b7e3980d47f052f04cChris Banesimport org.xmlpull.v1.XmlPullParser; 584c99f0e29b0926d8e5de44b7e3980d47f052f04cChris Banesimport org.xmlpull.v1.XmlPullParserException; 594c99f0e29b0926d8e5de44b7e3980d47f052f04cChris Banes 609f63864c84e3d48841d553521468c7a27189c4f0Chris Banesimport java.lang.ref.WeakReference; 61cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banesimport java.util.WeakHashMap; 62cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes 63469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes/** 64469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes * @hide 65469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes */ 66c39d9c75590eca86a5e7e32a8824ba04a0d42e9bAlan Viverette@RestrictTo(GROUP_ID) 677e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banespublic final class AppCompatDrawableManager { 687e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes 699f63864c84e3d48841d553521468c7a27189c4f0Chris Banes private interface InflateDelegate { 70bd3494de1d97206f366f52641a14b21eb06a7022Chris Banes Drawable createFromXmlInner(@NonNull Context context, @NonNull XmlPullParser parser, 71e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes @NonNull AttributeSet attrs, @Nullable Resources.Theme theme); 727e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes } 73fd1eb27a3700de31507de34fd1bcc51830fe876cChris Banes 749f63864c84e3d48841d553521468c7a27189c4f0Chris Banes private static final String TAG = "AppCompatDrawableManager"; 75469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes private static final boolean DEBUG = false; 76cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes private static final PorterDuff.Mode DEFAULT_MODE = PorterDuff.Mode.SRC_IN; 77e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes private static final String SKIP_DRAWABLE_TAG = "appcompat_skip_skip"; 78415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes 79ba13a44cd6db80ec983b1ed678b7dbeab04056a1Chris Banes private static final String PLATFORM_VD_CLAZZ = "android.graphics.drawable.VectorDrawable"; 80ba13a44cd6db80ec983b1ed678b7dbeab04056a1Chris Banes 817e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes private static AppCompatDrawableManager INSTANCE; 827e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes 837e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes public static AppCompatDrawableManager get() { 847e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes if (INSTANCE == null) { 857e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes INSTANCE = new AppCompatDrawableManager(); 86bd3494de1d97206f366f52641a14b21eb06a7022Chris Banes installDefaultInflateDelegates(INSTANCE); 87bd3494de1d97206f366f52641a14b21eb06a7022Chris Banes } 88bd3494de1d97206f366f52641a14b21eb06a7022Chris Banes return INSTANCE; 89bd3494de1d97206f366f52641a14b21eb06a7022Chris Banes } 90bd3494de1d97206f366f52641a14b21eb06a7022Chris Banes 91bd3494de1d97206f366f52641a14b21eb06a7022Chris Banes private static void installDefaultInflateDelegates(@NonNull AppCompatDrawableManager manager) { 92c59ac731f7d38ee41d0aba567a9d3b77b40df628Teng-Hui Zhu // This sdk version check will affect src:appCompat code path. 9395276d3d3d07337500b6255d6a6ae031591784f3Teng-Hui Zhu // Although VectorDrawable exists in Android framework from Lollipop, AppCompat will use the 9495276d3d3d07337500b6255d6a6ae031591784f3Teng-Hui Zhu // VectorDrawableCompat before Nougat to utilize the bug fixes in VectorDrawableCompat. 958f886fe8c7e23fe6ccb8734167c960c2ed3429c3Alan Viverette if (Build.VERSION.SDK_INT < 24) { 96bd3494de1d97206f366f52641a14b21eb06a7022Chris Banes manager.addDelegate("vector", new VdcInflateDelegate()); 978f886fe8c7e23fe6ccb8734167c960c2ed3429c3Alan Viverette if (Build.VERSION.SDK_INT >= 11) { 98bd3494de1d97206f366f52641a14b21eb06a7022Chris Banes // AnimatedVectorDrawableCompat only works on API v11+ 99bd3494de1d97206f366f52641a14b21eb06a7022Chris Banes manager.addDelegate("animated-vector", new AvdcInflateDelegate()); 1009f63864c84e3d48841d553521468c7a27189c4f0Chris Banes } 1017e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes } 1027e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes } 1037e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes 104469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes private static final ColorFilterLruCache COLOR_FILTER_CACHE = new ColorFilterLruCache(6); 105469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes 106469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes /** 107469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes * Drawables which should be tinted with the value of {@code R.attr.colorControlNormal}, 108cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes * using the default mode using a raw color filter. 109cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes */ 110cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes private static final int[] COLORFILTER_TINT_COLOR_CONTROL_NORMAL = { 111cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes R.drawable.abc_textfield_search_default_mtrl_alpha, 112cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes R.drawable.abc_textfield_default_mtrl_alpha, 113cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes R.drawable.abc_ab_share_pack_mtrl_alpha 114cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes }; 115cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes 116cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes /** 117cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes * Drawables which should be tinted with the value of {@code R.attr.colorControlNormal}, using 118cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes * {@link DrawableCompat}'s tinting functionality. 119469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes */ 120469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes private static final int[] TINT_COLOR_CONTROL_NORMAL = { 121469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes R.drawable.abc_ic_commit_search_api_mtrl_alpha, 122a59882b82ecd74c79e05538a0962646d003d4c0eChris Banes R.drawable.abc_seekbar_tick_mark_material, 123a59882b82ecd74c79e05538a0962646d003d4c0eChris Banes R.drawable.abc_ic_menu_share_mtrl_alpha, 124a59882b82ecd74c79e05538a0962646d003d4c0eChris Banes R.drawable.abc_ic_menu_copy_mtrl_am_alpha, 125a59882b82ecd74c79e05538a0962646d003d4c0eChris Banes R.drawable.abc_ic_menu_cut_mtrl_alpha, 126a59882b82ecd74c79e05538a0962646d003d4c0eChris Banes R.drawable.abc_ic_menu_selectall_mtrl_alpha, 127a59882b82ecd74c79e05538a0962646d003d4c0eChris Banes R.drawable.abc_ic_menu_paste_mtrl_am_alpha 128469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes }; 129469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes 130469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes /** 131469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes * Drawables which should be tinted with the value of {@code R.attr.colorControlActivated}, 132cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes * using a color filter. 133469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes */ 134cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes private static final int[] COLORFILTER_COLOR_CONTROL_ACTIVATED = { 135469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes R.drawable.abc_textfield_activated_mtrl_alpha, 13614f8f0dfae09445074dc0f7b5bbe5732d6922da7Chris Banes R.drawable.abc_textfield_search_activated_mtrl_alpha, 137911642499da7d796aa1e7c19178c3552a590f48bChris Banes R.drawable.abc_cab_background_top_mtrl_alpha, 13870d10816465bf5844901adab283ae7ab3e041396Chris Banes R.drawable.abc_text_cursor_material, 1390b8e0916ede7eb212e7d906956c5137a9b9dfe65Chris Banes R.drawable.abc_text_select_handle_left_mtrl_dark, 1400b8e0916ede7eb212e7d906956c5137a9b9dfe65Chris Banes R.drawable.abc_text_select_handle_middle_mtrl_dark, 1410b8e0916ede7eb212e7d906956c5137a9b9dfe65Chris Banes R.drawable.abc_text_select_handle_right_mtrl_dark, 1420b8e0916ede7eb212e7d906956c5137a9b9dfe65Chris Banes R.drawable.abc_text_select_handle_left_mtrl_light, 1430b8e0916ede7eb212e7d906956c5137a9b9dfe65Chris Banes R.drawable.abc_text_select_handle_middle_mtrl_light, 1440b8e0916ede7eb212e7d906956c5137a9b9dfe65Chris Banes R.drawable.abc_text_select_handle_right_mtrl_light 145469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes }; 146469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes 147469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes /** 148469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes * Drawables which should be tinted with the value of {@code android.R.attr.colorBackground}, 149cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes * using the {@link android.graphics.PorterDuff.Mode#MULTIPLY} mode and a color filter. 150469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes */ 151cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes private static final int[] COLORFILTER_COLOR_BACKGROUND_MULTIPLY = { 1521073132946bb0a53a788949fe4c060f72051cd57Chris Banes R.drawable.abc_popup_background_mtrl_mult, 15357c6de90985a63358129b99b9f0cd4d6afe887d6Chris Banes R.drawable.abc_cab_background_internal_bg, 15457c6de90985a63358129b99b9f0cd4d6afe887d6Chris Banes R.drawable.abc_menu_hardkey_panel_mtrl_mult 155469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes }; 156469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes 157469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes /** 158469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes * Drawables which should be tinted using a state list containing values of 159469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes * {@code R.attr.colorControlNormal} and {@code R.attr.colorControlActivated} 160469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes */ 161469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes private static final int[] TINT_COLOR_CONTROL_STATE_LIST = { 162469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes R.drawable.abc_tab_indicator_material, 16374433e220541f8236f4e01f658a96854b29acfd1Chris Banes R.drawable.abc_textfield_search_material 164469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes }; 165469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes 1661752dfd10e8242d6e017b3828c7d6e94f044691cChris Banes /** 1671752dfd10e8242d6e017b3828c7d6e94f044691cChris Banes * Drawables which should be tinted using a state list containing values of 1681752dfd10e8242d6e017b3828c7d6e94f044691cChris Banes * {@code R.attr.colorControlNormal} and {@code R.attr.colorControlActivated} for the checked 1691752dfd10e8242d6e017b3828c7d6e94f044691cChris Banes * state. 1701752dfd10e8242d6e017b3828c7d6e94f044691cChris Banes */ 1711752dfd10e8242d6e017b3828c7d6e94f044691cChris Banes private static final int[] TINT_CHECKABLE_BUTTON_LIST = { 1721752dfd10e8242d6e017b3828c7d6e94f044691cChris Banes R.drawable.abc_btn_check_material, 1731752dfd10e8242d6e017b3828c7d6e94f044691cChris Banes R.drawable.abc_btn_radio_material 1741752dfd10e8242d6e017b3828c7d6e94f044691cChris Banes }; 1751752dfd10e8242d6e017b3828c7d6e94f044691cChris Banes 1767e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes private WeakHashMap<Context, SparseArray<ColorStateList>> mTintLists; 177e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes private ArrayMap<String, InflateDelegate> mDelegates; 178e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes private SparseArray<String> mKnownDrawableIdTags; 179e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes 18060cc94cdf2f9dfd10f2fe0b3fa0fa438d51df271Chris Banes private final Object mDrawableCacheLock = new Object(); 1819f63864c84e3d48841d553521468c7a27189c4f0Chris Banes private final WeakHashMap<Context, LongSparseArray<WeakReference<Drawable.ConstantState>>> 18260cc94cdf2f9dfd10f2fe0b3fa0fa438d51df271Chris Banes mDrawableCaches = new WeakHashMap<>(0); 1839f63864c84e3d48841d553521468c7a27189c4f0Chris Banes 184e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes private TypedValue mTypedValue; 185469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes 186ba13a44cd6db80ec983b1ed678b7dbeab04056a1Chris Banes private boolean mHasCheckedVectorDrawableSetup; 187ba13a44cd6db80ec983b1ed678b7dbeab04056a1Chris Banes 1887e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes public Drawable getDrawable(@NonNull Context context, @DrawableRes int resId) { 1897e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes return getDrawable(context, resId, false); 190469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes } 191469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes 1924c99f0e29b0926d8e5de44b7e3980d47f052f04cChris Banes Drawable getDrawable(@NonNull Context context, @DrawableRes int resId, 1937e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes boolean failIfNotKnown) { 194eb648620b291eabbb5523f649baef5abb2e4687bAlan Viverette checkVectorDrawableSetup(context); 195eb648620b291eabbb5523f649baef5abb2e4687bAlan Viverette 196e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes Drawable drawable = loadDrawableFromDelegates(context, resId); 197e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes if (drawable == null) { 19860cc94cdf2f9dfd10f2fe0b3fa0fa438d51df271Chris Banes drawable = createDrawableIfNeeded(context, resId); 19960cc94cdf2f9dfd10f2fe0b3fa0fa438d51df271Chris Banes } 20060cc94cdf2f9dfd10f2fe0b3fa0fa438d51df271Chris Banes if (drawable == null) { 201e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes drawable = ContextCompat.getDrawable(context, resId); 202cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes } 20360cc94cdf2f9dfd10f2fe0b3fa0fa438d51df271Chris Banes 204e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes if (drawable != null) { 205aaa85b7d563d27fdf10048dd619a317451477ad5Chris Banes // Tint it if needed 206aaa85b7d563d27fdf10048dd619a317451477ad5Chris Banes drawable = tintDrawable(context, resId, failIfNotKnown, drawable); 207cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes } 208aaa85b7d563d27fdf10048dd619a317451477ad5Chris Banes if (drawable != null) { 209aaa85b7d563d27fdf10048dd619a317451477ad5Chris Banes // See if we need to 'fix' the drawable 210aaa85b7d563d27fdf10048dd619a317451477ad5Chris Banes DrawableUtils.fixDrawable(drawable); 211aaa85b7d563d27fdf10048dd619a317451477ad5Chris Banes } 212aaa85b7d563d27fdf10048dd619a317451477ad5Chris Banes return drawable; 213e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes } 214cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes 215dfdb7ac7893845f052e27c8c705b0708524b6a91Chris Banes public void onConfigurationChanged(@NonNull Context context) { 216dfdb7ac7893845f052e27c8c705b0708524b6a91Chris Banes synchronized (mDrawableCacheLock) { 217dfdb7ac7893845f052e27c8c705b0708524b6a91Chris Banes LongSparseArray<WeakReference<ConstantState>> cache = mDrawableCaches.get(context); 218dfdb7ac7893845f052e27c8c705b0708524b6a91Chris Banes if (cache != null) { 219dfdb7ac7893845f052e27c8c705b0708524b6a91Chris Banes // Crude, but we'll just clear the cache when the configuration changes 220dfdb7ac7893845f052e27c8c705b0708524b6a91Chris Banes cache.clear(); 221dfdb7ac7893845f052e27c8c705b0708524b6a91Chris Banes } 222dfdb7ac7893845f052e27c8c705b0708524b6a91Chris Banes } 223dfdb7ac7893845f052e27c8c705b0708524b6a91Chris Banes } 224dfdb7ac7893845f052e27c8c705b0708524b6a91Chris Banes 22560cc94cdf2f9dfd10f2fe0b3fa0fa438d51df271Chris Banes private static long createCacheKey(TypedValue tv) { 22660cc94cdf2f9dfd10f2fe0b3fa0fa438d51df271Chris Banes return (((long) tv.assetCookie) << 32) | tv.data; 22760cc94cdf2f9dfd10f2fe0b3fa0fa438d51df271Chris Banes } 22860cc94cdf2f9dfd10f2fe0b3fa0fa438d51df271Chris Banes 22960cc94cdf2f9dfd10f2fe0b3fa0fa438d51df271Chris Banes private Drawable createDrawableIfNeeded(@NonNull Context context, 23060cc94cdf2f9dfd10f2fe0b3fa0fa438d51df271Chris Banes @DrawableRes final int resId) { 23160cc94cdf2f9dfd10f2fe0b3fa0fa438d51df271Chris Banes if (mTypedValue == null) { 23260cc94cdf2f9dfd10f2fe0b3fa0fa438d51df271Chris Banes mTypedValue = new TypedValue(); 23360cc94cdf2f9dfd10f2fe0b3fa0fa438d51df271Chris Banes } 23460cc94cdf2f9dfd10f2fe0b3fa0fa438d51df271Chris Banes final TypedValue tv = mTypedValue; 23560cc94cdf2f9dfd10f2fe0b3fa0fa438d51df271Chris Banes context.getResources().getValue(resId, tv, true); 23660cc94cdf2f9dfd10f2fe0b3fa0fa438d51df271Chris Banes final long key = createCacheKey(tv); 23760cc94cdf2f9dfd10f2fe0b3fa0fa438d51df271Chris Banes 23860cc94cdf2f9dfd10f2fe0b3fa0fa438d51df271Chris Banes Drawable dr = getCachedDrawable(context, key); 23960cc94cdf2f9dfd10f2fe0b3fa0fa438d51df271Chris Banes if (dr != null) { 24060cc94cdf2f9dfd10f2fe0b3fa0fa438d51df271Chris Banes // If we got a cached drawable, return it 24160cc94cdf2f9dfd10f2fe0b3fa0fa438d51df271Chris Banes return dr; 24260cc94cdf2f9dfd10f2fe0b3fa0fa438d51df271Chris Banes } 24360cc94cdf2f9dfd10f2fe0b3fa0fa438d51df271Chris Banes 24460cc94cdf2f9dfd10f2fe0b3fa0fa438d51df271Chris Banes // Else we need to try and create one... 24560cc94cdf2f9dfd10f2fe0b3fa0fa438d51df271Chris Banes if (resId == R.drawable.abc_cab_background_top_material) { 24660cc94cdf2f9dfd10f2fe0b3fa0fa438d51df271Chris Banes dr = new LayerDrawable(new Drawable[]{ 24760cc94cdf2f9dfd10f2fe0b3fa0fa438d51df271Chris Banes getDrawable(context, R.drawable.abc_cab_background_internal_bg), 24860cc94cdf2f9dfd10f2fe0b3fa0fa438d51df271Chris Banes getDrawable(context, R.drawable.abc_cab_background_top_mtrl_alpha) 24960cc94cdf2f9dfd10f2fe0b3fa0fa438d51df271Chris Banes }); 25060cc94cdf2f9dfd10f2fe0b3fa0fa438d51df271Chris Banes } 25160cc94cdf2f9dfd10f2fe0b3fa0fa438d51df271Chris Banes 25260cc94cdf2f9dfd10f2fe0b3fa0fa438d51df271Chris Banes if (dr != null) { 25360cc94cdf2f9dfd10f2fe0b3fa0fa438d51df271Chris Banes dr.setChangingConfigurations(tv.changingConfigurations); 25460cc94cdf2f9dfd10f2fe0b3fa0fa438d51df271Chris Banes // If we reached here then we created a new drawable, add it to the cache 25560cc94cdf2f9dfd10f2fe0b3fa0fa438d51df271Chris Banes addDrawableToCache(context, key, dr); 25660cc94cdf2f9dfd10f2fe0b3fa0fa438d51df271Chris Banes } 25760cc94cdf2f9dfd10f2fe0b3fa0fa438d51df271Chris Banes 25860cc94cdf2f9dfd10f2fe0b3fa0fa438d51df271Chris Banes return dr; 25960cc94cdf2f9dfd10f2fe0b3fa0fa438d51df271Chris Banes } 26060cc94cdf2f9dfd10f2fe0b3fa0fa438d51df271Chris Banes 261e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes private Drawable tintDrawable(@NonNull Context context, @DrawableRes int resId, 262e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes boolean failIfNotKnown, @NonNull Drawable drawable) { 263e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes final ColorStateList tintList = getTintList(context, resId); 264e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes if (tintList != null) { 265e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes // First mutate the Drawable, then wrap it and set the tint list 266aaa85b7d563d27fdf10048dd619a317451477ad5Chris Banes if (DrawableUtils.canSafelyMutateDrawable(drawable)) { 267e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes drawable = drawable.mutate(); 268e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes } 269e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes drawable = DrawableCompat.wrap(drawable); 270e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes DrawableCompat.setTintList(drawable, tintList); 271469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes 272e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes // If there is a blending mode specified for the drawable, use it 273e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes final PorterDuff.Mode tintMode = getTintMode(resId); 274e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes if (tintMode != null) { 275e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes DrawableCompat.setTintMode(drawable, tintMode); 276e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes } 277e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes } else if (resId == R.drawable.abc_seekbar_track_material) { 278e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes LayerDrawable ld = (LayerDrawable) drawable; 279e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes setPorterDuffColorFilter(ld.findDrawableByLayerId(android.R.id.background), 280e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes getThemeAttrColor(context, R.attr.colorControlNormal), DEFAULT_MODE); 281e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes setPorterDuffColorFilter(ld.findDrawableByLayerId(android.R.id.secondaryProgress), 282e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes getThemeAttrColor(context, R.attr.colorControlNormal), DEFAULT_MODE); 283e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes setPorterDuffColorFilter(ld.findDrawableByLayerId(android.R.id.progress), 284e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes getThemeAttrColor(context, R.attr.colorControlActivated), DEFAULT_MODE); 28574433e220541f8236f4e01f658a96854b29acfd1Chris Banes } else if (resId == R.drawable.abc_ratingbar_material 28674433e220541f8236f4e01f658a96854b29acfd1Chris Banes || resId == R.drawable.abc_ratingbar_indicator_material 287e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes || resId == R.drawable.abc_ratingbar_small_material) { 288e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes LayerDrawable ld = (LayerDrawable) drawable; 289e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes setPorterDuffColorFilter(ld.findDrawableByLayerId(android.R.id.background), 290e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes getDisabledThemeAttrColor(context, R.attr.colorControlNormal), 291e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes DEFAULT_MODE); 292e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes setPorterDuffColorFilter(ld.findDrawableByLayerId(android.R.id.secondaryProgress), 293e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes getThemeAttrColor(context, R.attr.colorControlActivated), DEFAULT_MODE); 294e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes setPorterDuffColorFilter(ld.findDrawableByLayerId(android.R.id.progress), 295e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes getThemeAttrColor(context, R.attr.colorControlActivated), DEFAULT_MODE); 296e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes } else { 297e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes final boolean tinted = tintDrawableUsingColorFilter(context, resId, drawable); 298e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes if (!tinted && failIfNotKnown) { 299e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes // If we didn't tint using a ColorFilter, and we're set to fail if we don't 300e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes // know the id, return null 301e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes drawable = null; 302e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes } 303e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes } 304e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes return drawable; 305e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes } 306a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banes 307e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes private Drawable loadDrawableFromDelegates(@NonNull Context context, @DrawableRes int resId) { 308e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes if (mDelegates != null && !mDelegates.isEmpty()) { 309e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes if (mKnownDrawableIdTags != null) { 3109f63864c84e3d48841d553521468c7a27189c4f0Chris Banes final String cachedTagName = mKnownDrawableIdTags.get(resId); 311e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes if (SKIP_DRAWABLE_TAG.equals(cachedTagName) 312e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes || (cachedTagName != null && mDelegates.get(cachedTagName) == null)) { 313e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes // If we don't have a delegate for the drawable tag, or we've been set to 314e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes // skip it, fail fast and return null 315e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes if (DEBUG) { 3169f63864c84e3d48841d553521468c7a27189c4f0Chris Banes Log.d(TAG, "[loadDrawableFromDelegates] Skipping drawable: " 317e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes + context.getResources().getResourceName(resId)); 318e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes } 319e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes return null; 320a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banes } 321469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes } else { 322e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes // Create an id cache as we'll need one later 323e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes mKnownDrawableIdTags = new SparseArray<>(); 324e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes } 325e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes 326e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes if (mTypedValue == null) { 327e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes mTypedValue = new TypedValue(); 328e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes } 329e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes final TypedValue tv = mTypedValue; 330e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes final Resources res = context.getResources(); 331e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes res.getValue(resId, tv, true); 332e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes 33360cc94cdf2f9dfd10f2fe0b3fa0fa438d51df271Chris Banes final long key = createCacheKey(tv); 3349f63864c84e3d48841d553521468c7a27189c4f0Chris Banes 33560cc94cdf2f9dfd10f2fe0b3fa0fa438d51df271Chris Banes Drawable dr = getCachedDrawable(context, key); 3369f63864c84e3d48841d553521468c7a27189c4f0Chris Banes if (dr != null) { 3379f63864c84e3d48841d553521468c7a27189c4f0Chris Banes if (DEBUG) { 3389f63864c84e3d48841d553521468c7a27189c4f0Chris Banes Log.i(TAG, "[loadDrawableFromDelegates] Returning cached drawable: " + 3399f63864c84e3d48841d553521468c7a27189c4f0Chris Banes context.getResources().getResourceName(resId)); 3409f63864c84e3d48841d553521468c7a27189c4f0Chris Banes } 3419f63864c84e3d48841d553521468c7a27189c4f0Chris Banes // We have a cached drawable, return it! 3429f63864c84e3d48841d553521468c7a27189c4f0Chris Banes return dr; 3439f63864c84e3d48841d553521468c7a27189c4f0Chris Banes } 3449f63864c84e3d48841d553521468c7a27189c4f0Chris Banes 345e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes if (tv.string != null && tv.string.toString().endsWith(".xml")) { 346e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes // If the resource is an XML file, let's try and parse it 347e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes try { 348e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes final XmlPullParser parser = res.getXml(resId); 349e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes final AttributeSet attrs = Xml.asAttributeSet(parser); 350e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes int type; 351e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes while ((type = parser.next()) != XmlPullParser.START_TAG && 352e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes type != XmlPullParser.END_DOCUMENT) { 353e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes // Empty loop 354e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes } 355e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes if (type != XmlPullParser.START_TAG) { 356e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes throw new XmlPullParserException("No start tag found"); 357e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes } 358e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes 359e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes final String tagName = parser.getName(); 3609f63864c84e3d48841d553521468c7a27189c4f0Chris Banes // Add the tag name to the cache 3619f63864c84e3d48841d553521468c7a27189c4f0Chris Banes mKnownDrawableIdTags.append(resId, tagName); 362e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes 363e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes // Now try and find a delegate for the tag name and inflate if found 364e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes final InflateDelegate delegate = mDelegates.get(tagName); 365e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes if (delegate != null) { 366bd3494de1d97206f366f52641a14b21eb06a7022Chris Banes dr = delegate.createFromXmlInner(context, parser, attrs, 367bd3494de1d97206f366f52641a14b21eb06a7022Chris Banes context.getTheme()); 3689f63864c84e3d48841d553521468c7a27189c4f0Chris Banes } 3699f63864c84e3d48841d553521468c7a27189c4f0Chris Banes if (dr != null) { 3709f63864c84e3d48841d553521468c7a27189c4f0Chris Banes // Add it to the drawable cache 3719f63864c84e3d48841d553521468c7a27189c4f0Chris Banes dr.setChangingConfigurations(tv.changingConfigurations); 37260cc94cdf2f9dfd10f2fe0b3fa0fa438d51df271Chris Banes if (addDrawableToCache(context, key, dr) && DEBUG) { 3739f63864c84e3d48841d553521468c7a27189c4f0Chris Banes Log.i(TAG, "[loadDrawableFromDelegates] Saved drawable to cache: " + 3749f63864c84e3d48841d553521468c7a27189c4f0Chris Banes context.getResources().getResourceName(resId)); 3759f63864c84e3d48841d553521468c7a27189c4f0Chris Banes } 376e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes } 377e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes } catch (Exception e) { 378e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes Log.e(TAG, "Exception while inflating drawable", e); 3794ab820f4155444d20b37e105873775dd71907eefChris Banes } 380469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes } 3819f63864c84e3d48841d553521468c7a27189c4f0Chris Banes if (dr == null) { 3829f63864c84e3d48841d553521468c7a27189c4f0Chris Banes // If we reach here then the delegate inflation of the resource failed. Mark it as 3839f63864c84e3d48841d553521468c7a27189c4f0Chris Banes // bad so we skip the id next time 3849f63864c84e3d48841d553521468c7a27189c4f0Chris Banes mKnownDrawableIdTags.append(resId, SKIP_DRAWABLE_TAG); 3859f63864c84e3d48841d553521468c7a27189c4f0Chris Banes } 3869f63864c84e3d48841d553521468c7a27189c4f0Chris Banes return dr; 387469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes } 388e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes 389e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes return null; 390469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes } 391469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes 39260cc94cdf2f9dfd10f2fe0b3fa0fa438d51df271Chris Banes private Drawable getCachedDrawable(@NonNull final Context context, final long key) { 39360cc94cdf2f9dfd10f2fe0b3fa0fa438d51df271Chris Banes synchronized (mDrawableCacheLock) { 3949f63864c84e3d48841d553521468c7a27189c4f0Chris Banes final LongSparseArray<WeakReference<ConstantState>> cache 39560cc94cdf2f9dfd10f2fe0b3fa0fa438d51df271Chris Banes = mDrawableCaches.get(context); 3969f63864c84e3d48841d553521468c7a27189c4f0Chris Banes if (cache == null) { 3979f63864c84e3d48841d553521468c7a27189c4f0Chris Banes return null; 3989f63864c84e3d48841d553521468c7a27189c4f0Chris Banes } 3999f63864c84e3d48841d553521468c7a27189c4f0Chris Banes 4009f63864c84e3d48841d553521468c7a27189c4f0Chris Banes final WeakReference<ConstantState> wr = cache.get(key); 4019f63864c84e3d48841d553521468c7a27189c4f0Chris Banes if (wr != null) { 4029f63864c84e3d48841d553521468c7a27189c4f0Chris Banes // We have the key, and the secret 4039f63864c84e3d48841d553521468c7a27189c4f0Chris Banes ConstantState entry = wr.get(); 4049f63864c84e3d48841d553521468c7a27189c4f0Chris Banes if (entry != null) { 4059f63864c84e3d48841d553521468c7a27189c4f0Chris Banes return entry.newDrawable(context.getResources()); 4069f63864c84e3d48841d553521468c7a27189c4f0Chris Banes } else { 4079f63864c84e3d48841d553521468c7a27189c4f0Chris Banes // Our entry has been purged 4089f63864c84e3d48841d553521468c7a27189c4f0Chris Banes cache.delete(key); 4099f63864c84e3d48841d553521468c7a27189c4f0Chris Banes } 4109f63864c84e3d48841d553521468c7a27189c4f0Chris Banes } 4119f63864c84e3d48841d553521468c7a27189c4f0Chris Banes } 4129f63864c84e3d48841d553521468c7a27189c4f0Chris Banes return null; 4139f63864c84e3d48841d553521468c7a27189c4f0Chris Banes } 4149f63864c84e3d48841d553521468c7a27189c4f0Chris Banes 41560cc94cdf2f9dfd10f2fe0b3fa0fa438d51df271Chris Banes private boolean addDrawableToCache(@NonNull final Context context, final long key, 4169f63864c84e3d48841d553521468c7a27189c4f0Chris Banes @NonNull final Drawable drawable) { 4179f63864c84e3d48841d553521468c7a27189c4f0Chris Banes final ConstantState cs = drawable.getConstantState(); 4189f63864c84e3d48841d553521468c7a27189c4f0Chris Banes if (cs != null) { 41960cc94cdf2f9dfd10f2fe0b3fa0fa438d51df271Chris Banes synchronized (mDrawableCacheLock) { 42060cc94cdf2f9dfd10f2fe0b3fa0fa438d51df271Chris Banes LongSparseArray<WeakReference<ConstantState>> cache = mDrawableCaches.get(context); 4219f63864c84e3d48841d553521468c7a27189c4f0Chris Banes if (cache == null) { 4229f63864c84e3d48841d553521468c7a27189c4f0Chris Banes cache = new LongSparseArray<>(); 42360cc94cdf2f9dfd10f2fe0b3fa0fa438d51df271Chris Banes mDrawableCaches.put(context, cache); 4249f63864c84e3d48841d553521468c7a27189c4f0Chris Banes } 4259f63864c84e3d48841d553521468c7a27189c4f0Chris Banes cache.put(key, new WeakReference<ConstantState>(cs)); 4269f63864c84e3d48841d553521468c7a27189c4f0Chris Banes } 4279f63864c84e3d48841d553521468c7a27189c4f0Chris Banes return true; 4289f63864c84e3d48841d553521468c7a27189c4f0Chris Banes } 4299f63864c84e3d48841d553521468c7a27189c4f0Chris Banes return false; 4309f63864c84e3d48841d553521468c7a27189c4f0Chris Banes } 4319f63864c84e3d48841d553521468c7a27189c4f0Chris Banes 4324c99f0e29b0926d8e5de44b7e3980d47f052f04cChris Banes Drawable onDrawableLoadedFromResources(@NonNull Context context, 4335fa121a1f51114f3a2f6d705139e83c0e0aea610Chris Banes @NonNull VectorEnabledTintResources resources, @DrawableRes final int resId) { 4345fa121a1f51114f3a2f6d705139e83c0e0aea610Chris Banes Drawable drawable = loadDrawableFromDelegates(context, resId); 4355fa121a1f51114f3a2f6d705139e83c0e0aea610Chris Banes if (drawable == null) { 4365fa121a1f51114f3a2f6d705139e83c0e0aea610Chris Banes drawable = resources.superGetDrawable(resId); 4375fa121a1f51114f3a2f6d705139e83c0e0aea610Chris Banes } 4385fa121a1f51114f3a2f6d705139e83c0e0aea610Chris Banes if (drawable != null) { 4395fa121a1f51114f3a2f6d705139e83c0e0aea610Chris Banes return tintDrawable(context, resId, false, drawable); 4405fa121a1f51114f3a2f6d705139e83c0e0aea610Chris Banes } 4415fa121a1f51114f3a2f6d705139e83c0e0aea610Chris Banes return null; 4425fa121a1f51114f3a2f6d705139e83c0e0aea610Chris Banes } 4435fa121a1f51114f3a2f6d705139e83c0e0aea610Chris Banes 44460cc94cdf2f9dfd10f2fe0b3fa0fa438d51df271Chris Banes static boolean tintDrawableUsingColorFilter(@NonNull Context context, 4457e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes @DrawableRes final int resId, @NonNull Drawable drawable) { 44639cb4c1c59fa156ed28bc8835ef05eeb356ad13cChris Banes PorterDuff.Mode tintMode = DEFAULT_MODE; 447469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes boolean colorAttrSet = false; 448469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes int colorAttr = 0; 4490517b282bde8b9a0377dfe5bc4756405a196adb4Chris Banes int alpha = -1; 450469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes 451cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes if (arrayContains(COLORFILTER_TINT_COLOR_CONTROL_NORMAL, resId)) { 452469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes colorAttr = R.attr.colorControlNormal; 453469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes colorAttrSet = true; 454cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes } else if (arrayContains(COLORFILTER_COLOR_CONTROL_ACTIVATED, resId)) { 455469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes colorAttr = R.attr.colorControlActivated; 456469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes colorAttrSet = true; 457cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes } else if (arrayContains(COLORFILTER_COLOR_BACKGROUND_MULTIPLY, resId)) { 458469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes colorAttr = android.R.attr.colorBackground; 459469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes colorAttrSet = true; 460469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes tintMode = PorterDuff.Mode.MULTIPLY; 4610517b282bde8b9a0377dfe5bc4756405a196adb4Chris Banes } else if (resId == R.drawable.abc_list_divider_mtrl_alpha) { 4620517b282bde8b9a0377dfe5bc4756405a196adb4Chris Banes colorAttr = android.R.attr.colorForeground; 4630517b282bde8b9a0377dfe5bc4756405a196adb4Chris Banes colorAttrSet = true; 4640517b282bde8b9a0377dfe5bc4756405a196adb4Chris Banes alpha = Math.round(0.16f * 255); 465593d8bcc82c01a559f737dfcbc6e806005300a64Chris Banes } else if (resId == R.drawable.abc_dialog_material_background) { 466593d8bcc82c01a559f737dfcbc6e806005300a64Chris Banes colorAttr = android.R.attr.colorBackground; 467593d8bcc82c01a559f737dfcbc6e806005300a64Chris Banes colorAttrSet = true; 468469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes } 469469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes 470f31fb9d2fd3b5b130f3f5ac121b033546d869231Chris Banes if (colorAttrSet) { 471aaa85b7d563d27fdf10048dd619a317451477ad5Chris Banes if (DrawableUtils.canSafelyMutateDrawable(drawable)) { 472d20d889e7abef50efbaf6e975100a8fb73409b13Chris Banes drawable = drawable.mutate(); 473d20d889e7abef50efbaf6e975100a8fb73409b13Chris Banes } 474d20d889e7abef50efbaf6e975100a8fb73409b13Chris Banes 475cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes final int color = getThemeAttrColor(context, colorAttr); 47639cb4c1c59fa156ed28bc8835ef05eeb356ad13cChris Banes drawable.setColorFilter(getPorterDuffColorFilter(color, tintMode)); 477469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes 4780517b282bde8b9a0377dfe5bc4756405a196adb4Chris Banes if (alpha != -1) { 4790517b282bde8b9a0377dfe5bc4756405a196adb4Chris Banes drawable.setAlpha(alpha); 4800517b282bde8b9a0377dfe5bc4756405a196adb4Chris Banes } 4810517b282bde8b9a0377dfe5bc4756405a196adb4Chris Banes 482469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes if (DEBUG) { 4839f63864c84e3d48841d553521468c7a27189c4f0Chris Banes Log.d(TAG, "[tintDrawableUsingColorFilter] Tinted " 4849f63864c84e3d48841d553521468c7a27189c4f0Chris Banes + context.getResources().getResourceName(resId) + 485469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes " with color: #" + Integer.toHexString(color)); 486469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes } 4874ab820f4155444d20b37e105873775dd71907eefChris Banes return true; 488469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes } 4894ab820f4155444d20b37e105873775dd71907eefChris Banes return false; 490469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes } 491469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes 4929f63864c84e3d48841d553521468c7a27189c4f0Chris Banes private void addDelegate(@NonNull String tagName, @NonNull InflateDelegate delegate) { 4937e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes if (mDelegates == null) { 494e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes mDelegates = new ArrayMap<>(); 4957e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes } 496e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes mDelegates.put(tagName, delegate); 4977e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes } 4987e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes 4999f63864c84e3d48841d553521468c7a27189c4f0Chris Banes private void removeDelegate(@NonNull String tagName, @NonNull InflateDelegate delegate) { 500e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes if (mDelegates != null && mDelegates.get(tagName) == delegate) { 501e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes mDelegates.remove(tagName); 5027e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes } 5037e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes } 5047e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes 505469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes private static boolean arrayContains(int[] array, int value) { 506469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes for (int id : array) { 507469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes if (id == value) { 508469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes return true; 509469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes } 510469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes } 511469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes return false; 512469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes } 513469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes 5144c99f0e29b0926d8e5de44b7e3980d47f052f04cChris Banes static PorterDuff.Mode getTintMode(final int resId) { 515cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes PorterDuff.Mode mode = null; 516cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes 517cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes if (resId == R.drawable.abc_switch_thumb_material) { 518cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes mode = PorterDuff.Mode.MULTIPLY; 519cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes } 520cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes 521cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes return mode; 522a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banes } 523a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banes 5244c99f0e29b0926d8e5de44b7e3980d47f052f04cChris Banes ColorStateList getTintList(@NonNull Context context, @DrawableRes int resId) { 525c3fa0c344f27c192cf69c5f608d5dc7073a4dfb8Chris Banes return getTintList(context, resId, null); 526c3fa0c344f27c192cf69c5f608d5dc7073a4dfb8Chris Banes } 527c3fa0c344f27c192cf69c5f608d5dc7073a4dfb8Chris Banes 5284c99f0e29b0926d8e5de44b7e3980d47f052f04cChris Banes ColorStateList getTintList(@NonNull Context context, @DrawableRes int resId, 529c3fa0c344f27c192cf69c5f608d5dc7073a4dfb8Chris Banes @Nullable ColorStateList customTint) { 530c3fa0c344f27c192cf69c5f608d5dc7073a4dfb8Chris Banes // We only want to use the cache for the standard tints, not ones created using custom 531c3fa0c344f27c192cf69c5f608d5dc7073a4dfb8Chris Banes // tints 532c3fa0c344f27c192cf69c5f608d5dc7073a4dfb8Chris Banes final boolean useCache = customTint == null; 533c3fa0c344f27c192cf69c5f608d5dc7073a4dfb8Chris Banes 534cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes // Try the cache first (if it exists) 535c3fa0c344f27c192cf69c5f608d5dc7073a4dfb8Chris Banes ColorStateList tint = useCache ? getTintListFromCache(context, resId) : null; 536cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes 537cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes if (tint == null) { 538cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes // ...if the cache did not contain a color state list, try and create one 53990075479814eb758d97b822606b448e1a521c298Chris Banes if (resId == R.drawable.abc_edit_text_material) { 5409d5f84f33353a42e837c6b465412d1a6f2fc6eaaChris Banes tint = getColorStateList(context, R.color.abc_tint_edittext); 54190075479814eb758d97b822606b448e1a521c298Chris Banes } else if (resId == R.drawable.abc_switch_track_mtrl_alpha) { 5429d5f84f33353a42e837c6b465412d1a6f2fc6eaaChris Banes tint = getColorStateList(context, R.color.abc_tint_switch_track); 54390075479814eb758d97b822606b448e1a521c298Chris Banes } else if (resId == R.drawable.abc_switch_thumb_material) { 5449d5f84f33353a42e837c6b465412d1a6f2fc6eaaChris Banes tint = getColorStateList(context, R.color.abc_tint_switch_thumb); 545a1a9092aba48f3ca6400ff53f5f4734752180e81Chris Banes } else if (resId == R.drawable.abc_btn_default_mtrl_shape) { 546c3fa0c344f27c192cf69c5f608d5dc7073a4dfb8Chris Banes tint = createDefaultButtonColorStateList(context, customTint); 547a1a9092aba48f3ca6400ff53f5f4734752180e81Chris Banes } else if (resId == R.drawable.abc_btn_borderless_material) { 548c3fa0c344f27c192cf69c5f608d5dc7073a4dfb8Chris Banes tint = createBorderlessButtonColorStateList(context, customTint); 549b1131c6dfc9affe5751523f235878055cb699960Chris Banes } else if (resId == R.drawable.abc_btn_colored_material) { 550c3fa0c344f27c192cf69c5f608d5dc7073a4dfb8Chris Banes tint = createColoredButtonColorStateList(context, customTint); 551cdd1b1d70cefeb052c2b506738b396f2f982e519Chris Banes } else if (resId == R.drawable.abc_spinner_mtrl_am_alpha 552cdd1b1d70cefeb052c2b506738b396f2f982e519Chris Banes || resId == R.drawable.abc_spinner_textfield_background_material) { 5539d5f84f33353a42e837c6b465412d1a6f2fc6eaaChris Banes tint = getColorStateList(context, R.color.abc_tint_spinner); 554cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes } else if (arrayContains(TINT_COLOR_CONTROL_NORMAL, resId)) { 555cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes tint = getThemeAttrColorStateList(context, R.attr.colorControlNormal); 556cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes } else if (arrayContains(TINT_COLOR_CONTROL_STATE_LIST, resId)) { 5579d5f84f33353a42e837c6b465412d1a6f2fc6eaaChris Banes tint = getColorStateList(context, R.color.abc_tint_default); 5581752dfd10e8242d6e017b3828c7d6e94f044691cChris Banes } else if (arrayContains(TINT_CHECKABLE_BUTTON_LIST, resId)) { 5599d5f84f33353a42e837c6b465412d1a6f2fc6eaaChris Banes tint = getColorStateList(context, R.color.abc_tint_btn_checkable); 56010e2dbc1ad1c01d2824d921a8b0f070859d6f146Chris Banes } else if (resId == R.drawable.abc_seekbar_thumb_material) { 5619d5f84f33353a42e837c6b465412d1a6f2fc6eaaChris Banes tint = getColorStateList(context, R.color.abc_tint_seek_thumb); 56290075479814eb758d97b822606b448e1a521c298Chris Banes } 563a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banes 564c3fa0c344f27c192cf69c5f608d5dc7073a4dfb8Chris Banes if (useCache && tint != null) { 5657e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes addTintListToCache(context, resId, tint); 566cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes } 56790075479814eb758d97b822606b448e1a521c298Chris Banes } 568cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes return tint; 56990075479814eb758d97b822606b448e1a521c298Chris Banes } 57090075479814eb758d97b822606b448e1a521c298Chris Banes 5717e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes private ColorStateList getTintListFromCache(@NonNull Context context, @DrawableRes int resId) { 5727e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes if (mTintLists != null) { 5737e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes final SparseArray<ColorStateList> tints = mTintLists.get(context); 5747e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes return tints != null ? tints.get(resId) : null; 5757e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes } 5767e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes return null; 5777e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes } 578469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes 5797e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes private void addTintListToCache(@NonNull Context context, @DrawableRes int resId, 5807e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes @NonNull ColorStateList tintList) { 5817e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes if (mTintLists == null) { 5827e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes mTintLists = new WeakHashMap<>(); 5837e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes } 5847e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes SparseArray<ColorStateList> themeTints = mTintLists.get(context); 5857e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes if (themeTints == null) { 5867e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes themeTints = new SparseArray<>(); 5877e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes mTintLists.put(context, themeTints); 5887e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes } 5897e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes themeTints.append(resId, tintList); 5907e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes } 591469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes 592c3fa0c344f27c192cf69c5f608d5dc7073a4dfb8Chris Banes private ColorStateList createDefaultButtonColorStateList(@NonNull Context context, 593c3fa0c344f27c192cf69c5f608d5dc7073a4dfb8Chris Banes @Nullable ColorStateList customTint) { 594a1a9092aba48f3ca6400ff53f5f4734752180e81Chris Banes return createButtonColorStateList(context, 595c3fa0c344f27c192cf69c5f608d5dc7073a4dfb8Chris Banes getThemeAttrColor(context, R.attr.colorButtonNormal), customTint); 596a1a9092aba48f3ca6400ff53f5f4734752180e81Chris Banes } 597a1a9092aba48f3ca6400ff53f5f4734752180e81Chris Banes 598c3fa0c344f27c192cf69c5f608d5dc7073a4dfb8Chris Banes private ColorStateList createBorderlessButtonColorStateList(@NonNull Context context, 599c3fa0c344f27c192cf69c5f608d5dc7073a4dfb8Chris Banes @Nullable ColorStateList customTint) { 600c3fa0c344f27c192cf69c5f608d5dc7073a4dfb8Chris Banes // We ignore the custom tint for borderless buttons 601c3fa0c344f27c192cf69c5f608d5dc7073a4dfb8Chris Banes return createButtonColorStateList(context, Color.TRANSPARENT, null); 602b1131c6dfc9affe5751523f235878055cb699960Chris Banes } 603b1131c6dfc9affe5751523f235878055cb699960Chris Banes 604c3fa0c344f27c192cf69c5f608d5dc7073a4dfb8Chris Banes private ColorStateList createColoredButtonColorStateList(@NonNull Context context, 605c3fa0c344f27c192cf69c5f608d5dc7073a4dfb8Chris Banes @Nullable ColorStateList customTint) { 606c3fa0c344f27c192cf69c5f608d5dc7073a4dfb8Chris Banes return createButtonColorStateList(context, 607c3fa0c344f27c192cf69c5f608d5dc7073a4dfb8Chris Banes getThemeAttrColor(context, R.attr.colorAccent), customTint); 608b1131c6dfc9affe5751523f235878055cb699960Chris Banes } 609b1131c6dfc9affe5751523f235878055cb699960Chris Banes 610c3fa0c344f27c192cf69c5f608d5dc7073a4dfb8Chris Banes private ColorStateList createButtonColorStateList(@NonNull final Context context, 611c3fa0c344f27c192cf69c5f608d5dc7073a4dfb8Chris Banes @ColorInt final int baseColor, final @Nullable ColorStateList tint) { 61290075479814eb758d97b822606b448e1a521c298Chris Banes final int[][] states = new int[4][]; 61390075479814eb758d97b822606b448e1a521c298Chris Banes final int[] colors = new int[4]; 61490075479814eb758d97b822606b448e1a521c298Chris Banes int i = 0; 61590075479814eb758d97b822606b448e1a521c298Chris Banes 616eb0d0c030a15e93f456cc1403fffb909c0ae4e66Chris Banes final int colorControlHighlight = getThemeAttrColor(context, R.attr.colorControlHighlight); 617c3fa0c344f27c192cf69c5f608d5dc7073a4dfb8Chris Banes final int disabledColor = getDisabledThemeAttrColor(context, R.attr.colorButtonNormal); 618eb0d0c030a15e93f456cc1403fffb909c0ae4e66Chris Banes 61990075479814eb758d97b822606b448e1a521c298Chris Banes // Disabled state 620bb1a62b3eed874aa47ea4763ac972902c77e988fChris Banes states[i] = ThemeUtils.DISABLED_STATE_SET; 621c3fa0c344f27c192cf69c5f608d5dc7073a4dfb8Chris Banes colors[i] = tint == null ? disabledColor : tint.getColorForState(states[i], 0); 62290075479814eb758d97b822606b448e1a521c298Chris Banes i++; 62390075479814eb758d97b822606b448e1a521c298Chris Banes 624bb1a62b3eed874aa47ea4763ac972902c77e988fChris Banes states[i] = ThemeUtils.PRESSED_STATE_SET; 625c3fa0c344f27c192cf69c5f608d5dc7073a4dfb8Chris Banes colors[i] = compositeColors(colorControlHighlight, 626c3fa0c344f27c192cf69c5f608d5dc7073a4dfb8Chris Banes tint == null ? baseColor : tint.getColorForState(states[i], 0)); 62790075479814eb758d97b822606b448e1a521c298Chris Banes i++; 62890075479814eb758d97b822606b448e1a521c298Chris Banes 629bb1a62b3eed874aa47ea4763ac972902c77e988fChris Banes states[i] = ThemeUtils.FOCUSED_STATE_SET; 630c3fa0c344f27c192cf69c5f608d5dc7073a4dfb8Chris Banes colors[i] = compositeColors(colorControlHighlight, 631c3fa0c344f27c192cf69c5f608d5dc7073a4dfb8Chris Banes tint == null ? baseColor : tint.getColorForState(states[i], 0)); 63290075479814eb758d97b822606b448e1a521c298Chris Banes i++; 63390075479814eb758d97b822606b448e1a521c298Chris Banes 63490075479814eb758d97b822606b448e1a521c298Chris Banes // Default enabled state 635bb1a62b3eed874aa47ea4763ac972902c77e988fChris Banes states[i] = ThemeUtils.EMPTY_STATE_SET; 636c3fa0c344f27c192cf69c5f608d5dc7073a4dfb8Chris Banes colors[i] = tint == null ? baseColor : tint.getColorForState(states[i], 0); 63790075479814eb758d97b822606b448e1a521c298Chris Banes i++; 63890075479814eb758d97b822606b448e1a521c298Chris Banes 63990075479814eb758d97b822606b448e1a521c298Chris Banes return new ColorStateList(states, colors); 640a9585dae398a69dd67797e7ca86f44ffcabd9e86Chris Banes } 641a9585dae398a69dd67797e7ca86f44ffcabd9e86Chris Banes 642469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes private static class ColorFilterLruCache extends LruCache<Integer, PorterDuffColorFilter> { 643469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes 644469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes public ColorFilterLruCache(int maxSize) { 645469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes super(maxSize); 646469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes } 647469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes 648469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes PorterDuffColorFilter get(int color, PorterDuff.Mode mode) { 649469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes return get(generateCacheKey(color, mode)); 650469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes } 651469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes 652469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes PorterDuffColorFilter put(int color, PorterDuff.Mode mode, PorterDuffColorFilter filter) { 653469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes return put(generateCacheKey(color, mode), filter); 654469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes } 655469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes 656469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes private static int generateCacheKey(int color, PorterDuff.Mode mode) { 657469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes int hashCode = 1; 658469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes hashCode = 31 * hashCode + color; 659469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes hashCode = 31 * hashCode + mode.hashCode(); 660469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes return hashCode; 661469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes } 662469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes } 663a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banes 6644c99f0e29b0926d8e5de44b7e3980d47f052f04cChris Banes static void tintDrawable(Drawable drawable, TintInfo tint, int[] state) { 665aaa85b7d563d27fdf10048dd619a317451477ad5Chris Banes if (DrawableUtils.canSafelyMutateDrawable(drawable) 666aaa85b7d563d27fdf10048dd619a317451477ad5Chris Banes && drawable.mutate() != drawable) { 66744eb323d09e9bc3baca24ef4a8595aafb7ed2652Chris Banes Log.d(TAG, "Mutated drawable is not the same instance as the input."); 66844eb323d09e9bc3baca24ef4a8595aafb7ed2652Chris Banes return; 66944eb323d09e9bc3baca24ef4a8595aafb7ed2652Chris Banes } 670eb0d0c030a15e93f456cc1403fffb909c0ae4e66Chris Banes 67144eb323d09e9bc3baca24ef4a8595aafb7ed2652Chris Banes if (tint.mHasTintList || tint.mHasTintMode) { 67244eb323d09e9bc3baca24ef4a8595aafb7ed2652Chris Banes drawable.setColorFilter(createTintFilter( 67344eb323d09e9bc3baca24ef4a8595aafb7ed2652Chris Banes tint.mHasTintList ? tint.mTintList : null, 67444eb323d09e9bc3baca24ef4a8595aafb7ed2652Chris Banes tint.mHasTintMode ? tint.mTintMode : DEFAULT_MODE, 67544eb323d09e9bc3baca24ef4a8595aafb7ed2652Chris Banes state)); 67644eb323d09e9bc3baca24ef4a8595aafb7ed2652Chris Banes } else { 67744eb323d09e9bc3baca24ef4a8595aafb7ed2652Chris Banes drawable.clearColorFilter(); 67844eb323d09e9bc3baca24ef4a8595aafb7ed2652Chris Banes } 67944eb323d09e9bc3baca24ef4a8595aafb7ed2652Chris Banes 6807797b9f22c8c404309b778a0966266d2b1a84915Chris Banes if (Build.VERSION.SDK_INT <= 23) { 6817797b9f22c8c404309b778a0966266d2b1a84915Chris Banes // Pre-v23 there is no guarantee that a state change will invoke an invalidation, 6827797b9f22c8c404309b778a0966266d2b1a84915Chris Banes // so we force it ourselves 68344eb323d09e9bc3baca24ef4a8595aafb7ed2652Chris Banes drawable.invalidateSelf(); 68444eb323d09e9bc3baca24ef4a8595aafb7ed2652Chris Banes } 68544eb323d09e9bc3baca24ef4a8595aafb7ed2652Chris Banes } 68644eb323d09e9bc3baca24ef4a8595aafb7ed2652Chris Banes 68739cb4c1c59fa156ed28bc8835ef05eeb356ad13cChris Banes private static PorterDuffColorFilter createTintFilter(ColorStateList tint, 68839cb4c1c59fa156ed28bc8835ef05eeb356ad13cChris Banes PorterDuff.Mode tintMode, final int[] state) { 68939cb4c1c59fa156ed28bc8835ef05eeb356ad13cChris Banes if (tint == null || tintMode == null) { 69039cb4c1c59fa156ed28bc8835ef05eeb356ad13cChris Banes return null; 691cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes } 69239cb4c1c59fa156ed28bc8835ef05eeb356ad13cChris Banes final int color = tint.getColorForState(state, Color.TRANSPARENT); 69339cb4c1c59fa156ed28bc8835ef05eeb356ad13cChris Banes return getPorterDuffColorFilter(color, tintMode); 69439cb4c1c59fa156ed28bc8835ef05eeb356ad13cChris Banes } 695cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes 696fe1cbed21122206b7a4af97790ade439d49421d8Chris Banes public static PorterDuffColorFilter getPorterDuffColorFilter(int color, PorterDuff.Mode mode) { 697a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banes // First, lets see if the cache already contains the color filter 698a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banes PorterDuffColorFilter filter = COLOR_FILTER_CACHE.get(color, mode); 699a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banes 700a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banes if (filter == null) { 701a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banes // Cache miss, so create a color filter and add it to the cache 702a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banes filter = new PorterDuffColorFilter(color, mode); 703a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banes COLOR_FILTER_CACHE.put(color, mode, filter); 704a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banes } 705a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banes 70639cb4c1c59fa156ed28bc8835ef05eeb356ad13cChris Banes return filter; 707a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banes } 70810e2dbc1ad1c01d2824d921a8b0f070859d6f146Chris Banes 70910e2dbc1ad1c01d2824d921a8b0f070859d6f146Chris Banes private static void setPorterDuffColorFilter(Drawable d, int color, PorterDuff.Mode mode) { 710aaa85b7d563d27fdf10048dd619a317451477ad5Chris Banes if (DrawableUtils.canSafelyMutateDrawable(d)) { 711d20d889e7abef50efbaf6e975100a8fb73409b13Chris Banes d = d.mutate(); 712d20d889e7abef50efbaf6e975100a8fb73409b13Chris Banes } 71310e2dbc1ad1c01d2824d921a8b0f070859d6f146Chris Banes d.setColorFilter(getPorterDuffColorFilter(color, mode == null ? DEFAULT_MODE : mode)); 71410e2dbc1ad1c01d2824d921a8b0f070859d6f146Chris Banes } 7159f63864c84e3d48841d553521468c7a27189c4f0Chris Banes 716eb648620b291eabbb5523f649baef5abb2e4687bAlan Viverette private void checkVectorDrawableSetup(@NonNull Context context) { 7179ccbe0f4fa88c4ec959e1bdfaaa42caedf6886ccTeng-Hui Zhu if (mHasCheckedVectorDrawableSetup) { 718eb648620b291eabbb5523f649baef5abb2e4687bAlan Viverette // We've already checked so return now... 719eb648620b291eabbb5523f649baef5abb2e4687bAlan Viverette return; 720eb648620b291eabbb5523f649baef5abb2e4687bAlan Viverette } 721eb648620b291eabbb5523f649baef5abb2e4687bAlan Viverette // Here we will check that a known Vector drawable resource inside AppCompat can be 7223a5aeed1e19247ea088691186500877b4e3f7d69Chris Banes // correctly decoded 723dd61be0d0da04cc63c9c41c1b82348c20b2bf1cdYuichi Araki mHasCheckedVectorDrawableSetup = true; 7243a5aeed1e19247ea088691186500877b4e3f7d69Chris Banes final Drawable d = getDrawable(context, R.drawable.abc_vector_test); 725dd61be0d0da04cc63c9c41c1b82348c20b2bf1cdYuichi Araki if (d == null || !isVectorDrawable(d)) { 726dd61be0d0da04cc63c9c41c1b82348c20b2bf1cdYuichi Araki mHasCheckedVectorDrawableSetup = false; 727eb648620b291eabbb5523f649baef5abb2e4687bAlan Viverette throw new IllegalStateException("This app has been built with an incorrect " 728eb648620b291eabbb5523f649baef5abb2e4687bAlan Viverette + "configuration. Please configure your build for VectorDrawableCompat."); 729eb648620b291eabbb5523f649baef5abb2e4687bAlan Viverette } 730eb648620b291eabbb5523f649baef5abb2e4687bAlan Viverette } 731eb648620b291eabbb5523f649baef5abb2e4687bAlan Viverette 732ba13a44cd6db80ec983b1ed678b7dbeab04056a1Chris Banes private static boolean isVectorDrawable(@NonNull Drawable d) { 733ba13a44cd6db80ec983b1ed678b7dbeab04056a1Chris Banes return d instanceof VectorDrawableCompat 734ba13a44cd6db80ec983b1ed678b7dbeab04056a1Chris Banes || PLATFORM_VD_CLAZZ.equals(d.getClass().getName()); 735ba13a44cd6db80ec983b1ed678b7dbeab04056a1Chris Banes } 736ba13a44cd6db80ec983b1ed678b7dbeab04056a1Chris Banes 7379f63864c84e3d48841d553521468c7a27189c4f0Chris Banes private static class VdcInflateDelegate implements InflateDelegate { 7382c1bad7ecd5879bf0f29ce2ce1bc5bd67a3f4682Aurimas Liutikas VdcInflateDelegate() { 7392c1bad7ecd5879bf0f29ce2ce1bc5bd67a3f4682Aurimas Liutikas } 7402c1bad7ecd5879bf0f29ce2ce1bc5bd67a3f4682Aurimas Liutikas 7419f63864c84e3d48841d553521468c7a27189c4f0Chris Banes @Override 742bd3494de1d97206f366f52641a14b21eb06a7022Chris Banes public Drawable createFromXmlInner(@NonNull Context context, @NonNull XmlPullParser parser, 7439f63864c84e3d48841d553521468c7a27189c4f0Chris Banes @NonNull AttributeSet attrs, @Nullable Resources.Theme theme) { 7449f63864c84e3d48841d553521468c7a27189c4f0Chris Banes try { 745bd3494de1d97206f366f52641a14b21eb06a7022Chris Banes return VectorDrawableCompat 746bd3494de1d97206f366f52641a14b21eb06a7022Chris Banes .createFromXmlInner(context.getResources(), parser, attrs, theme); 7479f63864c84e3d48841d553521468c7a27189c4f0Chris Banes } catch (Exception e) { 7489f63864c84e3d48841d553521468c7a27189c4f0Chris Banes Log.e("VdcInflateDelegate", "Exception while inflating <vector>", e); 7499f63864c84e3d48841d553521468c7a27189c4f0Chris Banes return null; 7509f63864c84e3d48841d553521468c7a27189c4f0Chris Banes } 7519f63864c84e3d48841d553521468c7a27189c4f0Chris Banes } 7529f63864c84e3d48841d553521468c7a27189c4f0Chris Banes } 753bd3494de1d97206f366f52641a14b21eb06a7022Chris Banes 7548f886fe8c7e23fe6ccb8734167c960c2ed3429c3Alan Viverette @RequiresApi(11) 7558f886fe8c7e23fe6ccb8734167c960c2ed3429c3Alan Viverette @TargetApi(11) 756bd3494de1d97206f366f52641a14b21eb06a7022Chris Banes private static class AvdcInflateDelegate implements InflateDelegate { 7572c1bad7ecd5879bf0f29ce2ce1bc5bd67a3f4682Aurimas Liutikas AvdcInflateDelegate() { 7582c1bad7ecd5879bf0f29ce2ce1bc5bd67a3f4682Aurimas Liutikas } 7592c1bad7ecd5879bf0f29ce2ce1bc5bd67a3f4682Aurimas Liutikas 760bd3494de1d97206f366f52641a14b21eb06a7022Chris Banes @Override 761bd3494de1d97206f366f52641a14b21eb06a7022Chris Banes public Drawable createFromXmlInner(@NonNull Context context, @NonNull XmlPullParser parser, 762bd3494de1d97206f366f52641a14b21eb06a7022Chris Banes @NonNull AttributeSet attrs, @Nullable Resources.Theme theme) { 763bd3494de1d97206f366f52641a14b21eb06a7022Chris Banes try { 764bd3494de1d97206f366f52641a14b21eb06a7022Chris Banes return AnimatedVectorDrawableCompat 765bd3494de1d97206f366f52641a14b21eb06a7022Chris Banes .createFromXmlInner(context, context.getResources(), parser, attrs, theme); 766bd3494de1d97206f366f52641a14b21eb06a7022Chris Banes } catch (Exception e) { 767bd3494de1d97206f366f52641a14b21eb06a7022Chris Banes Log.e("AvdcInflateDelegate", "Exception while inflating <animated-vector>", e); 768bd3494de1d97206f366f52641a14b21eb06a7022Chris Banes return null; 769bd3494de1d97206f366f52641a14b21eb06a7022Chris Banes } 770bd3494de1d97206f366f52641a14b21eb06a7022Chris Banes } 771bd3494de1d97206f366f52641a14b21eb06a7022Chris Banes } 772469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes} 773