Fix AppCompat test failures

Turned out to be a gnarly framework bug on API levels
21-25. The test was failing due to the application
Resources seemingly being updated when it shouldn't
have been. Worked around by always using
applyOverrideConfiguration() on those API levels, to force
a new Resources instance.

Passing locally now (repeated ~5 times).

Also converted NightModeTestCase to Kotlin, and added
a new test.

Test: ./gradlew appcompat:conCh
BUG: 133142902

Change-Id: I8d809f9a31e25dae1f0d1cf36b66e4094d174e9f
(cherry picked from commit 1b06fa0c3e3194b96d4cf95bf8a0d7cc40a30f77)
diff --git a/appcompat/src/androidTest/java/androidx/appcompat/app/NightModeTestCase.java b/appcompat/src/androidTest/java/androidx/appcompat/app/NightModeTestCase.java
deleted file mode 100644
index 0550ec5..0000000
--- a/appcompat/src/androidTest/java/androidx/appcompat/app/NightModeTestCase.java
+++ /dev/null
@@ -1,351 +0,0 @@
-/*
- * 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 androidx.appcompat.app;
-
-import static androidx.appcompat.app.AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM;
-import static androidx.appcompat.app.AppCompatDelegate.MODE_NIGHT_NO;
-import static androidx.appcompat.app.AppCompatDelegate.MODE_NIGHT_YES;
-import static androidx.appcompat.testutils.NightModeUtils.assertConfigurationNightModeEquals;
-import static androidx.appcompat.testutils.NightModeUtils.isSystemNightThemeEnabled;
-import static androidx.appcompat.testutils.NightModeUtils.setNightModeAndWait;
-import static androidx.appcompat.testutils.NightModeUtils.setNightModeAndWaitForDestroy;
-import static androidx.appcompat.testutils.TestUtilsMatchers.isBackground;
-import static androidx.test.espresso.Espresso.onView;
-import static androidx.test.espresso.assertion.ViewAssertions.matches;
-import static androidx.test.espresso.matcher.ViewMatchers.withId;
-import static androidx.test.espresso.matcher.ViewMatchers.withText;
-import static androidx.testutils.LifecycleOwnerUtils.waitForRecreation;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNull;
-
-import android.app.Instrumentation;
-import android.content.res.Configuration;
-import android.webkit.WebView;
-
-import androidx.appcompat.test.R;
-import androidx.appcompat.testutils.NightModeUtils.NightSetMode;
-import androidx.core.content.ContextCompat;
-import androidx.test.filters.LargeTest;
-import androidx.test.platform.app.InstrumentationRegistry;
-import androidx.test.rule.ActivityTestRule;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
-
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.concurrent.CountDownLatch;
-
-@LargeTest
-@RunWith(Parameterized.class)
-public class NightModeTestCase {
-
-    private static final String STRING_DAY = "DAY";
-    private static final String STRING_NIGHT = "NIGHT";
-
-    @Parameterized.Parameters
-    public static Collection<NightSetMode> data() {
-        return Arrays.asList(NightSetMode.DEFAULT, NightSetMode.LOCAL);
-    }
-
-    private final NightSetMode mSetMode;
-
-    @Rule
-    public final ActivityTestRule<NightModeActivity> mActivityTestRule;
-
-    public NightModeTestCase(NightSetMode setMode) {
-        mSetMode = setMode;
-        mActivityTestRule = new ActivityTestRule<>(NightModeActivity.class, false, false);
-    }
-
-    @Before
-    public void setup() {
-        // By default we'll set the night mode to NO, which allows us to make better
-        // assumptions in the test below
-        AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO);
-        // Now launch the test activity
-        mActivityTestRule.launchActivity(null);
-    }
-
-    @Test
-    public void testLocalDayNightModeRecreatesActivity() throws Throwable {
-        if (mSetMode != NightSetMode.LOCAL) {
-            // This test is only applicable when using setLocalNightMode
-            return;
-        }
-
-        // Verify first that we're in day mode
-        onView(withId(R.id.text_night_mode)).check(matches(withText(STRING_DAY)));
-
-        // Now force the local night mode to be yes (aka night mode)
-        setNightModeAndWaitForDestroy(mActivityTestRule, MODE_NIGHT_YES, mSetMode);
-
-        // Assert that the new local night mode is returned
-        assertEquals(MODE_NIGHT_YES,
-                mActivityTestRule.getActivity().getDelegate().getLocalNightMode());
-
-        // Now check the text has changed, signifying that night resources are being used
-        onView(withId(R.id.text_night_mode)).check(matches(withText(STRING_NIGHT)));
-    }
-
-    @Test
-    public void testSwitchingYesToFollowSystem() throws Throwable {
-        // This test is only useful when the dark system theme is not enabled
-        if (!isSystemNightThemeEnabled(mActivityTestRule.getActivity())) {
-            // Ensure that we're currently running in light theme (from setup above)
-            onView(withId(R.id.text_night_mode)).check(matches(withText(STRING_DAY)));
-
-            // Force the night mode to be yes (aka night mode)
-            setNightModeAndWaitForDestroy(mActivityTestRule, MODE_NIGHT_YES, mSetMode);
-
-            // Now check the text has changed, signifying that night resources are being used
-            onView(withId(R.id.text_night_mode)).check(matches(withText(STRING_NIGHT)));
-
-            // Now force the local night mode to be FOLLOW_SYSTEM (which we know is light)
-            setNightModeAndWaitForDestroy(mActivityTestRule, MODE_NIGHT_FOLLOW_SYSTEM, mSetMode);
-
-            // Now check the text matches the system
-            onView(withId(R.id.text_night_mode)).check(matches(withText(STRING_DAY)));
-        }
-    }
-
-    @Test
-    public void testSwitchingNoToFollowSystem() throws Throwable {
-        // This test is only useful when the dark system theme is enabled
-        if (isSystemNightThemeEnabled(mActivityTestRule.getActivity())) {
-            // Ensure that we're currently running in light theme (from setup above)
-            onView(withId(R.id.text_night_mode)).check(matches(withText(STRING_DAY)));
-
-            // Now force the local night mode to be FOLLOW_SYSTEM (which we know is dark)
-            setNightModeAndWaitForDestroy(mActivityTestRule, MODE_NIGHT_FOLLOW_SYSTEM, mSetMode);
-
-            // Now check the text matches the system
-            onView(withId(R.id.text_night_mode)).check(matches(withText(STRING_NIGHT)));
-        }
-    }
-
-    @Test
-    public void testColorConvertedDrawableChangesWithNightMode() throws Throwable {
-        final NightModeActivity activity = mActivityTestRule.getActivity();
-        final int dayColor = ContextCompat.getColor(activity, R.color.color_sky_day);
-        final int nightColor = ContextCompat.getColor(activity, R.color.color_sky_night);
-
-        // Loop through and switching from day to night and vice-versa multiple times. It needs
-        // to be looped since the issue is with drawable caching, therefore we need to prime the
-        // cache for the issue to happen
-        for (int i = 0; i < 5; i++) {
-            // First force it to be night mode and assert the color
-            setNightModeAndWaitForDestroy(mActivityTestRule, MODE_NIGHT_YES, mSetMode);
-            onView(withId(R.id.view_background)).check(matches(isBackground(nightColor)));
-
-            // Now force the local night mode to be no (aka day mode) and assert the color
-            setNightModeAndWaitForDestroy(mActivityTestRule, MODE_NIGHT_NO, mSetMode);
-            onView(withId(R.id.view_background)).check(matches(isBackground(dayColor)));
-        }
-    }
-
-    @Test
-    public void testNightModeAutoTimeRecreatesOnTimeChange() throws Throwable {
-        // Create a fake TwilightManager and set it as the app instance
-        final FakeTwilightManager twilightManager = new FakeTwilightManager();
-        TwilightManager.setInstance(twilightManager);
-
-        // Verify that we're currently in day mode
-        onView(withId(R.id.text_night_mode)).check(matches(withText(STRING_DAY)));
-
-        // Set MODE_NIGHT_AUTO so that we will change to night mode automatically
-        setNightModeAndWait(mActivityTestRule, AppCompatDelegate.MODE_NIGHT_AUTO_TIME, mSetMode);
-        final AppCompatDelegateImpl newDelegate =
-                (AppCompatDelegateImpl) mActivityTestRule.getActivity().getDelegate();
-
-        // Update the fake twilight manager to be in night and trigger a fake 'time' change
-        mActivityTestRule.runOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                twilightManager.setIsNight(true);
-                newDelegate.getAutoTimeNightModeManager().onChange();
-            }
-        });
-
-        // Now wait until the Activity is destroyed (thus recreated)
-        waitForRecreation(mActivityTestRule);
-
-        // Check that the text has changed, signifying that night resources are being used
-        onView(withId(R.id.text_night_mode)).check(matches(withText(STRING_NIGHT)));
-    }
-
-    @Test
-    public void testNightModeAutoTimeRecreatesOnResume() throws Throwable {
-        // Create a fake TwilightManager and set it as the app instance
-        final FakeTwilightManager twilightManager = new FakeTwilightManager();
-        TwilightManager.setInstance(twilightManager);
-
-        // Set MODE_NIGHT_AUTO_TIME so that we will change to night mode automatically
-        setNightModeAndWait(mActivityTestRule, AppCompatDelegate.MODE_NIGHT_AUTO_TIME, mSetMode);
-
-        // Verify that we're currently in day mode
-        onView(withId(R.id.text_night_mode)).check(matches(withText(STRING_DAY)));
-
-        final CountDownLatch resumeCompleteLatch = new CountDownLatch(1);
-
-        mActivityTestRule.runOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                final NightModeActivity activity = mActivityTestRule.getActivity();
-                final Instrumentation instrumentation =
-                        InstrumentationRegistry.getInstrumentation();
-                // Now fool the Activity into thinking that it has gone into the background
-                instrumentation.callActivityOnPause(activity);
-                instrumentation.callActivityOnStop(activity);
-
-                // Now update the twilight manager while the Activity is in the 'background'
-                twilightManager.setIsNight(true);
-
-                // Now tell the Activity that it has gone into the foreground again
-                instrumentation.callActivityOnStart(activity);
-                instrumentation.callActivityOnResume(activity);
-
-                resumeCompleteLatch.countDown();
-            }
-        });
-
-        resumeCompleteLatch.await();
-        // finally check that the text has changed, signifying that night resources are being used
-        onView(withId(R.id.text_night_mode)).check(matches(withText(STRING_NIGHT)));
-    }
-
-    @Test
-    public void testOnNightModeChangedCalled() throws Throwable {
-        final NightModeActivity activity = mActivityTestRule.getActivity();
-        // Set local night mode to YES
-        setNightModeAndWait(mActivityTestRule, MODE_NIGHT_YES, mSetMode);
-        // Assert that the Activity received a new value
-        assertEquals(MODE_NIGHT_YES, activity.getLastNightModeAndReset());
-    }
-
-    @Test
-    public void testDialogDoesNotOverrideActivityConfiguration() throws Throwable {
-        // Set Activity local night mode to YES
-        setNightModeAndWaitForDestroy(mActivityTestRule, MODE_NIGHT_YES, mSetMode);
-
-        // Assert that the uiMode is as expected
-        assertConfigurationNightModeEquals(Configuration.UI_MODE_NIGHT_YES,
-                mActivityTestRule.getActivity());
-
-        // Now show a AppCompatDialog
-        mActivityTestRule.runOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                AppCompatDialog dialog = new AppCompatDialog(mActivityTestRule.getActivity());
-                dialog.show();
-            }
-        });
-
-        // Assert that the uiMode is unchanged
-        assertConfigurationNightModeEquals(Configuration.UI_MODE_NIGHT_YES,
-                mActivityTestRule.getActivity());
-    }
-
-    @Test
-    public void testLoadingWebViewMaintainsConfiguration() throws Throwable {
-        // Set night mode and wait for the new Activity
-        setNightModeAndWaitForDestroy(mActivityTestRule, MODE_NIGHT_YES, mSetMode);
-
-        // Assert that the context still has a night themed configuration
-        assertConfigurationNightModeEquals(
-                Configuration.UI_MODE_NIGHT_YES,
-                mActivityTestRule.getActivity().getResources().getConfiguration());
-
-        // Now load a WebView into the Activity
-        mActivityTestRule.runOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                final WebView webView = new WebView(mActivityTestRule.getActivity());
-            }
-        });
-
-        // Now assert that the context still has a night themed configuration
-        assertConfigurationNightModeEquals(
-                Configuration.UI_MODE_NIGHT_YES,
-                mActivityTestRule.getActivity().getResources().getConfiguration());
-    }
-
-    @Test
-    public void testDialogCleansUpAutoMode() throws Throwable {
-        mActivityTestRule.runOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                AppCompatDialog dialog = new AppCompatDialog(mActivityTestRule.getActivity());
-                AppCompatDelegateImpl delegate = (AppCompatDelegateImpl) dialog.getDelegate();
-
-                // Set the local night mode of the Dialog to be an AUTO mode
-                delegate.setLocalNightMode(AppCompatDelegate.MODE_NIGHT_AUTO_TIME);
-
-                // Now show and dismiss the dialog
-                dialog.show();
-                dialog.dismiss();
-
-                // Assert that the auto manager is destroyed (not listening)
-                assertFalse(delegate.getAutoTimeNightModeManager().isListening());
-            }
-        });
-    }
-
-    @Test
-    public void testOnConfigurationChangeNotCalled() throws Throwable {
-        NightModeActivity activity = mActivityTestRule.getActivity();
-        // Set local night mode to YES
-        setNightModeAndWait(mActivityTestRule, MODE_NIGHT_YES, mSetMode);
-        // Assert that onConfigurationChange was not called on the original activity
-        assertNull(activity.getLastConfigurationChangeAndClear());
-
-        activity = mActivityTestRule.getActivity();
-        // Set local night mode back to NO
-        setNightModeAndWait(mActivityTestRule, MODE_NIGHT_NO, mSetMode);
-        // Assert that onConfigurationChange was not called
-        assertNull(activity.getLastConfigurationChangeAndClear());
-    }
-
-    @After
-    public void cleanup() throws Throwable {
-        // Reset the default night mode
-        setNightModeAndWait(mActivityTestRule, MODE_NIGHT_NO, NightSetMode.DEFAULT);
-    }
-
-    private static class FakeTwilightManager extends TwilightManager {
-        private boolean mIsNight;
-
-        FakeTwilightManager() {
-            super(null, null);
-        }
-
-        @Override
-        boolean isNight() {
-            return mIsNight;
-        }
-
-        void setIsNight(boolean night) {
-            mIsNight = night;
-        }
-    }
-}
diff --git a/appcompat/src/androidTest/java/androidx/appcompat/app/NightModeTestCase.kt b/appcompat/src/androidTest/java/androidx/appcompat/app/NightModeTestCase.kt
new file mode 100644
index 0000000..8bd1b44
--- /dev/null
+++ b/appcompat/src/androidTest/java/androidx/appcompat/app/NightModeTestCase.kt
@@ -0,0 +1,319 @@
+/*
+ * 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 androidx.appcompat.app
+
+import android.content.Context
+import androidx.appcompat.app.AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM
+import androidx.appcompat.app.AppCompatDelegate.MODE_NIGHT_NO
+import androidx.appcompat.app.AppCompatDelegate.MODE_NIGHT_YES
+import androidx.appcompat.testutils.NightModeUtils.assertConfigurationNightModeEquals
+import androidx.appcompat.testutils.NightModeUtils.isSystemNightThemeEnabled
+import androidx.appcompat.testutils.NightModeUtils.setNightModeAndWait
+import androidx.appcompat.testutils.NightModeUtils.setNightModeAndWaitForDestroy
+import androidx.appcompat.testutils.TestUtilsMatchers.isBackground
+import androidx.test.espresso.Espresso.onView
+import androidx.test.espresso.assertion.ViewAssertions.matches
+import androidx.test.espresso.matcher.ViewMatchers.withId
+import androidx.test.espresso.matcher.ViewMatchers.withText
+import androidx.testutils.LifecycleOwnerUtils.waitForRecreation
+
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertFalse
+import org.junit.Assert.assertNull
+
+import android.content.res.Configuration
+import android.location.LocationManager
+import android.webkit.WebView
+
+import androidx.appcompat.test.R
+import androidx.appcompat.testutils.NightModeUtils.NightSetMode
+import androidx.core.content.ContextCompat
+import androidx.test.filters.LargeTest
+import androidx.test.platform.app.InstrumentationRegistry
+import androidx.test.rule.ActivityTestRule
+
+import org.junit.After
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.Parameterized
+
+import java.util.concurrent.CountDownLatch
+
+@Suppress("DEPRECATION")
+@LargeTest
+@RunWith(Parameterized::class)
+class NightModeTestCase(private val setMode: NightSetMode) {
+    @get:Rule
+    val rule = ActivityTestRule(NightModeActivity::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 test below
+        AppCompatDelegate.setDefaultNightMode(MODE_NIGHT_NO)
+        // Now launch the test activity
+        rule.launchActivity(null)
+    }
+
+    @Test
+    fun testLocalDayNightModeRecreatesActivity() {
+        if (setMode != NightSetMode.LOCAL) {
+            // This test is only applicable when using setLocalNightMode
+            return
+        }
+
+        // Verify first that we're in day mode
+        onView(withId(R.id.text_night_mode)).check(matches(withText(STRING_DAY)))
+
+        // Now force the local night mode to be yes (aka night mode)
+        setNightModeAndWaitForDestroy(rule, MODE_NIGHT_YES, setMode)
+
+        // Assert that the new local night mode is returned
+        assertEquals(MODE_NIGHT_YES, rule.activity.delegate.localNightMode)
+
+        // Now check the text has changed, signifying that night resources are being used
+        onView(withId(R.id.text_night_mode)).check(matches(withText(STRING_NIGHT)))
+    }
+
+    @Test
+    fun testSwitchingYesDoesNotAffectApplication() {
+        // This test is only useful when the dark system theme is not enabled
+        if (!isSystemNightThemeEnabled(rule.activity)) {
+            assertConfigurationNightModeEquals(Configuration.UI_MODE_NIGHT_NO,
+                rule.activity.applicationContext.resources.configuration)
+
+            // Force the night mode to be yes (aka night mode)
+            setNightModeAndWaitForDestroy(rule, MODE_NIGHT_YES, setMode)
+
+            assertConfigurationNightModeEquals(Configuration.UI_MODE_NIGHT_NO,
+                rule.activity.applicationContext.resources.configuration)
+        }
+    }
+
+    @Test
+    fun testSwitchingYesToFollowSystem() {
+        // This test is only useful when the dark system theme is enabled
+        if (!isSystemNightThemeEnabled(rule.activity)) {
+            // Now force the night mode to be YES, so we're in dark theme
+            setNightModeAndWaitForDestroy(rule, MODE_NIGHT_YES, setMode)
+            onView(withId(R.id.text_night_mode)).check(matches(withText(STRING_NIGHT)))
+
+            // Now force the local night mode to be FOLLOW_SYSTEM (which we know is dark)
+            setNightModeAndWaitForDestroy(rule, MODE_NIGHT_FOLLOW_SYSTEM, setMode)
+            onView(withId(R.id.text_night_mode)).check(matches(withText(STRING_DAY)))
+        }
+    }
+
+    @Test
+    fun testSwitchingNoToFollowSystem() {
+        // This test is only useful when the dark system theme is enabled
+        if (isSystemNightThemeEnabled(rule.activity)) {
+            // Now force the night mode to be NO, so we're in light theme
+            setNightModeAndWaitForDestroy(rule, MODE_NIGHT_NO, setMode)
+            onView(withId(R.id.text_night_mode)).check(matches(withText(STRING_DAY)))
+
+            // Now force the night mode to be FOLLOW_SYSTEM (which we know is dark)
+            setNightModeAndWaitForDestroy(rule, MODE_NIGHT_FOLLOW_SYSTEM, setMode)
+            onView(withId(R.id.text_night_mode)).check(matches(withText(STRING_NIGHT)))
+        }
+    }
+
+    @Test
+    fun testColorConvertedDrawableChangesWithNightMode() {
+        val activity = rule.activity
+        val dayColor = ContextCompat.getColor(activity, R.color.color_sky_day)
+        val nightColor = ContextCompat.getColor(activity, R.color.color_sky_night)
+
+        // Loop through and switching from day to night and vice-versa multiple times. It needs
+        // to be looped since the issue is with drawable caching, therefore we need to prime the
+        // cache for the issue to happen
+        for (i in 0 until 5) {
+            // First force it to be night mode and assert the color
+            setNightModeAndWaitForDestroy(rule, MODE_NIGHT_YES, setMode)
+            onView(withId(R.id.view_background)).check(matches(isBackground(nightColor)))
+
+            // Now force the local night mode to be no (aka day mode) and assert the color
+            setNightModeAndWaitForDestroy(rule, MODE_NIGHT_NO, setMode)
+            onView(withId(R.id.view_background)).check(matches(isBackground(dayColor)))
+        }
+    }
+
+    @Test
+    fun testNightModeAutoTimeRecreatesOnTimeChange() {
+        // Create a fake TwilightManager and set it as the app instance
+        val twilightManager = FakeTwilightManager(rule.activity)
+        TwilightManager.setInstance(twilightManager)
+
+        // Verify that we're currently in day mode
+        onView(withId(R.id.text_night_mode)).check(matches(withText(STRING_DAY)))
+
+        // Set MODE_NIGHT_AUTO so that we will change to night mode automatically
+        setNightModeAndWait(rule, AppCompatDelegate.MODE_NIGHT_AUTO_TIME, setMode)
+        val newDelegate = rule.activity.delegate as AppCompatDelegateImpl
+
+        // Update the fake twilight manager to be in night and trigger a fake 'time' change
+        rule.runOnUiThread {
+            twilightManager.isNightForTest = true
+            newDelegate.autoTimeNightModeManager.onChange()
+        }
+
+        // Now wait until the Activity is destroyed (thus recreated)
+        waitForRecreation(rule)
+
+        // Check that the text has changed, signifying that night resources are being used
+        onView(withId(R.id.text_night_mode)).check(matches(withText(STRING_NIGHT)))
+    }
+
+    @Test
+    fun testNightModeAutoTimeRecreatesOnResume() {
+        // Create a fake TwilightManager and set it as the app instance
+        val twilightManager = FakeTwilightManager(rule.activity)
+        TwilightManager.setInstance(twilightManager)
+
+        // Set MODE_NIGHT_AUTO_TIME so that we will change to night mode automatically
+        setNightModeAndWait(rule, AppCompatDelegate.MODE_NIGHT_AUTO_TIME, setMode)
+
+        // Verify that we're currently in day mode
+        onView(withId(R.id.text_night_mode)).check(matches(withText(STRING_DAY)))
+
+        val resumeCompleteLatch = CountDownLatch(1)
+
+        rule.runOnUiThread {
+            val activity = rule.activity
+            val instrumentation = InstrumentationRegistry.getInstrumentation()
+            // Now fool the Activity into thinking that it has gone into the background
+            instrumentation.callActivityOnPause(activity)
+            instrumentation.callActivityOnStop(activity)
+
+            // Now update the twilight manager while the Activity is in the 'background'
+            twilightManager.isNightForTest = true
+
+            // Now tell the Activity that it has gone into the foreground again
+            instrumentation.callActivityOnStart(activity)
+            instrumentation.callActivityOnResume(activity)
+
+            resumeCompleteLatch.countDown()
+        }
+
+        resumeCompleteLatch.await()
+        // finally check that the text has changed, signifying that night resources are being used
+        onView(withId(R.id.text_night_mode)).check(matches(withText(STRING_NIGHT)))
+    }
+
+    @Test
+    fun testOnNightModeChangedCalled() {
+        val activity = rule.activity
+        // Set local night mode to YES
+        setNightModeAndWait(rule, MODE_NIGHT_YES, setMode)
+        // Assert that the Activity received a new value
+        assertEquals(MODE_NIGHT_YES, activity.lastNightModeAndReset)
+    }
+
+    @Test
+    fun testDialogDoesNotOverrideActivityConfiguration() {
+        // Set Activity local night mode to YES
+        setNightModeAndWaitForDestroy(rule, MODE_NIGHT_YES, setMode)
+
+        // Assert that the uiMode is as expected
+        assertConfigurationNightModeEquals(Configuration.UI_MODE_NIGHT_YES, rule.activity)
+
+        // Now show a AppCompatDialog
+        rule.runOnUiThread {
+            AppCompatDialog(rule.activity).show()
+        }
+
+        // Assert that the uiMode is unchanged
+        assertConfigurationNightModeEquals(Configuration.UI_MODE_NIGHT_YES, rule.activity)
+    }
+
+    @Test
+    fun testLoadingWebViewMaintainsConfiguration() {
+        // Set night mode and wait for the new Activity
+        setNightModeAndWaitForDestroy(rule, MODE_NIGHT_YES, setMode)
+
+        // Assert that the context still has a night themed configuration
+        assertConfigurationNightModeEquals(Configuration.UI_MODE_NIGHT_YES, rule.activity)
+
+        // Now load a WebView into the Activity
+        rule.runOnUiThread { WebView(rule.activity) }
+
+        // Now assert that the context still has a night themed configuration
+        assertConfigurationNightModeEquals(Configuration.UI_MODE_NIGHT_YES, rule.activity)
+    }
+
+    @Test
+    fun testDialogCleansUpAutoMode() {
+        rule.runOnUiThread {
+            val dialog = AppCompatDialog(rule.activity)
+            val delegate = dialog.delegate as AppCompatDelegateImpl
+
+            // Set the local night mode of the Dialog to be an AUTO mode
+            delegate.localNightMode = AppCompatDelegate.MODE_NIGHT_AUTO_TIME
+
+            // Now show and dismiss the dialog
+            dialog.show()
+            dialog.dismiss()
+
+            // Assert that the auto manager is destroyed (not listening)
+            assertFalse(delegate.autoTimeNightModeManager.isListening)
+        }
+    }
+
+    @Test
+    fun testOnConfigurationChangeNotCalled() {
+        var activity = rule.activity
+        // Set local night mode to YES
+        setNightModeAndWait(rule, MODE_NIGHT_YES, setMode)
+        // Assert that onConfigurationChange was not called on the original activity
+        assertNull(activity.lastConfigurationChangeAndClear)
+
+        activity = rule.activity
+        // Set local night mode back to NO
+        setNightModeAndWait(rule, MODE_NIGHT_NO, setMode)
+        // Assert that onConfigurationChange was not called
+        assertNull(activity.lastConfigurationChangeAndClear)
+    }
+
+    @After
+    fun cleanup() {
+        rule.finishActivity()
+        // Reset the default night mode
+        setNightModeAndWait(rule, MODE_NIGHT_NO, NightSetMode.DEFAULT)
+    }
+
+    private class FakeTwilightManager(context: Context) : TwilightManager(
+        context,
+        ContextCompat.getSystemService(context, LocationManager::class.java)!!
+    ) {
+        var isNightForTest: Boolean = false
+
+        override fun isNight(): Boolean {
+            return isNightForTest
+        }
+    }
+
+    companion object {
+        private const val STRING_DAY = "DAY"
+        private const val STRING_NIGHT = "NIGHT"
+
+        @Parameterized.Parameters
+        @JvmStatic
+        fun data() = listOf(NightSetMode.DEFAULT, NightSetMode.LOCAL)
+    }
+}
diff --git a/appcompat/src/main/java/androidx/appcompat/app/AppCompatDelegateImpl.java b/appcompat/src/main/java/androidx/appcompat/app/AppCompatDelegateImpl.java
index 34415de..5dc207f 100644
--- a/appcompat/src/main/java/androidx/appcompat/app/AppCompatDelegateImpl.java
+++ b/appcompat/src/main/java/androidx/appcompat/app/AppCompatDelegateImpl.java
@@ -136,6 +136,16 @@
 
     private static boolean sInstalledExceptionHandler;
 
+    /**
+     * AppCompat selectively uses applyOverrideConfiguration() for DayNight functionality.
+     * Unfortunately the framework has a few bugs around Resources instances on SDKs 21-25,
+     * resulting in the root Resources instance (i.e. Application) being modified when it
+     * shouldn't be. We can work around it by always calling applyOverrideConfiguration()
+     * where available.
+     */
+    private static final boolean sAlwaysOverrideConfiguration = Build.VERSION.SDK_INT >= 21
+            && Build.VERSION.SDK_INT <= 25;
+
     static final String EXCEPTION_HANDLER_MESSAGE_SUFFIX= ". If the resource you are"
             + " trying to use is a vector resource, you may be referencing it in an unsupported"
             + " way. See AppCompatDelegate.setCompatVectorFromResourcesEnabled() for more info.";
@@ -2255,7 +2265,7 @@
 
         final boolean activityHandlingUiMode = isActivityManifestHandlingUiMode();
 
-        if (newNightMode != applicationNightMode
+        if ((sAlwaysOverrideConfiguration || newNightMode != applicationNightMode)
                 && !activityHandlingUiMode
                 && Build.VERSION.SDK_INT >= 17
                 && !mBaseContextAttached