1/* 2 * Copyright (C) 2015 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package android.support.v7.widget; 18 19import android.content.Context; 20import android.content.ContextWrapper; 21import android.content.res.Resources; 22import android.os.Build; 23import android.support.annotation.NonNull; 24import android.support.v7.app.AppCompatDelegate; 25import android.support.v7.widget.VectorEnabledTintResources; 26 27import java.lang.ref.WeakReference; 28import java.util.ArrayList; 29 30/** 31 * A {@link android.content.ContextWrapper} which returns a tint-aware 32 * {@link android.content.res.Resources} instance from {@link #getResources()}. 33 * 34 * @hide 35 */ 36public class TintContextWrapper extends ContextWrapper { 37 38 private static final ArrayList<WeakReference<TintContextWrapper>> sCache = new ArrayList<>(); 39 40 public static Context wrap(@NonNull final Context context) { 41 if (shouldWrap(context)) { 42 // First check our instance cache 43 for (int i = 0, count = sCache.size(); i < count; i++) { 44 final WeakReference<TintContextWrapper> ref = sCache.get(i); 45 final TintContextWrapper wrapper = ref != null ? ref.get() : null; 46 if (wrapper != null && wrapper.getBaseContext() == context) { 47 return wrapper; 48 } 49 } 50 // If we reach here then the cache didn't have a hit, so create a new instance 51 // and add it to the cache 52 final TintContextWrapper wrapper = new TintContextWrapper(context); 53 sCache.add(new WeakReference<>(wrapper)); 54 return wrapper; 55 } 56 57 return context; 58 } 59 60 private static boolean shouldWrap(@NonNull final Context context) { 61 if (context instanceof TintContextWrapper 62 || context.getResources() instanceof TintResources 63 || context.getResources() instanceof VectorEnabledTintResources) { 64 // If the Context already has a TintResources[Experimental] impl, no need to wrap again 65 // If the Context is already a TintContextWrapper, no need to wrap again 66 return false; 67 } 68 if (AppCompatDelegate.isCompatVectorFromResourcesEnabled() 69 && Build.VERSION.SDK_INT > VectorEnabledTintResources.MAX_SDK_WHERE_REQUIRED) { 70 // If we're running on API 21+ and have the vector resources enabled, there's 71 // no need to wrap 72 return false; 73 } 74 // Else, we should wrap 75 return true; 76 } 77 78 private Resources mResources; 79 private final Resources.Theme mTheme; 80 81 private TintContextWrapper(@NonNull final Context base) { 82 super(base); 83 84 if (VectorEnabledTintResources.shouldBeUsed()) { 85 // We need to create a copy of the Theme so that the Theme references our Resources 86 // instance 87 mTheme = getResources().newTheme(); 88 mTheme.setTo(base.getTheme()); 89 } else { 90 mTheme = null; 91 } 92 } 93 94 @Override 95 public Resources.Theme getTheme() { 96 return mTheme == null ? super.getTheme() : mTheme; 97 } 98 99 @Override 100 public void setTheme(int resid) { 101 if (mTheme == null) { 102 super.setTheme(resid); 103 } else { 104 mTheme.applyStyle(resid, true); 105 } 106 } 107 108 @Override 109 public Resources getResources() { 110 if (mResources == null) { 111 mResources = (mTheme == null) 112 ? new TintResources(this, super.getResources()) 113 : new VectorEnabledTintResources(this, super.getResources()); 114 } 115 return mResources; 116 } 117}