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