Home | History | Annotate | Download | only in widget
      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 }