Merge "Allow multiple Resources associated with an Activity" into nyc-dev
am: d17f96a
* commit 'd17f96ae007179007765c0f75b6bffb13c9285cb':
Allow multiple Resources associated with an Activity
Change-Id: I34dd24af289d3384deb72cf66ba076b2f7cfd2e8
This commit is contained in:
@@ -1993,7 +1993,7 @@ class ContextImpl extends Context {
|
||||
ContextImpl context = new ContextImpl(null, mainThread,
|
||||
packageInfo, null, null, 0, null, null, Display.INVALID_DISPLAY);
|
||||
context.mResources.updateConfiguration(context.mResourcesManager.getConfiguration(),
|
||||
context.mResourcesManager.getDisplayMetricsLocked());
|
||||
context.mResourcesManager.getDisplayMetrics());
|
||||
return context;
|
||||
}
|
||||
|
||||
@@ -2065,16 +2065,34 @@ class ContextImpl extends Context {
|
||||
|| overrideConfiguration != null
|
||||
|| (compatInfo != null && compatInfo.applicationScale
|
||||
!= resources.getCompatibilityInfo().applicationScale)) {
|
||||
resources = mResourcesManager.getResources(
|
||||
activityToken,
|
||||
packageInfo.getResDir(),
|
||||
packageInfo.getSplitResDirs(),
|
||||
packageInfo.getOverlayDirs(),
|
||||
packageInfo.getApplicationInfo().sharedLibraryFiles,
|
||||
displayId,
|
||||
overrideConfiguration,
|
||||
compatInfo,
|
||||
packageInfo.getClassLoader());
|
||||
|
||||
if (container != null) {
|
||||
// This is a nested Context, so it can't be a base Activity context.
|
||||
// Just create a regular Resources object associated with the Activity.
|
||||
resources = mResourcesManager.getResources(
|
||||
activityToken,
|
||||
packageInfo.getResDir(),
|
||||
packageInfo.getSplitResDirs(),
|
||||
packageInfo.getOverlayDirs(),
|
||||
packageInfo.getApplicationInfo().sharedLibraryFiles,
|
||||
displayId,
|
||||
overrideConfiguration,
|
||||
compatInfo,
|
||||
packageInfo.getClassLoader());
|
||||
} else {
|
||||
// This is not a nested Context, so it must be the root Activity context.
|
||||
// All other nested Contexts will inherit the configuration set here.
|
||||
resources = mResourcesManager.createBaseActivityResources(
|
||||
activityToken,
|
||||
packageInfo.getResDir(),
|
||||
packageInfo.getSplitResDirs(),
|
||||
packageInfo.getOverlayDirs(),
|
||||
packageInfo.getApplicationInfo().sharedLibraryFiles,
|
||||
displayId,
|
||||
overrideConfiguration,
|
||||
compatInfo,
|
||||
packageInfo.getClassLoader());
|
||||
}
|
||||
}
|
||||
}
|
||||
mResources = resources;
|
||||
|
||||
@@ -94,9 +94,18 @@ public class ResourcesManager {
|
||||
private final ArrayList<WeakReference<Resources>> mResourceReferences = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* Each Activity may have only one Resources object.
|
||||
* Resources and base configuration override associated with an Activity.
|
||||
*/
|
||||
private final WeakHashMap<IBinder, WeakReference<Resources>> mActivityResourceReferences =
|
||||
private static class ActivityResources {
|
||||
public final Configuration overrideConfig = new Configuration();
|
||||
public final ArrayList<WeakReference<Resources>> activityResources = new ArrayList<>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Each Activity may has a base override configuration that is applied to each Resources object,
|
||||
* which in turn may have their own override configuration specified.
|
||||
*/
|
||||
private final WeakHashMap<IBinder, ActivityResources> mActivityResourceReferences =
|
||||
new WeakHashMap<>();
|
||||
|
||||
/**
|
||||
@@ -115,18 +124,20 @@ public class ResourcesManager {
|
||||
}
|
||||
|
||||
public Configuration getConfiguration() {
|
||||
return mResConfiguration;
|
||||
synchronized (this) {
|
||||
return mResConfiguration;
|
||||
}
|
||||
}
|
||||
|
||||
DisplayMetrics getDisplayMetricsLocked() {
|
||||
return getDisplayMetricsLocked(Display.DEFAULT_DISPLAY);
|
||||
DisplayMetrics getDisplayMetrics() {
|
||||
return getDisplayMetrics(Display.DEFAULT_DISPLAY);
|
||||
}
|
||||
|
||||
/**
|
||||
* Protected so that tests can override and returns something a fixed value.
|
||||
*/
|
||||
@VisibleForTesting
|
||||
protected DisplayMetrics getDisplayMetricsLocked(int displayId) {
|
||||
protected DisplayMetrics getDisplayMetrics(int displayId) {
|
||||
DisplayMetrics dm = new DisplayMetrics();
|
||||
final Display display =
|
||||
getAdjustedDisplay(displayId, DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS);
|
||||
@@ -272,10 +283,9 @@ public class ResourcesManager {
|
||||
return config;
|
||||
}
|
||||
|
||||
|
||||
private ResourcesImpl createResourcesImpl(@NonNull ResourcesKey key) {
|
||||
AssetManager assets = createAssetManager(key);
|
||||
DisplayMetrics dm = getDisplayMetricsLocked(key.mDisplayId);
|
||||
DisplayMetrics dm = getDisplayMetrics(key.mDisplayId);
|
||||
Configuration config = generateConfig(key, dm);
|
||||
ResourcesImpl impl = new ResourcesImpl(assets, dm, config, key.mCompatInfo);
|
||||
if (DEBUG) {
|
||||
@@ -290,7 +300,7 @@ public class ResourcesManager {
|
||||
* @param key The key to match.
|
||||
* @return a ResourcesImpl if the key matches a cache entry, null otherwise.
|
||||
*/
|
||||
private ResourcesImpl findResourcesImplForKey(@NonNull ResourcesKey key) {
|
||||
private ResourcesImpl findResourcesImplForKeyLocked(@NonNull ResourcesKey key) {
|
||||
WeakReference<ResourcesImpl> weakImplRef = mResourceImpls.get(key);
|
||||
ResourcesImpl impl = weakImplRef != null ? weakImplRef.get() : null;
|
||||
if (impl != null && impl.getAssets().isUpToDate()) {
|
||||
@@ -303,7 +313,7 @@ public class ResourcesManager {
|
||||
* Find the ResourcesKey that this ResourcesImpl object is associated with.
|
||||
* @return the ResourcesKey or null if none was found.
|
||||
*/
|
||||
private ResourcesKey findKeyForResourceImpl(@NonNull ResourcesImpl resourceImpl) {
|
||||
private ResourcesKey findKeyForResourceImplLocked(@NonNull ResourcesImpl resourceImpl) {
|
||||
final int refCount = mResourceImpls.size();
|
||||
for (int i = 0; i < refCount; i++) {
|
||||
WeakReference<ResourcesImpl> weakImplRef = mResourceImpls.valueAt(i);
|
||||
@@ -315,36 +325,46 @@ public class ResourcesManager {
|
||||
return null;
|
||||
}
|
||||
|
||||
private ActivityResources getOrCreateActivityResourcesStructLocked(
|
||||
@NonNull IBinder activityToken) {
|
||||
ActivityResources activityResources = mActivityResourceReferences.get(activityToken);
|
||||
if (activityResources == null) {
|
||||
activityResources = new ActivityResources();
|
||||
mActivityResourceReferences.put(activityToken, activityResources);
|
||||
}
|
||||
return activityResources;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an existing Resources object tied to this Activity, or creates one if it doesn't exist
|
||||
* or the class loader is different.
|
||||
*/
|
||||
private Resources getOrCreateResourcesForActivityLocked(@NonNull IBinder activityToken,
|
||||
@NonNull ClassLoader classLoader, @NonNull ResourcesImpl impl) {
|
||||
// This is a request tied to an Activity, meaning we will need to update all
|
||||
// Activity related Resources to match this configuration.
|
||||
WeakReference<Resources> weakResourceRef = mActivityResourceReferences.get(activityToken);
|
||||
Resources resources = weakResourceRef != null ? weakResourceRef.get() : null;
|
||||
if (resources == null || !Objects.equals(resources.getClassLoader(), classLoader)) {
|
||||
resources = new Resources(classLoader);
|
||||
mActivityResourceReferences.put(activityToken, new WeakReference<>(resources));
|
||||
if (DEBUG) {
|
||||
Slog.d(TAG, "- creating new ref=" + resources);
|
||||
}
|
||||
} else {
|
||||
if (DEBUG) {
|
||||
Slog.d(TAG, "- using existing ref=" + resources);
|
||||
final ActivityResources activityResources = getOrCreateActivityResourcesStructLocked(
|
||||
activityToken);
|
||||
|
||||
final int refCount = activityResources.activityResources.size();
|
||||
for (int i = 0; i < refCount; i++) {
|
||||
WeakReference<Resources> weakResourceRef = activityResources.activityResources.get(i);
|
||||
Resources resources = weakResourceRef.get();
|
||||
|
||||
if (resources != null
|
||||
&& Objects.equals(resources.getClassLoader(), classLoader)
|
||||
&& resources.getImpl() == impl) {
|
||||
if (DEBUG) {
|
||||
Slog.d(TAG, "- using existing ref=" + resources);
|
||||
}
|
||||
return resources;
|
||||
}
|
||||
}
|
||||
|
||||
if (resources.getImpl() != impl) {
|
||||
if (DEBUG) {
|
||||
Slog.d(TAG, "- setting ref=" + resources + " with impl=" + impl);
|
||||
}
|
||||
|
||||
// Setting an impl is expensive because we update all ThemeImpl references.
|
||||
// too.
|
||||
resources.setImpl(impl);
|
||||
Resources resources = new Resources(classLoader);
|
||||
resources.setImpl(impl);
|
||||
activityResources.activityResources.add(new WeakReference<>(resources));
|
||||
if (DEBUG) {
|
||||
Slog.d(TAG, "- creating new ref=" + resources);
|
||||
Slog.d(TAG, "- setting ref=" + resources + " with impl=" + impl);
|
||||
}
|
||||
return resources;
|
||||
}
|
||||
@@ -359,7 +379,7 @@ public class ResourcesManager {
|
||||
final int refCount = mResourceReferences.size();
|
||||
for (int i = 0; i < refCount; i++) {
|
||||
WeakReference<Resources> weakResourceRef = mResourceReferences.get(i);
|
||||
Resources resources = weakResourceRef != null ? weakResourceRef.get() : null;
|
||||
Resources resources = weakResourceRef.get();
|
||||
if (resources != null &&
|
||||
Objects.equals(resources.getClassLoader(), classLoader) &&
|
||||
resources.getImpl() == impl) {
|
||||
@@ -381,6 +401,177 @@ public class ResourcesManager {
|
||||
return resources;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates base resources for an Activity. Calls to
|
||||
* {@link #getResources(IBinder, String, String[], String[], String[], int, Configuration,
|
||||
* CompatibilityInfo, ClassLoader)} with the same activityToken will have their override
|
||||
* configurations merged with the one specified here.
|
||||
*
|
||||
* @param activityToken Represents an Activity.
|
||||
* @param resDir The base resource path. Can be null (only framework resources will be loaded).
|
||||
* @param splitResDirs An array of split resource paths. Can be null.
|
||||
* @param overlayDirs An array of overlay paths. Can be null.
|
||||
* @param libDirs An array of resource library paths. Can be null.
|
||||
* @param displayId The ID of the display for which to create the resources.
|
||||
* @param overrideConfig The configuration to apply on top of the base configuration. Can be
|
||||
* null. This provides the base override for this Activity.
|
||||
* @param compatInfo The compatibility settings to use. Cannot be null. A default to use is
|
||||
* {@link CompatibilityInfo#DEFAULT_COMPATIBILITY_INFO}.
|
||||
* @param classLoader The class loader to use when inflating Resources. If null, the
|
||||
* {@link ClassLoader#getSystemClassLoader()} is used.
|
||||
* @return a Resources object from which to access resources.
|
||||
*/
|
||||
public Resources createBaseActivityResources(@NonNull IBinder activityToken,
|
||||
@Nullable String resDir,
|
||||
@Nullable String[] splitResDirs,
|
||||
@Nullable String[] overlayDirs,
|
||||
@Nullable String[] libDirs,
|
||||
int displayId,
|
||||
@Nullable Configuration overrideConfig,
|
||||
@NonNull CompatibilityInfo compatInfo,
|
||||
@Nullable ClassLoader classLoader) {
|
||||
final ResourcesKey key = new ResourcesKey(
|
||||
resDir,
|
||||
splitResDirs,
|
||||
overlayDirs,
|
||||
libDirs,
|
||||
displayId,
|
||||
overrideConfig != null ? new Configuration(overrideConfig) : null, // Copy
|
||||
compatInfo);
|
||||
classLoader = classLoader != null ? classLoader : ClassLoader.getSystemClassLoader();
|
||||
|
||||
synchronized (this) {
|
||||
final ActivityResources activityResources = getOrCreateActivityResourcesStructLocked(
|
||||
activityToken);
|
||||
|
||||
if (overrideConfig != null) {
|
||||
activityResources.overrideConfig.setTo(overrideConfig);
|
||||
} else {
|
||||
activityResources.overrideConfig.setToDefaults();
|
||||
}
|
||||
}
|
||||
|
||||
// Update any existing Activity Resources references.
|
||||
updateResourcesForActivity(activityToken, overrideConfig);
|
||||
|
||||
// Now request an actual Resources object.
|
||||
return getOrCreateResources(activityToken, key, classLoader);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an existing Resources object set with a ResourcesImpl object matching the given key,
|
||||
* or creates one if it doesn't exist.
|
||||
*
|
||||
* @param activityToken The Activity this Resources object should be associated with.
|
||||
* @param key The key describing the parameters of the ResourcesImpl object.
|
||||
* @param classLoader The classloader to use for the Resources object.
|
||||
* If null, {@link ClassLoader#getSystemClassLoader()} is used.
|
||||
* @return A Resources object that gets updated when
|
||||
* {@link #applyConfigurationToResourcesLocked(Configuration, CompatibilityInfo)}
|
||||
* is called.
|
||||
*/
|
||||
private Resources getOrCreateResources(@Nullable IBinder activityToken,
|
||||
@NonNull ResourcesKey key, @NonNull ClassLoader classLoader) {
|
||||
final boolean findSystemLocales;
|
||||
final boolean hasNonSystemLocales;
|
||||
synchronized (this) {
|
||||
findSystemLocales = (mSystemLocales == null || mSystemLocales.length == 0);
|
||||
hasNonSystemLocales = mHasNonSystemLocales;
|
||||
|
||||
if (DEBUG) {
|
||||
Throwable here = new Throwable();
|
||||
here.fillInStackTrace();
|
||||
Slog.w(TAG, "!! Get resources for activity=" + activityToken + " key=" + key, here);
|
||||
}
|
||||
|
||||
if (activityToken != null) {
|
||||
final ActivityResources activityResources = getOrCreateActivityResourcesStructLocked(
|
||||
activityToken);
|
||||
|
||||
// Clean up any dead references so they don't pile up.
|
||||
ArrayUtils.unstableRemoveIf(activityResources.activityResources,
|
||||
sEmptyReferencePredicate);
|
||||
|
||||
// Rebase the key's override config on top of the Activity's base override.
|
||||
if (key.hasOverrideConfiguration()
|
||||
&& !activityResources.overrideConfig.equals(Configuration.EMPTY)) {
|
||||
final Configuration temp = new Configuration(activityResources.overrideConfig);
|
||||
temp.updateFrom(key.mOverrideConfiguration);
|
||||
key.mOverrideConfiguration.setTo(temp);
|
||||
}
|
||||
|
||||
ResourcesImpl resourcesImpl = findResourcesImplForKeyLocked(key);
|
||||
if (resourcesImpl != null) {
|
||||
if (DEBUG) {
|
||||
Slog.d(TAG, "- using existing impl=" + resourcesImpl);
|
||||
}
|
||||
return getOrCreateResourcesForActivityLocked(activityToken, classLoader,
|
||||
resourcesImpl);
|
||||
}
|
||||
|
||||
// We will create the ResourcesImpl object outside of holding this lock.
|
||||
|
||||
} else {
|
||||
// Clean up any dead references so they don't pile up.
|
||||
ArrayUtils.unstableRemoveIf(mResourceReferences, sEmptyReferencePredicate);
|
||||
|
||||
// Not tied to an Activity, find a shared Resources that has the right ResourcesImpl
|
||||
ResourcesImpl resourcesImpl = findResourcesImplForKeyLocked(key);
|
||||
if (resourcesImpl != null) {
|
||||
if (DEBUG) {
|
||||
Slog.d(TAG, "- using existing impl=" + resourcesImpl);
|
||||
}
|
||||
return getOrCreateResourcesLocked(classLoader, resourcesImpl);
|
||||
}
|
||||
|
||||
// We will create the ResourcesImpl object outside of holding this lock.
|
||||
}
|
||||
}
|
||||
|
||||
// If we're here, we didn't find a suitable ResourcesImpl to use, so create one now.
|
||||
ResourcesImpl resourcesImpl = createResourcesImpl(key);
|
||||
|
||||
final String[] systemLocales = findSystemLocales
|
||||
? AssetManager.getSystem().getLocales() : null;
|
||||
final String[] nonSystemLocales = resourcesImpl.getAssets().getNonSystemLocales();
|
||||
// Avoid checking for non-pseudo-locales if we already know there were some from a previous
|
||||
// Resources. The default value (for when hasNonSystemLocales is true) doesn't matter,
|
||||
// since mHasNonSystemLocales will also be true, and thus isPseudoLocalesOnly would not be
|
||||
// able to affect mHasNonSystemLocales.
|
||||
final boolean isPseudoLocalesOnly = hasNonSystemLocales ||
|
||||
LocaleList.isPseudoLocalesOnly(nonSystemLocales);
|
||||
|
||||
synchronized (this) {
|
||||
if (mSystemLocales == null || mSystemLocales.length == 0) {
|
||||
mSystemLocales = systemLocales;
|
||||
}
|
||||
mNonSystemLocales.addAll(Arrays.asList(nonSystemLocales));
|
||||
mHasNonSystemLocales = mHasNonSystemLocales || !isPseudoLocalesOnly;
|
||||
|
||||
ResourcesImpl existingResourcesImpl = findResourcesImplForKeyLocked(key);
|
||||
if (existingResourcesImpl != null) {
|
||||
if (DEBUG) {
|
||||
Slog.d(TAG, "- got beat! existing impl=" + existingResourcesImpl
|
||||
+ " new impl=" + resourcesImpl);
|
||||
}
|
||||
resourcesImpl.getAssets().close();
|
||||
resourcesImpl = existingResourcesImpl;
|
||||
} else {
|
||||
// Add this ResourcesImpl to the cache.
|
||||
mResourceImpls.put(key, new WeakReference<>(resourcesImpl));
|
||||
}
|
||||
|
||||
final Resources resources;
|
||||
if (activityToken != null) {
|
||||
resources = getOrCreateResourcesForActivityLocked(activityToken, classLoader,
|
||||
resourcesImpl);
|
||||
} else {
|
||||
resources = getOrCreateResourcesLocked(classLoader, resourcesImpl);
|
||||
}
|
||||
return resources;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets or creates a new Resources object associated with the IBinder token. References returned
|
||||
* by this method live as long as the Activity, meaning they can be cached and used by the
|
||||
@@ -425,92 +616,8 @@ public class ResourcesManager {
|
||||
displayId,
|
||||
overrideConfig != null ? new Configuration(overrideConfig) : null, // Copy
|
||||
compatInfo);
|
||||
|
||||
classLoader = classLoader != null ? classLoader : ClassLoader.getSystemClassLoader();
|
||||
|
||||
final boolean findSystemLocales;
|
||||
final boolean hasNonSystemLocales;
|
||||
synchronized (this) {
|
||||
findSystemLocales = (mSystemLocales == null || mSystemLocales.length == 0);
|
||||
hasNonSystemLocales = mHasNonSystemLocales;
|
||||
|
||||
if (DEBUG) {
|
||||
Throwable here = new Throwable();
|
||||
here.fillInStackTrace();
|
||||
Slog.w(TAG, "!! Get resources for activity=" + activityToken + " key=" + key, here);
|
||||
}
|
||||
|
||||
if (activityToken != null) {
|
||||
ResourcesImpl resourcesImpl = findResourcesImplForKey(key);
|
||||
if (resourcesImpl != null) {
|
||||
if (DEBUG) {
|
||||
Slog.d(TAG, "- using existing impl=" + resourcesImpl);
|
||||
}
|
||||
return getOrCreateResourcesForActivityLocked(activityToken, classLoader,
|
||||
resourcesImpl);
|
||||
}
|
||||
|
||||
// We will create the ResourcesImpl object outside of holding this lock.
|
||||
|
||||
} else {
|
||||
// Clean up any dead references so they don't pile up.
|
||||
ArrayUtils.unstableRemoveIf(mResourceReferences, sEmptyReferencePredicate);
|
||||
|
||||
// Not tied to an Activity, find a shared Resources that has the right ResourcesImpl
|
||||
ResourcesImpl resourcesImpl = findResourcesImplForKey(key);
|
||||
if (resourcesImpl != null) {
|
||||
if (DEBUG) {
|
||||
Slog.d(TAG, "- using existing impl=" + resourcesImpl);
|
||||
}
|
||||
return getOrCreateResourcesLocked(classLoader, resourcesImpl);
|
||||
}
|
||||
|
||||
// We will create the ResourcesImpl object outside of holding this lock.
|
||||
}
|
||||
}
|
||||
|
||||
// If we're here, we didn't find a suitable ResourcesImpl to use, so create one now.
|
||||
ResourcesImpl resourcesImpl = createResourcesImpl(key);
|
||||
|
||||
final String[] systemLocales = findSystemLocales
|
||||
? AssetManager.getSystem().getLocales() : null;
|
||||
final String[] nonSystemLocales = resourcesImpl.getAssets().getNonSystemLocales();
|
||||
// Avoid checking for non-pseudo-locales if we already know there were some from a previous
|
||||
// Resources. The default value (for when hasNonSystemLocales is true) doesn't matter,
|
||||
// since mHasNonSystemLocales will also be true, and thus isPseudoLocalesOnly would not be
|
||||
// able to affect mHasNonSystemLocales.
|
||||
final boolean isPseudoLocalesOnly = hasNonSystemLocales ||
|
||||
LocaleList.isPseudoLocalesOnly(nonSystemLocales);
|
||||
|
||||
synchronized (this) {
|
||||
if (mSystemLocales == null || mSystemLocales.length == 0) {
|
||||
mSystemLocales = systemLocales;
|
||||
}
|
||||
mNonSystemLocales.addAll(Arrays.asList(nonSystemLocales));
|
||||
mHasNonSystemLocales = mHasNonSystemLocales || !isPseudoLocalesOnly;
|
||||
|
||||
ResourcesImpl existingResourcesImpl = findResourcesImplForKey(key);
|
||||
if (existingResourcesImpl != null) {
|
||||
if (DEBUG) {
|
||||
Slog.d(TAG, "- got beat! existing impl=" + existingResourcesImpl
|
||||
+ " new impl=" + resourcesImpl);
|
||||
}
|
||||
resourcesImpl.getAssets().close();
|
||||
resourcesImpl = existingResourcesImpl;
|
||||
} else {
|
||||
// Add this ResourcesImpl to the cache.
|
||||
mResourceImpls.put(key, new WeakReference<>(resourcesImpl));
|
||||
}
|
||||
|
||||
final Resources resources;
|
||||
if (activityToken != null) {
|
||||
resources = getOrCreateResourcesForActivityLocked(activityToken, classLoader,
|
||||
resourcesImpl);
|
||||
} else {
|
||||
resources = getOrCreateResourcesLocked(classLoader, resourcesImpl);
|
||||
}
|
||||
return resources;
|
||||
}
|
||||
return getOrCreateResources(activityToken, key, classLoader);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -524,31 +631,78 @@ public class ResourcesManager {
|
||||
*/
|
||||
public void updateResourcesForActivity(@NonNull IBinder activityToken,
|
||||
@Nullable Configuration overrideConfig) {
|
||||
final ClassLoader classLoader;
|
||||
final ResourcesKey oldKey;
|
||||
synchronized (this) {
|
||||
// Extract the ResourcesKey that was last used to create the Resources for this
|
||||
// activity.
|
||||
WeakReference<Resources> weakResRef = mActivityResourceReferences.get(activityToken);
|
||||
final Resources resources = weakResRef != null ? weakResRef.get() : null;
|
||||
if (resources == null) {
|
||||
Slog.e(TAG, "can't update resources for uncached activity " + activityToken);
|
||||
final ActivityResources activityResources = getOrCreateActivityResourcesStructLocked(
|
||||
activityToken);
|
||||
|
||||
if (Objects.equals(activityResources.overrideConfig, overrideConfig)) {
|
||||
// They are the same, no work to do.
|
||||
return;
|
||||
}
|
||||
|
||||
classLoader = resources.getClassLoader();
|
||||
oldKey = findKeyForResourceImpl(resources.getImpl());
|
||||
if (oldKey == null) {
|
||||
Slog.e(TAG, "can't find ResourcesKey for resources impl=" + resources.getImpl());
|
||||
return;
|
||||
// Grab a copy of the old configuration so we can create the delta's of each
|
||||
// Resources object associated with this Activity.
|
||||
final Configuration oldConfig = new Configuration(activityResources.overrideConfig);
|
||||
|
||||
// Update the Activity's base override.
|
||||
if (overrideConfig != null) {
|
||||
activityResources.overrideConfig.setTo(overrideConfig);
|
||||
} else {
|
||||
activityResources.overrideConfig.setToDefaults();
|
||||
}
|
||||
|
||||
final boolean activityHasOverrideConfig =
|
||||
!activityResources.overrideConfig.equals(Configuration.EMPTY);
|
||||
|
||||
// Rebase each Resources associated with this Activity.
|
||||
final int refCount = activityResources.activityResources.size();
|
||||
for (int i = 0; i < refCount; i++) {
|
||||
WeakReference<Resources> weakResRef = activityResources.activityResources.get(i);
|
||||
Resources resources = weakResRef.get();
|
||||
if (resources == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Extract the ResourcesKey that was last used to create the Resources for this
|
||||
// activity.
|
||||
final ResourcesKey oldKey = findKeyForResourceImplLocked(resources.getImpl());
|
||||
if (oldKey == null) {
|
||||
Slog.e(TAG, "can't find ResourcesKey for resources impl="
|
||||
+ resources.getImpl());
|
||||
continue;
|
||||
}
|
||||
|
||||
// Build the new override configuration for this ResourcesKey.
|
||||
final Configuration rebasedOverrideConfig = new Configuration();
|
||||
if (overrideConfig != null) {
|
||||
rebasedOverrideConfig.setTo(overrideConfig);
|
||||
}
|
||||
|
||||
if (activityHasOverrideConfig && oldKey.hasOverrideConfiguration()) {
|
||||
// Generate a delta between the old base Activity override configuration and
|
||||
// the actual final override configuration that was used to figure out the real
|
||||
// delta this Resources object wanted.
|
||||
Configuration overrideOverrideConfig = Configuration.generateDelta(
|
||||
oldConfig, oldKey.mOverrideConfiguration);
|
||||
rebasedOverrideConfig.updateFrom(overrideOverrideConfig);
|
||||
}
|
||||
|
||||
// Create the new ResourcesKey with the rebased override config.
|
||||
final ResourcesKey newKey = new ResourcesKey(oldKey.mResDir, oldKey.mSplitResDirs,
|
||||
oldKey.mOverlayDirs, oldKey.mLibDirs, oldKey.mDisplayId,
|
||||
rebasedOverrideConfig, oldKey.mCompatInfo);
|
||||
|
||||
ResourcesImpl resourcesImpl = findResourcesImplForKeyLocked(newKey);
|
||||
if (resourcesImpl == null) {
|
||||
resourcesImpl = createResourcesImpl(newKey);
|
||||
}
|
||||
|
||||
if (resourcesImpl != resources.getImpl()) {
|
||||
// Set the ResourcesImpl, updating it for all users of this Resources object.
|
||||
resources.setImpl(resourcesImpl);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Update the Resources object with the new override config and all of the existing
|
||||
// settings.
|
||||
getResources(activityToken, oldKey.mResDir, oldKey.mSplitResDirs, oldKey.mOverlayDirs,
|
||||
oldKey.mLibDirs, oldKey.mDisplayId, overrideConfig, oldKey.mCompatInfo,
|
||||
classLoader);
|
||||
}
|
||||
|
||||
/* package */ void setDefaultLocalesLocked(@NonNull LocaleList locales) {
|
||||
@@ -578,7 +732,7 @@ public class ResourcesManager {
|
||||
int changes = mResConfiguration.updateFrom(config);
|
||||
// Things might have changed in display manager, so clear the cached displays.
|
||||
mDisplays.clear();
|
||||
DisplayMetrics defaultDisplayMetrics = getDisplayMetricsLocked();
|
||||
DisplayMetrics defaultDisplayMetrics = getDisplayMetrics();
|
||||
|
||||
if (compat != null && (mResCompatibilityInfo == null ||
|
||||
!mResCompatibilityInfo.equals(compat))) {
|
||||
@@ -632,7 +786,7 @@ public class ResourcesManager {
|
||||
}
|
||||
tmpConfig.setTo(localeAdjustedConfig);
|
||||
if (!isDefaultDisplay) {
|
||||
dm = getDisplayMetricsLocked(displayId);
|
||||
dm = getDisplayMetrics(displayId);
|
||||
applyNonDefaultDisplayMetricsToConfiguration(dm, tmpConfig);
|
||||
}
|
||||
if (hasOverrideConfiguration) {
|
||||
|
||||
@@ -1251,6 +1251,15 @@
|
||||
<service android:name="android.os.BinderThreadPriorityService"
|
||||
android:process=":BinderThreadPriorityService" />
|
||||
|
||||
<!-- Used by ApplyOverrideConfigurationTest -->
|
||||
<activity android:name="android.app.activity.ApplyOverrideConfigurationActivity"
|
||||
android:configChanges="orientation|screenSize">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.FRAMEWORK_INSTRUMENTATION_TEST" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<!-- Application components used for search manager tests -->
|
||||
|
||||
<activity android:name="android.app.activity.SearchableActivity"
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* Copyright (C) 2016 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.app.activity;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.content.res.Configuration;
|
||||
|
||||
public class ApplyOverrideConfigurationActivity extends Activity {
|
||||
|
||||
@Override
|
||||
protected void attachBaseContext(Context newBase) {
|
||||
super.attachBaseContext(newBase);
|
||||
|
||||
Configuration overrideConfig = new Configuration();
|
||||
overrideConfig.smallestScreenWidthDp = ApplyOverrideConfigurationTest.OVERRIDE_WIDTH;
|
||||
applyOverrideConfiguration(overrideConfig);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* Copyright (C) 2016 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.app.activity;
|
||||
|
||||
import android.app.UiAutomation;
|
||||
import android.content.res.Configuration;
|
||||
import android.support.test.InstrumentationRegistry;
|
||||
import android.support.test.filters.SmallTest;
|
||||
import android.support.test.runner.AndroidJUnit4;
|
||||
import android.test.ActivityInstrumentationTestCase2;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
@SmallTest
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class ApplyOverrideConfigurationTest extends
|
||||
ActivityInstrumentationTestCase2<ApplyOverrideConfigurationActivity> {
|
||||
|
||||
public static final int OVERRIDE_WIDTH = 9999;
|
||||
|
||||
public ApplyOverrideConfigurationTest() {
|
||||
super(ApplyOverrideConfigurationActivity.class);
|
||||
}
|
||||
|
||||
@Before
|
||||
@Override
|
||||
public void setUp() throws Exception {
|
||||
super.setUp();
|
||||
injectInstrumentation(InstrumentationRegistry.getInstrumentation());
|
||||
getInstrumentation().getUiAutomation().setRotation(UiAutomation.ROTATION_FREEZE_0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testConfigurationIsOverriden() throws Exception {
|
||||
Configuration config = getActivity().getResources().getConfiguration();
|
||||
assertEquals(OVERRIDE_WIDTH, config.smallestScreenWidthDp);
|
||||
|
||||
getInstrumentation().getUiAutomation().setRotation(UiAutomation.ROTATION_FREEZE_90);
|
||||
|
||||
config = getActivity().getResources().getConfiguration();
|
||||
assertEquals(OVERRIDE_WIDTH, config.smallestScreenWidthDp);
|
||||
}
|
||||
|
||||
@After
|
||||
@Override
|
||||
public void tearDown() throws Exception {
|
||||
getInstrumentation().getUiAutomation().setRotation(UiAutomation.ROTATION_UNFREEZE);
|
||||
super.tearDown();
|
||||
}
|
||||
}
|
||||
@@ -18,7 +18,7 @@ package android.content.res;
|
||||
import android.annotation.NonNull;
|
||||
import android.app.ResourcesManager;
|
||||
import android.os.Binder;
|
||||
import android.test.suitebuilder.annotation.SmallTest;
|
||||
import android.support.test.filters.SmallTest;
|
||||
import android.util.DisplayMetrics;
|
||||
import android.util.LocaleList;
|
||||
import android.util.TypedValue;
|
||||
@@ -58,7 +58,7 @@ public class ResourcesManagerTest extends TestCase {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected DisplayMetrics getDisplayMetricsLocked(int displayId) {
|
||||
protected DisplayMetrics getDisplayMetrics(int displayId) {
|
||||
return mDisplayMetrics;
|
||||
}
|
||||
};
|
||||
@@ -173,25 +173,12 @@ public class ResourcesManagerTest extends TestCase {
|
||||
|
||||
// The implementations should be the same.
|
||||
assertSame(resources1.getImpl(), resources2.getImpl());
|
||||
|
||||
final Configuration overrideConfig = new Configuration();
|
||||
overrideConfig.orientation = Configuration.ORIENTATION_LANDSCAPE;
|
||||
Resources resources3 = mResourcesManager.getResources(
|
||||
activity2, APP_ONE_RES_DIR, null, null, null, Display.DEFAULT_DISPLAY,
|
||||
overrideConfig, CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, null);
|
||||
|
||||
// Since we requested new resources for activity2, the resource should be the same
|
||||
// as the one returned before for activity2.
|
||||
assertSame(resources2, resources3);
|
||||
|
||||
// But the implementation has changed.
|
||||
assertNotSame(resources1.getImpl(), resources2.getImpl());
|
||||
}
|
||||
|
||||
@SmallTest
|
||||
public void testThemesGetUpdatedWithNewImpl() {
|
||||
Binder activity1 = new Binder();
|
||||
Resources resources1 = mResourcesManager.getResources(
|
||||
Resources resources1 = mResourcesManager.createBaseActivityResources(
|
||||
activity1, APP_ONE_RES_DIR, null, null, null, Display.DEFAULT_DISPLAY, null,
|
||||
CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, null);
|
||||
assertNotNull(resources1);
|
||||
@@ -207,16 +194,59 @@ public class ResourcesManagerTest extends TestCase {
|
||||
|
||||
final Configuration overrideConfig = new Configuration();
|
||||
overrideConfig.orientation = Configuration.ORIENTATION_LANDSCAPE;
|
||||
Resources resources2 = mResourcesManager.getResources(
|
||||
activity1, APP_ONE_RES_DIR, null, null, null, Display.DEFAULT_DISPLAY,
|
||||
overrideConfig, CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, null);
|
||||
assertNotNull(resources2);
|
||||
assertSame(resources1, resources2);
|
||||
assertSame(resources2, theme.getResources());
|
||||
mResourcesManager.updateResourcesForActivity(activity1, overrideConfig);
|
||||
assertSame(resources1, theme.getResources());
|
||||
|
||||
// Make sure we can still access the data.
|
||||
assertTrue(theme.resolveAttribute(android.R.attr.windowNoTitle, value, true));
|
||||
assertEquals(TypedValue.TYPE_INT_BOOLEAN, value.type);
|
||||
assertTrue(value.data != 0);
|
||||
}
|
||||
|
||||
@SmallTest
|
||||
public void testMultipleResourcesForOneActivityGetUpdatedWhenActivityBaseUpdates() {
|
||||
Binder activity1 = new Binder();
|
||||
|
||||
// Create a Resources for the Activity.
|
||||
Configuration config1 = new Configuration();
|
||||
config1.densityDpi = 280;
|
||||
Resources resources1 = mResourcesManager.createBaseActivityResources(
|
||||
activity1, APP_ONE_RES_DIR, null, null, null, Display.DEFAULT_DISPLAY, config1,
|
||||
CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, null);
|
||||
assertNotNull(resources1);
|
||||
|
||||
// Create a Resources based on the Activity.
|
||||
Configuration config2 = new Configuration();
|
||||
config2.screenLayout |= Configuration.SCREENLAYOUT_ROUND_YES;
|
||||
Resources resources2 = mResourcesManager.getResources(
|
||||
activity1, APP_ONE_RES_DIR, null, null, null, Display.DEFAULT_DISPLAY, config2,
|
||||
CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, null);
|
||||
assertNotNull(resources2);
|
||||
|
||||
assertNotSame(resources1, resources2);
|
||||
assertNotSame(resources1.getImpl(), resources2.getImpl());
|
||||
|
||||
final Configuration expectedConfig1 = new Configuration();
|
||||
expectedConfig1.setLocales(LocaleList.getAdjustedDefault());
|
||||
expectedConfig1.densityDpi = 280;
|
||||
assertEquals(expectedConfig1, resources1.getConfiguration());
|
||||
|
||||
// resources2 should be based on the Activity's override config, so the density should
|
||||
// be the same as resources1.
|
||||
final Configuration expectedConfig2 = new Configuration();
|
||||
expectedConfig2.setLocales(LocaleList.getAdjustedDefault());
|
||||
expectedConfig2.densityDpi = 280;
|
||||
expectedConfig2.screenLayout |= Configuration.SCREENLAYOUT_ROUND_YES;
|
||||
assertEquals(expectedConfig2, resources2.getConfiguration());
|
||||
|
||||
// Now update the Activity base override, and both resources should update.
|
||||
config1.orientation = Configuration.ORIENTATION_LANDSCAPE;
|
||||
mResourcesManager.updateResourcesForActivity(activity1, config1);
|
||||
|
||||
expectedConfig1.orientation = Configuration.ORIENTATION_LANDSCAPE;
|
||||
assertEquals(expectedConfig1, resources1.getConfiguration());
|
||||
|
||||
expectedConfig2.orientation = Configuration.ORIENTATION_LANDSCAPE;
|
||||
assertEquals(expectedConfig2, resources2.getConfiguration());
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user