blob: e62f22b36c0cf28c5612782e9f6784fd02dd26e5 [file] [log] [blame]
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package androidx.appcompat.widget;
import static androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX;
import android.content.Context;
import android.content.ContextWrapper;
import android.content.res.AssetManager;
import android.content.res.Resources;
import android.os.Build;
import androidx.annotation.NonNull;
import androidx.annotation.RestrictTo;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
/**
* A {@link android.content.ContextWrapper} which returns a tint-aware
* {@link android.content.res.Resources} instance from {@link #getResources()}.
*
* @hide
*/
@RestrictTo(LIBRARY_GROUP_PREFIX)
public class TintContextWrapper extends ContextWrapper {
private static final Object CACHE_LOCK = new Object();
private static ArrayList<WeakReference<TintContextWrapper>> sCache;
public static Context wrap(@NonNull final Context context) {
if (shouldWrap(context)) {
synchronized (CACHE_LOCK) {
if (sCache == null) {
sCache = new ArrayList<>();
} else {
// This is a convenient place to prune any dead reference entries
for (int i = sCache.size() - 1; i >= 0; i--) {
final WeakReference<TintContextWrapper> ref = sCache.get(i);
if (ref == null || ref.get() == null) {
sCache.remove(i);
}
}
// Now check our instance cache
for (int i = sCache.size() - 1; i >= 0; i--) {
final WeakReference<TintContextWrapper> ref = sCache.get(i);
final TintContextWrapper wrapper = ref != null ? ref.get() : null;
if (wrapper != null && wrapper.getBaseContext() == context) {
return wrapper;
}
}
}
// If we reach here then the cache didn't have a hit, so create a new instance
// and add it to the cache
final TintContextWrapper wrapper = new TintContextWrapper(context);
sCache.add(new WeakReference<>(wrapper));
return wrapper;
}
}
return context;
}
private static boolean shouldWrap(@NonNull final Context context) {
if (context instanceof TintContextWrapper
|| context.getResources() instanceof TintResources
|| context.getResources() instanceof VectorEnabledTintResources) {
// If the Context already has a TintResources[Experimental] impl, no need to wrap again
// If the Context is already a TintContextWrapper, no need to wrap again
return false;
}
return Build.VERSION.SDK_INT < 21 || VectorEnabledTintResources.shouldBeUsed();
}
private final Resources mResources;
private final Resources.Theme mTheme;
private TintContextWrapper(@NonNull final Context base) {
super(base);
if (VectorEnabledTintResources.shouldBeUsed()) {
// We need to create a copy of the Theme so that the Theme references our
// new Resources instance
mResources = new VectorEnabledTintResources(this, base.getResources());
mTheme = mResources.newTheme();
mTheme.setTo(base.getTheme());
} else {
mResources = new TintResources(this, base.getResources());
mTheme = null;
}
}
@Override
public Resources.Theme getTheme() {
return mTheme == null ? super.getTheme() : mTheme;
}
@Override
public void setTheme(int resid) {
if (mTheme == null) {
super.setTheme(resid);
} else {
mTheme.applyStyle(resid, true);
}
}
@Override
public Resources getResources() {
return mResources;
}
@Override
public AssetManager getAssets() {
// Ensure we're returning assets with the correct configuration.
return mResources.getAssets();
}
}