1 /* 2 * Copyright (C) 2016 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package android.support.v7.app; 18 19 import static android.support.test.espresso.Espresso.onView; 20 import static android.support.test.espresso.assertion.ViewAssertions.matches; 21 import static android.support.test.espresso.matcher.ViewMatchers.withId; 22 import static android.support.test.espresso.matcher.ViewMatchers.withText; 23 import static android.support.v7.app.NightModeActivity.TOP_ACTIVITY; 24 import static android.support.v7.testutils.TestUtilsMatchers.isBackground; 25 26 import static org.junit.Assert.assertFalse; 27 28 import android.app.Instrumentation; 29 import android.support.test.InstrumentationRegistry; 30 import android.support.test.filters.LargeTest; 31 import android.support.test.filters.SdkSuppress; 32 import android.support.v4.content.ContextCompat; 33 import android.support.v7.appcompat.test.R; 34 35 import org.junit.Before; 36 import org.junit.Test; 37 38 @LargeTest 39 @SdkSuppress(minSdkVersion = 14) 40 public class NightModeTestCase extends BaseInstrumentationTestCase<NightModeActivity> { 41 42 private static final String STRING_DAY = "DAY"; 43 private static final String STRING_NIGHT = "NIGHT"; 44 45 public NightModeTestCase() { 46 super(NightModeActivity.class); 47 } 48 49 @Before 50 public void setup() { 51 // By default we'll set the night mode to NO, which allows us to make better 52 // assumptions in the test below 53 AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO); 54 } 55 56 @Test 57 public void testLocalDayNightModeRecreatesActivity() throws Throwable { 58 // Verify first that we're in day mode 59 onView(withId(R.id.text_night_mode)).check(matches(withText(STRING_DAY))); 60 61 // Now force the local night mode to be yes (aka night mode) 62 setLocalNightModeAndWaitForRecreate( 63 mActivityTestRule.getActivity(), AppCompatDelegate.MODE_NIGHT_YES); 64 65 // Now check the text has changed, signifying that night resources are being used 66 onView(withId(R.id.text_night_mode)).check(matches(withText(STRING_NIGHT))); 67 } 68 69 @Test 70 public void testColorConvertedDrawableChangesWithNightMode() throws Throwable { 71 final NightModeActivity activity = mActivityTestRule.getActivity(); 72 final int dayColor = ContextCompat.getColor(activity, R.color.color_sky_day); 73 final int nightColor = ContextCompat.getColor(activity, R.color.color_sky_night); 74 75 // Loop through and switching from day to night and vice-versa multiple times. It needs 76 // to be looped since the issue is with drawable caching, therefore we need to prime the 77 // cache for the issue to happen 78 for (int i = 0; i < 5; i++) { 79 // First force it to not be night mode 80 setLocalNightModeAndWaitForRecreate(TOP_ACTIVITY, AppCompatDelegate.MODE_NIGHT_NO); 81 // ... and verify first that we're in day mode 82 onView(withId(R.id.view_background)).check(matches(isBackground(dayColor))); 83 84 // Now force the local night mode to be yes (aka night mode) 85 setLocalNightModeAndWaitForRecreate(TOP_ACTIVITY, AppCompatDelegate.MODE_NIGHT_YES); 86 // ... and verify first that we're in night mode 87 onView(withId(R.id.view_background)).check(matches(isBackground(nightColor))); 88 } 89 } 90 91 @Test 92 public void testNightModeAutoRecreatesOnTimeChange() throws Throwable { 93 // Create a fake TwilightManager and set it as the app instance 94 final FakeTwilightManager twilightManager = new FakeTwilightManager(); 95 TwilightManager.setInstance(twilightManager); 96 97 final NightModeActivity activity = mActivityTestRule.getActivity(); 98 final AppCompatDelegateImplV14 delegate = (AppCompatDelegateImplV14) activity.getDelegate(); 99 100 // Verify that we're currently in day mode 101 onView(withId(R.id.text_night_mode)).check(matches(withText(STRING_DAY))); 102 103 // Now set MODE_NIGHT_AUTO so that we will change to night mode automatically 104 setLocalNightModeAndWaitForRecreate(activity, AppCompatDelegate.MODE_NIGHT_AUTO); 105 106 // Assert that the original Activity has not been destroyed yet 107 assertFalse(activity.isDestroyed()); 108 109 // Now update the fake twilight manager to be in night and trigger a fake 'time' change 110 mActivityTestRule.runOnUiThread(new Runnable() { 111 @Override 112 public void run() { 113 twilightManager.setIsNight(true); 114 delegate.getAutoNightModeManager().dispatchTimeChanged(); 115 } 116 }); 117 118 // Now wait for the recreate 119 getInstrumentation().waitForIdleSync(); 120 121 // Now check that the text has changed, signifying that night resources are being used 122 onView(withId(R.id.text_night_mode)).check(matches(withText(STRING_NIGHT))); 123 } 124 125 @Test 126 public void testNightModeAutoRecreatesOnResume() throws Throwable { 127 // Create a fake TwilightManager and set it as the app instance 128 final FakeTwilightManager twilightManager = new FakeTwilightManager(); 129 TwilightManager.setInstance(twilightManager); 130 131 final NightModeActivity activity = getActivity(); 132 133 // Set MODE_NIGHT_AUTO so that we will change to night mode automatically 134 setLocalNightModeAndWaitForRecreate(activity, AppCompatDelegate.MODE_NIGHT_AUTO); 135 // Verify that we're currently in day mode 136 onView(withId(R.id.text_night_mode)).check(matches(withText(STRING_DAY))); 137 138 mActivityTestRule.runOnUiThread(new Runnable() { 139 @Override 140 public void run() { 141 final Instrumentation instrumentation = getInstrumentation(); 142 // Now fool the Activity into thinking that it has gone into the background 143 instrumentation.callActivityOnPause(activity); 144 instrumentation.callActivityOnStop(activity); 145 146 // Now update the twilight manager while the Activity is in the 'background' 147 twilightManager.setIsNight(true); 148 149 // Now tell the Activity that it has gone into the foreground again 150 instrumentation.callActivityOnStart(activity); 151 instrumentation.callActivityOnResume(activity); 152 } 153 }); 154 155 // finally check that the text has changed, signifying that night resources are being used 156 onView(withId(R.id.text_night_mode)).check(matches(withText(STRING_NIGHT))); 157 } 158 159 private static class FakeTwilightManager extends TwilightManager { 160 private boolean mIsNight; 161 162 FakeTwilightManager() { 163 super(null, null); 164 } 165 166 @Override 167 boolean isNight() { 168 return mIsNight; 169 } 170 171 void setIsNight(boolean night) { 172 mIsNight = night; 173 } 174 } 175 176 private void setLocalNightModeAndWaitForRecreate(final AppCompatActivity activity, 177 @AppCompatDelegate.NightMode final int nightMode) throws Throwable { 178 final Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation(); 179 mActivityTestRule.runOnUiThread(new Runnable() { 180 @Override 181 public void run() { 182 activity.getDelegate().setLocalNightMode(nightMode); 183 } 184 }); 185 instrumentation.waitForIdleSync(); 186 } 187 } 188