1911642499da7d796aa1e7c19178c3552a590f48bChris Banes/* 2911642499da7d796aa1e7c19178c3552a590f48bChris Banes * Copyright (C) 2015 The Android Open Source Project 3911642499da7d796aa1e7c19178c3552a590f48bChris Banes * 4911642499da7d796aa1e7c19178c3552a590f48bChris Banes * Licensed under the Apache License, Version 2.0 (the "License"); 5911642499da7d796aa1e7c19178c3552a590f48bChris Banes * you may not use this file except in compliance with the License. 6911642499da7d796aa1e7c19178c3552a590f48bChris Banes * You may obtain a copy of the License at 7911642499da7d796aa1e7c19178c3552a590f48bChris Banes * 8911642499da7d796aa1e7c19178c3552a590f48bChris Banes * http://www.apache.org/licenses/LICENSE-2.0 9911642499da7d796aa1e7c19178c3552a590f48bChris Banes * 10911642499da7d796aa1e7c19178c3552a590f48bChris Banes * Unless required by applicable law or agreed to in writing, software 11911642499da7d796aa1e7c19178c3552a590f48bChris Banes * distributed under the License is distributed on an "AS IS" BASIS, 12911642499da7d796aa1e7c19178c3552a590f48bChris Banes * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13911642499da7d796aa1e7c19178c3552a590f48bChris Banes * See the License for the specific language governing permissions and 14911642499da7d796aa1e7c19178c3552a590f48bChris Banes * limitations under the License. 15911642499da7d796aa1e7c19178c3552a590f48bChris Banes */ 16911642499da7d796aa1e7c19178c3552a590f48bChris Banes 1766698bb15ba0f873aa1c2290cc50d6bb839a474aChris Banespackage android.support.v7.widget; 18911642499da7d796aa1e7c19178c3552a590f48bChris Banes 19911642499da7d796aa1e7c19178c3552a590f48bChris Banesimport android.content.Context; 20911642499da7d796aa1e7c19178c3552a590f48bChris Banesimport android.content.ContextWrapper; 21911642499da7d796aa1e7c19178c3552a590f48bChris Banesimport android.content.res.Resources; 225fa121a1f51114f3a2f6d705139e83c0e0aea610Chris Banesimport android.os.Build; 23e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banesimport android.support.annotation.NonNull; 245fa121a1f51114f3a2f6d705139e83c0e0aea610Chris Banesimport android.support.v7.app.AppCompatDelegate; 255fa121a1f51114f3a2f6d705139e83c0e0aea610Chris Banesimport android.support.v7.widget.VectorEnabledTintResources; 26911642499da7d796aa1e7c19178c3552a590f48bChris Banes 279f63864c84e3d48841d553521468c7a27189c4f0Chris Banesimport java.lang.ref.WeakReference; 289f63864c84e3d48841d553521468c7a27189c4f0Chris Banesimport java.util.ArrayList; 299f63864c84e3d48841d553521468c7a27189c4f0Chris Banes 30911642499da7d796aa1e7c19178c3552a590f48bChris Banes/** 31911642499da7d796aa1e7c19178c3552a590f48bChris Banes * A {@link android.content.ContextWrapper} which returns a tint-aware 32911642499da7d796aa1e7c19178c3552a590f48bChris Banes * {@link android.content.res.Resources} instance from {@link #getResources()}. 335fa121a1f51114f3a2f6d705139e83c0e0aea610Chris Banes * 345fa121a1f51114f3a2f6d705139e83c0e0aea610Chris Banes * @hide 35911642499da7d796aa1e7c19178c3552a590f48bChris Banes */ 365fa121a1f51114f3a2f6d705139e83c0e0aea610Chris Banespublic class TintContextWrapper extends ContextWrapper { 37911642499da7d796aa1e7c19178c3552a590f48bChris Banes 389f63864c84e3d48841d553521468c7a27189c4f0Chris Banes private static final ArrayList<WeakReference<TintContextWrapper>> sCache = new ArrayList<>(); 399f63864c84e3d48841d553521468c7a27189c4f0Chris Banes 409f63864c84e3d48841d553521468c7a27189c4f0Chris Banes public static Context wrap(@NonNull final Context context) { 415fa121a1f51114f3a2f6d705139e83c0e0aea610Chris Banes if (shouldWrap(context)) { 429f63864c84e3d48841d553521468c7a27189c4f0Chris Banes // First check our instance cache 439f63864c84e3d48841d553521468c7a27189c4f0Chris Banes for (int i = 0, count = sCache.size(); i < count; i++) { 449f63864c84e3d48841d553521468c7a27189c4f0Chris Banes final WeakReference<TintContextWrapper> ref = sCache.get(i); 459f63864c84e3d48841d553521468c7a27189c4f0Chris Banes final TintContextWrapper wrapper = ref != null ? ref.get() : null; 469f63864c84e3d48841d553521468c7a27189c4f0Chris Banes if (wrapper != null && wrapper.getBaseContext() == context) { 479f63864c84e3d48841d553521468c7a27189c4f0Chris Banes return wrapper; 489f63864c84e3d48841d553521468c7a27189c4f0Chris Banes } 499f63864c84e3d48841d553521468c7a27189c4f0Chris Banes } 509f63864c84e3d48841d553521468c7a27189c4f0Chris Banes // If we reach here then the cache didn't have a hit, so create a new instance 51604c6317360c5f744cfebb7c23b243358aef58e0Chris Banes // and add it to the cache 529f63864c84e3d48841d553521468c7a27189c4f0Chris Banes final TintContextWrapper wrapper = new TintContextWrapper(context); 53604c6317360c5f744cfebb7c23b243358aef58e0Chris Banes sCache.add(new WeakReference<>(wrapper)); 549f63864c84e3d48841d553521468c7a27189c4f0Chris Banes return wrapper; 55911642499da7d796aa1e7c19178c3552a590f48bChris Banes } 569f63864c84e3d48841d553521468c7a27189c4f0Chris Banes 57911642499da7d796aa1e7c19178c3552a590f48bChris Banes return context; 58911642499da7d796aa1e7c19178c3552a590f48bChris Banes } 59911642499da7d796aa1e7c19178c3552a590f48bChris Banes 605fa121a1f51114f3a2f6d705139e83c0e0aea610Chris Banes private static boolean shouldWrap(@NonNull final Context context) { 615fa121a1f51114f3a2f6d705139e83c0e0aea610Chris Banes if (context instanceof TintContextWrapper 625fa121a1f51114f3a2f6d705139e83c0e0aea610Chris Banes || context.getResources() instanceof TintResources 635fa121a1f51114f3a2f6d705139e83c0e0aea610Chris Banes || context.getResources() instanceof VectorEnabledTintResources) { 645fa121a1f51114f3a2f6d705139e83c0e0aea610Chris Banes // If the Context already has a TintResources[Experimental] impl, no need to wrap again 655fa121a1f51114f3a2f6d705139e83c0e0aea610Chris Banes // If the Context is already a TintContextWrapper, no need to wrap again 665fa121a1f51114f3a2f6d705139e83c0e0aea610Chris Banes return false; 675fa121a1f51114f3a2f6d705139e83c0e0aea610Chris Banes } 685fa121a1f51114f3a2f6d705139e83c0e0aea610Chris Banes if (AppCompatDelegate.isCompatVectorFromResourcesEnabled() 695fa121a1f51114f3a2f6d705139e83c0e0aea610Chris Banes && Build.VERSION.SDK_INT > VectorEnabledTintResources.MAX_SDK_WHERE_REQUIRED) { 705fa121a1f51114f3a2f6d705139e83c0e0aea610Chris Banes // If we're running on API 21+ and have the vector resources enabled, there's 715fa121a1f51114f3a2f6d705139e83c0e0aea610Chris Banes // no need to wrap 725fa121a1f51114f3a2f6d705139e83c0e0aea610Chris Banes return false; 735fa121a1f51114f3a2f6d705139e83c0e0aea610Chris Banes } 745fa121a1f51114f3a2f6d705139e83c0e0aea610Chris Banes // Else, we should wrap 755fa121a1f51114f3a2f6d705139e83c0e0aea610Chris Banes return true; 765fa121a1f51114f3a2f6d705139e83c0e0aea610Chris Banes } 775fa121a1f51114f3a2f6d705139e83c0e0aea610Chris Banes 78cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes private Resources mResources; 795fa121a1f51114f3a2f6d705139e83c0e0aea610Chris Banes private final Resources.Theme mTheme; 80cd6e77607caba0b3b26163791a361938afb8b9c5Chris Banes 815fa121a1f51114f3a2f6d705139e83c0e0aea610Chris Banes private TintContextWrapper(@NonNull final Context base) { 82911642499da7d796aa1e7c19178c3552a590f48bChris Banes super(base); 835fa121a1f51114f3a2f6d705139e83c0e0aea610Chris Banes 845fa121a1f51114f3a2f6d705139e83c0e0aea610Chris Banes if (VectorEnabledTintResources.shouldBeUsed()) { 855fa121a1f51114f3a2f6d705139e83c0e0aea610Chris Banes // We need to create a copy of the Theme so that the Theme references our Resources 865fa121a1f51114f3a2f6d705139e83c0e0aea610Chris Banes // instance 875fa121a1f51114f3a2f6d705139e83c0e0aea610Chris Banes mTheme = getResources().newTheme(); 885fa121a1f51114f3a2f6d705139e83c0e0aea610Chris Banes mTheme.setTo(base.getTheme()); 895fa121a1f51114f3a2f6d705139e83c0e0aea610Chris Banes } else { 905fa121a1f51114f3a2f6d705139e83c0e0aea610Chris Banes mTheme = null; 915fa121a1f51114f3a2f6d705139e83c0e0aea610Chris Banes } 925fa121a1f51114f3a2f6d705139e83c0e0aea610Chris Banes } 935fa121a1f51114f3a2f6d705139e83c0e0aea610Chris Banes 945fa121a1f51114f3a2f6d705139e83c0e0aea610Chris Banes @Override 955fa121a1f51114f3a2f6d705139e83c0e0aea610Chris Banes public Resources.Theme getTheme() { 965fa121a1f51114f3a2f6d705139e83c0e0aea610Chris Banes return mTheme == null ? super.getTheme() : mTheme; 975fa121a1f51114f3a2f6d705139e83c0e0aea610Chris Banes } 985fa121a1f51114f3a2f6d705139e83c0e0aea610Chris Banes 995fa121a1f51114f3a2f6d705139e83c0e0aea610Chris Banes @Override 1005fa121a1f51114f3a2f6d705139e83c0e0aea610Chris Banes public void setTheme(int resid) { 1015fa121a1f51114f3a2f6d705139e83c0e0aea610Chris Banes if (mTheme == null) { 1025fa121a1f51114f3a2f6d705139e83c0e0aea610Chris Banes super.setTheme(resid); 1035fa121a1f51114f3a2f6d705139e83c0e0aea610Chris Banes } else { 1045fa121a1f51114f3a2f6d705139e83c0e0aea610Chris Banes mTheme.applyStyle(resid, true); 1055fa121a1f51114f3a2f6d705139e83c0e0aea610Chris Banes } 106e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes } 1072cccf609662389d6a23dbc0711d5fb2e826e8c63Chris Banes 108e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes @Override 109e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes public Resources getResources() { 110e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes if (mResources == null) { 1115fa121a1f51114f3a2f6d705139e83c0e0aea610Chris Banes mResources = (mTheme == null) 1125fa121a1f51114f3a2f6d705139e83c0e0aea610Chris Banes ? new TintResources(this, super.getResources()) 1135fa121a1f51114f3a2f6d705139e83c0e0aea610Chris Banes : new VectorEnabledTintResources(this, super.getResources()); 1142cccf609662389d6a23dbc0711d5fb2e826e8c63Chris Banes } 115e4beadba70aecabbd7f6677943ab7c0b94809b8aChris Banes return mResources; 1162cccf609662389d6a23dbc0711d5fb2e826e8c63Chris Banes } 117911642499da7d796aa1e7c19178c3552a590f48bChris Banes}