summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAlan Viverette <alanv@google.com>2015-05-06 10:41:43 -0700
committerAlan Viverette <alanv@google.com>2015-05-06 10:41:43 -0700
commite54d245b993e1347cb32c23a6bdc907a45fab324 (patch)
treebe087de6d12733ca9800724ac77103a0b2173964
parent424681e43e454bfcbceb863ddccb875ea57ec26f (diff)
downloadframeworks_base-e54d245b993e1347cb32c23a6bdc907a45fab324.zip
frameworks_base-e54d245b993e1347cb32c23a6bdc907a45fab324.tar.gz
frameworks_base-e54d245b993e1347cb32c23a6bdc907a45fab324.tar.bz2
Improve keying for theme caches, rebase system theme on config change
Themes now use an array of applied styles rather than a String to store their history. They are keyed based on a hash code computed from the history of applied styles. The themed drawable cache has been abstracted out into its own class. Also updates system context to use DayNight as the default and ensures that GlobalActions uses the correct context, which exercises the change. CTS tests have been added in another CL. Bug: 20421157 Change-Id: I9eb4b7dffd198ad24d02f656eaf0839570b59caa
-rw-r--r--core/java/android/animation/AnimatorInflater.java4
-rw-r--r--core/java/android/app/ActivityThread.java10
-rw-r--r--core/java/android/content/res/AssetManager.java1
-rw-r--r--core/java/android/content/res/ConfigurationBoundResourceCache.java146
-rw-r--r--core/java/android/content/res/DrawableCache.java57
-rw-r--r--core/java/android/content/res/Resources.java296
-rw-r--r--core/java/android/content/res/ThemedResourceCache.java233
-rw-r--r--core/jni/android_util_AssetManager.cpp8
-rw-r--r--include/androidfw/ResourceTypes.h1
-rw-r--r--libs/androidfw/ResourceTypes.cpp24
-rw-r--r--services/core/java/com/android/server/policy/GlobalActions.java2
-rw-r--r--services/java/com/android/server/SystemServer.java9
12 files changed, 501 insertions, 290 deletions
diff --git a/core/java/android/animation/AnimatorInflater.java b/core/java/android/animation/AnimatorInflater.java
index 427ecce..435d5ab 100644
--- a/core/java/android/animation/AnimatorInflater.java
+++ b/core/java/android/animation/AnimatorInflater.java
@@ -108,7 +108,7 @@ public class AnimatorInflater {
float pathErrorScale) throws NotFoundException {
final ConfigurationBoundResourceCache<Animator> animatorCache = resources
.getAnimatorCache();
- Animator animator = animatorCache.get(id, theme);
+ Animator animator = animatorCache.getInstance(id, theme);
if (animator != null) {
if (DBG_ANIMATOR_INFLATER) {
Log.d(TAG, "loaded animator from cache, " + resources.getResourceName(id));
@@ -157,7 +157,7 @@ public class AnimatorInflater {
final ConfigurationBoundResourceCache<StateListAnimator> cache = resources
.getStateListAnimatorCache();
final Theme theme = context.getTheme();
- StateListAnimator animator = cache.get(id, theme);
+ StateListAnimator animator = cache.getInstance(id, theme);
if (animator != null) {
return animator;
}
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index da6d8c5..f16406a 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -38,6 +38,7 @@ import android.content.res.AssetManager;
import android.content.res.CompatibilityInfo;
import android.content.res.Configuration;
import android.content.res.Resources;
+import android.content.res.Resources.Theme;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteDebug;
import android.database.sqlite.SQLiteDebug.DbStats;
@@ -4186,9 +4187,14 @@ public final class ActivityThread {
if (!mConfiguration.isOtherSeqNewer(config) && compat == null) {
return;
}
- configDiff = mConfiguration.diff(config);
- mConfiguration.updateFrom(config);
+
+ configDiff = mConfiguration.updateFrom(config);
config = applyCompatConfiguration(mCurDefaultDisplayDpi);
+
+ final Theme systemTheme = getSystemContext().getTheme();
+ if ((systemTheme.getChangingConfigurations() & configDiff) != 0) {
+ systemTheme.rebase();
+ }
}
ArrayList<ComponentCallbacks2> callbacks = collectComponentCallbacks(false, config);
diff --git a/core/java/android/content/res/AssetManager.java b/core/java/android/content/res/AssetManager.java
index 525059f..8d96f5c 100644
--- a/core/java/android/content/res/AssetManager.java
+++ b/core/java/android/content/res/AssetManager.java
@@ -785,6 +785,7 @@ public final class AssetManager implements AutoCloseable {
private native final void deleteTheme(long theme);
/*package*/ native static final void applyThemeStyle(long theme, int styleRes, boolean force);
/*package*/ native static final void copyTheme(long dest, long source);
+ /*package*/ native static final void clearTheme(long theme);
/*package*/ native static final int loadThemeAttributeValue(long theme, int ident,
TypedValue outValue,
boolean resolve);
diff --git a/core/java/android/content/res/ConfigurationBoundResourceCache.java b/core/java/android/content/res/ConfigurationBoundResourceCache.java
index cde7e84..fecda87 100644
--- a/core/java/android/content/res/ConfigurationBoundResourceCache.java
+++ b/core/java/android/content/res/ConfigurationBoundResourceCache.java
@@ -1,138 +1,58 @@
/*
-* Copyright (C) 2014 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 android.content.res;
+ * Copyright (C) 2014 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.
+ */
-import android.util.ArrayMap;
-import android.util.LongSparseArray;
-import java.lang.ref.WeakReference;
+package android.content.res;
/**
* A Cache class which can be used to cache resource objects that are easy to clone but more
* expensive to inflate.
- * @hide
+ *
+ * @hide For internal use only.
*/
-public class ConfigurationBoundResourceCache<T> {
-
- private final ArrayMap<String, LongSparseArray<WeakReference<ConstantState<T>>>> mCache =
- new ArrayMap<String, LongSparseArray<WeakReference<ConstantState<T>>>>();
-
- final Resources mResources;
+public class ConfigurationBoundResourceCache<T> extends ThemedResourceCache<ConstantState<T>> {
+ private final Resources mResources;
/**
- * Creates a Resource cache for the given Resources instance.
+ * Creates a cache for the given Resources instance.
*
- * @param resources The Resource which can be used when creating new instances.
+ * @param resources the resources to use when creating new instances
*/
public ConfigurationBoundResourceCache(Resources resources) {
mResources = resources;
}
/**
- * Adds a new item to the cache.
- *
- * @param key A custom key that uniquely identifies the resource.
- * @param theme The Theme instance where this resource was loaded.
- * @param constantState The constant state that can create new instances of the resource.
+ * If the resource is cached, creates and returns a new instance of it.
*
+ * @param key a key that uniquely identifies the drawable resource
+ * @param theme the theme where the resource will be used
+ * @return a new instance of the resource, or {@code null} if not in
+ * the cache
*/
- public void put(long key, Resources.Theme theme, ConstantState<T> constantState) {
- if (constantState == null) {
- return;
- }
- final String themeKey = theme == null ? "" : theme.getKey();
- LongSparseArray<WeakReference<ConstantState<T>>> themedCache;
- synchronized (this) {
- themedCache = mCache.get(themeKey);
- if (themedCache == null) {
- themedCache = new LongSparseArray<WeakReference<ConstantState<T>>>(1);
- mCache.put(themeKey, themedCache);
- }
- themedCache.put(key, new WeakReference<ConstantState<T>>(constantState));
- }
- }
-
- /**
- * If the resource is cached, creates a new instance of it and returns.
- *
- * @param key The long key which can be used to uniquely identify the resource.
- * @param theme The The Theme instance where we want to load this resource.
- *
- * @return If this resources was loaded before, returns a new instance of it. Otherwise, returns
- * null.
- */
- public T get(long key, Resources.Theme theme) {
- final String themeKey = theme != null ? theme.getKey() : "";
- final LongSparseArray<WeakReference<ConstantState<T>>> themedCache;
- final WeakReference<ConstantState<T>> wr;
- synchronized (this) {
- themedCache = mCache.get(themeKey);
- if (themedCache == null) {
- return null;
- }
- wr = themedCache.get(key);
- }
- if (wr == null) {
- return null;
- }
- final ConstantState entry = wr.get();
+ public T getInstance(long key, Resources.Theme theme) {
+ final ConstantState<T> entry = get(key, theme);
if (entry != null) {
- return (T) entry.newInstance(mResources, theme);
- } else { // our entry has been purged
- synchronized (this) {
- // there is a potential race condition here where this entry may be put in
- // another thread. But we prefer it to minimize lock duration
- themedCache.delete(key);
- }
+ return entry.newInstance(mResources, theme);
}
- return null;
- }
- /**
- * Users of ConfigurationBoundResourceCache must call this method whenever a configuration
- * change happens. On this callback, the cache invalidates all resources that are not valid
- * anymore.
- *
- * @param configChanges The configuration changes
- */
- public void onConfigurationChange(final int configChanges) {
- synchronized (this) {
- final int size = mCache.size();
- for (int i = size - 1; i >= 0; i--) {
- final LongSparseArray<WeakReference<ConstantState<T>>>
- themeCache = mCache.valueAt(i);
- onConfigurationChangeInt(themeCache, configChanges);
- if (themeCache.size() == 0) {
- mCache.removeAt(i);
- }
- }
- }
+ return null;
}
- private void onConfigurationChangeInt(
- final LongSparseArray<WeakReference<ConstantState<T>>> themeCache,
- final int configChanges) {
- final int size = themeCache.size();
- for (int i = size - 1; i >= 0; i--) {
- final WeakReference<ConstantState<T>> wr = themeCache.valueAt(i);
- final ConstantState<T> constantState = wr.get();
- if (constantState == null || Configuration.needNewResources(
- configChanges, constantState.getChangingConfigurations())) {
- themeCache.removeAt(i);
- }
- }
+ @Override
+ public boolean shouldInvalidateEntry(ConstantState<T> entry, int configChanges) {
+ return Configuration.needNewResources(configChanges, entry.getChangingConfigurations());
}
-
}
diff --git a/core/java/android/content/res/DrawableCache.java b/core/java/android/content/res/DrawableCache.java
new file mode 100644
index 0000000..fc70bc6
--- /dev/null
+++ b/core/java/android/content/res/DrawableCache.java
@@ -0,0 +1,57 @@
+/*
+ * 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 android.content.res;
+
+import android.graphics.drawable.Drawable;
+
+/**
+ * Class which can be used to cache Drawable resources against a theme.
+ */
+class DrawableCache extends ThemedResourceCache<Drawable.ConstantState> {
+ private final Resources mResources;
+
+ /**
+ * Creates a cache for the given Resources instance.
+ *
+ * @param resources the resources to use when creating new instances
+ */
+ public DrawableCache(Resources resources) {
+ mResources = resources;
+ }
+
+ /**
+ * If the resource is cached, creates and returns a new instance of it.
+ *
+ * @param key a key that uniquely identifies the drawable resource
+ * @param theme the theme where the resource will be used
+ * @return a new instance of the resource, or {@code null} if not in
+ * the cache
+ */
+ public Drawable getInstance(long key, Resources.Theme theme) {
+ final Drawable.ConstantState entry = get(key, theme);
+ if (entry != null) {
+ return entry.newDrawable(mResources, theme);
+ }
+
+ return null;
+ }
+
+ @Override
+ public boolean shouldInvalidateEntry(Drawable.ConstantState entry, int configChanges) {
+ return false;
+ }
+}
diff --git a/core/java/android/content/res/Resources.java b/core/java/android/content/res/Resources.java
index ae41b69..1d108a2 100644
--- a/core/java/android/content/res/Resources.java
+++ b/core/java/android/content/res/Resources.java
@@ -20,6 +20,8 @@ import android.annotation.AttrRes;
import android.annotation.ColorInt;
import android.annotation.StyleRes;
import android.annotation.StyleableRes;
+
+import com.android.internal.util.GrowingArrayUtils;
import com.android.internal.util.XmlUtils;
import org.xmlpull.v1.XmlPullParser;
@@ -132,10 +134,8 @@ public class Resources {
// These are protected by mAccessLock.
private final Object mAccessLock = new Object();
private final Configuration mTmpConfig = new Configuration();
- private final ArrayMap<String, LongSparseArray<WeakReference<ConstantState>>> mDrawableCache =
- new ArrayMap<>();
- private final ArrayMap<String, LongSparseArray<WeakReference<ConstantState>>> mColorDrawableCache =
- new ArrayMap<>();
+ private final DrawableCache mDrawableCache = new DrawableCache(this);
+ private final DrawableCache mColorDrawableCache = new DrawableCache(this);
private final ConfigurationBoundResourceCache<ColorStateList> mColorStateListCache =
new ConfigurationBoundResourceCache<>(this);
private final ConfigurationBoundResourceCache<Animator> mAnimatorCache =
@@ -1441,7 +1441,7 @@ public class Resources {
AssetManager.applyThemeStyle(mTheme, resId, force);
mThemeResId = resId;
- mKey += Integer.toHexString(resId) + (force ? "! " : " ");
+ mKey.append(resId, force);
}
/**
@@ -1457,7 +1457,7 @@ public class Resources {
AssetManager.copyTheme(mTheme, other.mTheme);
mThemeResId = other.mThemeResId;
- mKey = other.mKey;
+ mKey.setTo(other.getKey());
}
/**
@@ -1765,6 +1765,9 @@ public class Resources {
mTheme = mAssets.createTheme();
}
+ /** Unique key for the series of styles applied to this theme. */
+ private final ThemeKey mKey = new ThemeKey();
+
@SuppressWarnings("hiding")
private final AssetManager mAssets;
private final long mTheme;
@@ -1772,9 +1775,6 @@ public class Resources {
/** Resource identifier for the theme. */
private int mThemeResId = 0;
- /** Unique key for the series of styles applied to this theme. */
- private String mKey = "";
-
// Needed by layoutlib.
/*package*/ long getNativeTheme() {
return mTheme;
@@ -1784,7 +1784,7 @@ public class Resources {
return mThemeResId;
}
- /*package*/ String getKey() {
+ /*package*/ ThemeKey getKey() {
return mKey;
}
@@ -1793,29 +1793,119 @@ public class Resources {
}
/**
- * Parses {@link #mKey} and returns a String array that holds pairs of adjacent Theme data:
- * resource name followed by whether or not it was forced, as specified by
- * {@link #applyStyle(int, boolean)}.
+ * Parses {@link #mKey} and returns a String array that holds pairs of
+ * adjacent Theme data: resource name followed by whether or not it was
+ * forced, as specified by {@link #applyStyle(int, boolean)}.
*
* @hide
*/
@ViewDebug.ExportedProperty(category = "theme", hasAdjacentMapping = true)
public String[] getTheme() {
- String[] themeData = mKey.split(" ");
- String[] themes = new String[themeData.length * 2];
- String theme;
- boolean forced;
-
- for (int i = 0, j = themeData.length - 1; i < themes.length; i += 2, --j) {
- theme = themeData[j];
- forced = theme.endsWith("!");
- themes[i] = forced ?
- getResourceNameFromHexString(theme.substring(0, theme.length() - 1)) :
- getResourceNameFromHexString(theme);
+ final int N = mKey.mCount;
+ final String[] themes = new String[N * 2];
+ for (int i = 0, j = N - 1; i < themes.length; i += 2, --j) {
+ final int resId = mKey.mResId[i];
+ final boolean forced = mKey.mForce[i];
+ themes[i] = getResourceName(resId);
themes[i + 1] = forced ? "forced" : "not forced";
}
return themes;
}
+
+ /**
+ * Rebases the theme against the parent Resource object's current
+ * configuration by re-applying the styles passed to
+ * {@link #applyStyle(int, boolean)}.
+ *
+ * @hide
+ */
+ public void rebase() {
+ AssetManager.clearTheme(mTheme);
+
+ // Reapply the same styles in the same order.
+ for (int i = 0; i < mKey.mCount; i++) {
+ final int resId = mKey.mResId[i];
+ final boolean force = mKey.mForce[i];
+ AssetManager.applyThemeStyle(mTheme, resId, force);
+ }
+ }
+ }
+
+ static class ThemeKey implements Cloneable {
+ int[] mResId;
+ boolean[] mForce;
+ int mCount;
+
+ private int mHashCode = 0;
+
+ public void append(int resId, boolean force) {
+ if (mResId == null) {
+ mResId = new int[4];
+ }
+
+ if (mForce == null) {
+ mForce = new boolean[4];
+ }
+
+ mResId = GrowingArrayUtils.append(mResId, mCount, resId);
+ mForce = GrowingArrayUtils.append(mForce, mCount, force);
+ mCount++;
+
+ mHashCode = 31 * (31 * mHashCode + resId) + (force ? 1 : 0);
+ }
+
+ /**
+ * Sets up this key as a deep copy of another key.
+ *
+ * @param other the key to deep copy into this key
+ */
+ public void setTo(ThemeKey other) {
+ mResId = other.mResId == null ? null : other.mResId.clone();
+ mForce = other.mForce == null ? null : other.mForce.clone();
+ mCount = other.mCount;
+ }
+
+ @Override
+ public int hashCode() {
+ return mHashCode;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+
+ if (o == null || getClass() != o.getClass() || hashCode() != o.hashCode()) {
+ return false;
+ }
+
+ final ThemeKey t = (ThemeKey) o;
+ if (mCount != t.mCount) {
+ return false;
+ }
+
+ final int N = mCount;
+ for (int i = 0; i < N; i++) {
+ if (mResId[i] != t.mResId[i] || mForce[i] != t.mForce[i]) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * @return a shallow copy of this key
+ */
+ @Override
+ public ThemeKey clone() {
+ final ThemeKey other = new ThemeKey();
+ other.mResId = mResId;
+ other.mForce = mForce;
+ other.mCount = mCount;
+ return other;
+ }
}
/**
@@ -1944,8 +2034,8 @@ public class Resources {
+ " final compat is " + mCompatibilityInfo);
}
- clearDrawableCachesLocked(mDrawableCache, configChanges);
- clearDrawableCachesLocked(mColorDrawableCache, configChanges);
+ mDrawableCache.onConfigurationChange(configChanges);
+ mColorDrawableCache.onConfigurationChange(configChanges);
mColorStateListCache.onConfigurationChange(configChanges);
mAnimatorCache.onConfigurationChange(configChanges);
mStateListAnimatorCache.onConfigurationChange(configChanges);
@@ -1983,48 +2073,6 @@ public class Resources {
return configChanges;
}
- private void clearDrawableCachesLocked(
- ArrayMap<String, LongSparseArray<WeakReference<ConstantState>>> caches,
- int configChanges) {
- final int N = caches.size();
- for (int i = 0; i < N; i++) {
- clearDrawableCacheLocked(caches.valueAt(i), configChanges);
- }
- }
-
- private void clearDrawableCacheLocked(
- LongSparseArray<WeakReference<ConstantState>> cache, int configChanges) {
- if (DEBUG_CONFIG) {
- Log.d(TAG, "Cleaning up drawables config changes: 0x"
- + Integer.toHexString(configChanges));
- }
- final int N = cache.size();
- for (int i = 0; i < N; i++) {
- final WeakReference<ConstantState> ref = cache.valueAt(i);
- if (ref != null) {
- final ConstantState cs = ref.get();
- if (cs != null) {
- if (Configuration.needNewResources(
- configChanges, cs.getChangingConfigurations())) {
- if (DEBUG_CONFIG) {
- Log.d(TAG, "FLUSHING #0x"
- + Long.toHexString(cache.keyAt(i))
- + " / " + cs + " with changes: 0x"
- + Integer.toHexString(cs.getChangingConfigurations()));
- }
- cache.setValueAt(i, null);
- } else if (DEBUG_CONFIG) {
- Log.d(TAG, "(Keeping #0x"
- + Long.toHexString(cache.keyAt(i))
- + " / " + cs + " with changes: 0x"
- + Integer.toHexString(cs.getChangingConfigurations())
- + ")");
- }
- }
- }
- }
- }
-
/**
* {@code Locale.toLanguageTag} will transform the obsolete (and deprecated)
* language codes "in", "ji" and "iw" to "id", "yi" and "he" respectively.
@@ -2436,7 +2484,7 @@ public class Resources {
}
final boolean isColorDrawable;
- final ArrayMap<String, LongSparseArray<WeakReference<ConstantState>>> caches;
+ final DrawableCache caches;
final long key;
if (value.type >= TypedValue.TYPE_FIRST_COLOR_INT
&& value.type <= TypedValue.TYPE_LAST_COLOR_INT) {
@@ -2452,7 +2500,7 @@ public class Resources {
// First, check whether we have a cached version of this drawable
// that was inflated against the specified theme.
if (!mPreloading) {
- final Drawable cachedDrawable = getCachedDrawable(caches, key, theme);
+ final Drawable cachedDrawable = caches.getInstance(key, theme);
if (cachedDrawable != null) {
return cachedDrawable;
}
@@ -2479,13 +2527,8 @@ public class Resources {
// Determine if the drawable has unresolved theme attributes. If it
// does, we'll need to apply a theme and store it in a theme-specific
// cache.
- final String cacheKey;
- if (!dr.canApplyTheme()) {
- cacheKey = CACHE_NOT_THEMED;
- } else if (theme == null) {
- cacheKey = CACHE_NULL_THEME;
- } else {
- cacheKey = theme.getKey();
+ final boolean canApplyTheme = dr.canApplyTheme();
+ if (canApplyTheme && theme != null) {
dr = dr.mutate();
dr.applyTheme(theme);
dr.clearMutated();
@@ -2495,15 +2538,14 @@ public class Resources {
// cache: preload, not themed, null theme, or theme-specific.
if (dr != null) {
dr.setChangingConfigurations(value.changingConfigurations);
- cacheDrawable(value, isColorDrawable, caches, cacheKey, key, dr);
+ cacheDrawable(value, isColorDrawable, caches, theme, canApplyTheme, key, dr);
}
return dr;
}
- private void cacheDrawable(TypedValue value, boolean isColorDrawable,
- ArrayMap<String, LongSparseArray<WeakReference<ConstantState>>> caches,
- String cacheKey, long key, Drawable dr) {
+ private void cacheDrawable(TypedValue value, boolean isColorDrawable, DrawableCache caches,
+ Theme theme, boolean usesTheme, long key, Drawable dr) {
final ConstantState cs = dr.getConstantState();
if (cs == null) {
return;
@@ -2531,51 +2573,9 @@ public class Resources {
}
} else {
synchronized (mAccessLock) {
- LongSparseArray<WeakReference<ConstantState>> themedCache = caches.get(cacheKey);
- if (themedCache == null) {
- // Clean out the caches before we add more. This shouldn't
- // happen very often.
- pruneCaches(caches);
- themedCache = new LongSparseArray<>(1);
- caches.put(cacheKey, themedCache);
- }
- themedCache.put(key, new WeakReference<>(cs));
- }
- }
- }
-
- /**
- * Prunes empty caches from the cache map.
- *
- * @param caches The map of caches to prune.
- */
- private void pruneCaches(ArrayMap<String,
- LongSparseArray<WeakReference<ConstantState>>> caches) {
- final int N = caches.size();
- for (int i = N - 1; i >= 0; i--) {
- final LongSparseArray<WeakReference<ConstantState>> cache = caches.valueAt(i);
- if (pruneCache(cache)) {
- caches.removeAt(i);
- }
- }
- }
-
- /**
- * Prunes obsolete weak references from a cache, returning {@code true} if
- * the cache is empty and should be removed.
- *
- * @param cache The cache of weak references to prune.
- * @return {@code true} if the cache is empty and should be removed.
- */
- private boolean pruneCache(LongSparseArray<WeakReference<ConstantState>> cache) {
- final int N = cache.size();
- for (int i = N - 1; i >= 0; i--) {
- final WeakReference entry = cache.valueAt(i);
- if (entry == null || entry.get() == null) {
- cache.removeAt(i);
+ caches.put(key, theme, cs, usesTheme);
}
}
- return cache.size() == 0;
}
/**
@@ -2631,51 +2631,6 @@ public class Resources {
return dr;
}
- private Drawable getCachedDrawable(
- ArrayMap<String, LongSparseArray<WeakReference<ConstantState>>> caches,
- long key, Theme theme) {
- synchronized (mAccessLock) {
- // First search theme-agnostic cache.
- final Drawable unthemedDrawable = getCachedDrawableLocked(
- caches, key, CACHE_NOT_THEMED);
- if (unthemedDrawable != null) {
- return unthemedDrawable;
- }
-
- // Next search theme-specific cache.
- final String themeKey = theme != null ? theme.getKey() : CACHE_NULL_THEME;
- return getCachedDrawableLocked(caches, key, themeKey);
- }
- }
-
- private Drawable getCachedDrawableLocked(
- ArrayMap<String, LongSparseArray<WeakReference<ConstantState>>> caches,
- long key, String themeKey) {
- final LongSparseArray<WeakReference<ConstantState>> cache = caches.get(themeKey);
- if (cache != null) {
- final ConstantState entry = getConstantStateLocked(cache, key);
- if (entry != null) {
- return entry.newDrawable(this);
- }
- }
- return null;
- }
-
- private ConstantState getConstantStateLocked(
- LongSparseArray<WeakReference<ConstantState>> drawableCache, long key) {
- final WeakReference<ConstantState> wr = drawableCache.get(key);
- if (wr != null) {
- final ConstantState entry = wr.get();
- if (entry != null) {
- return entry;
- } else {
- // Our entry has been purged.
- drawableCache.delete(key);
- }
- }
- return null;
- }
-
@Nullable
ColorStateList loadColorStateList(TypedValue value, int id, Theme theme)
throws NotFoundException {
@@ -2713,8 +2668,7 @@ public class Resources {
}
final ConfigurationBoundResourceCache<ColorStateList> cache = mColorStateListCache;
-
- csl = cache.get(key, theme);
+ csl = cache.getInstance(key, theme);
if (csl != null) {
return csl;
}
diff --git a/core/java/android/content/res/ThemedResourceCache.java b/core/java/android/content/res/ThemedResourceCache.java
new file mode 100644
index 0000000..9a2d06147
--- /dev/null
+++ b/core/java/android/content/res/ThemedResourceCache.java
@@ -0,0 +1,233 @@
+/*
+ * 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 android.content.res;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.res.Resources.Theme;
+import android.content.res.Resources.ThemeKey;
+import android.util.LongSparseArray;
+import android.util.ArrayMap;
+
+import java.lang.ref.WeakReference;
+
+/**
+ * Data structure used for caching data against themes.
+ *
+ * @param <T> type of data to cache
+ */
+abstract class ThemedResourceCache<T> {
+ private ArrayMap<ThemeKey, LongSparseArray<WeakReference<T>>> mThemedEntries;
+ private LongSparseArray<WeakReference<T>> mUnthemedEntries;
+ private LongSparseArray<WeakReference<T>> mNullThemedEntries;
+
+ /**
+ * Adds a new theme-dependent entry to the cache.
+ *
+ * @param key a key that uniquely identifies the entry
+ * @param theme the theme against which this entry was inflated, or
+ * {@code null} if the entry has no theme applied
+ * @param entry the entry to cache
+ */
+ public void put(long key, @Nullable Theme theme, @NonNull T entry) {
+ put(key, theme, entry, true);
+ }
+
+ /**
+ * Adds a new entry to the cache.
+ *
+ * @param key a key that uniquely identifies the entry
+ * @param theme the theme against which this entry was inflated, or
+ * {@code null} if the entry has no theme applied
+ * @param entry the entry to cache
+ * @param usesTheme {@code true} if the entry is affected theme changes,
+ * {@code false} otherwise
+ */
+ public void put(long key, @Nullable Theme theme, @NonNull T entry, boolean usesTheme) {
+ if (entry == null) {
+ return;
+ }
+
+ synchronized (this) {
+ final LongSparseArray<WeakReference<T>> entries;
+ if (!usesTheme) {
+ entries = getUnthemedLocked(true);
+ } else {
+ entries = getThemedLocked(theme, true);
+ }
+ if (entries != null) {
+ entries.put(key, new WeakReference<>(entry));
+ }
+ }
+ }
+
+ /**
+ * Returns an entry from the cache.
+ *
+ * @param key a key that uniquely identifies the entry
+ * @param theme the theme where the entry will be used
+ * @return a cached entry, or {@code null} if not in the cache
+ */
+ @Nullable
+ public T get(long key, @Nullable Theme theme) {
+ // The themed (includes null-themed) and unthemed caches are mutually
+ // exclusive, so we'll give priority to whichever one we think we'll
+ // hit first. Since most of the framework drawables are themed, that's
+ // probably going to be the themed cache.
+ synchronized (this) {
+ final LongSparseArray<WeakReference<T>> themedEntries = getThemedLocked(theme, false);
+ if (themedEntries != null) {
+ final WeakReference<T> themedEntry = themedEntries.get(key);
+ if (themedEntry != null) {
+ return themedEntry.get();
+ }
+ }
+
+ final LongSparseArray<WeakReference<T>> unthemedEntries = getUnthemedLocked(false);
+ if (unthemedEntries != null) {
+ final WeakReference<T> unthemedEntry = unthemedEntries.get(key);
+ if (unthemedEntry != null) {
+ return unthemedEntry.get();
+ }
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * Prunes cache entries that have been invalidated by a configuration
+ * change.
+ *
+ * @param configChanges a bitmask of configuration changes
+ */
+ public void onConfigurationChange(int configChanges) {
+ prune(configChanges);
+ }
+
+ /**
+ * Returns whether a cached entry has been invalidated by a configuration
+ * change.
+ *
+ * @param entry a cached entry
+ * @param configChanges a non-zero bitmask of configuration changes
+ * @return {@code true} if the entry is invalid, {@code false} otherwise
+ */
+ protected abstract boolean shouldInvalidateEntry(@NonNull T entry, int configChanges);
+
+ /**
+ * Returns the cached data for the specified theme, optionally creating a
+ * new entry if one does not already exist.
+ *
+ * @param t the theme for which to return cached data
+ * @param create {@code true} to create an entry if one does not already
+ * exist, {@code false} otherwise
+ * @return the cached data for the theme, or {@code null} if the cache is
+ * empty and {@code create} was {@code false}
+ */
+ @Nullable
+ private LongSparseArray<WeakReference<T>> getThemedLocked(@Nullable Theme t, boolean create) {
+ if (t == null) {
+ if (mNullThemedEntries == null && create) {
+ mNullThemedEntries = new LongSparseArray<>(1);
+ }
+ return mNullThemedEntries;
+ }
+
+ if (mThemedEntries == null) {
+ if (create) {
+ mThemedEntries = new ArrayMap<>(1);
+ } else {
+ return null;
+ }
+ }
+
+ final ThemeKey key = t.getKey();
+ LongSparseArray<WeakReference<T>> cache = mThemedEntries.get(key);
+ if (cache == null && create) {
+ cache = new LongSparseArray<>(1);
+
+ final ThemeKey keyClone = key.clone();
+ mThemedEntries.put(keyClone, cache);
+ }
+
+ return cache;
+ }
+
+ /**
+ * Returns the theme-agnostic cached data.
+ *
+ * @param create {@code true} to create an entry if one does not already
+ * exist, {@code false} otherwise
+ * @return the theme-agnostic cached data, or {@code null} if the cache is
+ * empty and {@code create} was {@code false}
+ */
+ @Nullable
+ private LongSparseArray<WeakReference<T>> getUnthemedLocked(boolean create) {
+ if (mUnthemedEntries == null && create) {
+ mUnthemedEntries = new LongSparseArray<>(1);
+ }
+ return mUnthemedEntries;
+ }
+
+ /**
+ * Prunes cache entries affected by configuration changes or where weak
+ * references have expired.
+ *
+ * @param configChanges a bitmask of configuration changes, or {@code 0} to
+ * simply prune missing weak references
+ * @return {@code true} if the cache is completely empty after pruning
+ */
+ private boolean prune(int configChanges) {
+ synchronized (this) {
+ if (mThemedEntries != null) {
+ for (int i = mThemedEntries.size() - 1; i >= 0; i--) {
+ if (pruneEntriesLocked(mThemedEntries.valueAt(i), configChanges)) {
+ mThemedEntries.removeAt(i);
+ }
+ }
+ }
+
+ pruneEntriesLocked(mNullThemedEntries, configChanges);
+ pruneEntriesLocked(mUnthemedEntries, configChanges);
+
+ return mThemedEntries == null && mNullThemedEntries == null
+ && mUnthemedEntries == null;
+ }
+ }
+
+ private boolean pruneEntriesLocked(@Nullable LongSparseArray<WeakReference<T>> entries,
+ int configChanges) {
+ if (entries == null) {
+ return true;
+ }
+
+ for (int i = entries.size() - 1; i >= 0; i--) {
+ final WeakReference<T> ref = entries.valueAt(i);
+ if (ref == null || pruneEntryLocked(ref.get(), configChanges)) {
+ entries.removeAt(i);
+ }
+ }
+
+ return entries.size() == 0;
+ }
+
+ private boolean pruneEntryLocked(@Nullable T entry, int configChanges) {
+ return entry == null || (configChanges != 0
+ && shouldInvalidateEntry(entry, configChanges));
+ }
+}
diff --git a/core/jni/android_util_AssetManager.cpp b/core/jni/android_util_AssetManager.cpp
index db495dd..74a9e4e 100644
--- a/core/jni/android_util_AssetManager.cpp
+++ b/core/jni/android_util_AssetManager.cpp
@@ -976,6 +976,12 @@ static void android_content_AssetManager_copyTheme(JNIEnv* env, jobject clazz,
dest->setTo(*src);
}
+static void android_content_AssetManager_clearTheme(JNIEnv* env, jobject clazz, jlong themeHandle)
+{
+ ResTable::Theme* theme = reinterpret_cast<ResTable::Theme*>(themeHandle);
+ theme->clear();
+}
+
static jint android_content_AssetManager_loadThemeAttributeValue(
JNIEnv* env, jobject clazz, jlong themeHandle, jint ident, jobject outValue, jboolean resolve)
{
@@ -2108,6 +2114,8 @@ static JNINativeMethod gAssetManagerMethods[] = {
(void*) android_content_AssetManager_applyThemeStyle },
{ "copyTheme", "(JJ)V",
(void*) android_content_AssetManager_copyTheme },
+ { "clearTheme", "(J)V",
+ (void*) android_content_AssetManager_clearTheme },
{ "loadThemeAttributeValue", "(JILandroid/util/TypedValue;Z)I",
(void*) android_content_AssetManager_loadThemeAttributeValue },
{ "getThemeChangingConfigurations", "(J)I",
diff --git a/include/androidfw/ResourceTypes.h b/include/androidfw/ResourceTypes.h
index 587e7fa..b15caeb 100644
--- a/include/androidfw/ResourceTypes.h
+++ b/include/androidfw/ResourceTypes.h
@@ -1631,6 +1631,7 @@ public:
status_t applyStyle(uint32_t resID, bool force=false);
status_t setTo(const Theme& other);
+ status_t clear();
/**
* Retrieve a value in the theme. If the theme defines this
diff --git a/libs/androidfw/ResourceTypes.cpp b/libs/androidfw/ResourceTypes.cpp
index 19a5beb..2ae7b08 100644
--- a/libs/androidfw/ResourceTypes.cpp
+++ b/libs/androidfw/ResourceTypes.cpp
@@ -3336,6 +3336,30 @@ status_t ResTable::Theme::setTo(const Theme& other)
return NO_ERROR;
}
+status_t ResTable::Theme::clear()
+{
+ if (kDebugTableTheme) {
+ ALOGI("Clearing theme %p...\n", this);
+ dumpToLog();
+ }
+
+ for (size_t i = 0; i < Res_MAXPACKAGE; i++) {
+ if (mPackages[i] != NULL) {
+ free_package(mPackages[i]);
+ mPackages[i] = NULL;
+ }
+ }
+
+ mTypeSpecFlags = 0;
+
+ if (kDebugTableTheme) {
+ ALOGI("Final theme:");
+ dumpToLog();
+ }
+
+ return NO_ERROR;
+}
+
ssize_t ResTable::Theme::getAttribute(uint32_t resID, Res_value* outValue,
uint32_t* outTypeSpecFlags) const
{
diff --git a/services/core/java/com/android/server/policy/GlobalActions.java b/services/core/java/com/android/server/policy/GlobalActions.java
index b431b33..3cee927 100644
--- a/services/core/java/com/android/server/policy/GlobalActions.java
+++ b/services/core/java/com/android/server/policy/GlobalActions.java
@@ -1138,7 +1138,7 @@ class GlobalActions implements DialogInterface.OnDismissListener, DialogInterfac
public GlobalActionsDialog(Context context, AlertParams params) {
super(context, getDialogTheme(context));
- mContext = context;
+ mContext = getContext();
mAlert = new AlertController(mContext, this, getWindow());
mAdapter = (MyAdapter) params.mAdapter;
mWindowTouchSlop = ViewConfiguration.get(context).getScaledWindowTouchSlop();
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 2d265e2..925a609 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -28,6 +28,7 @@ import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.res.Configuration;
+import android.content.res.Resources.Theme;
import android.os.Build;
import android.os.Environment;
import android.os.FactoryTest;
@@ -291,7 +292,7 @@ public final class SystemServer {
private void createSystemContext() {
ActivityThread activityThread = ActivityThread.systemMain();
mSystemContext = activityThread.getSystemContext();
- mSystemContext.setTheme(android.R.style.Theme_DeviceDefault_Light_DarkActionBar);
+ mSystemContext.setTheme(android.R.style.Theme_Material_DayNight_DarkActionBar);
}
/**
@@ -1026,6 +1027,12 @@ public final class SystemServer {
w.getDefaultDisplay().getMetrics(metrics);
context.getResources().updateConfiguration(config, metrics);
+ // The system context's theme may be configuration-dependent.
+ final Theme systemTheme = context.getTheme();
+ if (systemTheme.getChangingConfigurations() != 0) {
+ systemTheme.rebase();
+ }
+
try {
// TODO: use boot phase
mPowerManagerService.systemReady(mActivityManagerService.getAppOpsService());