AppCompatDrawableManager.java revision a59882b82ecd74c79e05538a0962646d003d4c0e
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 19e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banesimport org.xmlpull.v1.XmlPullParser; 20e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banesimport org.xmlpull.v1.XmlPullParserException; 21e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes 22469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banesimport android.content.Context; 23469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banesimport android.content.res.ColorStateList; 24e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banesimport android.content.res.Resources; 2539cb4c1c59fa156ed28bc8835ef05eeb356ad13cChris Banesimport android.graphics.Color; 26469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banesimport android.graphics.PorterDuff; 27469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banesimport android.graphics.PorterDuffColorFilter; 28469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banesimport android.graphics.drawable.Drawable; 299f63864c84e3d48841d553521468c7a27189c4f0Chris Banesimport android.graphics.drawable.Drawable.ConstantState; 30cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banesimport android.graphics.drawable.LayerDrawable; 3160cc94cdf2f9dfd10f2fe0b3fa0fa438d51df271Chris Banesimport android.graphics.drawable.StateListDrawable; 32fd1eb27a3700de31507de34fd1bcc51830fe876cChris Banesimport android.os.Build; 33a1a9092aba48f3ca6400ff53f5f4734752180e81Chris Banesimport android.support.annotation.ColorInt; 347e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banesimport android.support.annotation.DrawableRes; 357e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banesimport android.support.annotation.NonNull; 367e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banesimport android.support.annotation.Nullable; 37bd3494de1d97206f366f52641a14b21eb06a7022Chris Banesimport android.support.graphics.drawable.AnimatedVectorDrawableCompat; 38e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banesimport android.support.graphics.drawable.VectorDrawableCompat; 39469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banesimport android.support.v4.content.ContextCompat; 40eb0d0c030a15e93f456cc1403fffb909c0ae4e66Chris Banesimport android.support.v4.graphics.ColorUtils; 417e82b99953680915596eaf0eb35927388e574ca8Chris Banesimport android.support.v4.graphics.drawable.DrawableCompat; 42e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banesimport android.support.v4.util.ArrayMap; 439f63864c84e3d48841d553521468c7a27189c4f0Chris Banesimport android.support.v4.util.LongSparseArray; 44469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banesimport android.support.v4.util.LruCache; 45469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banesimport android.support.v7.appcompat.R; 465fa121a1f51114f3a2f6d705139e83c0e0aea610Chris Banesimport android.support.v7.widget.VectorEnabledTintResources; 47e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banesimport android.util.AttributeSet; 48469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banesimport android.util.Log; 4990075479814eb758d97b822606b448e1a521c298Chris Banesimport android.util.SparseArray; 50e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banesimport android.util.TypedValue; 51e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banesimport android.util.Xml; 52469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes 539f63864c84e3d48841d553521468c7a27189c4f0Chris Banesimport java.lang.ref.WeakReference; 54cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banesimport java.util.WeakHashMap; 55cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes 5615aeaf26caa61ed5f3cd367044801d03c1a0a2b5Chris Banesimport static android.support.v7.content.res.AppCompatResources.getColorStateList; 5766698bb15ba0f873aa1c2290cc50d6bb839a474aChris Banesimport static android.support.v7.widget.ThemeUtils.getDisabledThemeAttrColor; 5866698bb15ba0f873aa1c2290cc50d6bb839a474aChris Banesimport static android.support.v7.widget.ThemeUtils.getThemeAttrColor; 5966698bb15ba0f873aa1c2290cc50d6bb839a474aChris Banesimport static android.support.v7.widget.ThemeUtils.getThemeAttrColorStateList; 602aeb0f4237bca35d7f650c3145354416306d4f7bChris Banes 61469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes/** 62469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes * @hide 63469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes */ 647e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banespublic final class AppCompatDrawableManager { 657e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes 669f63864c84e3d48841d553521468c7a27189c4f0Chris Banes private interface InflateDelegate { 67bd3494de1d97206f366f52641a14b21eb06a7022Chris Banes Drawable createFromXmlInner(@NonNull Context context, @NonNull XmlPullParser parser, 68e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes @NonNull AttributeSet attrs, @Nullable Resources.Theme theme); 697e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes } 70fd1eb27a3700de31507de34fd1bcc51830fe876cChris Banes 719f63864c84e3d48841d553521468c7a27189c4f0Chris Banes private static final String TAG = "AppCompatDrawableManager"; 72469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes private static final boolean DEBUG = false; 73cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes private static final PorterDuff.Mode DEFAULT_MODE = PorterDuff.Mode.SRC_IN; 74e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes private static final String SKIP_DRAWABLE_TAG = "appcompat_skip_skip"; 75415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes 76ba13a44cd6db80ec983b1ed678b7dbeab04056a1Chris Banes private static final String PLATFORM_VD_CLAZZ = "android.graphics.drawable.VectorDrawable"; 77ba13a44cd6db80ec983b1ed678b7dbeab04056a1Chris Banes 787e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes private static AppCompatDrawableManager INSTANCE; 797e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes 807e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes public static AppCompatDrawableManager get() { 817e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes if (INSTANCE == null) { 827e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes INSTANCE = new AppCompatDrawableManager(); 83bd3494de1d97206f366f52641a14b21eb06a7022Chris Banes installDefaultInflateDelegates(INSTANCE); 84bd3494de1d97206f366f52641a14b21eb06a7022Chris Banes } 85bd3494de1d97206f366f52641a14b21eb06a7022Chris Banes return INSTANCE; 86bd3494de1d97206f366f52641a14b21eb06a7022Chris Banes } 87bd3494de1d97206f366f52641a14b21eb06a7022Chris Banes 88bd3494de1d97206f366f52641a14b21eb06a7022Chris Banes private static void installDefaultInflateDelegates(@NonNull AppCompatDrawableManager manager) { 89bd3494de1d97206f366f52641a14b21eb06a7022Chris Banes final int sdk = Build.VERSION.SDK_INT; 9080e3ecf1aae0badd8056206db0614e9b9d1934b5Teng-Hui Zhu if (sdk < 23) { 91bd3494de1d97206f366f52641a14b21eb06a7022Chris Banes // We only want to use the automatic VectorDrawableCompat handling where it's 92bd3494de1d97206f366f52641a14b21eb06a7022Chris Banes // needed: on devices running before Lollipop 93bd3494de1d97206f366f52641a14b21eb06a7022Chris Banes manager.addDelegate("vector", new VdcInflateDelegate()); 949f63864c84e3d48841d553521468c7a27189c4f0Chris Banes 95bd3494de1d97206f366f52641a14b21eb06a7022Chris Banes if (sdk >= 11) { 96bd3494de1d97206f366f52641a14b21eb06a7022Chris Banes // AnimatedVectorDrawableCompat only works on API v11+ 97bd3494de1d97206f366f52641a14b21eb06a7022Chris Banes manager.addDelegate("animated-vector", new AvdcInflateDelegate()); 989f63864c84e3d48841d553521468c7a27189c4f0Chris Banes } 997e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes } 1007e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes } 1017e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes 102469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes private static final ColorFilterLruCache COLOR_FILTER_CACHE = new ColorFilterLruCache(6); 103469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes 104469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes /** 105469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes * Drawables which should be tinted with the value of {@code R.attr.colorControlNormal}, 106cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes * using the default mode using a raw color filter. 107cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes */ 108cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes private static final int[] COLORFILTER_TINT_COLOR_CONTROL_NORMAL = { 109cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes R.drawable.abc_textfield_search_default_mtrl_alpha, 110cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes R.drawable.abc_textfield_default_mtrl_alpha, 111cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes R.drawable.abc_ab_share_pack_mtrl_alpha 112cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes }; 113cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes 114cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes /** 115cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes * Drawables which should be tinted with the value of {@code R.attr.colorControlNormal}, using 116cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes * {@link DrawableCompat}'s tinting functionality. 117469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes */ 118469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes private static final int[] TINT_COLOR_CONTROL_NORMAL = { 119469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes R.drawable.abc_ic_commit_search_api_mtrl_alpha, 120a59882b82ecd74c79e05538a0962646d003d4c0eChris Banes R.drawable.abc_seekbar_tick_mark_material, 121a59882b82ecd74c79e05538a0962646d003d4c0eChris Banes R.drawable.abc_ic_menu_share_mtrl_alpha, 122a59882b82ecd74c79e05538a0962646d003d4c0eChris Banes R.drawable.abc_ic_menu_copy_mtrl_am_alpha, 123a59882b82ecd74c79e05538a0962646d003d4c0eChris Banes R.drawable.abc_ic_menu_cut_mtrl_alpha, 124a59882b82ecd74c79e05538a0962646d003d4c0eChris Banes R.drawable.abc_ic_menu_selectall_mtrl_alpha, 125a59882b82ecd74c79e05538a0962646d003d4c0eChris Banes R.drawable.abc_ic_menu_paste_mtrl_am_alpha 126469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes }; 127469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes 128469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes /** 129469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes * Drawables which should be tinted with the value of {@code R.attr.colorControlActivated}, 130cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes * using a color filter. 131469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes */ 132cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes private static final int[] COLORFILTER_COLOR_CONTROL_ACTIVATED = { 133469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes R.drawable.abc_textfield_activated_mtrl_alpha, 13414f8f0dfae09445074dc0f7b5bbe5732d6922da7Chris Banes R.drawable.abc_textfield_search_activated_mtrl_alpha, 135911642499da7d796aa1e7c19178c3552a590f48bChris Banes R.drawable.abc_cab_background_top_mtrl_alpha, 136f65da421a61773f1ce03550230dcd6f58cee54e9Chris Banes R.drawable.abc_text_cursor_material 137469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes }; 138469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes 139469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes /** 140469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes * Drawables which should be tinted with the value of {@code android.R.attr.colorBackground}, 141cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes * using the {@link android.graphics.PorterDuff.Mode#MULTIPLY} mode and a color filter. 142469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes */ 143cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes private static final int[] COLORFILTER_COLOR_BACKGROUND_MULTIPLY = { 1441073132946bb0a53a788949fe4c060f72051cd57Chris Banes R.drawable.abc_popup_background_mtrl_mult, 14557c6de90985a63358129b99b9f0cd4d6afe887d6Chris Banes R.drawable.abc_cab_background_internal_bg, 14657c6de90985a63358129b99b9f0cd4d6afe887d6Chris Banes R.drawable.abc_menu_hardkey_panel_mtrl_mult 147469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes }; 148469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes 149469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes /** 150469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes * Drawables which should be tinted using a state list containing values of 151469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes * {@code R.attr.colorControlNormal} and {@code R.attr.colorControlActivated} 152469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes */ 153469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes private static final int[] TINT_COLOR_CONTROL_STATE_LIST = { 154469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes R.drawable.abc_tab_indicator_material, 15574433e220541f8236f4e01f658a96854b29acfd1Chris Banes R.drawable.abc_textfield_search_material 156469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes }; 157469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes 1581752dfd10e8242d6e017b3828c7d6e94f044691cChris Banes /** 1591752dfd10e8242d6e017b3828c7d6e94f044691cChris Banes * Drawables which should be tinted using a state list containing values of 1601752dfd10e8242d6e017b3828c7d6e94f044691cChris Banes * {@code R.attr.colorControlNormal} and {@code R.attr.colorControlActivated} for the checked 1611752dfd10e8242d6e017b3828c7d6e94f044691cChris Banes * state. 1621752dfd10e8242d6e017b3828c7d6e94f044691cChris Banes */ 1631752dfd10e8242d6e017b3828c7d6e94f044691cChris Banes private static final int[] TINT_CHECKABLE_BUTTON_LIST = { 1641752dfd10e8242d6e017b3828c7d6e94f044691cChris Banes R.drawable.abc_btn_check_material, 1651752dfd10e8242d6e017b3828c7d6e94f044691cChris Banes R.drawable.abc_btn_radio_material 1661752dfd10e8242d6e017b3828c7d6e94f044691cChris Banes }; 1671752dfd10e8242d6e017b3828c7d6e94f044691cChris Banes 1687e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes private WeakHashMap<Context, SparseArray<ColorStateList>> mTintLists; 169e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes private ArrayMap<String, InflateDelegate> mDelegates; 170e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes private SparseArray<String> mKnownDrawableIdTags; 171e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes 17260cc94cdf2f9dfd10f2fe0b3fa0fa438d51df271Chris Banes private final Object mDrawableCacheLock = new Object(); 1739f63864c84e3d48841d553521468c7a27189c4f0Chris Banes private final WeakHashMap<Context, LongSparseArray<WeakReference<Drawable.ConstantState>>> 17460cc94cdf2f9dfd10f2fe0b3fa0fa438d51df271Chris Banes mDrawableCaches = new WeakHashMap<>(0); 1759f63864c84e3d48841d553521468c7a27189c4f0Chris Banes 176e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes private TypedValue mTypedValue; 177469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes 178ba13a44cd6db80ec983b1ed678b7dbeab04056a1Chris Banes private boolean mHasCheckedVectorDrawableSetup; 179ba13a44cd6db80ec983b1ed678b7dbeab04056a1Chris Banes 1807e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes public Drawable getDrawable(@NonNull Context context, @DrawableRes int resId) { 1817e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes return getDrawable(context, resId, false); 182469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes } 183469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes 1847e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes public Drawable getDrawable(@NonNull Context context, @DrawableRes int resId, 1857e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes boolean failIfNotKnown) { 186eb648620b291eabbb5523f649baef5abb2e4687bAlan Viverette checkVectorDrawableSetup(context); 187eb648620b291eabbb5523f649baef5abb2e4687bAlan Viverette 188e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes Drawable drawable = loadDrawableFromDelegates(context, resId); 189e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes if (drawable == null) { 19060cc94cdf2f9dfd10f2fe0b3fa0fa438d51df271Chris Banes drawable = createDrawableIfNeeded(context, resId); 19160cc94cdf2f9dfd10f2fe0b3fa0fa438d51df271Chris Banes } 19260cc94cdf2f9dfd10f2fe0b3fa0fa438d51df271Chris Banes if (drawable == null) { 193e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes drawable = ContextCompat.getDrawable(context, resId); 194cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes } 19560cc94cdf2f9dfd10f2fe0b3fa0fa438d51df271Chris Banes 196e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes if (drawable != null) { 197aaa85b7d563d27fdf10048dd619a317451477ad5Chris Banes // Tint it if needed 198aaa85b7d563d27fdf10048dd619a317451477ad5Chris Banes drawable = tintDrawable(context, resId, failIfNotKnown, drawable); 199cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes } 200aaa85b7d563d27fdf10048dd619a317451477ad5Chris Banes if (drawable != null) { 201aaa85b7d563d27fdf10048dd619a317451477ad5Chris Banes // See if we need to 'fix' the drawable 202aaa85b7d563d27fdf10048dd619a317451477ad5Chris Banes DrawableUtils.fixDrawable(drawable); 203aaa85b7d563d27fdf10048dd619a317451477ad5Chris Banes } 204aaa85b7d563d27fdf10048dd619a317451477ad5Chris Banes return drawable; 205e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes } 206cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes 20760cc94cdf2f9dfd10f2fe0b3fa0fa438d51df271Chris Banes private static long createCacheKey(TypedValue tv) { 20860cc94cdf2f9dfd10f2fe0b3fa0fa438d51df271Chris Banes return (((long) tv.assetCookie) << 32) | tv.data; 20960cc94cdf2f9dfd10f2fe0b3fa0fa438d51df271Chris Banes } 21060cc94cdf2f9dfd10f2fe0b3fa0fa438d51df271Chris Banes 21160cc94cdf2f9dfd10f2fe0b3fa0fa438d51df271Chris Banes private Drawable createDrawableIfNeeded(@NonNull Context context, 21260cc94cdf2f9dfd10f2fe0b3fa0fa438d51df271Chris Banes @DrawableRes final int resId) { 21360cc94cdf2f9dfd10f2fe0b3fa0fa438d51df271Chris Banes if (mTypedValue == null) { 21460cc94cdf2f9dfd10f2fe0b3fa0fa438d51df271Chris Banes mTypedValue = new TypedValue(); 21560cc94cdf2f9dfd10f2fe0b3fa0fa438d51df271Chris Banes } 21660cc94cdf2f9dfd10f2fe0b3fa0fa438d51df271Chris Banes final TypedValue tv = mTypedValue; 21760cc94cdf2f9dfd10f2fe0b3fa0fa438d51df271Chris Banes context.getResources().getValue(resId, tv, true); 21860cc94cdf2f9dfd10f2fe0b3fa0fa438d51df271Chris Banes final long key = createCacheKey(tv); 21960cc94cdf2f9dfd10f2fe0b3fa0fa438d51df271Chris Banes 22060cc94cdf2f9dfd10f2fe0b3fa0fa438d51df271Chris Banes Drawable dr = getCachedDrawable(context, key); 22160cc94cdf2f9dfd10f2fe0b3fa0fa438d51df271Chris Banes if (dr != null) { 22260cc94cdf2f9dfd10f2fe0b3fa0fa438d51df271Chris Banes // If we got a cached drawable, return it 22360cc94cdf2f9dfd10f2fe0b3fa0fa438d51df271Chris Banes return dr; 22460cc94cdf2f9dfd10f2fe0b3fa0fa438d51df271Chris Banes } 22560cc94cdf2f9dfd10f2fe0b3fa0fa438d51df271Chris Banes 22660cc94cdf2f9dfd10f2fe0b3fa0fa438d51df271Chris Banes // Else we need to try and create one... 22760cc94cdf2f9dfd10f2fe0b3fa0fa438d51df271Chris Banes if (resId == R.drawable.abc_cab_background_top_material) { 22860cc94cdf2f9dfd10f2fe0b3fa0fa438d51df271Chris Banes dr = new LayerDrawable(new Drawable[]{ 22960cc94cdf2f9dfd10f2fe0b3fa0fa438d51df271Chris Banes getDrawable(context, R.drawable.abc_cab_background_internal_bg), 23060cc94cdf2f9dfd10f2fe0b3fa0fa438d51df271Chris Banes getDrawable(context, R.drawable.abc_cab_background_top_mtrl_alpha) 23160cc94cdf2f9dfd10f2fe0b3fa0fa438d51df271Chris Banes }); 23260cc94cdf2f9dfd10f2fe0b3fa0fa438d51df271Chris Banes } 23360cc94cdf2f9dfd10f2fe0b3fa0fa438d51df271Chris Banes 23460cc94cdf2f9dfd10f2fe0b3fa0fa438d51df271Chris Banes if (dr != null) { 23560cc94cdf2f9dfd10f2fe0b3fa0fa438d51df271Chris Banes dr.setChangingConfigurations(tv.changingConfigurations); 23660cc94cdf2f9dfd10f2fe0b3fa0fa438d51df271Chris Banes // If we reached here then we created a new drawable, add it to the cache 23760cc94cdf2f9dfd10f2fe0b3fa0fa438d51df271Chris Banes addDrawableToCache(context, key, dr); 23860cc94cdf2f9dfd10f2fe0b3fa0fa438d51df271Chris Banes } 23960cc94cdf2f9dfd10f2fe0b3fa0fa438d51df271Chris Banes 24060cc94cdf2f9dfd10f2fe0b3fa0fa438d51df271Chris Banes return dr; 24160cc94cdf2f9dfd10f2fe0b3fa0fa438d51df271Chris Banes } 24260cc94cdf2f9dfd10f2fe0b3fa0fa438d51df271Chris Banes 243e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes private Drawable tintDrawable(@NonNull Context context, @DrawableRes int resId, 244e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes boolean failIfNotKnown, @NonNull Drawable drawable) { 245e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes final ColorStateList tintList = getTintList(context, resId); 246e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes if (tintList != null) { 247e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes // First mutate the Drawable, then wrap it and set the tint list 248aaa85b7d563d27fdf10048dd619a317451477ad5Chris Banes if (DrawableUtils.canSafelyMutateDrawable(drawable)) { 249e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes drawable = drawable.mutate(); 250e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes } 251e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes drawable = DrawableCompat.wrap(drawable); 252e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes DrawableCompat.setTintList(drawable, tintList); 253469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes 254e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes // If there is a blending mode specified for the drawable, use it 255e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes final PorterDuff.Mode tintMode = getTintMode(resId); 256e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes if (tintMode != null) { 257e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes DrawableCompat.setTintMode(drawable, tintMode); 258e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes } 259e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes } else if (resId == R.drawable.abc_seekbar_track_material) { 260e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes LayerDrawable ld = (LayerDrawable) drawable; 261e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes setPorterDuffColorFilter(ld.findDrawableByLayerId(android.R.id.background), 262e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes getThemeAttrColor(context, R.attr.colorControlNormal), DEFAULT_MODE); 263e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes setPorterDuffColorFilter(ld.findDrawableByLayerId(android.R.id.secondaryProgress), 264e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes getThemeAttrColor(context, R.attr.colorControlNormal), DEFAULT_MODE); 265e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes setPorterDuffColorFilter(ld.findDrawableByLayerId(android.R.id.progress), 266e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes getThemeAttrColor(context, R.attr.colorControlActivated), DEFAULT_MODE); 26774433e220541f8236f4e01f658a96854b29acfd1Chris Banes } else if (resId == R.drawable.abc_ratingbar_material 26874433e220541f8236f4e01f658a96854b29acfd1Chris Banes || resId == R.drawable.abc_ratingbar_indicator_material 269e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes || resId == R.drawable.abc_ratingbar_small_material) { 270e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes LayerDrawable ld = (LayerDrawable) drawable; 271e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes setPorterDuffColorFilter(ld.findDrawableByLayerId(android.R.id.background), 272e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes getDisabledThemeAttrColor(context, R.attr.colorControlNormal), 273e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes DEFAULT_MODE); 274e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes setPorterDuffColorFilter(ld.findDrawableByLayerId(android.R.id.secondaryProgress), 275e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes getThemeAttrColor(context, R.attr.colorControlActivated), DEFAULT_MODE); 276e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes setPorterDuffColorFilter(ld.findDrawableByLayerId(android.R.id.progress), 277e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes getThemeAttrColor(context, R.attr.colorControlActivated), DEFAULT_MODE); 278e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes } else { 279e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes final boolean tinted = tintDrawableUsingColorFilter(context, resId, drawable); 280e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes if (!tinted && failIfNotKnown) { 281e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes // If we didn't tint using a ColorFilter, and we're set to fail if we don't 282e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes // know the id, return null 283e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes drawable = null; 284e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes } 285e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes } 286e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes return drawable; 287e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes } 288a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banes 289e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes private Drawable loadDrawableFromDelegates(@NonNull Context context, @DrawableRes int resId) { 290e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes if (mDelegates != null && !mDelegates.isEmpty()) { 291e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes if (mKnownDrawableIdTags != null) { 2929f63864c84e3d48841d553521468c7a27189c4f0Chris Banes final String cachedTagName = mKnownDrawableIdTags.get(resId); 293e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes if (SKIP_DRAWABLE_TAG.equals(cachedTagName) 294e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes || (cachedTagName != null && mDelegates.get(cachedTagName) == null)) { 295e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes // If we don't have a delegate for the drawable tag, or we've been set to 296e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes // skip it, fail fast and return null 297e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes if (DEBUG) { 2989f63864c84e3d48841d553521468c7a27189c4f0Chris Banes Log.d(TAG, "[loadDrawableFromDelegates] Skipping drawable: " 299e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes + context.getResources().getResourceName(resId)); 300e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes } 301e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes return null; 302a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banes } 303469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes } else { 304e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes // Create an id cache as we'll need one later 305e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes mKnownDrawableIdTags = new SparseArray<>(); 306e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes } 307e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes 308e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes if (mTypedValue == null) { 309e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes mTypedValue = new TypedValue(); 310e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes } 311e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes final TypedValue tv = mTypedValue; 312e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes final Resources res = context.getResources(); 313e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes res.getValue(resId, tv, true); 314e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes 31560cc94cdf2f9dfd10f2fe0b3fa0fa438d51df271Chris Banes final long key = createCacheKey(tv); 3169f63864c84e3d48841d553521468c7a27189c4f0Chris Banes 31760cc94cdf2f9dfd10f2fe0b3fa0fa438d51df271Chris Banes Drawable dr = getCachedDrawable(context, key); 3189f63864c84e3d48841d553521468c7a27189c4f0Chris Banes if (dr != null) { 3199f63864c84e3d48841d553521468c7a27189c4f0Chris Banes if (DEBUG) { 3209f63864c84e3d48841d553521468c7a27189c4f0Chris Banes Log.i(TAG, "[loadDrawableFromDelegates] Returning cached drawable: " + 3219f63864c84e3d48841d553521468c7a27189c4f0Chris Banes context.getResources().getResourceName(resId)); 3229f63864c84e3d48841d553521468c7a27189c4f0Chris Banes } 3239f63864c84e3d48841d553521468c7a27189c4f0Chris Banes // We have a cached drawable, return it! 3249f63864c84e3d48841d553521468c7a27189c4f0Chris Banes return dr; 3259f63864c84e3d48841d553521468c7a27189c4f0Chris Banes } 3269f63864c84e3d48841d553521468c7a27189c4f0Chris Banes 327e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes if (tv.string != null && tv.string.toString().endsWith(".xml")) { 328e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes // If the resource is an XML file, let's try and parse it 329e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes try { 330e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes final XmlPullParser parser = res.getXml(resId); 331e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes final AttributeSet attrs = Xml.asAttributeSet(parser); 332e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes int type; 333e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes while ((type = parser.next()) != XmlPullParser.START_TAG && 334e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes type != XmlPullParser.END_DOCUMENT) { 335e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes // Empty loop 336e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes } 337e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes if (type != XmlPullParser.START_TAG) { 338e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes throw new XmlPullParserException("No start tag found"); 339e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes } 340e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes 341e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes final String tagName = parser.getName(); 3429f63864c84e3d48841d553521468c7a27189c4f0Chris Banes // Add the tag name to the cache 3439f63864c84e3d48841d553521468c7a27189c4f0Chris Banes mKnownDrawableIdTags.append(resId, tagName); 344e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes 345e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes // Now try and find a delegate for the tag name and inflate if found 346e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes final InflateDelegate delegate = mDelegates.get(tagName); 347e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes if (delegate != null) { 348bd3494de1d97206f366f52641a14b21eb06a7022Chris Banes dr = delegate.createFromXmlInner(context, parser, attrs, 349bd3494de1d97206f366f52641a14b21eb06a7022Chris Banes context.getTheme()); 3509f63864c84e3d48841d553521468c7a27189c4f0Chris Banes } 3519f63864c84e3d48841d553521468c7a27189c4f0Chris Banes if (dr != null) { 3529f63864c84e3d48841d553521468c7a27189c4f0Chris Banes // Add it to the drawable cache 3539f63864c84e3d48841d553521468c7a27189c4f0Chris Banes dr.setChangingConfigurations(tv.changingConfigurations); 35460cc94cdf2f9dfd10f2fe0b3fa0fa438d51df271Chris Banes if (addDrawableToCache(context, key, dr) && DEBUG) { 3559f63864c84e3d48841d553521468c7a27189c4f0Chris Banes Log.i(TAG, "[loadDrawableFromDelegates] Saved drawable to cache: " + 3569f63864c84e3d48841d553521468c7a27189c4f0Chris Banes context.getResources().getResourceName(resId)); 3579f63864c84e3d48841d553521468c7a27189c4f0Chris Banes } 358e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes } 359e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes } catch (Exception e) { 360e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes Log.e(TAG, "Exception while inflating drawable", e); 3614ab820f4155444d20b37e105873775dd71907eefChris Banes } 362469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes } 3639f63864c84e3d48841d553521468c7a27189c4f0Chris Banes if (dr == null) { 3649f63864c84e3d48841d553521468c7a27189c4f0Chris Banes // If we reach here then the delegate inflation of the resource failed. Mark it as 3659f63864c84e3d48841d553521468c7a27189c4f0Chris Banes // bad so we skip the id next time 3669f63864c84e3d48841d553521468c7a27189c4f0Chris Banes mKnownDrawableIdTags.append(resId, SKIP_DRAWABLE_TAG); 3679f63864c84e3d48841d553521468c7a27189c4f0Chris Banes } 3689f63864c84e3d48841d553521468c7a27189c4f0Chris Banes return dr; 369469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes } 370e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes 371e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes return null; 372469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes } 373469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes 37460cc94cdf2f9dfd10f2fe0b3fa0fa438d51df271Chris Banes private Drawable getCachedDrawable(@NonNull final Context context, final long key) { 37560cc94cdf2f9dfd10f2fe0b3fa0fa438d51df271Chris Banes synchronized (mDrawableCacheLock) { 3769f63864c84e3d48841d553521468c7a27189c4f0Chris Banes final LongSparseArray<WeakReference<ConstantState>> cache 37760cc94cdf2f9dfd10f2fe0b3fa0fa438d51df271Chris Banes = mDrawableCaches.get(context); 3789f63864c84e3d48841d553521468c7a27189c4f0Chris Banes if (cache == null) { 3799f63864c84e3d48841d553521468c7a27189c4f0Chris Banes return null; 3809f63864c84e3d48841d553521468c7a27189c4f0Chris Banes } 3819f63864c84e3d48841d553521468c7a27189c4f0Chris Banes 3829f63864c84e3d48841d553521468c7a27189c4f0Chris Banes final WeakReference<ConstantState> wr = cache.get(key); 3839f63864c84e3d48841d553521468c7a27189c4f0Chris Banes if (wr != null) { 3849f63864c84e3d48841d553521468c7a27189c4f0Chris Banes // We have the key, and the secret 3859f63864c84e3d48841d553521468c7a27189c4f0Chris Banes ConstantState entry = wr.get(); 3869f63864c84e3d48841d553521468c7a27189c4f0Chris Banes if (entry != null) { 3879f63864c84e3d48841d553521468c7a27189c4f0Chris Banes return entry.newDrawable(context.getResources()); 3889f63864c84e3d48841d553521468c7a27189c4f0Chris Banes } else { 3899f63864c84e3d48841d553521468c7a27189c4f0Chris Banes // Our entry has been purged 3909f63864c84e3d48841d553521468c7a27189c4f0Chris Banes cache.delete(key); 3919f63864c84e3d48841d553521468c7a27189c4f0Chris Banes } 3929f63864c84e3d48841d553521468c7a27189c4f0Chris Banes } 3939f63864c84e3d48841d553521468c7a27189c4f0Chris Banes } 3949f63864c84e3d48841d553521468c7a27189c4f0Chris Banes return null; 3959f63864c84e3d48841d553521468c7a27189c4f0Chris Banes } 3969f63864c84e3d48841d553521468c7a27189c4f0Chris Banes 39760cc94cdf2f9dfd10f2fe0b3fa0fa438d51df271Chris Banes private boolean addDrawableToCache(@NonNull final Context context, final long key, 3989f63864c84e3d48841d553521468c7a27189c4f0Chris Banes @NonNull final Drawable drawable) { 3999f63864c84e3d48841d553521468c7a27189c4f0Chris Banes final ConstantState cs = drawable.getConstantState(); 4009f63864c84e3d48841d553521468c7a27189c4f0Chris Banes if (cs != null) { 40160cc94cdf2f9dfd10f2fe0b3fa0fa438d51df271Chris Banes synchronized (mDrawableCacheLock) { 40260cc94cdf2f9dfd10f2fe0b3fa0fa438d51df271Chris Banes LongSparseArray<WeakReference<ConstantState>> cache = mDrawableCaches.get(context); 4039f63864c84e3d48841d553521468c7a27189c4f0Chris Banes if (cache == null) { 4049f63864c84e3d48841d553521468c7a27189c4f0Chris Banes cache = new LongSparseArray<>(); 40560cc94cdf2f9dfd10f2fe0b3fa0fa438d51df271Chris Banes mDrawableCaches.put(context, cache); 4069f63864c84e3d48841d553521468c7a27189c4f0Chris Banes } 4079f63864c84e3d48841d553521468c7a27189c4f0Chris Banes cache.put(key, new WeakReference<ConstantState>(cs)); 4089f63864c84e3d48841d553521468c7a27189c4f0Chris Banes } 4099f63864c84e3d48841d553521468c7a27189c4f0Chris Banes return true; 4109f63864c84e3d48841d553521468c7a27189c4f0Chris Banes } 4119f63864c84e3d48841d553521468c7a27189c4f0Chris Banes return false; 4129f63864c84e3d48841d553521468c7a27189c4f0Chris Banes } 4139f63864c84e3d48841d553521468c7a27189c4f0Chris Banes 4145fa121a1f51114f3a2f6d705139e83c0e0aea610Chris Banes public final Drawable onDrawableLoadedFromResources(@NonNull Context context, 4155fa121a1f51114f3a2f6d705139e83c0e0aea610Chris Banes @NonNull VectorEnabledTintResources resources, @DrawableRes final int resId) { 4165fa121a1f51114f3a2f6d705139e83c0e0aea610Chris Banes Drawable drawable = loadDrawableFromDelegates(context, resId); 4175fa121a1f51114f3a2f6d705139e83c0e0aea610Chris Banes if (drawable == null) { 4185fa121a1f51114f3a2f6d705139e83c0e0aea610Chris Banes drawable = resources.superGetDrawable(resId); 4195fa121a1f51114f3a2f6d705139e83c0e0aea610Chris Banes } 4205fa121a1f51114f3a2f6d705139e83c0e0aea610Chris Banes if (drawable != null) { 4215fa121a1f51114f3a2f6d705139e83c0e0aea610Chris Banes return tintDrawable(context, resId, false, drawable); 4225fa121a1f51114f3a2f6d705139e83c0e0aea610Chris Banes } 4235fa121a1f51114f3a2f6d705139e83c0e0aea610Chris Banes return null; 4245fa121a1f51114f3a2f6d705139e83c0e0aea610Chris Banes } 4255fa121a1f51114f3a2f6d705139e83c0e0aea610Chris Banes 42660cc94cdf2f9dfd10f2fe0b3fa0fa438d51df271Chris Banes static boolean tintDrawableUsingColorFilter(@NonNull Context context, 4277e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes @DrawableRes final int resId, @NonNull Drawable drawable) { 42839cb4c1c59fa156ed28bc8835ef05eeb356ad13cChris Banes PorterDuff.Mode tintMode = DEFAULT_MODE; 429469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes boolean colorAttrSet = false; 430469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes int colorAttr = 0; 4310517b282bde8b9a0377dfe5bc4756405a196adb4Chris Banes int alpha = -1; 432469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes 433cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes if (arrayContains(COLORFILTER_TINT_COLOR_CONTROL_NORMAL, resId)) { 434469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes colorAttr = R.attr.colorControlNormal; 435469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes colorAttrSet = true; 436cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes } else if (arrayContains(COLORFILTER_COLOR_CONTROL_ACTIVATED, resId)) { 437469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes colorAttr = R.attr.colorControlActivated; 438469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes colorAttrSet = true; 439cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes } else if (arrayContains(COLORFILTER_COLOR_BACKGROUND_MULTIPLY, resId)) { 440469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes colorAttr = android.R.attr.colorBackground; 441469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes colorAttrSet = true; 442469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes tintMode = PorterDuff.Mode.MULTIPLY; 4430517b282bde8b9a0377dfe5bc4756405a196adb4Chris Banes } else if (resId == R.drawable.abc_list_divider_mtrl_alpha) { 4440517b282bde8b9a0377dfe5bc4756405a196adb4Chris Banes colorAttr = android.R.attr.colorForeground; 4450517b282bde8b9a0377dfe5bc4756405a196adb4Chris Banes colorAttrSet = true; 4460517b282bde8b9a0377dfe5bc4756405a196adb4Chris Banes alpha = Math.round(0.16f * 255); 447593d8bcc82c01a559f737dfcbc6e806005300a64Chris Banes } else if (resId == R.drawable.abc_dialog_material_background) { 448593d8bcc82c01a559f737dfcbc6e806005300a64Chris Banes colorAttr = android.R.attr.colorBackground; 449593d8bcc82c01a559f737dfcbc6e806005300a64Chris Banes colorAttrSet = true; 450469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes } 451469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes 452f31fb9d2fd3b5b130f3f5ac121b033546d869231Chris Banes if (colorAttrSet) { 453aaa85b7d563d27fdf10048dd619a317451477ad5Chris Banes if (DrawableUtils.canSafelyMutateDrawable(drawable)) { 454d20d889e7abef50efbaf6e975100a8fb73409b13Chris Banes drawable = drawable.mutate(); 455d20d889e7abef50efbaf6e975100a8fb73409b13Chris Banes } 456d20d889e7abef50efbaf6e975100a8fb73409b13Chris Banes 457cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes final int color = getThemeAttrColor(context, colorAttr); 45839cb4c1c59fa156ed28bc8835ef05eeb356ad13cChris Banes drawable.setColorFilter(getPorterDuffColorFilter(color, tintMode)); 459469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes 4600517b282bde8b9a0377dfe5bc4756405a196adb4Chris Banes if (alpha != -1) { 4610517b282bde8b9a0377dfe5bc4756405a196adb4Chris Banes drawable.setAlpha(alpha); 4620517b282bde8b9a0377dfe5bc4756405a196adb4Chris Banes } 4630517b282bde8b9a0377dfe5bc4756405a196adb4Chris Banes 464469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes if (DEBUG) { 4659f63864c84e3d48841d553521468c7a27189c4f0Chris Banes Log.d(TAG, "[tintDrawableUsingColorFilter] Tinted " 4669f63864c84e3d48841d553521468c7a27189c4f0Chris Banes + context.getResources().getResourceName(resId) + 467469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes " with color: #" + Integer.toHexString(color)); 468469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes } 4694ab820f4155444d20b37e105873775dd71907eefChris Banes return true; 470469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes } 4714ab820f4155444d20b37e105873775dd71907eefChris Banes return false; 472469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes } 473469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes 4749f63864c84e3d48841d553521468c7a27189c4f0Chris Banes private void addDelegate(@NonNull String tagName, @NonNull InflateDelegate delegate) { 4757e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes if (mDelegates == null) { 476e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes mDelegates = new ArrayMap<>(); 4777e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes } 478e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes mDelegates.put(tagName, delegate); 4797e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes } 4807e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes 4819f63864c84e3d48841d553521468c7a27189c4f0Chris Banes private void removeDelegate(@NonNull String tagName, @NonNull InflateDelegate delegate) { 482e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes if (mDelegates != null && mDelegates.get(tagName) == delegate) { 483e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes mDelegates.remove(tagName); 4847e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes } 4857e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes } 4867e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes 487469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes private static boolean arrayContains(int[] array, int value) { 488469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes for (int id : array) { 489469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes if (id == value) { 490469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes return true; 491469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes } 492469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes } 493469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes return false; 494469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes } 495469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes 496cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes final PorterDuff.Mode getTintMode(final int resId) { 497cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes PorterDuff.Mode mode = null; 498cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes 499cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes if (resId == R.drawable.abc_switch_thumb_material) { 500cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes mode = PorterDuff.Mode.MULTIPLY; 501cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes } 502cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes 503cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes return mode; 504a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banes } 505a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banes 5067e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes public final ColorStateList getTintList(@NonNull Context context, @DrawableRes int resId) { 507cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes // Try the cache first (if it exists) 5087e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes ColorStateList tint = getTintListFromCache(context, resId); 509cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes 510cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes if (tint == null) { 511cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes // ...if the cache did not contain a color state list, try and create one 51290075479814eb758d97b822606b448e1a521c298Chris Banes if (resId == R.drawable.abc_edit_text_material) { 5139d5f84f33353a42e837c6b465412d1a6f2fc6eaaChris Banes tint = getColorStateList(context, R.color.abc_tint_edittext); 51490075479814eb758d97b822606b448e1a521c298Chris Banes } else if (resId == R.drawable.abc_switch_track_mtrl_alpha) { 5159d5f84f33353a42e837c6b465412d1a6f2fc6eaaChris Banes tint = getColorStateList(context, R.color.abc_tint_switch_track); 51690075479814eb758d97b822606b448e1a521c298Chris Banes } else if (resId == R.drawable.abc_switch_thumb_material) { 5179d5f84f33353a42e837c6b465412d1a6f2fc6eaaChris Banes tint = getColorStateList(context, R.color.abc_tint_switch_thumb); 518a1a9092aba48f3ca6400ff53f5f4734752180e81Chris Banes } else if (resId == R.drawable.abc_btn_default_mtrl_shape) { 519b1131c6dfc9affe5751523f235878055cb699960Chris Banes tint = createDefaultButtonColorStateList(context); 520a1a9092aba48f3ca6400ff53f5f4734752180e81Chris Banes } else if (resId == R.drawable.abc_btn_borderless_material) { 521a1a9092aba48f3ca6400ff53f5f4734752180e81Chris Banes tint = createBorderlessButtonColorStateList(context); 522b1131c6dfc9affe5751523f235878055cb699960Chris Banes } else if (resId == R.drawable.abc_btn_colored_material) { 523b1131c6dfc9affe5751523f235878055cb699960Chris Banes tint = createColoredButtonColorStateList(context); 524cdd1b1d70cefeb052c2b506738b396f2f982e519Chris Banes } else if (resId == R.drawable.abc_spinner_mtrl_am_alpha 525cdd1b1d70cefeb052c2b506738b396f2f982e519Chris Banes || resId == R.drawable.abc_spinner_textfield_background_material) { 5269d5f84f33353a42e837c6b465412d1a6f2fc6eaaChris Banes tint = getColorStateList(context, R.color.abc_tint_spinner); 527cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes } else if (arrayContains(TINT_COLOR_CONTROL_NORMAL, resId)) { 528cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes tint = getThemeAttrColorStateList(context, R.attr.colorControlNormal); 529cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes } else if (arrayContains(TINT_COLOR_CONTROL_STATE_LIST, resId)) { 5309d5f84f33353a42e837c6b465412d1a6f2fc6eaaChris Banes tint = getColorStateList(context, R.color.abc_tint_default); 5311752dfd10e8242d6e017b3828c7d6e94f044691cChris Banes } else if (arrayContains(TINT_CHECKABLE_BUTTON_LIST, resId)) { 5329d5f84f33353a42e837c6b465412d1a6f2fc6eaaChris Banes tint = getColorStateList(context, R.color.abc_tint_btn_checkable); 53310e2dbc1ad1c01d2824d921a8b0f070859d6f146Chris Banes } else if (resId == R.drawable.abc_seekbar_thumb_material) { 5349d5f84f33353a42e837c6b465412d1a6f2fc6eaaChris Banes tint = getColorStateList(context, R.color.abc_tint_seek_thumb); 53590075479814eb758d97b822606b448e1a521c298Chris Banes } 536a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banes 537cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes if (tint != null) { 5387e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes addTintListToCache(context, resId, tint); 539cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes } 54090075479814eb758d97b822606b448e1a521c298Chris Banes } 541cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes return tint; 54290075479814eb758d97b822606b448e1a521c298Chris Banes } 54390075479814eb758d97b822606b448e1a521c298Chris Banes 5447e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes private ColorStateList getTintListFromCache(@NonNull Context context, @DrawableRes int resId) { 5457e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes if (mTintLists != null) { 5467e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes final SparseArray<ColorStateList> tints = mTintLists.get(context); 5477e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes return tints != null ? tints.get(resId) : null; 5487e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes } 5497e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes return null; 5507e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes } 551469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes 5527e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes private void addTintListToCache(@NonNull Context context, @DrawableRes int resId, 5537e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes @NonNull ColorStateList tintList) { 5547e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes if (mTintLists == null) { 5557e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes mTintLists = new WeakHashMap<>(); 5567e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes } 5577e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes SparseArray<ColorStateList> themeTints = mTintLists.get(context); 5587e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes if (themeTints == null) { 5597e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes themeTints = new SparseArray<>(); 5607e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes mTintLists.put(context, themeTints); 5617e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes } 5627e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes themeTints.append(resId, tintList); 5637e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes } 564469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes 565b1131c6dfc9affe5751523f235878055cb699960Chris Banes private ColorStateList createDefaultButtonColorStateList(Context context) { 566a1a9092aba48f3ca6400ff53f5f4734752180e81Chris Banes return createButtonColorStateList(context, 567a1a9092aba48f3ca6400ff53f5f4734752180e81Chris Banes getThemeAttrColor(context, R.attr.colorButtonNormal)); 568a1a9092aba48f3ca6400ff53f5f4734752180e81Chris Banes } 569a1a9092aba48f3ca6400ff53f5f4734752180e81Chris Banes 570a1a9092aba48f3ca6400ff53f5f4734752180e81Chris Banes private ColorStateList createBorderlessButtonColorStateList(Context context) { 571a1a9092aba48f3ca6400ff53f5f4734752180e81Chris Banes return createButtonColorStateList(context, Color.TRANSPARENT); 572b1131c6dfc9affe5751523f235878055cb699960Chris Banes } 573b1131c6dfc9affe5751523f235878055cb699960Chris Banes 574b1131c6dfc9affe5751523f235878055cb699960Chris Banes private ColorStateList createColoredButtonColorStateList(Context context) { 575a1a9092aba48f3ca6400ff53f5f4734752180e81Chris Banes return createButtonColorStateList(context, getThemeAttrColor(context, R.attr.colorAccent)); 576b1131c6dfc9affe5751523f235878055cb699960Chris Banes } 577b1131c6dfc9affe5751523f235878055cb699960Chris Banes 578a1a9092aba48f3ca6400ff53f5f4734752180e81Chris Banes private ColorStateList createButtonColorStateList(Context context, @ColorInt int baseColor) { 57990075479814eb758d97b822606b448e1a521c298Chris Banes final int[][] states = new int[4][]; 58090075479814eb758d97b822606b448e1a521c298Chris Banes final int[] colors = new int[4]; 58190075479814eb758d97b822606b448e1a521c298Chris Banes int i = 0; 58290075479814eb758d97b822606b448e1a521c298Chris Banes 583eb0d0c030a15e93f456cc1403fffb909c0ae4e66Chris Banes final int colorControlHighlight = getThemeAttrColor(context, R.attr.colorControlHighlight); 584eb0d0c030a15e93f456cc1403fffb909c0ae4e66Chris Banes 58590075479814eb758d97b822606b448e1a521c298Chris Banes // Disabled state 586bb1a62b3eed874aa47ea4763ac972902c77e988fChris Banes states[i] = ThemeUtils.DISABLED_STATE_SET; 587cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes colors[i] = getDisabledThemeAttrColor(context, R.attr.colorButtonNormal); 58890075479814eb758d97b822606b448e1a521c298Chris Banes i++; 58990075479814eb758d97b822606b448e1a521c298Chris Banes 590bb1a62b3eed874aa47ea4763ac972902c77e988fChris Banes states[i] = ThemeUtils.PRESSED_STATE_SET; 591b1131c6dfc9affe5751523f235878055cb699960Chris Banes colors[i] = ColorUtils.compositeColors(colorControlHighlight, baseColor); 59290075479814eb758d97b822606b448e1a521c298Chris Banes i++; 59390075479814eb758d97b822606b448e1a521c298Chris Banes 594bb1a62b3eed874aa47ea4763ac972902c77e988fChris Banes states[i] = ThemeUtils.FOCUSED_STATE_SET; 595b1131c6dfc9affe5751523f235878055cb699960Chris Banes colors[i] = ColorUtils.compositeColors(colorControlHighlight, baseColor); 59690075479814eb758d97b822606b448e1a521c298Chris Banes i++; 59790075479814eb758d97b822606b448e1a521c298Chris Banes 59890075479814eb758d97b822606b448e1a521c298Chris Banes // Default enabled state 599bb1a62b3eed874aa47ea4763ac972902c77e988fChris Banes states[i] = ThemeUtils.EMPTY_STATE_SET; 600b1131c6dfc9affe5751523f235878055cb699960Chris Banes colors[i] = baseColor; 60190075479814eb758d97b822606b448e1a521c298Chris Banes i++; 60290075479814eb758d97b822606b448e1a521c298Chris Banes 60390075479814eb758d97b822606b448e1a521c298Chris Banes return new ColorStateList(states, colors); 604a9585dae398a69dd67797e7ca86f44ffcabd9e86Chris Banes } 605a9585dae398a69dd67797e7ca86f44ffcabd9e86Chris Banes 606469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes private static class ColorFilterLruCache extends LruCache<Integer, PorterDuffColorFilter> { 607469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes 608469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes public ColorFilterLruCache(int maxSize) { 609469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes super(maxSize); 610469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes } 611469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes 612469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes PorterDuffColorFilter get(int color, PorterDuff.Mode mode) { 613469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes return get(generateCacheKey(color, mode)); 614469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes } 615469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes 616469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes PorterDuffColorFilter put(int color, PorterDuff.Mode mode, PorterDuffColorFilter filter) { 617469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes return put(generateCacheKey(color, mode), filter); 618469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes } 619469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes 620469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes private static int generateCacheKey(int color, PorterDuff.Mode mode) { 621469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes int hashCode = 1; 622469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes hashCode = 31 * hashCode + color; 623469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes hashCode = 31 * hashCode + mode.hashCode(); 624469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes return hashCode; 625469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes } 626469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes } 627a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banes 628f7b73431b366b76bcf58536b7b1086489e4683b2Chris Banes public static void tintDrawable(Drawable drawable, TintInfo tint, int[] state) { 629aaa85b7d563d27fdf10048dd619a317451477ad5Chris Banes if (DrawableUtils.canSafelyMutateDrawable(drawable) 630aaa85b7d563d27fdf10048dd619a317451477ad5Chris Banes && drawable.mutate() != drawable) { 63144eb323d09e9bc3baca24ef4a8595aafb7ed2652Chris Banes Log.d(TAG, "Mutated drawable is not the same instance as the input."); 63244eb323d09e9bc3baca24ef4a8595aafb7ed2652Chris Banes return; 63344eb323d09e9bc3baca24ef4a8595aafb7ed2652Chris Banes } 634eb0d0c030a15e93f456cc1403fffb909c0ae4e66Chris Banes 63544eb323d09e9bc3baca24ef4a8595aafb7ed2652Chris Banes if (tint.mHasTintList || tint.mHasTintMode) { 63644eb323d09e9bc3baca24ef4a8595aafb7ed2652Chris Banes drawable.setColorFilter(createTintFilter( 63744eb323d09e9bc3baca24ef4a8595aafb7ed2652Chris Banes tint.mHasTintList ? tint.mTintList : null, 63844eb323d09e9bc3baca24ef4a8595aafb7ed2652Chris Banes tint.mHasTintMode ? tint.mTintMode : DEFAULT_MODE, 63944eb323d09e9bc3baca24ef4a8595aafb7ed2652Chris Banes state)); 64044eb323d09e9bc3baca24ef4a8595aafb7ed2652Chris Banes } else { 64144eb323d09e9bc3baca24ef4a8595aafb7ed2652Chris Banes drawable.clearColorFilter(); 64244eb323d09e9bc3baca24ef4a8595aafb7ed2652Chris Banes } 64344eb323d09e9bc3baca24ef4a8595aafb7ed2652Chris Banes 6447797b9f22c8c404309b778a0966266d2b1a84915Chris Banes if (Build.VERSION.SDK_INT <= 23) { 6457797b9f22c8c404309b778a0966266d2b1a84915Chris Banes // Pre-v23 there is no guarantee that a state change will invoke an invalidation, 6467797b9f22c8c404309b778a0966266d2b1a84915Chris Banes // so we force it ourselves 64744eb323d09e9bc3baca24ef4a8595aafb7ed2652Chris Banes drawable.invalidateSelf(); 64844eb323d09e9bc3baca24ef4a8595aafb7ed2652Chris Banes } 64944eb323d09e9bc3baca24ef4a8595aafb7ed2652Chris Banes } 65044eb323d09e9bc3baca24ef4a8595aafb7ed2652Chris Banes 65139cb4c1c59fa156ed28bc8835ef05eeb356ad13cChris Banes private static PorterDuffColorFilter createTintFilter(ColorStateList tint, 65239cb4c1c59fa156ed28bc8835ef05eeb356ad13cChris Banes PorterDuff.Mode tintMode, final int[] state) { 65339cb4c1c59fa156ed28bc8835ef05eeb356ad13cChris Banes if (tint == null || tintMode == null) { 65439cb4c1c59fa156ed28bc8835ef05eeb356ad13cChris Banes return null; 655cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes } 65639cb4c1c59fa156ed28bc8835ef05eeb356ad13cChris Banes final int color = tint.getColorForState(state, Color.TRANSPARENT); 65739cb4c1c59fa156ed28bc8835ef05eeb356ad13cChris Banes return getPorterDuffColorFilter(color, tintMode); 65839cb4c1c59fa156ed28bc8835ef05eeb356ad13cChris Banes } 659cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes 660fe1cbed21122206b7a4af97790ade439d49421d8Chris Banes public static PorterDuffColorFilter getPorterDuffColorFilter(int color, PorterDuff.Mode mode) { 661a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banes // First, lets see if the cache already contains the color filter 662a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banes PorterDuffColorFilter filter = COLOR_FILTER_CACHE.get(color, mode); 663a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banes 664a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banes if (filter == null) { 665a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banes // Cache miss, so create a color filter and add it to the cache 666a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banes filter = new PorterDuffColorFilter(color, mode); 667a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banes COLOR_FILTER_CACHE.put(color, mode, filter); 668a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banes } 669a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banes 67039cb4c1c59fa156ed28bc8835ef05eeb356ad13cChris Banes return filter; 671a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banes } 67210e2dbc1ad1c01d2824d921a8b0f070859d6f146Chris Banes 67310e2dbc1ad1c01d2824d921a8b0f070859d6f146Chris Banes private static void setPorterDuffColorFilter(Drawable d, int color, PorterDuff.Mode mode) { 674aaa85b7d563d27fdf10048dd619a317451477ad5Chris Banes if (DrawableUtils.canSafelyMutateDrawable(d)) { 675d20d889e7abef50efbaf6e975100a8fb73409b13Chris Banes d = d.mutate(); 676d20d889e7abef50efbaf6e975100a8fb73409b13Chris Banes } 67710e2dbc1ad1c01d2824d921a8b0f070859d6f146Chris Banes d.setColorFilter(getPorterDuffColorFilter(color, mode == null ? DEFAULT_MODE : mode)); 67810e2dbc1ad1c01d2824d921a8b0f070859d6f146Chris Banes } 6799f63864c84e3d48841d553521468c7a27189c4f0Chris Banes 680eb648620b291eabbb5523f649baef5abb2e4687bAlan Viverette private void checkVectorDrawableSetup(@NonNull Context context) { 681eb648620b291eabbb5523f649baef5abb2e4687bAlan Viverette if (!mHasCheckedVectorDrawableSetup) { 682eb648620b291eabbb5523f649baef5abb2e4687bAlan Viverette // We've already checked so return now... 683eb648620b291eabbb5523f649baef5abb2e4687bAlan Viverette return; 684eb648620b291eabbb5523f649baef5abb2e4687bAlan Viverette } 685eb648620b291eabbb5523f649baef5abb2e4687bAlan Viverette // Here we will check that a known Vector drawable resource inside AppCompat can be 686eb648620b291eabbb5523f649baef5abb2e4687bAlan Viverette // correctly decoded. We use one that will almost definitely be used in the future to 687eb648620b291eabbb5523f649baef5abb2e4687bAlan Viverette // negate any wasted work 688eb648620b291eabbb5523f649baef5abb2e4687bAlan Viverette final Drawable d = getDrawable(context, R.drawable.abc_ic_ab_back_material); 689eb648620b291eabbb5523f649baef5abb2e4687bAlan Viverette if (d != null && isVectorDrawable(d)) { 690eb648620b291eabbb5523f649baef5abb2e4687bAlan Viverette mHasCheckedVectorDrawableSetup = true; 691eb648620b291eabbb5523f649baef5abb2e4687bAlan Viverette } else { 692eb648620b291eabbb5523f649baef5abb2e4687bAlan Viverette throw new IllegalStateException("This app has been built with an incorrect " 693eb648620b291eabbb5523f649baef5abb2e4687bAlan Viverette + "configuration. Please configure your build for VectorDrawableCompat."); 694eb648620b291eabbb5523f649baef5abb2e4687bAlan Viverette } 695eb648620b291eabbb5523f649baef5abb2e4687bAlan Viverette } 696eb648620b291eabbb5523f649baef5abb2e4687bAlan Viverette 697ba13a44cd6db80ec983b1ed678b7dbeab04056a1Chris Banes private static boolean isVectorDrawable(@NonNull Drawable d) { 698ba13a44cd6db80ec983b1ed678b7dbeab04056a1Chris Banes return d instanceof VectorDrawableCompat 699ba13a44cd6db80ec983b1ed678b7dbeab04056a1Chris Banes || PLATFORM_VD_CLAZZ.equals(d.getClass().getName()); 700ba13a44cd6db80ec983b1ed678b7dbeab04056a1Chris Banes } 701ba13a44cd6db80ec983b1ed678b7dbeab04056a1Chris Banes 7029f63864c84e3d48841d553521468c7a27189c4f0Chris Banes private static class VdcInflateDelegate implements InflateDelegate { 7039f63864c84e3d48841d553521468c7a27189c4f0Chris Banes @Override 704bd3494de1d97206f366f52641a14b21eb06a7022Chris Banes public Drawable createFromXmlInner(@NonNull Context context, @NonNull XmlPullParser parser, 7059f63864c84e3d48841d553521468c7a27189c4f0Chris Banes @NonNull AttributeSet attrs, @Nullable Resources.Theme theme) { 7069f63864c84e3d48841d553521468c7a27189c4f0Chris Banes try { 707bd3494de1d97206f366f52641a14b21eb06a7022Chris Banes return VectorDrawableCompat 708bd3494de1d97206f366f52641a14b21eb06a7022Chris Banes .createFromXmlInner(context.getResources(), parser, attrs, theme); 7099f63864c84e3d48841d553521468c7a27189c4f0Chris Banes } catch (Exception e) { 7109f63864c84e3d48841d553521468c7a27189c4f0Chris Banes Log.e("VdcInflateDelegate", "Exception while inflating <vector>", e); 7119f63864c84e3d48841d553521468c7a27189c4f0Chris Banes return null; 7129f63864c84e3d48841d553521468c7a27189c4f0Chris Banes } 7139f63864c84e3d48841d553521468c7a27189c4f0Chris Banes } 7149f63864c84e3d48841d553521468c7a27189c4f0Chris Banes } 715bd3494de1d97206f366f52641a14b21eb06a7022Chris Banes 716bd3494de1d97206f366f52641a14b21eb06a7022Chris Banes private static class AvdcInflateDelegate implements InflateDelegate { 717bd3494de1d97206f366f52641a14b21eb06a7022Chris Banes @Override 718bd3494de1d97206f366f52641a14b21eb06a7022Chris Banes public Drawable createFromXmlInner(@NonNull Context context, @NonNull XmlPullParser parser, 719bd3494de1d97206f366f52641a14b21eb06a7022Chris Banes @NonNull AttributeSet attrs, @Nullable Resources.Theme theme) { 720bd3494de1d97206f366f52641a14b21eb06a7022Chris Banes try { 721bd3494de1d97206f366f52641a14b21eb06a7022Chris Banes return AnimatedVectorDrawableCompat 722bd3494de1d97206f366f52641a14b21eb06a7022Chris Banes .createFromXmlInner(context, context.getResources(), parser, attrs, theme); 723bd3494de1d97206f366f52641a14b21eb06a7022Chris Banes } catch (Exception e) { 724bd3494de1d97206f366f52641a14b21eb06a7022Chris Banes Log.e("AvdcInflateDelegate", "Exception while inflating <animated-vector>", e); 725bd3494de1d97206f366f52641a14b21eb06a7022Chris Banes return null; 726bd3494de1d97206f366f52641a14b21eb06a7022Chris Banes } 727bd3494de1d97206f366f52641a14b21eb06a7022Chris Banes } 728bd3494de1d97206f366f52641a14b21eb06a7022Chris Banes } 729469286122bcbbecbdd0bef74fb50f9d8920e77b9Chris Banes} 730