diff options
author | Adam Lesinski <adamlesinski@google.com> | 2016-03-30 22:37:35 +0000 |
---|---|---|
committer | android-build-merger <android-build-merger@google.com> | 2016-03-30 22:37:35 +0000 |
commit | 7baa5212c24f283fad1eda53d6ef0fd01f1501db (patch) | |
tree | f8c4ad01c5d1ab29557adb3552da47e5aae08b1b | |
parent | 05b3ab5d892363c30e52817d42e57f852d11ffd0 (diff) | |
parent | d17f96ae007179007765c0f75b6bffb13c9285cb (diff) |
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
6 files changed, 415 insertions, 106 deletions
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java index d28f1fbf0744..6bb853afd90d 100644 --- a/core/java/android/app/ContextImpl.java +++ b/core/java/android/app/ContextImpl.java @@ -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; diff --git a/core/java/android/app/ResourcesManager.java b/core/java/android/app/ResourcesManager.java index 1a31332ba516..3f2238537d88 100644 --- a/core/java/android/app/ResourcesManager.java +++ b/core/java/android/app/ResourcesManager.java @@ -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); - if (resources.getImpl() != impl) { - if (DEBUG) { - Slog.d(TAG, "- setting ref=" + resources + " with impl=" + impl); + 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; } + } - // 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) { @@ -382,33 +402,26 @@ public class ResourcesManager { } /** - * 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 - * Activity even after a configuration change. If any other parameter is changed - * (resDir, splitResDirs, overrideConfig) for a given Activity, the same Resources object - * is updated and handed back to the caller. However, changing the class loader will result in a - * new Resources object. - * <p/> - * If activityToken is null, a cached Resources object will be returned if it matches the - * input parameters. Otherwise a new Resources object that satisfies these parameters is - * returned. + * 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. If null, global resources are assumed. + * @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. Mostly used with Activities that are in multi-window which may override width and - * height properties from the base config. + * 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}. + * {@link CompatibilityInfo#DEFAULT_COMPATIBILITY_INFO}. * @param classLoader The class loader to use when inflating Resources. If null, the - * {@link ClassLoader#getSystemClassLoader()} is used. + * {@link ClassLoader#getSystemClassLoader()} is used. * @return a Resources object from which to access resources. */ - public Resources getResources(@Nullable IBinder activityToken, + public Resources createBaseActivityResources(@NonNull IBinder activityToken, @Nullable String resDir, @Nullable String[] splitResDirs, @Nullable String[] overlayDirs, @@ -425,9 +438,40 @@ public class ResourcesManager { 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) { @@ -441,7 +485,22 @@ public class ResourcesManager { } if (activityToken != null) { - ResourcesImpl resourcesImpl = findResourcesImplForKey(key); + 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); @@ -457,7 +516,7 @@ public class ResourcesManager { ArrayUtils.unstableRemoveIf(mResourceReferences, sEmptyReferencePredicate); // Not tied to an Activity, find a shared Resources that has the right ResourcesImpl - ResourcesImpl resourcesImpl = findResourcesImplForKey(key); + ResourcesImpl resourcesImpl = findResourcesImplForKeyLocked(key); if (resourcesImpl != null) { if (DEBUG) { Slog.d(TAG, "- using existing impl=" + resourcesImpl); @@ -489,7 +548,7 @@ public class ResourcesManager { mNonSystemLocales.addAll(Arrays.asList(nonSystemLocales)); mHasNonSystemLocales = mHasNonSystemLocales || !isPseudoLocalesOnly; - ResourcesImpl existingResourcesImpl = findResourcesImplForKey(key); + ResourcesImpl existingResourcesImpl = findResourcesImplForKeyLocked(key); if (existingResourcesImpl != null) { if (DEBUG) { Slog.d(TAG, "- got beat! existing impl=" + existingResourcesImpl @@ -514,6 +573,54 @@ public class ResourcesManager { } /** + * 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 + * Activity even after a configuration change. If any other parameter is changed + * (resDir, splitResDirs, overrideConfig) for a given Activity, the same Resources object + * is updated and handed back to the caller. However, changing the class loader will result in a + * new Resources object. + * <p/> + * If activityToken is null, a cached Resources object will be returned if it matches the + * input parameters. Otherwise a new Resources object that satisfies these parameters is + * returned. + * + * @param activityToken Represents an Activity. If null, global resources are assumed. + * @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. Mostly used with Activities that are in multi-window which may override width and + * height properties from the base config. + * @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 getResources(@Nullable 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(); + return getOrCreateResources(activityToken, key, classLoader); + } + + /** * Updates an Activity's Resources object with overrideConfig. The Resources object * that was previously returned by * {@link #getResources(IBinder, String, String[], String[], String[], int, Configuration, @@ -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(); } - } - // 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); + 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); + } + } + } } /* 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) { diff --git a/core/tests/coretests/AndroidManifest.xml b/core/tests/coretests/AndroidManifest.xml index b78077892c9b..ca6fc3db449e 100644 --- a/core/tests/coretests/AndroidManifest.xml +++ b/core/tests/coretests/AndroidManifest.xml @@ -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" diff --git a/core/tests/coretests/src/android/app/activity/ApplyOverrideConfigurationActivity.java b/core/tests/coretests/src/android/app/activity/ApplyOverrideConfigurationActivity.java new file mode 100644 index 000000000000..3df522da6aef --- /dev/null +++ b/core/tests/coretests/src/android/app/activity/ApplyOverrideConfigurationActivity.java @@ -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); + } +} diff --git a/core/tests/coretests/src/android/app/activity/ApplyOverrideConfigurationTest.java b/core/tests/coretests/src/android/app/activity/ApplyOverrideConfigurationTest.java new file mode 100644 index 000000000000..15ed77ec6940 --- /dev/null +++ b/core/tests/coretests/src/android/app/activity/ApplyOverrideConfigurationTest.java @@ -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(); + } +} diff --git a/core/tests/coretests/src/android/content/res/ResourcesManagerTest.java b/core/tests/coretests/src/android/content/res/ResourcesManagerTest.java index 3cadbf64c7a3..d4bb0f3e1125 100644 --- a/core/tests/coretests/src/android/content/res/ResourcesManagerTest.java +++ b/core/tests/coretests/src/android/content/res/ResourcesManagerTest.java @@ -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()); + } } |