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 17 package android.support.v7.widget; 18 19 import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP; 20 21 import android.content.Context; 22 import android.content.ContextWrapper; 23 import android.content.res.AssetManager; 24 import android.content.res.Resources; 25 import android.os.Build; 26 import android.support.annotation.NonNull; 27 import android.support.annotation.RestrictTo; 28 29 import java.lang.ref.WeakReference; 30 import java.util.ArrayList; 31 32 /** 33 * A {@link android.content.ContextWrapper} which returns a tint-aware 34 * {@link android.content.res.Resources} instance from {@link #getResources()}. 35 * 36 * @hide 37 */ 38 @RestrictTo(LIBRARY_GROUP) 39 public class TintContextWrapper extends ContextWrapper { 40 41 private static final Object CACHE_LOCK = new Object(); 42 private static ArrayList<WeakReference<TintContextWrapper>> sCache; 43 44 public static Context wrap(@NonNull final Context context) { 45 if (shouldWrap(context)) { 46 synchronized (CACHE_LOCK) { 47 if (sCache == null) { 48 sCache = new ArrayList<>(); 49 } else { 50 // This is a convenient place to prune any dead reference entries 51 for (int i = sCache.size() - 1; i >= 0; i--) { 52 final WeakReference<TintContextWrapper> ref = sCache.get(i); 53 if (ref == null || ref.get() == null) { 54 sCache.remove(i); 55 } 56 } 57 // Now check our instance cache 58 for (int i = sCache.size() - 1; i >= 0; i--) { 59 final WeakReference<TintContextWrapper> ref = sCache.get(i); 60 final TintContextWrapper wrapper = ref != null ? ref.get() : null; 61 if (wrapper != null && wrapper.getBaseContext() == context) { 62 return wrapper; 63 } 64 } 65 } 66 // If we reach here then the cache didn't have a hit, so create a new instance 67 // and add it to the cache 68 final TintContextWrapper wrapper = new TintContextWrapper(context); 69 sCache.add(new WeakReference<>(wrapper)); 70 return wrapper; 71 } 72 } 73 return context; 74 } 75 76 private static boolean shouldWrap(@NonNull final Context context) { 77 if (context instanceof TintContextWrapper 78 || context.getResources() instanceof TintResources 79 || context.getResources() instanceof VectorEnabledTintResources) { 80 // If the Context already has a TintResources[Experimental] impl, no need to wrap again 81 // If the Context is already a TintContextWrapper, no need to wrap again 82 return false; 83 } 84 return Build.VERSION.SDK_INT < 21 || VectorEnabledTintResources.shouldBeUsed(); 85 } 86 87 private final Resources mResources; 88 private final Resources.Theme mTheme; 89 90 private TintContextWrapper(@NonNull final Context base) { 91 super(base); 92 93 if (VectorEnabledTintResources.shouldBeUsed()) { 94 // We need to create a copy of the Theme so that the Theme references our 95 // new Resources instance 96 mResources = new VectorEnabledTintResources(this, base.getResources()); 97 mTheme = mResources.newTheme(); 98 mTheme.setTo(base.getTheme()); 99 } else { 100 mResources = new TintResources(this, base.getResources()); 101 mTheme = null; 102 } 103 } 104 105 @Override 106 public Resources.Theme getTheme() { 107 return mTheme == null ? super.getTheme() : mTheme; 108 } 109 110 @Override 111 public void setTheme(int resid) { 112 if (mTheme == null) { 113 super.setTheme(resid); 114 } else { 115 mTheme.applyStyle(resid, true); 116 } 117 } 118 119 @Override 120 public Resources getResources() { 121 return mResources; 122 } 123 124 @Override 125 public AssetManager getAssets() { 126 // Ensure we're returning assets with the correct configuration. 127 return mResources.getAssets(); 128 } 129 }