Merge "Add Night display feature" into nyc-mr1-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
76262b8e14
@@ -6147,6 +6147,30 @@ public final class Settings {
|
||||
"camera_double_tap_power_gesture_disabled";
|
||||
|
||||
/**
|
||||
* Control whether Night display is currently activated.
|
||||
* @hide
|
||||
*/
|
||||
public static final String NIGHT_DISPLAY_ACTIVATED = "night_display_activated";
|
||||
|
||||
/**
|
||||
* Control whether Night display will automatically activate/deactivate.
|
||||
* @hide
|
||||
*/
|
||||
public static final String NIGHT_DISPLAY_AUTO_MODE = "night_display_auto_mode";
|
||||
|
||||
/**
|
||||
* Custom time when Night display is scheduled to activate.
|
||||
* Represented as milliseconds from midnight (e.g. 79200000 == 10pm).
|
||||
* @hide
|
||||
*/
|
||||
public static final String NIGHT_DISPLAY_CUSTOM_START_TIME = "night_display_custom_start_time";
|
||||
|
||||
/**
|
||||
* Custom time when Night display is scheduled to deactivate.
|
||||
* Represented as milliseconds from midnight (e.g. 21600000 == 6am).
|
||||
* @hide
|
||||
*/
|
||||
public static final String NIGHT_DISPLAY_CUSTOM_END_TIME = "night_display_custom_end_time";
|
||||
|
||||
/**
|
||||
* Behavior of twilight on the device.
|
||||
|
||||
421
core/java/com/android/internal/app/NightDisplayController.java
Normal file
421
core/java/com/android/internal/app/NightDisplayController.java
Normal file
@@ -0,0 +1,421 @@
|
||||
/*
|
||||
* Copyright (C) 2016 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.internal.app;
|
||||
|
||||
import android.annotation.IntDef;
|
||||
import android.annotation.NonNull;
|
||||
import android.content.ContentResolver;
|
||||
import android.content.Context;
|
||||
import android.database.ContentObserver;
|
||||
import android.net.Uri;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.os.UserHandle;
|
||||
import android.provider.Settings.Secure;
|
||||
import android.util.Slog;
|
||||
|
||||
import com.android.internal.R;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.util.Calendar;
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* Controller for managing Night display settings.
|
||||
* <p/>
|
||||
* Night display tints your screen red at night. This makes it easier to look at your screen in
|
||||
* dim light and may help you fall asleep more easily.
|
||||
*/
|
||||
public final class NightDisplayController {
|
||||
|
||||
private static final String TAG = "NightDisplayController";
|
||||
private static final boolean DEBUG = false;
|
||||
|
||||
/** @hide */
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
@IntDef({ AUTO_MODE_DISABLED, AUTO_MODE_CUSTOM, AUTO_MODE_TWILIGHT })
|
||||
public @interface AutoMode {}
|
||||
|
||||
/**
|
||||
* Auto mode value to prevent Night display from being automatically activated. It can still
|
||||
* be activated manually via {@link #setActivated(boolean)}.
|
||||
*
|
||||
* @see #setAutoMode(int)
|
||||
*/
|
||||
public static final int AUTO_MODE_DISABLED = 0;
|
||||
/**
|
||||
* Auto mode value to automatically activate Night display at a specific start and end time.
|
||||
*
|
||||
* @see #setAutoMode(int)
|
||||
* @see #setCustomStartTime(LocalTime)
|
||||
* @see #setCustomEndTime(LocalTime)
|
||||
*/
|
||||
public static final int AUTO_MODE_CUSTOM = 1;
|
||||
/**
|
||||
* Auto mode value to automatically activate Night display from sunset to sunrise.
|
||||
*
|
||||
* @see #setAutoMode(int)
|
||||
*/
|
||||
public static final int AUTO_MODE_TWILIGHT = 2;
|
||||
|
||||
private final Context mContext;
|
||||
private final int mUserId;
|
||||
|
||||
private final ContentObserver mContentObserver;
|
||||
|
||||
private Callback mCallback;
|
||||
|
||||
public NightDisplayController(@NonNull Context context) {
|
||||
this(context, UserHandle.myUserId());
|
||||
}
|
||||
|
||||
public NightDisplayController(@NonNull Context context, int userId) {
|
||||
mContext = context.getApplicationContext();
|
||||
mUserId = userId;
|
||||
|
||||
mContentObserver = new ContentObserver(new Handler(Looper.getMainLooper())) {
|
||||
@Override
|
||||
public void onChange(boolean selfChange, Uri uri) {
|
||||
super.onChange(selfChange, uri);
|
||||
|
||||
final String setting = uri == null ? null : uri.getLastPathSegment();
|
||||
if (setting != null) {
|
||||
onSettingChanged(setting);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@code true} when Night display is activated (the display is tinted red).
|
||||
*/
|
||||
public boolean isActivated() {
|
||||
return Secure.getIntForUser(mContext.getContentResolver(),
|
||||
Secure.NIGHT_DISPLAY_ACTIVATED, 0, mUserId) == 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets whether Night display should be activated.
|
||||
*
|
||||
* @param activated {@code true} if Night display should be activated
|
||||
* @return {@code true} if the activated value was set successfully
|
||||
*/
|
||||
public boolean setActivated(boolean activated) {
|
||||
return Secure.putIntForUser(mContext.getContentResolver(),
|
||||
Secure.NIGHT_DISPLAY_ACTIVATED, activated ? 1 : 0, mUserId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current auto mode value controlling when Night display will be automatically
|
||||
* activated. One of {@link #AUTO_MODE_DISABLED}, {@link #AUTO_MODE_CUSTOM}, or
|
||||
* {@link #AUTO_MODE_TWILIGHT}.
|
||||
*/
|
||||
public @AutoMode int getAutoMode() {
|
||||
int autoMode = Secure.getIntForUser(mContext.getContentResolver(),
|
||||
Secure.NIGHT_DISPLAY_AUTO_MODE, -1, mUserId);
|
||||
if (autoMode == -1) {
|
||||
if (DEBUG) {
|
||||
Slog.d(TAG, "Using default value for setting: " + Secure.NIGHT_DISPLAY_AUTO_MODE);
|
||||
}
|
||||
autoMode = mContext.getResources().getInteger(
|
||||
R.integer.config_defaultNightDisplayAutoMode);
|
||||
}
|
||||
|
||||
if (autoMode != AUTO_MODE_DISABLED
|
||||
&& autoMode != AUTO_MODE_CUSTOM
|
||||
&& autoMode != AUTO_MODE_TWILIGHT) {
|
||||
Slog.e(TAG, "Invalid autoMode: " + autoMode);
|
||||
autoMode = AUTO_MODE_DISABLED;
|
||||
}
|
||||
|
||||
return autoMode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the current auto mode value controlling when Night display will be automatically
|
||||
* activated. One of {@link #AUTO_MODE_DISABLED}, {@link #AUTO_MODE_CUSTOM}, or
|
||||
* {@link #AUTO_MODE_TWILIGHT}.
|
||||
*
|
||||
* @param autoMode the new auto mode to use
|
||||
* @return {@code true} if new auto mode was set successfully
|
||||
*/
|
||||
public boolean setAutoMode(@AutoMode int autoMode) {
|
||||
if (autoMode != AUTO_MODE_DISABLED
|
||||
&& autoMode != AUTO_MODE_CUSTOM
|
||||
&& autoMode != AUTO_MODE_TWILIGHT) {
|
||||
throw new IllegalArgumentException("Invalid autoMode: " + autoMode);
|
||||
}
|
||||
|
||||
return Secure.putIntForUser(mContext.getContentResolver(),
|
||||
Secure.NIGHT_DISPLAY_AUTO_MODE, autoMode, mUserId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the local time when Night display will be automatically activated when using
|
||||
* {@link #AUTO_MODE_CUSTOM}.
|
||||
*/
|
||||
public @NonNull LocalTime getCustomStartTime() {
|
||||
int startTimeValue = Secure.getIntForUser(mContext.getContentResolver(),
|
||||
Secure.NIGHT_DISPLAY_CUSTOM_START_TIME, -1, mUserId);
|
||||
if (startTimeValue == -1) {
|
||||
if (DEBUG) {
|
||||
Slog.d(TAG, "Using default value for setting: "
|
||||
+ Secure.NIGHT_DISPLAY_CUSTOM_START_TIME);
|
||||
}
|
||||
startTimeValue = mContext.getResources().getInteger(
|
||||
R.integer.config_defaultNightDisplayCustomStartTime);
|
||||
}
|
||||
|
||||
return LocalTime.valueOf(startTimeValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the local time when Night display will be automatically activated when using
|
||||
* {@link #AUTO_MODE_CUSTOM}.
|
||||
*
|
||||
* @param startTime the local time to automatically activate Night display
|
||||
* @return {@code true} if the new custom start time was set successfully
|
||||
*/
|
||||
public boolean setCustomStartTime(@NonNull LocalTime startTime) {
|
||||
if (startTime == null) {
|
||||
throw new IllegalArgumentException("startTime cannot be null");
|
||||
}
|
||||
return Secure.putIntForUser(mContext.getContentResolver(),
|
||||
Secure.NIGHT_DISPLAY_CUSTOM_START_TIME, startTime.toMillis(), mUserId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the local time when Night display will be automatically deactivated when using
|
||||
* {@link #AUTO_MODE_CUSTOM}.
|
||||
*/
|
||||
public @NonNull LocalTime getCustomEndTime() {
|
||||
int endTimeValue = Secure.getIntForUser(mContext.getContentResolver(),
|
||||
Secure.NIGHT_DISPLAY_CUSTOM_END_TIME, -1, mUserId);
|
||||
if (endTimeValue == -1) {
|
||||
if (DEBUG) {
|
||||
Slog.d(TAG, "Using default value for setting: "
|
||||
+ Secure.NIGHT_DISPLAY_CUSTOM_END_TIME);
|
||||
}
|
||||
endTimeValue = mContext.getResources().getInteger(
|
||||
R.integer.config_defaultNightDisplayCustomEndTime);
|
||||
}
|
||||
|
||||
return LocalTime.valueOf(endTimeValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the local time when Night display will be automatically deactivated when using
|
||||
* {@link #AUTO_MODE_CUSTOM}.
|
||||
*
|
||||
* @param endTime the local time to automatically deactivate Night display
|
||||
* @return {@code true} if the new custom end time was set successfully
|
||||
*/
|
||||
public boolean setCustomEndTime(@NonNull LocalTime endTime) {
|
||||
if (endTime == null) {
|
||||
throw new IllegalArgumentException("endTime cannot be null");
|
||||
}
|
||||
return Secure.putIntForUser(mContext.getContentResolver(),
|
||||
Secure.NIGHT_DISPLAY_CUSTOM_END_TIME, endTime.toMillis(), mUserId);
|
||||
}
|
||||
|
||||
private void onSettingChanged(@NonNull String setting) {
|
||||
if (DEBUG) {
|
||||
Slog.d(TAG, "onSettingChanged: " + setting);
|
||||
}
|
||||
|
||||
if (mCallback != null) {
|
||||
switch (setting) {
|
||||
case Secure.NIGHT_DISPLAY_ACTIVATED:
|
||||
mCallback.onActivated(isActivated());
|
||||
break;
|
||||
case Secure.NIGHT_DISPLAY_AUTO_MODE:
|
||||
mCallback.onAutoModeChanged(getAutoMode());
|
||||
break;
|
||||
case Secure.NIGHT_DISPLAY_CUSTOM_START_TIME:
|
||||
mCallback.onCustomStartTimeChanged(getCustomStartTime());
|
||||
break;
|
||||
case Secure.NIGHT_DISPLAY_CUSTOM_END_TIME:
|
||||
mCallback.onCustomEndTimeChanged(getCustomEndTime());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a callback to be invoked whenever the Night display settings are changed.
|
||||
*/
|
||||
public void setListener(Callback callback) {
|
||||
final Callback oldCallback = mCallback;
|
||||
if (oldCallback != callback) {
|
||||
mCallback = callback;
|
||||
|
||||
if (callback == null) {
|
||||
// Stop listening for changes now that there IS NOT a listener.
|
||||
mContext.getContentResolver().unregisterContentObserver(mContentObserver);
|
||||
} else if (oldCallback == null) {
|
||||
// Start listening for changes now that there IS a listener.
|
||||
final ContentResolver cr = mContext.getContentResolver();
|
||||
cr.registerContentObserver(Secure.getUriFor(Secure.NIGHT_DISPLAY_ACTIVATED),
|
||||
false /* notifyForDescendants */, mContentObserver, mUserId);
|
||||
cr.registerContentObserver(Secure.getUriFor(Secure.NIGHT_DISPLAY_AUTO_MODE),
|
||||
false /* notifyForDescendants */, mContentObserver, mUserId);
|
||||
cr.registerContentObserver(Secure.getUriFor(Secure.NIGHT_DISPLAY_CUSTOM_START_TIME),
|
||||
false /* notifyForDescendants */, mContentObserver, mUserId);
|
||||
cr.registerContentObserver(Secure.getUriFor(Secure.NIGHT_DISPLAY_CUSTOM_END_TIME),
|
||||
false /* notifyForDescendants */, mContentObserver, mUserId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@code true} if Night display is supported by the device.
|
||||
*/
|
||||
public static boolean isAvailable(Context context) {
|
||||
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.
|
||||
*/
|
||||
public interface Callback {
|
||||
/**
|
||||
* Callback invoked when the activated state changes.
|
||||
*
|
||||
* @param activated {@code true} if Night display is activated
|
||||
*/
|
||||
default void onActivated(boolean activated) {}
|
||||
/**
|
||||
* Callback invoked when the auto mode changes.
|
||||
*
|
||||
* @param autoMode the auto mode to use
|
||||
*/
|
||||
default void onAutoModeChanged(int autoMode) {}
|
||||
/**
|
||||
* Callback invoked when the time to automatically activate Night display changes.
|
||||
*
|
||||
* @param startTime the local time to automatically activate Night display
|
||||
*/
|
||||
default void onCustomStartTimeChanged(LocalTime startTime) {}
|
||||
/**
|
||||
* Callback invoked when the time to automatically deactivate Night display changes.
|
||||
*
|
||||
* @param endTime the local time to automatically deactivate Night display
|
||||
*/
|
||||
default void onCustomEndTimeChanged(LocalTime endTime) {}
|
||||
}
|
||||
}
|
||||
@@ -754,6 +754,26 @@
|
||||
-->
|
||||
<integer name="config_defaultNightMode">1</integer>
|
||||
|
||||
<!-- Control whether Night display is available. This should only be enabled on devices
|
||||
with HWC 2.0 or higher. -->
|
||||
<bool name="config_nightDisplayAvailable">false</bool>
|
||||
|
||||
<!-- Default mode to control how Night display is automatically activated.
|
||||
One of the following values (see NightDisplayController.java):
|
||||
0 - AUTO_MODE_DISABLED
|
||||
1 - AUTO_MODE_CUSTOM
|
||||
2 - AUTO_MODE_TWILIGHT
|
||||
-->
|
||||
<integer name="config_defaultNightDisplayAutoMode">1</integer>
|
||||
|
||||
<!-- Default time when Night display is automatically activated.
|
||||
Represented as milliseconds from midnight (e.g. 79200000 == 10pm). -->
|
||||
<integer name="config_defaultNightDisplayCustomStartTime">79200000</integer>
|
||||
|
||||
<!-- Default time when Night display is automatically deactivated.
|
||||
Represented as milliseconds from midnight (e.g. 21600000 == 6am). -->
|
||||
<integer name="config_defaultNightDisplayCustomEndTime">21600000</integer>
|
||||
|
||||
<!-- Indicate whether to allow the device to suspend when the screen is off
|
||||
due to the proximity sensor. This resource should only be set to true
|
||||
if the sensor HAL correctly handles the proximity sensor as a wake-up source.
|
||||
|
||||
@@ -2664,4 +2664,8 @@
|
||||
<java-symbol type="drawable" name="ic_doc_video" />
|
||||
<java-symbol type="drawable" name="ic_doc_generic" />
|
||||
|
||||
<java-symbol type="bool" name="config_nightDisplayAvailable" />
|
||||
<java-symbol type="integer" name="config_defaultNightDisplayAutoMode" />
|
||||
<java-symbol type="integer" name="config_defaultNightDisplayCustomStartTime" />
|
||||
<java-symbol type="integer" name="config_defaultNightDisplayCustomEndTime" />
|
||||
</resources>
|
||||
|
||||
@@ -2367,6 +2367,10 @@ message MetricsEvent {
|
||||
// ACTION: Settings -> Support -> "Travel Abroad" Button -> Tolled Phone
|
||||
ACTION_SUPPORT_DIAL_TOLLED = 487;
|
||||
|
||||
// OPEN: Settings > Display > Night display
|
||||
// CATEGORY: SETTINGS
|
||||
NIGHT_DISPLAY_SETTINGS = 488;
|
||||
|
||||
// ---- End N-MR1 Constants, all N-MR1 constants go above this line ----
|
||||
// Add new aosp constants above this line.
|
||||
// END OF AOSP CONSTANTS
|
||||
|
||||
@@ -0,0 +1,362 @@
|
||||
/*
|
||||
* Copyright (C) 2016 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.display;
|
||||
|
||||
import android.app.AlarmManager;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.ContentResolver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.os.UserHandle;
|
||||
import android.provider.Settings.Secure;
|
||||
import android.util.Slog;
|
||||
|
||||
import com.android.internal.app.NightDisplayController;
|
||||
import com.android.server.SystemService;
|
||||
import com.android.server.twilight.TwilightListener;
|
||||
import com.android.server.twilight.TwilightManager;
|
||||
import com.android.server.twilight.TwilightState;
|
||||
|
||||
import java.util.Calendar;
|
||||
import java.util.TimeZone;
|
||||
|
||||
/**
|
||||
* Tints the display at night.
|
||||
*/
|
||||
public final class NightDisplayService extends SystemService
|
||||
implements NightDisplayController.Callback {
|
||||
|
||||
private static final String TAG = "NightDisplayService";
|
||||
private static final boolean DEBUG = false;
|
||||
|
||||
/**
|
||||
* Night mode ~= 3400 K.
|
||||
*/
|
||||
private static final String MATRIX_NIGHT = "1,0,0,0,0,.754,0,0,0,0,.516,0,0,0,0,1";
|
||||
|
||||
private int mCurrentUser = UserHandle.USER_NULL;
|
||||
private boolean mBootCompleted;
|
||||
|
||||
private NightDisplayController mController;
|
||||
private Boolean mIsActivated;
|
||||
private AutoMode mAutoMode;
|
||||
|
||||
public NightDisplayService(Context context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStart() {
|
||||
// Nothing to publish.
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStartUser(int userHandle) {
|
||||
super.onStartUser(userHandle);
|
||||
|
||||
// Register listeners for the new user.
|
||||
if (mCurrentUser == UserHandle.USER_NULL) {
|
||||
mCurrentUser = userHandle;
|
||||
if (mBootCompleted) {
|
||||
setUpNightMode();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSwitchUser(int userHandle) {
|
||||
super.onSwitchUser(userHandle);
|
||||
|
||||
// Unregister listeners for the old user.
|
||||
if (mBootCompleted && mCurrentUser != UserHandle.USER_NULL) {
|
||||
tearDownNightMode();
|
||||
}
|
||||
|
||||
// Register listeners for the new user.
|
||||
mCurrentUser = userHandle;
|
||||
if (mBootCompleted) {
|
||||
setUpNightMode();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStopUser(int userHandle) {
|
||||
super.onStopUser(userHandle);
|
||||
|
||||
// Unregister listeners for the old user.
|
||||
if (mCurrentUser == userHandle) {
|
||||
if (mBootCompleted) {
|
||||
tearDownNightMode();
|
||||
}
|
||||
mCurrentUser = UserHandle.USER_NULL;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBootPhase(int phase) {
|
||||
if (phase == PHASE_BOOT_COMPLETED) {
|
||||
mBootCompleted = true;
|
||||
|
||||
// Register listeners now that boot is complete.
|
||||
if (mCurrentUser != UserHandle.USER_NULL) {
|
||||
setUpNightMode();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void setUpNightMode() {
|
||||
// Create a new controller for the current user and start listening for changes.
|
||||
mController = new NightDisplayController(getContext(), mCurrentUser);
|
||||
mController.setListener(this);
|
||||
|
||||
// Initialize the current auto mode.
|
||||
onAutoModeChanged(mController.getAutoMode());
|
||||
|
||||
// Force the initialization current activated state.
|
||||
if (mIsActivated == null) {
|
||||
onActivated(mController.isActivated());
|
||||
}
|
||||
}
|
||||
|
||||
private void tearDownNightMode() {
|
||||
mController.setListener(null);
|
||||
|
||||
if (mAutoMode != null) {
|
||||
mAutoMode.onStop();
|
||||
mAutoMode = null;
|
||||
}
|
||||
|
||||
mIsActivated = null;
|
||||
mController = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivated(boolean activated) {
|
||||
if (mIsActivated == null || mIsActivated != activated) {
|
||||
Slog.i(TAG, activated ? "Turning on night display" : "Turning off night display");
|
||||
|
||||
mIsActivated = activated;
|
||||
|
||||
if (mAutoMode != null) {
|
||||
mAutoMode.onActivated(activated);
|
||||
}
|
||||
|
||||
// Update the current color matrix.
|
||||
final ContentResolver cr = getContext().getContentResolver();
|
||||
Secure.putStringForUser(cr, Secure.ACCESSIBILITY_DISPLAY_COLOR_MATRIX,
|
||||
activated ? MATRIX_NIGHT : null, mCurrentUser);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAutoModeChanged(int autoMode) {
|
||||
if (mAutoMode != null) {
|
||||
mAutoMode.onStop();
|
||||
mAutoMode = null;
|
||||
}
|
||||
|
||||
if (autoMode == NightDisplayController.AUTO_MODE_CUSTOM) {
|
||||
mAutoMode = new CustomAutoMode();
|
||||
} else if (autoMode == NightDisplayController.AUTO_MODE_TWILIGHT) {
|
||||
mAutoMode = new TwilightAutoMode();
|
||||
}
|
||||
|
||||
if (mAutoMode != null) {
|
||||
mAutoMode.onStart();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCustomStartTimeChanged(NightDisplayController.LocalTime startTime) {
|
||||
if (mAutoMode != null) {
|
||||
mAutoMode.onCustomStartTimeChanged(startTime);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCustomEndTimeChanged(NightDisplayController.LocalTime endTime) {
|
||||
if (mAutoMode != null) {
|
||||
mAutoMode.onCustomEndTimeChanged(endTime);
|
||||
}
|
||||
}
|
||||
|
||||
private abstract class AutoMode implements NightDisplayController.Callback {
|
||||
public abstract void onStart();
|
||||
public abstract void onStop();
|
||||
}
|
||||
|
||||
private class CustomAutoMode extends AutoMode implements AlarmManager.OnAlarmListener {
|
||||
|
||||
private final AlarmManager mAlarmManager;
|
||||
private final BroadcastReceiver mTimeChangedReceiver;
|
||||
|
||||
private NightDisplayController.LocalTime mStartTime;
|
||||
private NightDisplayController.LocalTime mEndTime;
|
||||
|
||||
private Calendar mLastActivatedTime;
|
||||
|
||||
public CustomAutoMode() {
|
||||
mAlarmManager = (AlarmManager) getContext().getSystemService(Context.ALARM_SERVICE);
|
||||
mTimeChangedReceiver = new BroadcastReceiver() {
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
updateActivated();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private void updateActivated() {
|
||||
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) {
|
||||
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);
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
||||
if (setActivated) {
|
||||
mController.setActivated(activated);
|
||||
}
|
||||
updateNextAlarm();
|
||||
}
|
||||
|
||||
private void updateNextAlarm() {
|
||||
if (mIsActivated != null) {
|
||||
final Calendar now = Calendar.getInstance();
|
||||
final Calendar next = mIsActivated ? mEndTime.getDateTimeAfter(now)
|
||||
: mStartTime.getDateTimeAfter(now);
|
||||
mAlarmManager.setExact(AlarmManager.RTC, next.getTimeInMillis(), TAG, this, null);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStart() {
|
||||
final IntentFilter intentFilter = new IntentFilter(Intent.ACTION_TIME_CHANGED);
|
||||
intentFilter.addAction(Intent.ACTION_TIMEZONE_CHANGED);
|
||||
getContext().registerReceiver(mTimeChangedReceiver, intentFilter);
|
||||
|
||||
mStartTime = mController.getCustomStartTime();
|
||||
mEndTime = mController.getCustomEndTime();
|
||||
|
||||
// Force an update to initialize state.
|
||||
updateActivated();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStop() {
|
||||
getContext().unregisterReceiver(mTimeChangedReceiver);
|
||||
|
||||
mAlarmManager.cancel(this);
|
||||
mLastActivatedTime = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivated(boolean activated) {
|
||||
mLastActivatedTime = Calendar.getInstance();
|
||||
updateNextAlarm();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCustomStartTimeChanged(NightDisplayController.LocalTime startTime) {
|
||||
mStartTime = startTime;
|
||||
mLastActivatedTime = null;
|
||||
updateActivated();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCustomEndTimeChanged(NightDisplayController.LocalTime endTime) {
|
||||
mEndTime = endTime;
|
||||
mLastActivatedTime = null;
|
||||
updateActivated();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAlarm() {
|
||||
if (DEBUG) Slog.d(TAG, "onAlarm");
|
||||
updateActivated();
|
||||
}
|
||||
}
|
||||
|
||||
private class TwilightAutoMode extends AutoMode implements TwilightListener {
|
||||
|
||||
private final TwilightManager mTwilightManager;
|
||||
private final Handler mHandler;
|
||||
|
||||
private boolean mIsNight;
|
||||
|
||||
public TwilightAutoMode() {
|
||||
mTwilightManager = getLocalService(TwilightManager.class);
|
||||
mHandler = new Handler(Looper.getMainLooper());
|
||||
}
|
||||
|
||||
private void updateActivated() {
|
||||
final TwilightState state = mTwilightManager.getCurrentState();
|
||||
final boolean isNight = state != null && state.isNight();
|
||||
if (mIsNight != isNight) {
|
||||
mIsNight = isNight;
|
||||
|
||||
if (mIsActivated == null || mIsActivated != isNight) {
|
||||
mController.setActivated(isNight);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStart() {
|
||||
mTwilightManager.registerListener(this, mHandler);
|
||||
|
||||
// Force an update to initialize state.
|
||||
updateActivated();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStop() {
|
||||
mTwilightManager.unregisterListener(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTwilightStateChanged() {
|
||||
if (DEBUG) Slog.d(TAG, "onTwilightStateChanged");
|
||||
updateActivated();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -556,7 +556,7 @@ public final class TwilightService extends SystemService {
|
||||
public void onChange(boolean selfChange) {
|
||||
super.onChange(selfChange);
|
||||
int value = Secure.getIntForUser(getContext().getContentResolver(),
|
||||
Secure.TWILIGHT_MODE, Secure.TWILIGHT_MODE_LOCKED_OFF, mCurrentUser);
|
||||
Secure.TWILIGHT_MODE, Secure.TWILIGHT_MODE_AUTO, mCurrentUser);
|
||||
if (value == Secure.TWILIGHT_MODE_LOCKED_OFF) {
|
||||
setLockedState(new TwilightState(false, 0));
|
||||
} else if (value == Secure.TWILIGHT_MODE_LOCKED_ON) {
|
||||
|
||||
@@ -51,6 +51,7 @@ import android.util.Slog;
|
||||
import android.view.WindowManager;
|
||||
|
||||
import com.android.internal.R;
|
||||
import com.android.internal.app.NightDisplayController;
|
||||
import com.android.internal.os.BinderInternal;
|
||||
import com.android.internal.os.SamplingProfilerIntegration;
|
||||
import com.android.internal.os.ZygoteInit;
|
||||
@@ -63,6 +64,7 @@ import com.android.server.clipboard.ClipboardService;
|
||||
import com.android.server.connectivity.MetricsLoggerService;
|
||||
import com.android.server.devicepolicy.DevicePolicyManagerService;
|
||||
import com.android.server.display.DisplayManagerService;
|
||||
import com.android.server.display.NightDisplayService;
|
||||
import com.android.server.dreams.DreamManagerService;
|
||||
import com.android.server.fingerprint.FingerprintService;
|
||||
import com.android.server.hdmi.HdmiControlService;
|
||||
@@ -995,6 +997,10 @@ public final class SystemServer {
|
||||
|
||||
mSystemServiceManager.startService(TwilightService.class);
|
||||
|
||||
if (NightDisplayController.isAvailable(context)) {
|
||||
mSystemServiceManager.startService(NightDisplayService.class);
|
||||
}
|
||||
|
||||
mSystemServiceManager.startService(JobSchedulerService.class);
|
||||
|
||||
mSystemServiceManager.startService(SoundTriggerService.class);
|
||||
|
||||
Reference in New Issue
Block a user