Merge "Handle night display state when timezone changes" into oc-mr1-dev

This commit is contained in:
Christine Franks
2017-09-20 00:23:28 +00:00
committed by Android (Google) Code Review
7 changed files with 132 additions and 188 deletions

View File

@@ -5243,7 +5243,6 @@ com.android.internal.app.IVoiceInteractor
com.android.internal.app.IVoiceInteractor$Stub
com.android.internal.app.NightDisplayController
com.android.internal.app.NightDisplayController$Callback
com.android.internal.app.NightDisplayController$LocalTime
com.android.internal.app.ProcessMap
com.android.internal.app.ResolverActivity
com.android.internal.app.ToolbarActionBar

View File

@@ -2784,7 +2784,6 @@ com.android.internal.app.IVoiceInteractionManagerService$Stub
com.android.internal.app.IVoiceInteractor
com.android.internal.app.IVoiceInteractor$Stub
com.android.internal.app.NightDisplayController
com.android.internal.app.NightDisplayController$1
com.android.internal.appwidget.IAppWidgetService
com.android.internal.appwidget.IAppWidgetService$Stub
com.android.internal.appwidget.IAppWidgetService$Stub$Proxy

View File

@@ -6931,8 +6931,9 @@ public final class Settings {
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.
* A String representing the LocalDateTime when Night display was last activated. Use to
* decide whether to apply the current activated state after a reboot or user change. In
* legacy cases, this is represented by the time in milliseconds (since epoch).
* @hide
*/
public static final String NIGHT_DISPLAY_LAST_ACTIVATED_TIME =

View File

@@ -32,8 +32,12 @@ import com.android.internal.R;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.Calendar;
import java.util.Locale;
import java.time.DateTimeException;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.ZoneId;
import java.time.format.DateTimeParseException;
/**
* Controller for managing Night display settings.
@@ -116,8 +120,9 @@ public final class NightDisplayController {
*/
public boolean setActivated(boolean activated) {
if (isActivated() != activated) {
Secure.putLongForUser(mContext.getContentResolver(),
Secure.NIGHT_DISPLAY_LAST_ACTIVATED_TIME, System.currentTimeMillis(),
Secure.putStringForUser(mContext.getContentResolver(),
Secure.NIGHT_DISPLAY_LAST_ACTIVATED_TIME,
LocalDateTime.now().toString(),
mUserId);
}
return Secure.putIntForUser(mContext.getContentResolver(),
@@ -128,17 +133,22 @@ public final class NightDisplayController {
* Returns the time when Night display's activation state last changed, or {@code null} if it
* has never been changed.
*/
public Calendar getLastActivatedTime() {
public LocalDateTime getLastActivatedTime() {
final ContentResolver cr = mContext.getContentResolver();
final long lastActivatedTimeMillis = Secure.getLongForUser(
cr, Secure.NIGHT_DISPLAY_LAST_ACTIVATED_TIME, -1, mUserId);
if (lastActivatedTimeMillis < 0) {
return null;
final String lastActivatedTime = Secure.getStringForUser(
cr, Secure.NIGHT_DISPLAY_LAST_ACTIVATED_TIME, mUserId);
if (lastActivatedTime != null) {
try {
return LocalDateTime.parse(lastActivatedTime);
} catch (DateTimeParseException ignored) {}
// Uses the old epoch time.
try {
return LocalDateTime.ofInstant(
Instant.ofEpochMilli(Long.parseLong(lastActivatedTime)),
ZoneId.systemDefault());
} catch (DateTimeException|NumberFormatException ignored) {}
}
final Calendar lastActivatedTime = Calendar.getInstance();
lastActivatedTime.setTimeInMillis(lastActivatedTimeMillis);
return lastActivatedTime;
return null;
}
/**
@@ -183,8 +193,10 @@ public final class NightDisplayController {
}
if (getAutoMode() != autoMode) {
Secure.putLongForUser(mContext.getContentResolver(),
Secure.NIGHT_DISPLAY_LAST_ACTIVATED_TIME, -1L, mUserId);
Secure.putStringForUser(mContext.getContentResolver(),
Secure.NIGHT_DISPLAY_LAST_ACTIVATED_TIME,
null,
mUserId);
}
return Secure.putIntForUser(mContext.getContentResolver(),
Secure.NIGHT_DISPLAY_AUTO_MODE, autoMode, mUserId);
@@ -206,7 +218,7 @@ public final class NightDisplayController {
R.integer.config_defaultNightDisplayCustomStartTime);
}
return LocalTime.valueOf(startTimeValue);
return LocalTime.ofSecondOfDay(startTimeValue / 1000);
}
/**
@@ -221,7 +233,7 @@ public final class NightDisplayController {
throw new IllegalArgumentException("startTime cannot be null");
}
return Secure.putIntForUser(mContext.getContentResolver(),
Secure.NIGHT_DISPLAY_CUSTOM_START_TIME, startTime.toMillis(), mUserId);
Secure.NIGHT_DISPLAY_CUSTOM_START_TIME, startTime.toSecondOfDay() * 1000, mUserId);
}
/**
@@ -240,7 +252,7 @@ public final class NightDisplayController {
R.integer.config_defaultNightDisplayCustomEndTime);
}
return LocalTime.valueOf(endTimeValue);
return LocalTime.ofSecondOfDay(endTimeValue / 1000);
}
/**
@@ -255,7 +267,7 @@ public final class NightDisplayController {
throw new IllegalArgumentException("endTime cannot be null");
}
return Secure.putIntForUser(mContext.getContentResolver(),
Secure.NIGHT_DISPLAY_CUSTOM_END_TIME, endTime.toMillis(), mUserId);
Secure.NIGHT_DISPLAY_CUSTOM_END_TIME, endTime.toSecondOfDay() * 1000, mUserId);
}
/**
@@ -378,106 +390,6 @@ public final class NightDisplayController {
return context.getResources().getBoolean(R.bool.config_nightDisplayAvailable);
}
/**
* A time without a time-zone or date.
*/
public static class LocalTime {
/**
* The hour of the day from 0 - 23.
*/
public final int hourOfDay;
/**
* The minute within the hour from 0 - 59.
*/
public final int minute;
public LocalTime(int hourOfDay, int minute) {
if (hourOfDay < 0 || hourOfDay > 23) {
throw new IllegalArgumentException("Invalid hourOfDay: " + hourOfDay);
} else if (minute < 0 || minute > 59) {
throw new IllegalArgumentException("Invalid minute: " + minute);
}
this.hourOfDay = hourOfDay;
this.minute = minute;
}
/**
* Returns the first date time corresponding to this local time that occurs before the
* provided date time.
*
* @param time the date time to compare against
* @return the prior date time corresponding to this local time
*/
public Calendar getDateTimeBefore(Calendar time) {
final Calendar c = Calendar.getInstance();
c.set(Calendar.YEAR, time.get(Calendar.YEAR));
c.set(Calendar.DAY_OF_YEAR, time.get(Calendar.DAY_OF_YEAR));
c.set(Calendar.HOUR_OF_DAY, hourOfDay);
c.set(Calendar.MINUTE, minute);
c.set(Calendar.SECOND, 0);
c.set(Calendar.MILLISECOND, 0);
// Check if the local time has past, if so return the same time tomorrow.
if (c.after(time)) {
c.add(Calendar.DATE, -1);
}
return c;
}
/**
* Returns the first date time corresponding to this local time that occurs after the
* provided date time.
*
* @param time the date time to compare against
* @return the next date time corresponding to this local time
*/
public Calendar getDateTimeAfter(Calendar time) {
final Calendar c = Calendar.getInstance();
c.set(Calendar.YEAR, time.get(Calendar.YEAR));
c.set(Calendar.DAY_OF_YEAR, time.get(Calendar.DAY_OF_YEAR));
c.set(Calendar.HOUR_OF_DAY, hourOfDay);
c.set(Calendar.MINUTE, minute);
c.set(Calendar.SECOND, 0);
c.set(Calendar.MILLISECOND, 0);
// Check if the local time has past, if so return the same time tomorrow.
if (c.before(time)) {
c.add(Calendar.DATE, 1);
}
return c;
}
/**
* Returns a local time corresponding the given number of milliseconds from midnight.
*
* @param millis the number of milliseconds from midnight
* @return the corresponding local time
*/
private static LocalTime valueOf(int millis) {
final int hourOfDay = (millis / 3600000) % 24;
final int minutes = (millis / 60000) % 60;
return new LocalTime(hourOfDay, minutes);
}
/**
* Returns the local time represented as milliseconds from midnight.
*/
private int toMillis() {
return hourOfDay * 3600000 + minute * 60000;
}
@Override
public String toString() {
return String.format(Locale.US, "%02d:%02d", hourOfDay, minute);
}
}
/**
* Callback invoked whenever the Night display settings are changed.
*/

View File

@@ -48,8 +48,10 @@ import com.android.server.twilight.TwilightListener;
import com.android.server.twilight.TwilightManager;
import com.android.server.twilight.TwilightState;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.ZoneId;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.Calendar;
import java.util.TimeZone;
import com.android.internal.R;
@@ -308,7 +310,7 @@ public final class NightDisplayService extends SystemService
}
@Override
public void onCustomStartTimeChanged(NightDisplayController.LocalTime startTime) {
public void onCustomStartTimeChanged(LocalTime startTime) {
Slog.d(TAG, "onCustomStartTimeChanged: startTime=" + startTime);
if (mAutoMode != null) {
@@ -317,7 +319,7 @@ public final class NightDisplayService extends SystemService
}
@Override
public void onCustomEndTimeChanged(NightDisplayController.LocalTime endTime) {
public void onCustomEndTimeChanged(LocalTime endTime) {
Slog.d(TAG, "onCustomEndTimeChanged: endTime=" + endTime);
if (mAutoMode != null) {
@@ -416,6 +418,36 @@ public final class NightDisplayService extends SystemService
outTemp[10] = blue;
}
/**
* Returns the first date time corresponding to the local time that occurs before the
* provided date time.
*
* @param compareTime the LocalDateTime to compare against
* @return the prior LocalDateTime corresponding to this local time
*/
public static LocalDateTime getDateTimeBefore(LocalTime localTime, LocalDateTime compareTime) {
final LocalDateTime ldt = LocalDateTime.of(compareTime.getYear(), compareTime.getMonth(),
compareTime.getDayOfMonth(), localTime.getHour(), localTime.getMinute());
// Check if the local time has passed, if so return the same time yesterday.
return ldt.isAfter(compareTime) ? ldt.minusDays(1) : ldt;
}
/**
* Returns the first date time corresponding to this local time that occurs after the
* provided date time.
*
* @param compareTime the LocalDateTime to compare against
* @return the next LocalDateTime corresponding to this local time
*/
public static LocalDateTime getDateTimeAfter(LocalTime localTime, LocalDateTime compareTime) {
final LocalDateTime ldt = LocalDateTime.of(compareTime.getYear(), compareTime.getMonth(),
compareTime.getDayOfMonth(), localTime.getHour(), localTime.getMinute());
// Check if the local time has passed, if so return the same time tomorrow.
return ldt.isBefore(compareTime) ? ldt.plusDays(1) : ldt;
}
private abstract class AutoMode implements NightDisplayController.Callback {
public abstract void onStart();
@@ -427,10 +459,10 @@ public final class NightDisplayService extends SystemService
private final AlarmManager mAlarmManager;
private final BroadcastReceiver mTimeChangedReceiver;
private NightDisplayController.LocalTime mStartTime;
private NightDisplayController.LocalTime mEndTime;
private LocalTime mStartTime;
private LocalTime mEndTime;
private Calendar mLastActivatedTime;
private LocalDateTime mLastActivatedTime;
CustomAutoMode() {
mAlarmManager = (AlarmManager) getContext().getSystemService(Context.ALARM_SERVICE);
@@ -443,31 +475,15 @@ public final class NightDisplayService extends SystemService
}
private void updateActivated() {
final Calendar now = Calendar.getInstance();
final Calendar startTime = mStartTime.getDateTimeBefore(now);
final Calendar endTime = mEndTime.getDateTimeAfter(startTime);
final LocalDateTime now = LocalDateTime.now();
final LocalDateTime start = getDateTimeBefore(mStartTime, now);
final LocalDateTime end = getDateTimeAfter(mEndTime, start);
boolean activate = now.isBefore(end);
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);
final int dayOfYear = mLastActivatedTime.get(Calendar.DAY_OF_YEAR);
final int hourOfDay = mLastActivatedTime.get(Calendar.HOUR_OF_DAY);
final int minute = mLastActivatedTime.get(Calendar.MINUTE);
mLastActivatedTime.setTimeZone(currentTimeZone);
mLastActivatedTime.set(Calendar.YEAR, year);
mLastActivatedTime.set(Calendar.DAY_OF_YEAR, dayOfYear);
mLastActivatedTime.set(Calendar.HOUR_OF_DAY, hourOfDay);
mLastActivatedTime.set(Calendar.MINUTE, minute);
}
// Maintain the existing activated state if within the current period.
if (mLastActivatedTime.before(now)
&& mLastActivatedTime.after(startTime)
&& (mLastActivatedTime.after(endTime) || now.before(endTime))) {
if (mLastActivatedTime.isBefore(now) && mLastActivatedTime.isAfter(start)
&& (mLastActivatedTime.isAfter(end) || now.isBefore(end))) {
activate = mController.isActivated();
}
}
@@ -475,14 +491,16 @@ public final class NightDisplayService extends SystemService
if (mIsActivated == null || mIsActivated != activate) {
mController.setActivated(activate);
}
updateNextAlarm(mIsActivated, now);
}
private void updateNextAlarm(@Nullable Boolean activated, @NonNull Calendar now) {
private void updateNextAlarm(@Nullable Boolean activated, @NonNull LocalDateTime now) {
if (activated != null) {
final Calendar next = activated ? mEndTime.getDateTimeAfter(now)
: mStartTime.getDateTimeAfter(now);
mAlarmManager.setExact(AlarmManager.RTC, next.getTimeInMillis(), TAG, this, null);
final LocalDateTime next = activated ? getDateTimeAfter(mEndTime, now)
: getDateTimeAfter(mStartTime, now);
final long millis = next.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli();
mAlarmManager.setExact(AlarmManager.RTC, millis, TAG, this, null);
}
}
@@ -512,18 +530,18 @@ public final class NightDisplayService extends SystemService
@Override
public void onActivated(boolean activated) {
mLastActivatedTime = mController.getLastActivatedTime();
updateNextAlarm(activated, Calendar.getInstance());
updateNextAlarm(activated, LocalDateTime.now());
}
@Override
public void onCustomStartTimeChanged(NightDisplayController.LocalTime startTime) {
public void onCustomStartTimeChanged(LocalTime startTime) {
mStartTime = startTime;
mLastActivatedTime = null;
updateActivated();
}
@Override
public void onCustomEndTimeChanged(NightDisplayController.LocalTime endTime) {
public void onCustomEndTimeChanged(LocalTime endTime) {
mEndTime = endTime;
mLastActivatedTime = null;
updateActivated();
@@ -552,15 +570,14 @@ public final class NightDisplayService extends SystemService
}
boolean activate = state.isNight();
final Calendar lastActivatedTime = mController.getLastActivatedTime();
final LocalDateTime lastActivatedTime = mController.getLastActivatedTime();
if (lastActivatedTime != null) {
final Calendar now = Calendar.getInstance();
final Calendar sunrise = state.sunrise();
final Calendar sunset = state.sunset();
final LocalDateTime now = LocalDateTime.now();
final LocalDateTime sunrise = state.sunrise();
final LocalDateTime sunset = state.sunset();
// Maintain the existing activated state if within the current period.
if (lastActivatedTime.before(now)
&& (lastActivatedTime.after(sunrise) ^ lastActivatedTime.after(sunset))) {
if (lastActivatedTime.isBefore(now) && (lastActivatedTime.isBefore(sunrise)
^ lastActivatedTime.isBefore(sunset))) {
activate = mController.isActivated();
}
}

View File

@@ -18,7 +18,10 @@ package com.android.server.twilight;
import android.text.format.DateFormat;
import java.util.Calendar;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.TimeZone;
/**
* The twilight state, consisting of the sunrise and sunset times (in millis) for the current
@@ -45,12 +48,11 @@ public final class TwilightState {
}
/**
* Returns a new {@link Calendar} instance initialized to {@link #sunriseTimeMillis()}.
* Returns a new {@link LocalDateTime} instance initialized to {@link #sunriseTimeMillis()}.
*/
public Calendar sunrise() {
final Calendar sunrise = Calendar.getInstance();
sunrise.setTimeInMillis(mSunriseTimeMillis);
return sunrise;
public LocalDateTime sunrise() {
final ZoneId zoneId = TimeZone.getDefault().toZoneId();
return LocalDateTime.ofInstant(Instant.ofEpochMilli(mSunriseTimeMillis), zoneId);
}
/**
@@ -62,12 +64,11 @@ public final class TwilightState {
}
/**
* Returns a new {@link Calendar} instance initialized to {@link #sunsetTimeMillis()}.
* Returns a new {@link LocalDateTime} instance initialized to {@link #sunsetTimeMillis()}.
*/
public Calendar sunset() {
final Calendar sunset = Calendar.getInstance();
sunset.setTimeInMillis(mSunsetTimeMillis);
return sunset;
public LocalDateTime sunset() {
final ZoneId zoneId = TimeZone.getDefault().toZoneId();
return LocalDateTime.ofInstant(Instant.ofEpochMilli(mSunsetTimeMillis), zoneId);
}
/**

View File

@@ -30,13 +30,14 @@ 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.TwilightListener;
import com.android.server.twilight.TwilightManager;
import com.android.server.twilight.TwilightState;
import java.time.LocalDateTime;
import java.time.ZoneId;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
@@ -45,6 +46,7 @@ import org.mockito.Mockito;
import java.util.Calendar;
import java.util.HashMap;
import java.time.LocalTime;
import java.util.Map;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
@@ -926,11 +928,10 @@ public class NightDisplayServiceTest {
*/
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);
Secure.putStringForUser(mContext.getContentResolver(),
Secure.NIGHT_DISPLAY_LAST_ACTIVATED_TIME,
LocalDateTime.now().plusMinutes(lastActivatedTimeOffset).toString(),
mUserId);
}
/**
@@ -969,7 +970,7 @@ public class NightDisplayServiceTest {
private static 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));
return LocalTime.of(c.get(Calendar.HOUR_OF_DAY), c.get(Calendar.MINUTE));
}
/**
@@ -984,13 +985,27 @@ public class NightDisplayServiceTest {
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();
final LocalDateTime now = LocalDateTime.now();
final ZoneId zoneId = ZoneId.systemDefault();
long sunsetMillis = NightDisplayService.getDateTimeBefore(sunset, now)
.atZone(zoneId)
.toInstant()
.toEpochMilli();
long sunriseMillis = NightDisplayService.getDateTimeBefore(sunrise, now)
.atZone(zoneId)
.toInstant()
.toEpochMilli();
if (sunsetMillis < sunriseMillis) {
sunsetMillis = sunset.getDateTimeAfter(now).getTimeInMillis();
sunsetMillis = NightDisplayService.getDateTimeAfter(sunset, now)
.atZone(zoneId)
.toInstant()
.toEpochMilli();
} else {
sunriseMillis = sunrise.getDateTimeAfter(now).getTimeInMillis();
sunriseMillis = NightDisplayService.getDateTimeAfter(sunrise, now)
.atZone(zoneId)
.toInstant()
.toEpochMilli();
}
return new TwilightState(sunriseMillis, sunsetMillis);