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