Merge "Night display status restores on reboot/user change" into oc-dev
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index f215ae7..024738a 100755
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -6789,7 +6789,8 @@
* Represented as milliseconds from midnight (e.g. 79200000 == 10pm).
* @hide
*/
- public static final String NIGHT_DISPLAY_CUSTOM_START_TIME = "night_display_custom_start_time";
+ public static final String NIGHT_DISPLAY_CUSTOM_START_TIME =
+ "night_display_custom_start_time";
/**
* Custom time when Night display is scheduled to deactivate.
@@ -6799,6 +6800,14 @@
public static final String NIGHT_DISPLAY_CUSTOM_END_TIME = "night_display_custom_end_time";
/**
+ * Time in milliseconds (since epoch) when Night display was last activated. Use to decide
+ * whether to apply the current activated state after a reboot or user change.
+ * @hide
+ */
+ public static final String NIGHT_DISPLAY_LAST_ACTIVATED_TIME =
+ "night_display_last_activated_time";
+
+ /**
* Names of the service components that the current user has explicitly allowed to
* be a VR mode listener, separated by ':'.
*
@@ -7024,6 +7033,7 @@
NIGHT_DISPLAY_CUSTOM_END_TIME,
NIGHT_DISPLAY_COLOR_TEMPERATURE,
NIGHT_DISPLAY_AUTO_MODE,
+ NIGHT_DISPLAY_LAST_ACTIVATED_TIME,
NIGHT_DISPLAY_ACTIVATED,
SYNC_PARENT_SOUNDS,
CAMERA_DOUBLE_TWIST_TO_FLIP_ENABLED,
diff --git a/services/core/java/com/android/server/display/NightDisplayService.java b/services/core/java/com/android/server/display/NightDisplayService.java
index d1275bb..7849896 100644
--- a/services/core/java/com/android/server/display/NightDisplayService.java
+++ b/services/core/java/com/android/server/display/NightDisplayService.java
@@ -47,7 +47,6 @@
import com.android.server.twilight.TwilightListener;
import com.android.server.twilight.TwilightManager;
import com.android.server.twilight.TwilightState;
-import com.android.server.vr.VrManagerService;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.Calendar;
@@ -62,7 +61,6 @@
implements NightDisplayController.Callback {
private static final String TAG = "NightDisplayService";
- private static final boolean DEBUG = false;
/**
* The transition time, in milliseconds, for Night Display to turn on/off.
@@ -151,8 +149,9 @@
@Override
public void onBootPhase(int phase) {
- if (phase == PHASE_SYSTEM_SERVICES_READY) {
- IVrManager vrManager = (IVrManager) getBinderService(Context.VR_SERVICE);
+ if (phase >= PHASE_SYSTEM_SERVICES_READY) {
+ final IVrManager vrManager = IVrManager.Stub.asInterface(
+ getBinderService(Context.VR_SERVICE));
if (vrManager != null) {
try {
vrManager.registerListener(mVrStateCallbacks);
@@ -160,7 +159,9 @@
Slog.e(TAG, "Failed to register VR mode state listener: " + e);
}
}
- } else if (phase == PHASE_BOOT_COMPLETED) {
+ }
+
+ if (phase >= PHASE_BOOT_COMPLETED) {
mBootCompleted = true;
// Register listeners now that boot is complete.
@@ -284,12 +285,18 @@
if (mIsActivated == null || mIsActivated != activated) {
Slog.i(TAG, activated ? "Turning on night display" : "Turning off night display");
- if (mAutoMode != null) {
- mAutoMode.onActivated(activated);
+ if (mIsActivated != null) {
+ Secure.putLongForUser(getContext().getContentResolver(),
+ Secure.NIGHT_DISPLAY_LAST_ACTIVATED_TIME, System.currentTimeMillis(),
+ mCurrentUser);
}
mIsActivated = activated;
+ if (mAutoMode != null) {
+ mAutoMode.onActivated(activated);
+ }
+
applyTint(false);
}
}
@@ -401,7 +408,7 @@
* Set the color transformation {@code MATRIX_NIGHT} to the given color temperature.
*
* @param colorTemperature color temperature in Kelvin
- * @param outTemp the 4x4 display transformation matrix for that color temperature
+ * @param outTemp the 4x4 display transformation matrix for that color temperature
*/
private void setMatrix(int colorTemperature, float[] outTemp) {
if (outTemp.length != 16) {
@@ -423,8 +430,22 @@
outTemp[10] = blue;
}
+ private Calendar getLastActivatedTime() {
+ final ContentResolver cr = getContext().getContentResolver();
+ final long lastActivatedTimeMillis = Secure.getLongForUser(
+ cr, Secure.NIGHT_DISPLAY_LAST_ACTIVATED_TIME, -1, mCurrentUser);
+ if (lastActivatedTimeMillis < 0) {
+ return null;
+ }
+
+ final Calendar lastActivatedTime = Calendar.getInstance();
+ lastActivatedTime.setTimeInMillis(lastActivatedTimeMillis);
+ return lastActivatedTime;
+ }
+
private abstract class AutoMode implements NightDisplayController.Callback {
public abstract void onStart();
+
public abstract void onStop();
}
@@ -438,7 +459,7 @@
private Calendar mLastActivatedTime;
- public CustomAutoMode() {
+ CustomAutoMode() {
mAlarmManager = (AlarmManager) getContext().getSystemService(Context.ALARM_SERVICE);
mTimeChangedReceiver = new BroadcastReceiver() {
@Override
@@ -452,10 +473,10 @@
final Calendar now = Calendar.getInstance();
final Calendar startTime = mStartTime.getDateTimeBefore(now);
final Calendar endTime = mEndTime.getDateTimeAfter(startTime);
- final boolean activated = now.before(endTime);
- boolean setActivated = mIsActivated == null || mLastActivatedTime == null;
- if (!setActivated && mIsActivated != activated) {
+ boolean activate = now.before(endTime);
+ if (mLastActivatedTime != null) {
+ // Convert mLastActivatedTime to the current timezone if needed.
final TimeZone currentTimeZone = now.getTimeZone();
if (!currentTimeZone.equals(mLastActivatedTime.getTimeZone())) {
final int year = mLastActivatedTime.get(Calendar.YEAR);
@@ -470,17 +491,16 @@
mLastActivatedTime.set(Calendar.MINUTE, minute);
}
- if (mIsActivated) {
- setActivated = now.before(mStartTime.getDateTimeBefore(mLastActivatedTime))
- || now.after(mEndTime.getDateTimeAfter(mLastActivatedTime));
- } else {
- setActivated = now.before(mEndTime.getDateTimeBefore(mLastActivatedTime))
- || now.after(mStartTime.getDateTimeAfter(mLastActivatedTime));
+ // Maintain the existing activated state if within the current period.
+ if (mLastActivatedTime.before(now)
+ && mLastActivatedTime.after(startTime)
+ && (mLastActivatedTime.after(endTime) || now.before(endTime))) {
+ activate = mController.isActivated();
}
}
- if (setActivated) {
- mController.setActivated(activated);
+ if (mIsActivated == null || mIsActivated != activate) {
+ mController.setActivated(activate);
}
updateNextAlarm(mIsActivated, now);
}
@@ -502,6 +522,8 @@
mStartTime = mController.getCustomStartTime();
mEndTime = mController.getCustomEndTime();
+ mLastActivatedTime = getLastActivatedTime();
+
// Force an update to initialize state.
updateActivated();
}
@@ -516,11 +538,8 @@
@Override
public void onActivated(boolean activated) {
- final Calendar now = Calendar.getInstance();
- if (mIsActivated != null) {
- mLastActivatedTime = now;
- }
- updateNextAlarm(activated, now);
+ mLastActivatedTime = getLastActivatedTime();
+ updateNextAlarm(activated, Calendar.getInstance());
}
@Override
@@ -550,33 +569,33 @@
private Calendar mLastActivatedTime;
- public TwilightAutoMode() {
+ TwilightAutoMode() {
mTwilightManager = getLocalService(TwilightManager.class);
}
private void updateActivated(TwilightState state) {
- final boolean isNight = state != null && state.isNight();
- boolean setActivated = mIsActivated == null || mIsActivated != isNight;
- if (setActivated && state != null && mLastActivatedTime != null) {
+ boolean activate = state != null && state.isNight();
+ if (state != null && mLastActivatedTime != null) {
+ final Calendar now = Calendar.getInstance();
final Calendar sunrise = state.sunrise();
final Calendar sunset = state.sunset();
- if (sunrise.before(sunset)) {
- setActivated = mLastActivatedTime.before(sunrise)
- || mLastActivatedTime.after(sunset);
- } else {
- setActivated = mLastActivatedTime.before(sunset)
- || mLastActivatedTime.after(sunrise);
+
+ // Maintain the existing activated state if within the current period.
+ if (mLastActivatedTime.before(now)
+ && (mLastActivatedTime.after(sunrise) ^ mLastActivatedTime.after(sunset))) {
+ activate = mController.isActivated();
}
}
- if (setActivated) {
- mController.setActivated(isNight);
+ if (mIsActivated == null || mIsActivated != activate) {
+ mController.setActivated(activate);
}
}
@Override
public void onStart() {
mTwilightManager.registerListener(this, mHandler);
+ mLastActivatedTime = getLastActivatedTime();
// Force an update to initialize state.
updateActivated(mTwilightManager.getLastTwilightState());
@@ -591,7 +610,7 @@
@Override
public void onActivated(boolean activated) {
if (mIsActivated != null) {
- mLastActivatedTime = Calendar.getInstance();
+ mLastActivatedTime = getLastActivatedTime();
}
}
diff --git a/services/core/java/com/android/server/twilight/TwilightState.java b/services/core/java/com/android/server/twilight/TwilightState.java
index a12965d..30a8ccc 100644
--- a/services/core/java/com/android/server/twilight/TwilightState.java
+++ b/services/core/java/com/android/server/twilight/TwilightState.java
@@ -31,7 +31,7 @@
private final long mSunriseTimeMillis;
private final long mSunsetTimeMillis;
- TwilightState(long sunriseTimeMillis, long sunsetTimeMillis) {
+ public TwilightState(long sunriseTimeMillis, long sunsetTimeMillis) {
mSunriseTimeMillis = sunriseTimeMillis;
mSunsetTimeMillis = sunsetTimeMillis;
}
diff --git a/services/tests/servicestests/AndroidManifest.xml b/services/tests/servicestests/AndroidManifest.xml
index cc682c4..fa72416 100644
--- a/services/tests/servicestests/AndroidManifest.xml
+++ b/services/tests/servicestests/AndroidManifest.xml
@@ -18,6 +18,7 @@
package="com.android.frameworks.servicestests">
<uses-permission android:name="android.permission.READ_LOGS" />
+ <uses-permission android:name="android.permission.ACCESS_VR_MANAGER" />
<uses-permission android:name="android.permission.ACCOUNT_MANAGER" />
<uses-permission android:name="android.permission.WRITE_SETTINGS" />
<uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" />
diff --git a/services/tests/servicestests/src/com/android/server/NightDisplayServiceTest.java b/services/tests/servicestests/src/com/android/server/NightDisplayServiceTest.java
new file mode 100644
index 0000000..9a9c243
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/NightDisplayServiceTest.java
@@ -0,0 +1,620 @@
+/*
+ * Copyright (C) 2017 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 com.android.server;
+
+import android.app.ActivityManager;
+import android.app.AlarmManager;
+import android.content.Context;
+import android.content.ContextWrapper;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.provider.Settings.Secure;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.runner.AndroidJUnit4;
+import android.test.mock.MockContentResolver;
+
+import com.android.internal.app.NightDisplayController;
+import com.android.internal.app.NightDisplayController.LocalTime;
+import com.android.internal.util.test.FakeSettingsProvider;
+import com.android.server.display.DisplayTransformManager;
+import com.android.server.display.NightDisplayService;
+import com.android.server.twilight.TwilightManager;
+import com.android.server.twilight.TwilightState;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mockito;
+
+import java.util.Calendar;
+
+import static com.google.common.truth.Truth.assertWithMessage;
+import static org.mockito.Mockito.doReturn;
+
+@RunWith(AndroidJUnit4.class)
+public class NightDisplayServiceTest {
+
+ private Context mContext;
+ private int mUserId;
+
+ private TwilightManager mTwilightManager;
+
+ private NightDisplayController mNightDisplayController;
+ private NightDisplayService mNightDisplayService;
+
+ @Before
+ public void setUp() {
+ mContext = Mockito.spy(new ContextWrapper(InstrumentationRegistry.getTargetContext()));
+ mUserId = ActivityManager.getCurrentUser();
+
+ doReturn(mContext).when(mContext).getApplicationContext();
+
+ final MockContentResolver cr = new MockContentResolver(mContext);
+ cr.addProvider(Settings.AUTHORITY, new FakeSettingsProvider());
+ doReturn(cr).when(mContext).getContentResolver();
+
+ final AlarmManager am = Mockito.mock(AlarmManager.class);
+ doReturn(am).when(mContext).getSystemService(Context.ALARM_SERVICE);
+
+ final DisplayTransformManager dtm = Mockito.mock(DisplayTransformManager.class);
+ LocalServices.addService(DisplayTransformManager.class, dtm);
+
+ mTwilightManager = Mockito.mock(TwilightManager.class);
+ LocalServices.addService(TwilightManager.class, mTwilightManager);
+
+ mNightDisplayController = new NightDisplayController(mContext, mUserId);
+ mNightDisplayService = new NightDisplayService(mContext);
+ }
+
+ @After
+ public void tearDown() {
+ LocalServices.removeServiceForTest(DisplayTransformManager.class);
+ LocalServices.removeServiceForTest(TwilightManager.class);
+
+ mNightDisplayService = null;
+ mNightDisplayController = null;
+
+ mTwilightManager = null;
+
+ mUserId = UserHandle.USER_NULL;
+ mContext = null;
+ }
+
+ @Test
+ public void customSchedule_whenStartedAfterNight_ifOffAfterNight_turnsOff() {
+ setAutoModeCustom(-120 /* startTimeOffset */, -60 /* endTimeOffset */);
+ setActivated(false /* activated */, -30 /* lastActivatedTimeOffset */);
+
+ startService();
+ assertActivated(false /* activated */);
+ }
+
+ @Test
+ public void customSchedule_whenStartedAfterNight_ifOffBeforeNight_turnsOff() {
+ setAutoModeCustom(-120 /* startTimeOffset */, -60 /* endTimeOffset */);
+ setActivated(false /* activated */, -180 /* lastActivatedTimeOffset */);
+
+ startService();
+ assertActivated(false /* activated */);
+ }
+
+ @Test
+ public void customSchedule_whenStartedAfterNight_ifOffDuringNight_turnsOff() {
+ setAutoModeCustom(-120 /* startTimeOffset */, -60 /* endTimeOffset */);
+ setActivated(false /* activated */, -90 /* lastActivatedTimeOffset */);
+
+ startService();
+ assertActivated(false /* activated */);
+ }
+
+ @Test
+ public void customSchedule_whenStartedAfterNight_ifOffInFuture_turnsOff() {
+ setAutoModeCustom(-120 /* startTimeOffset */, -60 /* endTimeOffset */);
+ setActivated(false /* activated */, 30 /* lastActivatedTimeOffset */);
+
+ startService();
+ assertActivated(false /* activated */);
+ }
+
+ @Test
+ public void customSchedule_whenStartedAfterNight_ifOnAfterNight_turnsOn() {
+ setAutoModeCustom(-120 /* startTimeOffset */, -60 /* endTimeOffset */);
+ setActivated(true /* activated */, -30 /* lastActivatedTimeOffset */);
+
+ startService();
+ assertActivated(true /* activated */);
+ }
+
+ @Test
+ public void customSchedule_whenStartedAfterNight_ifOnBeforeNight_turnsOff() {
+ setAutoModeCustom(-120 /* startTimeOffset */, -60 /* endTimeOffset */);
+ setActivated(true /* activated */, -180 /* lastActivatedTimeOffset */);
+
+ startService();
+ assertActivated(false /* activated */);
+ }
+
+ @Test
+ public void customSchedule_whenStartedAfterNight_ifOnDuringNight_turnsOff() {
+ setAutoModeCustom(-120 /* startTimeOffset */, -60 /* endTimeOffset */);
+ setActivated(true /* activated */, -90 /* lastActivatedTimeOffset */);
+
+ startService();
+ assertActivated(false /* activated */);
+ }
+
+ @Test
+ public void customSchedule_whenStartedAfterNight_ifOnInFuture_turnsOff() {
+ setAutoModeCustom(-120 /* startTimeOffset */, -60 /* endTimeOffset */);
+ setActivated(true /* activated */, 30 /* lastActivatedTimeOffset */);
+
+ startService();
+ assertActivated(false /* activated */);
+ }
+
+ @Test
+ public void customSchedule_whenStartedBeforeNight_ifOffAfterNight_turnsOff() {
+ setAutoModeCustom(60 /* startTimeOffset */, 120 /* endTimeOffset */);
+ setActivated(false /* activated */, 180 /* lastActivatedTimeOffset */);
+
+ startService();
+ assertActivated(false /* activated */);
+ }
+
+ @Test
+ public void customSchedule_whenStartedBeforeNight_ifOffBeforeNight_turnsOff() {
+ setAutoModeCustom(60 /* startTimeOffset */, 120 /* endTimeOffset */);
+ setActivated(false /* activated */, 30 /* lastActivatedTimeOffset */);
+
+ startService();
+ assertActivated(false /* activated */);
+ }
+
+ @Test
+ public void customSchedule_whenStartedBeforeNight_ifOffDuringNight_turnsOff() {
+ setAutoModeCustom(60 /* startTimeOffset */, 120 /* endTimeOffset */);
+ setActivated(false /* activated */, 90 /* lastActivatedTimeOffset */);
+
+ startService();
+ assertActivated(false /* activated */);
+ }
+
+ @Test
+ public void customSchedule_whenStartedBeforeNight_ifOffInPast_turnsOff() {
+ setAutoModeCustom(60 /* startTimeOffset */, 120 /* endTimeOffset */);
+ setActivated(false /* activated */, -30 /* lastActivatedTimeOffset */);
+
+ startService();
+ assertActivated(false /* activated */);
+ }
+
+ @Test
+ public void customSchedule_whenStartedBeforeNight_ifOnAfterNight_turnsOff() {
+ setAutoModeCustom(60 /* startTimeOffset */, 120 /* endTimeOffset */);
+ setActivated(true /* activated */, 180 /* lastActivatedTimeOffset */);
+
+ startService();
+ assertActivated(false /* activated */);
+ }
+
+ @Test
+ public void customSchedule_whenStartedBeforeNight_ifOnBeforeNight_turnsOff() {
+ setAutoModeCustom(60 /* startTimeOffset */, 120 /* endTimeOffset */);
+ setActivated(true /* activated */, 30 /* lastActivatedTimeOffset */);
+
+ startService();
+ assertActivated(false /* activated */);
+ }
+
+ @Test
+ public void customSchedule_whenStartedBeforeNight_ifOnDuringNight_turnsOff() {
+ setAutoModeCustom(60 /* startTimeOffset */, 120 /* endTimeOffset */);
+ setActivated(true /* activated */, 90 /* lastActivatedTimeOffset */);
+
+ startService();
+ assertActivated(false /* activated */);
+ }
+
+ @Test
+ public void customSchedule_whenStartedBeforeNight_ifOnInPast_turnsOn() {
+ setAutoModeCustom(60 /* startTimeOffset */, 120 /* endTimeOffset */);
+ setActivated(true /* activated */, -30 /* lastActivatedTimeOffset */);
+
+ startService();
+ assertActivated(true /* activated */);
+ }
+
+ @Test
+ public void customSchedule_whenStartedDuringNight_ifOffAfterNight_turnsOn() {
+ setAutoModeCustom(-60 /* startTimeOffset */, 60 /* endTimeOffset */);
+ setActivated(false /* activated */, 90 /* lastActivatedTimeOffset */);
+
+ startService();
+ assertActivated(true /* activated */);
+ }
+
+ @Test
+ public void customSchedule_whenStartedDuringNight_ifOffBeforeNight_turnsOn() {
+ setAutoModeCustom(-60 /* startTimeOffset */, 60 /* endTimeOffset */);
+ setActivated(false /* activated */, -90 /* lastActivatedTimeOffset */);
+
+ startService();
+ assertActivated(true /* activated */);
+ }
+
+ @Test
+ public void customSchedule_whenStartedDuringNight_ifOffDuringNightInFuture_turnsOn() {
+ setAutoModeCustom(-60 /* startTimeOffset */, 60 /* endTimeOffset */);
+ setActivated(false /* activated */, 30 /* lastActivatedTimeOffset */);
+
+ startService();
+ assertActivated(true /* activated */);
+ }
+
+ @Test
+ public void customSchedule_whenStartedDuringNight_ifOffDuringNightInPast_turnsOff() {
+ setAutoModeCustom(-60 /* startTimeOffset */, 60 /* endTimeOffset */);
+ setActivated(false /* activated */, -30 /* lastActivatedTimeOffset */);
+
+ startService();
+ assertActivated(false /* activated */);
+ }
+
+ @Test
+ public void customSchedule_whenStartedDuringNight_ifOnAfterNight_turnsOn() {
+ setAutoModeCustom(-60 /* startTimeOffset */, 60 /* endTimeOffset */);
+ setActivated(true /* activated */, 90 /* lastActivatedTimeOffset */);
+
+ startService();
+ assertActivated(true /* activated */);
+ }
+
+ @Test
+ public void customSchedule_whenStartedDuringNight_ifOnBeforeNight_turnsOn() {
+ setAutoModeCustom(-60 /* startTimeOffset */, 60 /* endTimeOffset */);
+ setActivated(true /* activated */, -90 /* lastActivatedTimeOffset */);
+
+ startService();
+ assertActivated(true /* activated */);
+ }
+
+ @Test
+ public void customSchedule_whenStartedDuringNight_ifOnDuringNightInFuture_turnsOn() {
+ setAutoModeCustom(-60 /* startTimeOffset */, 60 /* endTimeOffset */);
+ setActivated(true /* activated */, 30 /* lastActivatedTimeOffset */);
+
+ startService();
+ assertActivated(true /* activated */);
+ }
+
+ @Test
+ public void customSchedule_whenStartedDuringNight_ifOnDuringNightInPast_turnsOn() {
+ setAutoModeCustom(-60 /* startTimeOffset */, 60 /* endTimeOffset */);
+ setActivated(true /* activated */, -30 /* lastActivatedTimeOffset */);
+
+ startService();
+ assertActivated(true /* activated */);
+ }
+
+ @Test
+ public void twilightSchedule_whenStartedAfterNight_ifOffAfterNight_turnsOff() {
+ setAutoModeTwilight(-120 /* sunsetOffset */, -60 /* sunriseOffset */);
+ setActivated(false /* activated */, -30 /* lastActivatedTimeOffset */);
+
+ startService();
+ assertActivated(false /* activated */);
+ }
+
+ @Test
+ public void twilightSchedule_whenStartedAfterNight_ifOffBeforeNight_turnsOff() {
+ setAutoModeTwilight(-120 /* sunsetOffset */, -60 /* sunriseOffset */);
+ setActivated(false /* activated */, -180 /* lastActivatedTimeOffset */);
+
+ startService();
+ assertActivated(false /* activated */);
+ }
+
+ @Test
+ public void twilightSchedule_whenStartedAfterNight_ifOffDuringNight_turnsOff() {
+ setAutoModeTwilight(-120 /* sunsetOffset */, -60 /* sunriseOffset */);
+ setActivated(false /* activated */, -90 /* lastActivatedTimeOffset */);
+
+ startService();
+ assertActivated(false /* activated */);
+ }
+
+ @Test
+ public void twilightSchedule_whenStartedAfterNight_ifOffInFuture_turnsOff() {
+ setAutoModeTwilight(-120 /* sunsetOffset */, -60 /* sunriseOffset */);
+ setActivated(false /* activated */, 30 /* lastActivatedTimeOffset */);
+
+ startService();
+ assertActivated(false /* activated */);
+ }
+
+ @Test
+ public void twilightSchedule_whenStartedAfterNight_ifOnAfterNight_turnsOn() {
+ setAutoModeTwilight(-120 /* sunsetOffset */, -60 /* sunriseOffset */);
+ setActivated(true /* activated */, -30 /* lastActivatedTimeOffset */);
+
+ startService();
+ assertActivated(true /* activated */);
+ }
+
+ @Test
+ public void twilightSchedule_whenStartedAfterNight_ifOnBeforeNight_turnsOff() {
+ setAutoModeTwilight(-120 /* sunsetOffset */, -60 /* sunriseOffset */);
+ setActivated(true /* activated */, -180 /* lastActivatedTimeOffset */);
+
+ startService();
+ assertActivated(false /* activated */);
+ }
+
+ @Test
+ public void twilightSchedule_whenStartedAfterNight_ifOnDuringNight_turnsOff() {
+ setAutoModeTwilight(-120 /* sunsetOffset */, -60 /* sunriseOffset */);
+ setActivated(true /* activated */, -90 /* lastActivatedTimeOffset */);
+
+ startService();
+ assertActivated(false /* activated */);
+ }
+
+ @Test
+ public void twilightSchedule_whenStartedAfterNight_ifOnInFuture_turnsOff() {
+ setAutoModeTwilight(-120 /* sunsetOffset */, -60 /* sunriseOffset */);
+ setActivated(true /* activated */, 30 /* lastActivatedTimeOffset */);
+
+ startService();
+ assertActivated(false /* activated */);
+ }
+
+ @Test
+ public void twilightSchedule_whenStartedBeforeNight_ifOffAfterNight_turnsOff() {
+ setAutoModeTwilight(60 /* sunsetOffset */, 120 /* sunriseOffset */);
+ setActivated(false /* activated */, 180 /* lastActivatedTimeOffset */);
+
+ startService();
+ assertActivated(false /* activated */);
+ }
+
+ @Test
+ public void twilightSchedule_whenStartedBeforeNight_ifOffBeforeNight_turnsOff() {
+ setAutoModeTwilight(60 /* sunsetOffset */, 120 /* sunriseOffset */);
+ setActivated(false /* activated */, 30 /* lastActivatedTimeOffset */);
+
+ startService();
+ assertActivated(false /* activated */);
+ }
+
+ @Test
+ public void twilightSchedule_whenStartedBeforeNight_ifOffDuringNight_turnsOff() {
+ setAutoModeTwilight(60 /* sunsetOffset */, 120 /* sunriseOffset */);
+ setActivated(false /* activated */, 90 /* lastActivatedTimeOffset */);
+
+ startService();
+ assertActivated(false /* activated */);
+ }
+
+ @Test
+ public void twilightSchedule_whenStartedBeforeNight_ifOffInPast_turnsOff() {
+ setAutoModeTwilight(60 /* sunsetOffset */, 120 /* sunriseOffset */);
+ setActivated(false /* activated */, -30 /* lastActivatedTimeOffset */);
+
+ startService();
+ assertActivated(false /* activated */);
+ }
+
+ @Test
+ public void twilightSchedule_whenStartedBeforeNight_ifOnAfterNight_turnsOff() {
+ setAutoModeTwilight(60 /* sunsetOffset */, 120 /* sunriseOffset */);
+ setActivated(true /* activated */, 180 /* lastActivatedTimeOffset */);
+
+ startService();
+ assertActivated(false /* activated */);
+ }
+
+ @Test
+ public void twilightSchedule_whenStartedBeforeNight_ifOnBeforeNight_turnsOff() {
+ setAutoModeTwilight(60 /* sunsetOffset */, 120 /* sunriseOffset */);
+ setActivated(true /* activated */, 30 /* lastActivatedTimeOffset */);
+
+ startService();
+ assertActivated(false /* activated */);
+ }
+
+ @Test
+ public void twilightSchedule_whenStartedBeforeNight_ifOnDuringNight_turnsOff() {
+ setAutoModeTwilight(60 /* sunsetOffset */, 120 /* sunriseOffset */);
+ setActivated(true /* activated */, 90 /* lastActivatedTimeOffset */);
+
+ startService();
+ assertActivated(false /* activated */);
+ }
+
+ @Test
+ public void twilightSchedule_whenStartedBeforeNight_ifOnInPast_turnsOn() {
+ setAutoModeTwilight(60 /* sunsetOffset */, 120 /* sunriseOffset */);
+ setActivated(true /* activated */, -30 /* lastActivatedTimeOffset */);
+
+ startService();
+ assertActivated(true /* activated */);
+ }
+
+ @Test
+ public void twilightSchedule_whenStartedDuringNight_ifOffAfterNight_turnsOn() {
+ setAutoModeTwilight(-60 /* sunsetOffset */, 60 /* sunriseOffset */);
+ setActivated(false /* activated */, 90 /* lastActivatedTimeOffset */);
+
+ startService();
+ assertActivated(true /* activated */);
+ }
+
+ @Test
+ public void twilightSchedule_whenStartedDuringNight_ifOffBeforeNight_turnsOn() {
+ setAutoModeTwilight(-60 /* sunsetOffset */, 60 /* sunriseOffset */);
+ setActivated(false /* activated */, -90 /* lastActivatedTimeOffset */);
+
+ startService();
+ assertActivated(true /* activated */);
+ }
+
+ @Test
+ public void twilightSchedule_whenStartedDuringNight_ifOffDuringNightInFuture_turnsOn() {
+ setAutoModeTwilight(-60 /* sunsetOffset */, 60 /* sunriseOffset */);
+ setActivated(false /* activated */, 30 /* lastActivatedTimeOffset */);
+
+ startService();
+ assertActivated(true /* activated */);
+ }
+
+ @Test
+ public void twilightSchedule_whenStartedDuringNight_ifOffDuringNightInPast_turnsOff() {
+ setAutoModeTwilight(-60 /* sunsetOffset */, 60 /* sunriseOffset */);
+ setActivated(false /* activated */, -30 /* lastActivatedTimeOffset */);
+
+ startService();
+ assertActivated(false /* activated */);
+ }
+
+ @Test
+ public void twilightSchedule_whenStartedDuringNight_ifOnAfterNight_turnsOn() {
+ setAutoModeTwilight(-60 /* sunsetOffset */, 60 /* sunriseOffset */);
+ setActivated(true /* activated */, 90 /* lastActivatedTimeOffset */);
+
+ startService();
+ assertActivated(true /* activated */);
+ }
+
+ @Test
+ public void twilightSchedule_whenStartedDuringNight_ifOnBeforeNight_turnsOn() {
+ setAutoModeTwilight(-60 /* sunsetOffset */, 60 /* sunriseOffset */);
+ setActivated(true /* activated */, -90 /* lastActivatedTimeOffset */);
+
+ startService();
+ assertActivated(true /* activated */);
+ }
+
+ @Test
+ public void twilightSchedule_whenStartedDuringNight_ifOnDuringNightInFuture_turnsOn() {
+ setAutoModeTwilight(-60 /* sunsetOffset */, 60 /* sunriseOffset */);
+ setActivated(true /* activated */, 30 /* lastActivatedTimeOffset */);
+
+ startService();
+ assertActivated(true /* activated */);
+ }
+
+ @Test
+ public void twilightSchedule_whenStartedDuringNight_ifOnDuringNightInPast_turnsOn() {
+ setAutoModeTwilight(-60 /* sunsetOffset */, 60 /* sunriseOffset */);
+ setActivated(true /* activated */, -30 /* lastActivatedTimeOffset */);
+
+ startService();
+ assertActivated(true /* activated */);
+ }
+
+ /**
+ * Convenience for making a {@link LocalTime} instance with an offset relative to now.
+ *
+ * @param offsetMinutes the offset relative to now (in minutes)
+ * @return the LocalTime instance
+ */
+ private LocalTime getLocalTimeRelativeToNow(int offsetMinutes) {
+ final Calendar c = Calendar.getInstance();
+ c.add(Calendar.MINUTE, offsetMinutes);
+ return new LocalTime(c.get(Calendar.HOUR_OF_DAY), c.get(Calendar.MINUTE));
+ }
+
+ /**
+ * Configures Night display to use a custom schedule.
+ *
+ * @param startTimeOffset the offset relative to now to activate Night display (in minutes)
+ * @param endTimeOffset the offset relative to now to deactivate Night display (in minutes)
+ */
+ private void setAutoModeCustom(int startTimeOffset, int endTimeOffset) {
+ mNightDisplayController.setAutoMode(NightDisplayController.AUTO_MODE_CUSTOM);
+ mNightDisplayController.setCustomStartTime(getLocalTimeRelativeToNow(startTimeOffset));
+ mNightDisplayController.setCustomEndTime(getLocalTimeRelativeToNow(endTimeOffset));
+ }
+
+ /**
+ * Configures Night display to use the twilight schedule.
+ *
+ * @param sunsetOffset the offset relative to now for sunset (in minutes)
+ * @param sunriseOffset the offset relative to now for sunrise (in minutes)
+ */
+ private void setAutoModeTwilight(int sunsetOffset, int sunriseOffset) {
+ mNightDisplayController.setAutoMode(NightDisplayController.AUTO_MODE_TWILIGHT);
+
+ final LocalTime sunset = getLocalTimeRelativeToNow(sunsetOffset);
+ final LocalTime sunrise = getLocalTimeRelativeToNow(sunriseOffset);
+
+ final Calendar now = Calendar.getInstance();
+ long sunsetMillis = sunset.getDateTimeBefore(now).getTimeInMillis();
+ long sunriseMillis = sunrise.getDateTimeBefore(now).getTimeInMillis();
+ if (sunsetMillis < sunriseMillis) {
+ sunsetMillis = sunset.getDateTimeAfter(now).getTimeInMillis();
+ } else {
+ sunriseMillis = sunrise.getDateTimeAfter(now).getTimeInMillis();
+ }
+
+ final TwilightState state = new TwilightState(sunriseMillis, sunsetMillis);
+ doReturn(state).when(mTwilightManager).getLastTwilightState();
+ }
+
+ /**
+ * Configures the Night display activated state.
+ *
+ * @param activated {@code true} if Night display should be activated
+ * @param lastActivatedTimeOffset the offset relative to now to record that Night display was
+ activated (in minutes)
+ */
+ private void setActivated(boolean activated, int lastActivatedTimeOffset) {
+ mNightDisplayController.setActivated(activated);
+
+ final Calendar c = Calendar.getInstance();
+ c.add(Calendar.MINUTE, lastActivatedTimeOffset);
+ Secure.putLongForUser(mContext.getContentResolver(),
+ Secure.NIGHT_DISPLAY_LAST_ACTIVATED_TIME, c.getTimeInMillis(), mUserId);
+ }
+
+ /**
+ * Convenience method to start {@link #mNightDisplayService}.
+ */
+ private void startService() {
+ Secure.putIntForUser(mContext.getContentResolver(), Secure.USER_SETUP_COMPLETE, 1, mUserId);
+
+ InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
+ @Override
+ public void run() {
+ mNightDisplayService.onStart();
+ mNightDisplayService.onBootPhase(SystemService.PHASE_BOOT_COMPLETED);
+ mNightDisplayService.onStartUser(mUserId);
+ }
+ });
+ }
+
+ /**
+ * Convenience method for asserting whether Night display should be activated.
+ *
+ * @param activated the expected activated state of Night display
+ */
+ private void assertActivated(boolean activated) {
+ assertWithMessage("Invalid Night display activated state")
+ .that(mNightDisplayController.isActivated())
+ .isEqualTo(activated);
+ }
+}