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