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