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:
Adam Lesinski
2016-03-30 22:37:35 +00:00
committed by android-build-merger
6 changed files with 480 additions and 171 deletions

View File

@@ -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;

View File

@@ -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) {

View File

@@ -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"

View File

@@ -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);
}
}

View File

@@ -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();
}
}

View File

@@ -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());
}
}