2 * Copyright (C) 2016 The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 package com.android.internal.app;
19 import android.annotation.IntDef;
20 import android.annotation.NonNull;
21 import android.content.ContentResolver;
22 import android.content.Context;
23 import android.database.ContentObserver;
24 import android.net.Uri;
25 import android.os.Handler;
26 import android.os.Looper;
27 import android.os.UserHandle;
28 import android.provider.Settings.Secure;
29 import android.util.Slog;
31 import com.android.internal.R;
33 import java.lang.annotation.Retention;
34 import java.lang.annotation.RetentionPolicy;
35 import java.util.Calendar;
36 import java.util.Locale;
39 * Controller for managing Night display settings.
41 * Night display tints your screen red at night. This makes it easier to look at your screen in
42 * dim light and may help you fall asleep more easily.
44 public final class NightDisplayController {
46 private static final String TAG = "NightDisplayController";
47 private static final boolean DEBUG = false;
50 @Retention(RetentionPolicy.SOURCE)
51 @IntDef({ AUTO_MODE_DISABLED, AUTO_MODE_CUSTOM, AUTO_MODE_TWILIGHT })
52 public @interface AutoMode {}
55 * Auto mode value to prevent Night display from being automatically activated. It can still
56 * be activated manually via {@link #setActivated(boolean)}.
58 * @see #setAutoMode(int)
60 public static final int AUTO_MODE_DISABLED = 0;
62 * Auto mode value to automatically activate Night display at a specific start and end time.
64 * @see #setAutoMode(int)
65 * @see #setCustomStartTime(LocalTime)
66 * @see #setCustomEndTime(LocalTime)
68 public static final int AUTO_MODE_CUSTOM = 1;
70 * Auto mode value to automatically activate Night display from sunset to sunrise.
72 * @see #setAutoMode(int)
74 public static final int AUTO_MODE_TWILIGHT = 2;
76 private final Context mContext;
77 private final int mUserId;
79 private final ContentObserver mContentObserver;
81 private Callback mCallback;
83 public NightDisplayController(@NonNull Context context) {
84 this(context, UserHandle.myUserId());
87 public NightDisplayController(@NonNull Context context, int userId) {
88 mContext = context.getApplicationContext();
91 mContentObserver = new ContentObserver(new Handler(Looper.getMainLooper())) {
93 public void onChange(boolean selfChange, Uri uri) {
94 super.onChange(selfChange, uri);
96 final String setting = uri == null ? null : uri.getLastPathSegment();
97 if (setting != null) {
98 onSettingChanged(setting);
105 * Returns {@code true} when Night display is activated (the display is tinted red).
107 public boolean isActivated() {
108 return Secure.getIntForUser(mContext.getContentResolver(),
109 Secure.NIGHT_DISPLAY_ACTIVATED, 0, mUserId) == 1;
113 * Sets whether Night display should be activated.
115 * @param activated {@code true} if Night display should be activated
116 * @return {@code true} if the activated value was set successfully
118 public boolean setActivated(boolean activated) {
119 return Secure.putIntForUser(mContext.getContentResolver(),
120 Secure.NIGHT_DISPLAY_ACTIVATED, activated ? 1 : 0, mUserId);
124 * Returns the current auto mode value controlling when Night display will be automatically
125 * activated. One of {@link #AUTO_MODE_DISABLED}, {@link #AUTO_MODE_CUSTOM}, or
126 * {@link #AUTO_MODE_TWILIGHT}.
128 public @AutoMode int getAutoMode() {
129 int autoMode = Secure.getIntForUser(mContext.getContentResolver(),
130 Secure.NIGHT_DISPLAY_AUTO_MODE, -1, mUserId);
131 if (autoMode == -1) {
133 Slog.d(TAG, "Using default value for setting: " + Secure.NIGHT_DISPLAY_AUTO_MODE);
135 autoMode = mContext.getResources().getInteger(
136 R.integer.config_defaultNightDisplayAutoMode);
139 if (autoMode != AUTO_MODE_DISABLED
140 && autoMode != AUTO_MODE_CUSTOM
141 && autoMode != AUTO_MODE_TWILIGHT) {
142 Slog.e(TAG, "Invalid autoMode: " + autoMode);
143 autoMode = AUTO_MODE_DISABLED;
150 * Sets the current auto mode value controlling when Night display will be automatically
151 * activated. One of {@link #AUTO_MODE_DISABLED}, {@link #AUTO_MODE_CUSTOM}, or
152 * {@link #AUTO_MODE_TWILIGHT}.
154 * @param autoMode the new auto mode to use
155 * @return {@code true} if new auto mode was set successfully
157 public boolean setAutoMode(@AutoMode int autoMode) {
158 if (autoMode != AUTO_MODE_DISABLED
159 && autoMode != AUTO_MODE_CUSTOM
160 && autoMode != AUTO_MODE_TWILIGHT) {
161 throw new IllegalArgumentException("Invalid autoMode: " + autoMode);
164 return Secure.putIntForUser(mContext.getContentResolver(),
165 Secure.NIGHT_DISPLAY_AUTO_MODE, autoMode, mUserId);
169 * Returns the local time when Night display will be automatically activated when using
170 * {@link #AUTO_MODE_CUSTOM}.
172 public @NonNull LocalTime getCustomStartTime() {
173 int startTimeValue = Secure.getIntForUser(mContext.getContentResolver(),
174 Secure.NIGHT_DISPLAY_CUSTOM_START_TIME, -1, mUserId);
175 if (startTimeValue == -1) {
177 Slog.d(TAG, "Using default value for setting: "
178 + Secure.NIGHT_DISPLAY_CUSTOM_START_TIME);
180 startTimeValue = mContext.getResources().getInteger(
181 R.integer.config_defaultNightDisplayCustomStartTime);
184 return LocalTime.valueOf(startTimeValue);
188 * Sets the local time when Night display will be automatically activated when using
189 * {@link #AUTO_MODE_CUSTOM}.
191 * @param startTime the local time to automatically activate Night display
192 * @return {@code true} if the new custom start time was set successfully
194 public boolean setCustomStartTime(@NonNull LocalTime startTime) {
195 if (startTime == null) {
196 throw new IllegalArgumentException("startTime cannot be null");
198 return Secure.putIntForUser(mContext.getContentResolver(),
199 Secure.NIGHT_DISPLAY_CUSTOM_START_TIME, startTime.toMillis(), mUserId);
203 * Returns the local time when Night display will be automatically deactivated when using
204 * {@link #AUTO_MODE_CUSTOM}.
206 public @NonNull LocalTime getCustomEndTime() {
207 int endTimeValue = Secure.getIntForUser(mContext.getContentResolver(),
208 Secure.NIGHT_DISPLAY_CUSTOM_END_TIME, -1, mUserId);
209 if (endTimeValue == -1) {
211 Slog.d(TAG, "Using default value for setting: "
212 + Secure.NIGHT_DISPLAY_CUSTOM_END_TIME);
214 endTimeValue = mContext.getResources().getInteger(
215 R.integer.config_defaultNightDisplayCustomEndTime);
218 return LocalTime.valueOf(endTimeValue);
222 * Sets the local time when Night display will be automatically deactivated when using
223 * {@link #AUTO_MODE_CUSTOM}.
225 * @param endTime the local time to automatically deactivate Night display
226 * @return {@code true} if the new custom end time was set successfully
228 public boolean setCustomEndTime(@NonNull LocalTime endTime) {
229 if (endTime == null) {
230 throw new IllegalArgumentException("endTime cannot be null");
232 return Secure.putIntForUser(mContext.getContentResolver(),
233 Secure.NIGHT_DISPLAY_CUSTOM_END_TIME, endTime.toMillis(), mUserId);
236 private void onSettingChanged(@NonNull String setting) {
238 Slog.d(TAG, "onSettingChanged: " + setting);
241 if (mCallback != null) {
243 case Secure.NIGHT_DISPLAY_ACTIVATED:
244 mCallback.onActivated(isActivated());
246 case Secure.NIGHT_DISPLAY_AUTO_MODE:
247 mCallback.onAutoModeChanged(getAutoMode());
249 case Secure.NIGHT_DISPLAY_CUSTOM_START_TIME:
250 mCallback.onCustomStartTimeChanged(getCustomStartTime());
252 case Secure.NIGHT_DISPLAY_CUSTOM_END_TIME:
253 mCallback.onCustomEndTimeChanged(getCustomEndTime());
260 * Register a callback to be invoked whenever the Night display settings are changed.
262 public void setListener(Callback callback) {
263 final Callback oldCallback = mCallback;
264 if (oldCallback != callback) {
265 mCallback = callback;
267 if (callback == null) {
268 // Stop listening for changes now that there IS NOT a listener.
269 mContext.getContentResolver().unregisterContentObserver(mContentObserver);
270 } else if (oldCallback == null) {
271 // Start listening for changes now that there IS a listener.
272 final ContentResolver cr = mContext.getContentResolver();
273 cr.registerContentObserver(Secure.getUriFor(Secure.NIGHT_DISPLAY_ACTIVATED),
274 false /* notifyForDescendants */, mContentObserver, mUserId);
275 cr.registerContentObserver(Secure.getUriFor(Secure.NIGHT_DISPLAY_AUTO_MODE),
276 false /* notifyForDescendants */, mContentObserver, mUserId);
277 cr.registerContentObserver(Secure.getUriFor(Secure.NIGHT_DISPLAY_CUSTOM_START_TIME),
278 false /* notifyForDescendants */, mContentObserver, mUserId);
279 cr.registerContentObserver(Secure.getUriFor(Secure.NIGHT_DISPLAY_CUSTOM_END_TIME),
280 false /* notifyForDescendants */, mContentObserver, mUserId);
286 * Returns {@code true} if Night display is supported by the device.
288 public static boolean isAvailable(Context context) {
289 return context.getResources().getBoolean(R.bool.config_nightDisplayAvailable);
293 * A time without a time-zone or date.
295 public static class LocalTime {
298 * The hour of the day from 0 - 23.
300 public final int hourOfDay;
302 * The minute within the hour from 0 - 59.
304 public final int minute;
306 public LocalTime(int hourOfDay, int minute) {
307 if (hourOfDay < 0 || hourOfDay > 23) {
308 throw new IllegalArgumentException("Invalid hourOfDay: " + hourOfDay);
309 } else if (minute < 0 || minute > 59) {
310 throw new IllegalArgumentException("Invalid minute: " + minute);
313 this.hourOfDay = hourOfDay;
314 this.minute = minute;
318 * Returns the first date time corresponding to this local time that occurs before the
319 * provided date time.
321 * @param time the date time to compare against
322 * @return the prior date time corresponding to this local time
324 public Calendar getDateTimeBefore(Calendar time) {
325 final Calendar c = Calendar.getInstance();
326 c.set(Calendar.YEAR, time.get(Calendar.YEAR));
327 c.set(Calendar.DAY_OF_YEAR, time.get(Calendar.DAY_OF_YEAR));
329 c.set(Calendar.HOUR_OF_DAY, hourOfDay);
330 c.set(Calendar.MINUTE, minute);
331 c.set(Calendar.SECOND, 0);
332 c.set(Calendar.MILLISECOND, 0);
334 // Check if the local time has past, if so return the same time tomorrow.
336 c.add(Calendar.DATE, -1);
343 * Returns the first date time corresponding to this local time that occurs after the
344 * provided date time.
346 * @param time the date time to compare against
347 * @return the next date time corresponding to this local time
349 public Calendar getDateTimeAfter(Calendar time) {
350 final Calendar c = Calendar.getInstance();
351 c.set(Calendar.YEAR, time.get(Calendar.YEAR));
352 c.set(Calendar.DAY_OF_YEAR, time.get(Calendar.DAY_OF_YEAR));
354 c.set(Calendar.HOUR_OF_DAY, hourOfDay);
355 c.set(Calendar.MINUTE, minute);
356 c.set(Calendar.SECOND, 0);
357 c.set(Calendar.MILLISECOND, 0);
359 // Check if the local time has past, if so return the same time tomorrow.
360 if (c.before(time)) {
361 c.add(Calendar.DATE, 1);
368 * Returns a local time corresponding the given number of milliseconds from midnight.
370 * @param millis the number of milliseconds from midnight
371 * @return the corresponding local time
373 private static LocalTime valueOf(int millis) {
374 final int hourOfDay = (millis / 3600000) % 24;
375 final int minutes = (millis / 60000) % 60;
376 return new LocalTime(hourOfDay, minutes);
380 * Returns the local time represented as milliseconds from midnight.
382 private int toMillis() {
383 return hourOfDay * 3600000 + minute * 60000;
387 public String toString() {
388 return String.format(Locale.US, "%02d:%02d", hourOfDay, minute);
393 * Callback invoked whenever the Night display settings are changed.
395 public interface Callback {
397 * Callback invoked when the activated state changes.
399 * @param activated {@code true} if Night display is activated
401 default void onActivated(boolean activated) {}
403 * Callback invoked when the auto mode changes.
405 * @param autoMode the auto mode to use
407 default void onAutoModeChanged(int autoMode) {}
409 * Callback invoked when the time to automatically activate Night display changes.
411 * @param startTime the local time to automatically activate Night display
413 default void onCustomStartTimeChanged(LocalTime startTime) {}
415 * Callback invoked when the time to automatically deactivate Night display changes.
417 * @param endTime the local time to automatically deactivate Night display
419 default void onCustomEndTimeChanged(LocalTime endTime) {}