Allow use of custom configurations via attachBaseContext

Test: ./gradlew appcompat:connectedCheck
BUG: 138325067

Change-Id: I229a35efaafb85b0f006b5467bb6d6d896dff2b8
diff --git a/appcompat/src/androidTest/AndroidManifest.xml b/appcompat/src/androidTest/AndroidManifest.xml
index bb6f018..09c7dc4 100644
--- a/appcompat/src/androidTest/AndroidManifest.xml
+++ b/appcompat/src/androidTest/AndroidManifest.xml
@@ -165,6 +165,10 @@
             android:configChanges="orientation|screenSize"/>
 
         <activity
+            android:name="androidx.appcompat.app.NightModeCustomConfigurationActivity"
+            android:theme="@style/Theme.AppCompat.DayNight"/>
+
+        <activity
                 android:name="androidx.appcompat.app.AppCompatVectorDrawableIntegrationActivity"/>
 
         <activity
diff --git a/appcompat/src/androidTest/java/androidx/appcompat/app/NightModeCustomConfigurationActivity.java b/appcompat/src/androidTest/java/androidx/appcompat/app/NightModeCustomConfigurationActivity.java
new file mode 100644
index 0000000..ec3d5b7
--- /dev/null
+++ b/appcompat/src/androidTest/java/androidx/appcompat/app/NightModeCustomConfigurationActivity.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.appcompat.app;
+
+import android.content.Context;
+import android.content.res.Configuration;
+
+import java.util.Locale;
+
+public class NightModeCustomConfigurationActivity extends NightModeActivity {
+    public static final float CUSTOM_FONT_SCALE = 4.24f;
+    public static final Locale CUSTOM_LOCALE = Locale.CANADA_FRENCH;
+
+    @Override
+    protected void attachBaseContext(Context newBase) {
+        Configuration config = new Configuration();
+        config.fontScale = CUSTOM_FONT_SCALE;
+        config.locale = CUSTOM_LOCALE;
+
+        Context configContext = newBase.createConfigurationContext(config);
+        super.attachBaseContext(configContext);
+    }
+}
diff --git a/appcompat/src/androidTest/java/androidx/appcompat/app/NightModeCustomFontScaleTestCase.kt b/appcompat/src/androidTest/java/androidx/appcompat/app/NightModeCustomFontScaleTestCase.kt
new file mode 100644
index 0000000..b43493b
--- /dev/null
+++ b/appcompat/src/androidTest/java/androidx/appcompat/app/NightModeCustomFontScaleTestCase.kt
@@ -0,0 +1,79 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.appcompat.app
+
+import androidx.appcompat.app.AppCompatDelegate.MODE_NIGHT_NO
+import androidx.appcompat.app.AppCompatDelegate.MODE_NIGHT_YES
+import androidx.appcompat.app.NightModeCustomConfigurationActivity.CUSTOM_FONT_SCALE
+import androidx.appcompat.testutils.NightModeUtils
+import androidx.appcompat.testutils.NightModeUtils.NightSetMode
+import androidx.appcompat.testutils.NightModeUtils.setNightModeAndWaitForDestroy
+import androidx.test.filters.LargeTest
+import androidx.test.rule.ActivityTestRule
+import org.junit.After
+import org.junit.Assert.assertEquals
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.Parameterized
+
+@LargeTest
+@RunWith(Parameterized::class)
+class NightModeCustomFontScaleTestCase(private val setMode: NightSetMode) {
+    @get:Rule
+    val rule = ActivityTestRule(NightModeCustomConfigurationActivity::class.java, false, false)
+
+    @Before
+    fun setup() {
+        // By default we'll set the night mode to NO, which allows us to make better
+        // assumptions in the tests below
+        AppCompatDelegate.setDefaultNightMode(MODE_NIGHT_NO)
+        // Launch the test activity
+        rule.launchActivity(null)
+    }
+
+    @Test
+    fun testFontScaleIsMaintained() {
+        // Check that the custom configuration properties are maintained
+        val config = rule.activity.resources.configuration
+        assertEquals(CUSTOM_FONT_SCALE, config.fontScale)
+    }
+
+    @Test
+    fun testFontScaleIsMaintainedInDarkTheme() {
+        // Set local night mode to YES
+        setNightModeAndWaitForDestroy(rule, MODE_NIGHT_YES, setMode)
+
+        // Check that the custom configuration properties are maintained
+        val config = rule.activity.resources.configuration
+        assertEquals(CUSTOM_FONT_SCALE, config.fontScale)
+    }
+
+    @After
+    fun cleanup() {
+        rule.finishActivity()
+        // Reset the default night mode
+        NightModeUtils.setNightModeAndWait(rule, MODE_NIGHT_NO, NightSetMode.DEFAULT)
+    }
+
+    companion object {
+        @JvmStatic
+        @Parameterized.Parameters
+        fun data() = listOf(NightSetMode.DEFAULT, NightSetMode.LOCAL)
+    }
+}
diff --git a/appcompat/src/androidTest/java/androidx/appcompat/app/NightModeTestSuite.kt b/appcompat/src/androidTest/java/androidx/appcompat/app/NightModeTestSuite.kt
index 8c24920..cc8aa03 100644
--- a/appcompat/src/androidTest/java/androidx/appcompat/app/NightModeTestSuite.kt
+++ b/appcompat/src/androidTest/java/androidx/appcompat/app/NightModeTestSuite.kt
@@ -25,6 +25,7 @@
     NightModeOrientationConfigChangesTestCase::class,
     NightModeLateOnCreateTestCase::class,
     NightModePreventOverrideConfigTestCase::class,
-    NightModeUiModeConfigChangesTestCase::class
+    NightModeUiModeConfigChangesTestCase::class,
+    NightModeCustomFontScaleTestCase::class
 )
 class NightModeTestSuite
\ No newline at end of file
diff --git a/appcompat/src/main/java/androidx/appcompat/app/AppCompatDelegateImpl.java b/appcompat/src/main/java/androidx/appcompat/app/AppCompatDelegateImpl.java
index c33e630..6eb3889 100644
--- a/appcompat/src/main/java/androidx/appcompat/app/AppCompatDelegateImpl.java
+++ b/appcompat/src/main/java/androidx/appcompat/app/AppCompatDelegateImpl.java
@@ -331,7 +331,7 @@
     @Override
     public void attachBaseContext(Context context) {
         // Activity.recreate() cannot be called before attach is complete.
-        applyDayNight(false);
+        applyDayNight(false, context.getResources().getConfiguration());
         mBaseContextAttached = true;
     }
 
@@ -343,7 +343,7 @@
 
         // Our implicit call to applyDayNight() should not recreate until after the Activity is
         // created
-        applyDayNight(false);
+        applyDayNight(false, null);
 
         // We lazily fetch the Window for Activities, to allow DayNight to apply in
         // attachBaseContext
@@ -498,7 +498,7 @@
 
         // Re-apply Day/Night with the new configuration but disable recreations. Since this
         // configuration change has only just happened we can safely just update the resources now
-        applyDayNight(false);
+        applyDayNight(false, null);
     }
 
     @Override
@@ -2158,10 +2158,11 @@
 
     @Override
     public boolean applyDayNight() {
-        return applyDayNight(true);
+        return applyDayNight(true, null);
     }
 
-    private boolean applyDayNight(final boolean allowRecreation) {
+    private boolean applyDayNight(final boolean allowRecreation,
+            @Nullable Configuration baseConfiguration) {
         if (mIsDestroyed) {
             // If we're destroyed, ignore the call
             return false;
@@ -2169,7 +2170,7 @@
 
         @NightMode final int nightMode = calculateNightMode();
         @ApplyableNightMode final int modeToApply = mapNightMode(nightMode);
-        final boolean applied = updateForNightMode(modeToApply, allowRecreation);
+        final boolean applied = updateForNightMode(modeToApply, allowRecreation, baseConfiguration);
 
         if (nightMode == MODE_NIGHT_AUTO_TIME) {
             getAutoTimeNightModeManager().setup();
@@ -2240,10 +2241,11 @@
      *
      * @param mode The new night mode to apply
      * @param allowRecreation whether to attempt activity recreate
+     * @param baseConfiguration the base configuration to use, if any
      * @return true if an action has been taken (recreation, resources updating, etc)
      */
     private boolean updateForNightMode(@ApplyableNightMode final int mode,
-            final boolean allowRecreation) {
+            final boolean allowRecreation, @Nullable Configuration baseConfiguration) {
         boolean handled = false;
 
         final int applicationNightMode = mContext.getApplicationContext()
@@ -2273,7 +2275,7 @@
                 && !mBaseContextAttached
                 && mHost instanceof android.view.ContextThemeWrapper) {
             // If we're here then we can try and apply an override configuration on the Context.
-            final Configuration conf = new Configuration();
+            final Configuration conf = new Configuration(baseConfiguration);
             conf.uiMode = newNightMode | (conf.uiMode & ~Configuration.UI_MODE_NIGHT_MASK);
 
             try {
@@ -2322,7 +2324,8 @@
             if (DEBUG) {
                 Log.d(TAG, "updateForNightMode. Updating resources config");
             }
-            updateResourcesConfigurationForNightMode(newNightMode, activityHandlingUiMode);
+            updateResourcesConfigurationForNightMode(newNightMode, activityHandlingUiMode,
+                    baseConfiguration);
             handled = true;
         }
 
@@ -2340,11 +2343,15 @@
     }
 
     private void updateResourcesConfigurationForNightMode(
-            final int uiModeNightModeValue, final boolean callOnConfigChange) {
+            final int uiModeNightModeValue, final boolean callOnConfigChange,
+            @Nullable Configuration baseConfiguration) {
         // If the Activity is not set to handle uiMode config changes we will
         // update the Resources with a new Configuration with an updated UI Mode
         final Resources res = mContext.getResources();
         final Configuration conf = new Configuration(res.getConfiguration());
+        if (baseConfiguration != null) {
+            conf.updateFrom(baseConfiguration);
+        }
         conf.uiMode = uiModeNightModeValue
                 | (res.getConfiguration().uiMode & ~Configuration.UI_MODE_NIGHT_MASK);
         res.updateConfiguration(conf, null);