blob: 6cc964bf16ed2d30a0f80160e743faeb60ea3156 [file] [log] [blame]
Justin Klaassen911e8892016-06-21 18:24:24 -07001/*
2 * Copyright (C) 2016 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.internal.app;
18
19import android.annotation.IntDef;
20import android.annotation.NonNull;
Christine Franks69c2d1d2017-01-23 14:45:29 -080021import android.app.ActivityManager;
Justin Klaassen911e8892016-06-21 18:24:24 -070022import android.content.ContentResolver;
23import android.content.Context;
24import android.database.ContentObserver;
Christine Franks8ad6f6d2017-12-13 18:40:43 -080025import android.metrics.LogMaker;
Justin Klaassen911e8892016-06-21 18:24:24 -070026import android.net.Uri;
27import android.os.Handler;
28import android.os.Looper;
Christine Franks17947172017-10-30 16:22:37 -070029import android.os.SystemProperties;
Justin Klaassen911e8892016-06-21 18:24:24 -070030import android.provider.Settings.Secure;
Christine Franks8ad71492017-10-24 19:04:22 -070031import android.provider.Settings.System;
Justin Klaassen911e8892016-06-21 18:24:24 -070032import android.util.Slog;
33
34import com.android.internal.R;
Christine Franks8ad6f6d2017-12-13 18:40:43 -080035import com.android.internal.logging.MetricsLogger;
36import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
Justin Klaassen911e8892016-06-21 18:24:24 -070037
38import java.lang.annotation.Retention;
39import java.lang.annotation.RetentionPolicy;
Christine Franks03213462017-08-25 13:57:26 -070040import java.time.DateTimeException;
41import java.time.Instant;
42import java.time.LocalDateTime;
43import java.time.LocalTime;
44import java.time.ZoneId;
45import java.time.format.DateTimeParseException;
Justin Klaassen911e8892016-06-21 18:24:24 -070046
47/**
Christine Franks5397f032017-11-01 18:35:16 -070048 * Controller for managing night display and color mode settings.
Justin Klaassen911e8892016-06-21 18:24:24 -070049 * <p/>
50 * Night display tints your screen red at night. This makes it easier to look at your screen in
51 * dim light and may help you fall asleep more easily.
52 */
Christine Franks5397f032017-11-01 18:35:16 -070053public final class ColorDisplayController {
Justin Klaassen911e8892016-06-21 18:24:24 -070054
Christine Franks5397f032017-11-01 18:35:16 -070055 private static final String TAG = "ColorDisplayController";
Justin Klaassen911e8892016-06-21 18:24:24 -070056 private static final boolean DEBUG = false;
57
Justin Klaassen911e8892016-06-21 18:24:24 -070058 @Retention(RetentionPolicy.SOURCE)
59 @IntDef({ AUTO_MODE_DISABLED, AUTO_MODE_CUSTOM, AUTO_MODE_TWILIGHT })
60 public @interface AutoMode {}
61
62 /**
63 * Auto mode value to prevent Night display from being automatically activated. It can still
64 * be activated manually via {@link #setActivated(boolean)}.
65 *
66 * @see #setAutoMode(int)
67 */
68 public static final int AUTO_MODE_DISABLED = 0;
69 /**
70 * Auto mode value to automatically activate Night display at a specific start and end time.
71 *
72 * @see #setAutoMode(int)
73 * @see #setCustomStartTime(LocalTime)
74 * @see #setCustomEndTime(LocalTime)
75 */
76 public static final int AUTO_MODE_CUSTOM = 1;
77 /**
78 * Auto mode value to automatically activate Night display from sunset to sunrise.
79 *
80 * @see #setAutoMode(int)
81 */
82 public static final int AUTO_MODE_TWILIGHT = 2;
83
Christine Franks8ad71492017-10-24 19:04:22 -070084 @Retention(RetentionPolicy.SOURCE)
Chia-I Wu1e0e7172018-03-14 15:45:29 -070085 @IntDef({ COLOR_MODE_NATURAL, COLOR_MODE_BOOSTED, COLOR_MODE_SATURATED, COLOR_MODE_AUTOMATIC })
Christine Franks8ad71492017-10-24 19:04:22 -070086 public @interface ColorMode {}
87
88 /**
89 * Color mode with natural colors.
90 *
91 * @see #setColorMode(int)
92 */
93 public static final int COLOR_MODE_NATURAL = 0;
94 /**
95 * Color mode with boosted colors.
96 *
97 * @see #setColorMode(int)
98 */
99 public static final int COLOR_MODE_BOOSTED = 1;
100 /**
101 * Color mode with saturated colors.
102 *
103 * @see #setColorMode(int)
104 */
105 public static final int COLOR_MODE_SATURATED = 2;
Christine Franks17947172017-10-30 16:22:37 -0700106 /**
Chia-I Wu1e0e7172018-03-14 15:45:29 -0700107 * Color mode with automatic colors.
108 *
109 * @see #setColorMode(int)
Christine Franks17947172017-10-30 16:22:37 -0700110 */
Chia-I Wu1e0e7172018-03-14 15:45:29 -0700111 public static final int COLOR_MODE_AUTOMATIC = 3;
Christine Franks17947172017-10-30 16:22:37 -0700112
Justin Klaassen911e8892016-06-21 18:24:24 -0700113 private final Context mContext;
114 private final int mUserId;
Justin Klaassen911e8892016-06-21 18:24:24 -0700115 private final ContentObserver mContentObserver;
116
117 private Callback mCallback;
Christine Franks8ad6f6d2017-12-13 18:40:43 -0800118 private MetricsLogger mMetricsLogger;
Justin Klaassen911e8892016-06-21 18:24:24 -0700119
Christine Franks5397f032017-11-01 18:35:16 -0700120 public ColorDisplayController(@NonNull Context context) {
Christine Franks69c2d1d2017-01-23 14:45:29 -0800121 this(context, ActivityManager.getCurrentUser());
Justin Klaassen911e8892016-06-21 18:24:24 -0700122 }
123
Christine Franks5397f032017-11-01 18:35:16 -0700124 public ColorDisplayController(@NonNull Context context, int userId) {
Justin Klaassen911e8892016-06-21 18:24:24 -0700125 mContext = context.getApplicationContext();
126 mUserId = userId;
127
128 mContentObserver = new ContentObserver(new Handler(Looper.getMainLooper())) {
129 @Override
130 public void onChange(boolean selfChange, Uri uri) {
131 super.onChange(selfChange, uri);
132
133 final String setting = uri == null ? null : uri.getLastPathSegment();
134 if (setting != null) {
135 onSettingChanged(setting);
136 }
137 }
138 };
139 }
140
141 /**
142 * Returns {@code true} when Night display is activated (the display is tinted red).
143 */
144 public boolean isActivated() {
145 return Secure.getIntForUser(mContext.getContentResolver(),
146 Secure.NIGHT_DISPLAY_ACTIVATED, 0, mUserId) == 1;
147 }
148
149 /**
Christine Franks1454eae2017-05-31 10:52:22 -0700150 * Sets whether Night display should be activated. This also sets the last activated time.
Justin Klaassen911e8892016-06-21 18:24:24 -0700151 *
152 * @param activated {@code true} if Night display should be activated
153 * @return {@code true} if the activated value was set successfully
154 */
155 public boolean setActivated(boolean activated) {
Christine Franks1454eae2017-05-31 10:52:22 -0700156 if (isActivated() != activated) {
Christine Franks03213462017-08-25 13:57:26 -0700157 Secure.putStringForUser(mContext.getContentResolver(),
158 Secure.NIGHT_DISPLAY_LAST_ACTIVATED_TIME,
159 LocalDateTime.now().toString(),
Christine Franks1454eae2017-05-31 10:52:22 -0700160 mUserId);
161 }
Justin Klaassen911e8892016-06-21 18:24:24 -0700162 return Secure.putIntForUser(mContext.getContentResolver(),
163 Secure.NIGHT_DISPLAY_ACTIVATED, activated ? 1 : 0, mUserId);
164 }
165
166 /**
Christine Franks1454eae2017-05-31 10:52:22 -0700167 * Returns the time when Night display's activation state last changed, or {@code null} if it
168 * has never been changed.
169 */
Christine Franks03213462017-08-25 13:57:26 -0700170 public LocalDateTime getLastActivatedTime() {
Christine Franks1454eae2017-05-31 10:52:22 -0700171 final ContentResolver cr = mContext.getContentResolver();
Christine Franks03213462017-08-25 13:57:26 -0700172 final String lastActivatedTime = Secure.getStringForUser(
173 cr, Secure.NIGHT_DISPLAY_LAST_ACTIVATED_TIME, mUserId);
174 if (lastActivatedTime != null) {
175 try {
176 return LocalDateTime.parse(lastActivatedTime);
177 } catch (DateTimeParseException ignored) {}
178 // Uses the old epoch time.
179 try {
180 return LocalDateTime.ofInstant(
181 Instant.ofEpochMilli(Long.parseLong(lastActivatedTime)),
182 ZoneId.systemDefault());
183 } catch (DateTimeException|NumberFormatException ignored) {}
Christine Franks1454eae2017-05-31 10:52:22 -0700184 }
Christine Franks03213462017-08-25 13:57:26 -0700185 return null;
Christine Franks1454eae2017-05-31 10:52:22 -0700186 }
187
188 /**
Justin Klaassen911e8892016-06-21 18:24:24 -0700189 * Returns the current auto mode value controlling when Night display will be automatically
190 * activated. One of {@link #AUTO_MODE_DISABLED}, {@link #AUTO_MODE_CUSTOM}, or
191 * {@link #AUTO_MODE_TWILIGHT}.
192 */
193 public @AutoMode int getAutoMode() {
194 int autoMode = Secure.getIntForUser(mContext.getContentResolver(),
195 Secure.NIGHT_DISPLAY_AUTO_MODE, -1, mUserId);
196 if (autoMode == -1) {
197 if (DEBUG) {
198 Slog.d(TAG, "Using default value for setting: " + Secure.NIGHT_DISPLAY_AUTO_MODE);
199 }
200 autoMode = mContext.getResources().getInteger(
201 R.integer.config_defaultNightDisplayAutoMode);
202 }
203
204 if (autoMode != AUTO_MODE_DISABLED
205 && autoMode != AUTO_MODE_CUSTOM
206 && autoMode != AUTO_MODE_TWILIGHT) {
207 Slog.e(TAG, "Invalid autoMode: " + autoMode);
208 autoMode = AUTO_MODE_DISABLED;
209 }
210
211 return autoMode;
212 }
213
214 /**
Christine Franks8ad6f6d2017-12-13 18:40:43 -0800215 * Returns the current auto mode value, without validation, or {@code 1} if the auto mode has
216 * never been set.
217 */
218 public int getAutoModeRaw() {
219 return Secure.getIntForUser(mContext.getContentResolver(), Secure.NIGHT_DISPLAY_AUTO_MODE,
220 -1, mUserId);
221 }
222
223 /**
Justin Klaassen911e8892016-06-21 18:24:24 -0700224 * Sets the current auto mode value controlling when Night display will be automatically
225 * activated. One of {@link #AUTO_MODE_DISABLED}, {@link #AUTO_MODE_CUSTOM}, or
226 * {@link #AUTO_MODE_TWILIGHT}.
227 *
228 * @param autoMode the new auto mode to use
229 * @return {@code true} if new auto mode was set successfully
230 */
231 public boolean setAutoMode(@AutoMode int autoMode) {
232 if (autoMode != AUTO_MODE_DISABLED
233 && autoMode != AUTO_MODE_CUSTOM
234 && autoMode != AUTO_MODE_TWILIGHT) {
235 throw new IllegalArgumentException("Invalid autoMode: " + autoMode);
236 }
237
Justin Klaassen4bcc06f2017-06-20 10:08:50 -0700238 if (getAutoMode() != autoMode) {
Christine Franks03213462017-08-25 13:57:26 -0700239 Secure.putStringForUser(mContext.getContentResolver(),
240 Secure.NIGHT_DISPLAY_LAST_ACTIVATED_TIME,
241 null,
242 mUserId);
Christine Franks8ad6f6d2017-12-13 18:40:43 -0800243 getMetricsLogger().write(new LogMaker(
244 MetricsEvent.ACTION_NIGHT_DISPLAY_AUTO_MODE_CHANGED)
245 .setType(MetricsEvent.TYPE_ACTION)
246 .setSubtype(autoMode));
Justin Klaassen4bcc06f2017-06-20 10:08:50 -0700247 }
Christine Franks8ad6f6d2017-12-13 18:40:43 -0800248
Justin Klaassen911e8892016-06-21 18:24:24 -0700249 return Secure.putIntForUser(mContext.getContentResolver(),
250 Secure.NIGHT_DISPLAY_AUTO_MODE, autoMode, mUserId);
251 }
252
253 /**
254 * Returns the local time when Night display will be automatically activated when using
255 * {@link #AUTO_MODE_CUSTOM}.
256 */
257 public @NonNull LocalTime getCustomStartTime() {
258 int startTimeValue = Secure.getIntForUser(mContext.getContentResolver(),
259 Secure.NIGHT_DISPLAY_CUSTOM_START_TIME, -1, mUserId);
260 if (startTimeValue == -1) {
261 if (DEBUG) {
262 Slog.d(TAG, "Using default value for setting: "
263 + Secure.NIGHT_DISPLAY_CUSTOM_START_TIME);
264 }
265 startTimeValue = mContext.getResources().getInteger(
266 R.integer.config_defaultNightDisplayCustomStartTime);
267 }
268
Christine Franks03213462017-08-25 13:57:26 -0700269 return LocalTime.ofSecondOfDay(startTimeValue / 1000);
Justin Klaassen911e8892016-06-21 18:24:24 -0700270 }
271
272 /**
273 * Sets the local time when Night display will be automatically activated when using
274 * {@link #AUTO_MODE_CUSTOM}.
275 *
276 * @param startTime the local time to automatically activate Night display
277 * @return {@code true} if the new custom start time was set successfully
278 */
279 public boolean setCustomStartTime(@NonNull LocalTime startTime) {
280 if (startTime == null) {
281 throw new IllegalArgumentException("startTime cannot be null");
282 }
Christine Franks8ad6f6d2017-12-13 18:40:43 -0800283 getMetricsLogger().write(new LogMaker(
284 MetricsEvent.ACTION_NIGHT_DISPLAY_AUTO_MODE_CUSTOM_TIME_CHANGED)
285 .setType(MetricsEvent.TYPE_ACTION)
286 .setSubtype(0));
Justin Klaassen911e8892016-06-21 18:24:24 -0700287 return Secure.putIntForUser(mContext.getContentResolver(),
Christine Franks03213462017-08-25 13:57:26 -0700288 Secure.NIGHT_DISPLAY_CUSTOM_START_TIME, startTime.toSecondOfDay() * 1000, mUserId);
Justin Klaassen911e8892016-06-21 18:24:24 -0700289 }
290
291 /**
292 * Returns the local time when Night display will be automatically deactivated when using
293 * {@link #AUTO_MODE_CUSTOM}.
294 */
295 public @NonNull LocalTime getCustomEndTime() {
296 int endTimeValue = Secure.getIntForUser(mContext.getContentResolver(),
297 Secure.NIGHT_DISPLAY_CUSTOM_END_TIME, -1, mUserId);
298 if (endTimeValue == -1) {
299 if (DEBUG) {
300 Slog.d(TAG, "Using default value for setting: "
301 + Secure.NIGHT_DISPLAY_CUSTOM_END_TIME);
302 }
303 endTimeValue = mContext.getResources().getInteger(
304 R.integer.config_defaultNightDisplayCustomEndTime);
305 }
306
Christine Franks03213462017-08-25 13:57:26 -0700307 return LocalTime.ofSecondOfDay(endTimeValue / 1000);
Justin Klaassen911e8892016-06-21 18:24:24 -0700308 }
309
310 /**
311 * Sets the local time when Night display will be automatically deactivated when using
312 * {@link #AUTO_MODE_CUSTOM}.
313 *
314 * @param endTime the local time to automatically deactivate Night display
315 * @return {@code true} if the new custom end time was set successfully
316 */
317 public boolean setCustomEndTime(@NonNull LocalTime endTime) {
318 if (endTime == null) {
319 throw new IllegalArgumentException("endTime cannot be null");
320 }
Christine Franks8ad6f6d2017-12-13 18:40:43 -0800321 getMetricsLogger().write(new LogMaker(
322 MetricsEvent.ACTION_NIGHT_DISPLAY_AUTO_MODE_CUSTOM_TIME_CHANGED)
323 .setType(MetricsEvent.TYPE_ACTION)
324 .setSubtype(1));
Justin Klaassen911e8892016-06-21 18:24:24 -0700325 return Secure.putIntForUser(mContext.getContentResolver(),
Christine Franks03213462017-08-25 13:57:26 -0700326 Secure.NIGHT_DISPLAY_CUSTOM_END_TIME, endTime.toSecondOfDay() * 1000, mUserId);
Justin Klaassen911e8892016-06-21 18:24:24 -0700327 }
328
Christine Franks6418d0b2017-02-13 09:48:00 -0800329 /**
330 * Returns the color temperature (in Kelvin) to tint the display when activated.
331 */
332 public int getColorTemperature() {
333 int colorTemperature = Secure.getIntForUser(mContext.getContentResolver(),
334 Secure.NIGHT_DISPLAY_COLOR_TEMPERATURE, -1, mUserId);
335 if (colorTemperature == -1) {
336 if (DEBUG) {
337 Slog.d(TAG, "Using default value for setting: "
338 + Secure.NIGHT_DISPLAY_COLOR_TEMPERATURE);
339 }
340 colorTemperature = getDefaultColorTemperature();
341 }
342 final int minimumTemperature = getMinimumColorTemperature();
343 final int maximumTemperature = getMaximumColorTemperature();
344 if (colorTemperature < minimumTemperature) {
345 colorTemperature = minimumTemperature;
346 } else if (colorTemperature > maximumTemperature) {
347 colorTemperature = maximumTemperature;
348 }
349
350 return colorTemperature;
351 }
352
353 /**
354 * Sets the current temperature.
355 *
356 * @param colorTemperature the temperature, in Kelvin.
357 * @return {@code true} if new temperature was set successfully.
358 */
359 public boolean setColorTemperature(int colorTemperature) {
360 return Secure.putIntForUser(mContext.getContentResolver(),
361 Secure.NIGHT_DISPLAY_COLOR_TEMPERATURE, colorTemperature, mUserId);
362 }
363
364 /**
Chia-I Wu1e0e7172018-03-14 15:45:29 -0700365 * Get the current color mode from system properties, or return -1.
366 *
367 * See com.android.server.display.DisplayTransformManager.
368 */
369 private @ColorMode int getCurrentColorModeFromSystemProperties() {
370 int displayColorSetting = SystemProperties.getInt("persist.sys.sf.native_mode", 0);
371 if (displayColorSetting == 0) {
372 return "1.0".equals(SystemProperties.get("persist.sys.sf.color_saturation"))
373 ? COLOR_MODE_NATURAL : COLOR_MODE_BOOSTED;
374 } else if (displayColorSetting == 1) {
375 return COLOR_MODE_SATURATED;
376 } else if (displayColorSetting == 2) {
377 return COLOR_MODE_AUTOMATIC;
378 } else {
379 return -1;
380 }
381 }
382
383 private boolean isColorModeAvailable(@ColorMode int colorMode) {
384 // SATURATED is always allowed
385 if (colorMode == COLOR_MODE_SATURATED) {
386 return true;
387 }
388
389 final int[] availableColorModes = mContext.getResources().getIntArray(
390 R.array.config_availableColorModes);
391 for (int mode : availableColorModes) {
392 if (mode == colorMode) {
393 return true;
394 }
395 }
396 return false;
397 }
398
399 /**
Christine Franks8ad71492017-10-24 19:04:22 -0700400 * Get the current color mode.
401 */
402 public int getColorMode() {
Daniel Solomon317a3572018-03-30 18:36:37 -0700403 if (getAccessibilityTransformActivated()) {
404 return COLOR_MODE_SATURATED;
405 }
406
Chia-I Wu1e0e7172018-03-14 15:45:29 -0700407 int colorMode = System.getIntForUser(mContext.getContentResolver(),
Christine Franks17947172017-10-30 16:22:37 -0700408 System.DISPLAY_COLOR_MODE, -1, mUserId);
Chia-I Wu1e0e7172018-03-14 15:45:29 -0700409 if (colorMode == -1) {
Christine Franks17947172017-10-30 16:22:37 -0700410 // There still might be a legacy system property controlling color mode that we need to
411 // respect.
Chia-I Wu1e0e7172018-03-14 15:45:29 -0700412 colorMode = getCurrentColorModeFromSystemProperties();
Christine Franks8ad71492017-10-24 19:04:22 -0700413 }
Chia-I Wu1e0e7172018-03-14 15:45:29 -0700414
415 // This happens when a color mode is no longer available (e.g., after system update or B&R)
416 // or the device does not support any color mode.
417 if (!isColorModeAvailable(colorMode)) {
418 if (colorMode == COLOR_MODE_BOOSTED && isColorModeAvailable(COLOR_MODE_NATURAL)) {
419 colorMode = COLOR_MODE_NATURAL;
420 } else if (colorMode == COLOR_MODE_SATURATED
421 && isColorModeAvailable(COLOR_MODE_AUTOMATIC)) {
422 colorMode = COLOR_MODE_AUTOMATIC;
423 } else {
424 colorMode = COLOR_MODE_SATURATED;
425 }
426 }
427
Christine Franks8ad71492017-10-24 19:04:22 -0700428 return colorMode;
429 }
430
431 /**
432 * Set the current color mode.
433 *
434 * @param colorMode the color mode
435 */
436 public void setColorMode(@ColorMode int colorMode) {
Chia-I Wu1e0e7172018-03-14 15:45:29 -0700437 if (!isColorModeAvailable(colorMode)) {
Christine Franks8ad71492017-10-24 19:04:22 -0700438 throw new IllegalArgumentException("Invalid colorMode: " + colorMode);
439 }
440 System.putIntForUser(mContext.getContentResolver(), System.DISPLAY_COLOR_MODE, colorMode,
441 mUserId);
442 }
443
444 /**
Christine Franks6418d0b2017-02-13 09:48:00 -0800445 * Returns the minimum allowed color temperature (in Kelvin) to tint the display when activated.
446 */
447 public int getMinimumColorTemperature() {
448 return mContext.getResources().getInteger(
449 R.integer.config_nightDisplayColorTemperatureMin);
450 }
451
452 /**
453 * Returns the maximum allowed color temperature (in Kelvin) to tint the display when activated.
454 */
455 public int getMaximumColorTemperature() {
456 return mContext.getResources().getInteger(
457 R.integer.config_nightDisplayColorTemperatureMax);
458 }
459
460 /**
461 * Returns the default color temperature (in Kelvin) to tint the display when activated.
462 */
463 public int getDefaultColorTemperature() {
464 return mContext.getResources().getInteger(
465 R.integer.config_nightDisplayColorTemperatureDefault);
466 }
467
Daniel Solomon317a3572018-03-30 18:36:37 -0700468 /**
469 * Returns true if any Accessibility color transforms are enabled.
470 */
471 public boolean getAccessibilityTransformActivated() {
472 final ContentResolver cr = mContext.getContentResolver();
473 return
474 Secure.getIntForUser(cr, Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED,
475 0, mUserId) == 1
476 || Secure.getIntForUser(cr, Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED,
477 0, mUserId) == 1;
478 }
479
Justin Klaassen911e8892016-06-21 18:24:24 -0700480 private void onSettingChanged(@NonNull String setting) {
481 if (DEBUG) {
482 Slog.d(TAG, "onSettingChanged: " + setting);
483 }
484
485 if (mCallback != null) {
486 switch (setting) {
487 case Secure.NIGHT_DISPLAY_ACTIVATED:
488 mCallback.onActivated(isActivated());
489 break;
490 case Secure.NIGHT_DISPLAY_AUTO_MODE:
491 mCallback.onAutoModeChanged(getAutoMode());
492 break;
493 case Secure.NIGHT_DISPLAY_CUSTOM_START_TIME:
494 mCallback.onCustomStartTimeChanged(getCustomStartTime());
495 break;
496 case Secure.NIGHT_DISPLAY_CUSTOM_END_TIME:
497 mCallback.onCustomEndTimeChanged(getCustomEndTime());
498 break;
Christine Franks6418d0b2017-02-13 09:48:00 -0800499 case Secure.NIGHT_DISPLAY_COLOR_TEMPERATURE:
500 mCallback.onColorTemperatureChanged(getColorTemperature());
501 break;
Christine Franks8ad71492017-10-24 19:04:22 -0700502 case System.DISPLAY_COLOR_MODE:
503 mCallback.onDisplayColorModeChanged(getColorMode());
504 break;
Daniel Solomon317a3572018-03-30 18:36:37 -0700505 case Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED:
506 case Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED:
507 mCallback.onAccessibilityTransformChanged(getAccessibilityTransformActivated());
508 break;
Justin Klaassen911e8892016-06-21 18:24:24 -0700509 }
510 }
511 }
512
513 /**
514 * Register a callback to be invoked whenever the Night display settings are changed.
515 */
516 public void setListener(Callback callback) {
517 final Callback oldCallback = mCallback;
518 if (oldCallback != callback) {
519 mCallback = callback;
520
521 if (callback == null) {
522 // Stop listening for changes now that there IS NOT a listener.
523 mContext.getContentResolver().unregisterContentObserver(mContentObserver);
524 } else if (oldCallback == null) {
525 // Start listening for changes now that there IS a listener.
526 final ContentResolver cr = mContext.getContentResolver();
527 cr.registerContentObserver(Secure.getUriFor(Secure.NIGHT_DISPLAY_ACTIVATED),
528 false /* notifyForDescendants */, mContentObserver, mUserId);
529 cr.registerContentObserver(Secure.getUriFor(Secure.NIGHT_DISPLAY_AUTO_MODE),
530 false /* notifyForDescendants */, mContentObserver, mUserId);
531 cr.registerContentObserver(Secure.getUriFor(Secure.NIGHT_DISPLAY_CUSTOM_START_TIME),
532 false /* notifyForDescendants */, mContentObserver, mUserId);
533 cr.registerContentObserver(Secure.getUriFor(Secure.NIGHT_DISPLAY_CUSTOM_END_TIME),
534 false /* notifyForDescendants */, mContentObserver, mUserId);
Christine Franks6418d0b2017-02-13 09:48:00 -0800535 cr.registerContentObserver(Secure.getUriFor(Secure.NIGHT_DISPLAY_COLOR_TEMPERATURE),
536 false /* notifyForDescendants */, mContentObserver, mUserId);
Christine Franks8ad71492017-10-24 19:04:22 -0700537 cr.registerContentObserver(System.getUriFor(System.DISPLAY_COLOR_MODE),
538 false /* notifyForDecendants */, mContentObserver, mUserId);
Daniel Solomon317a3572018-03-30 18:36:37 -0700539 cr.registerContentObserver(
540 Secure.getUriFor(Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED),
541 false /* notifyForDecendants */, mContentObserver, mUserId);
542 cr.registerContentObserver(
543 Secure.getUriFor(Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED),
544 false /* notifyForDecendants */, mContentObserver, mUserId);
Justin Klaassen911e8892016-06-21 18:24:24 -0700545 }
546 }
547 }
548
Christine Franks8ad6f6d2017-12-13 18:40:43 -0800549 private MetricsLogger getMetricsLogger() {
550 if (mMetricsLogger == null) {
551 mMetricsLogger = new MetricsLogger();
552 }
553 return mMetricsLogger;
554 }
555
Justin Klaassen911e8892016-06-21 18:24:24 -0700556 /**
557 * Returns {@code true} if Night display is supported by the device.
558 */
559 public static boolean isAvailable(Context context) {
560 return context.getResources().getBoolean(R.bool.config_nightDisplayAvailable);
561 }
562
563 /**
Justin Klaassen911e8892016-06-21 18:24:24 -0700564 * Callback invoked whenever the Night display settings are changed.
565 */
566 public interface Callback {
567 /**
568 * Callback invoked when the activated state changes.
569 *
570 * @param activated {@code true} if Night display is activated
571 */
572 default void onActivated(boolean activated) {}
573 /**
574 * Callback invoked when the auto mode changes.
575 *
576 * @param autoMode the auto mode to use
577 */
578 default void onAutoModeChanged(int autoMode) {}
579 /**
580 * Callback invoked when the time to automatically activate Night display changes.
581 *
582 * @param startTime the local time to automatically activate Night display
583 */
584 default void onCustomStartTimeChanged(LocalTime startTime) {}
585 /**
586 * Callback invoked when the time to automatically deactivate Night display changes.
587 *
588 * @param endTime the local time to automatically deactivate Night display
589 */
590 default void onCustomEndTimeChanged(LocalTime endTime) {}
Christine Franks6418d0b2017-02-13 09:48:00 -0800591
592 /**
593 * Callback invoked when the color temperature changes.
594 *
595 * @param colorTemperature the color temperature to tint the screen
596 */
597 default void onColorTemperatureChanged(int colorTemperature) {}
Christine Franks8ad71492017-10-24 19:04:22 -0700598
599 /**
600 * Callback invoked when the color mode changes.
601 *
602 * @param displayColorMode the color mode
603 */
604 default void onDisplayColorModeChanged(int displayColorMode) {}
Daniel Solomon317a3572018-03-30 18:36:37 -0700605
606 /**
607 * Callback invoked when Accessibility color transforms change.
608 *
609 * @param state the state Accessibility color transforms (true of active)
610 */
611 default void onAccessibilityTransformChanged(boolean state) {}
Justin Klaassen911e8892016-06-21 18:24:24 -0700612 }
613}