blob: bce8f80f5c3e2f7b860141107a0ae08f399f1f81 [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.server.display;
18
Christine Franks245ffd42018-11-16 13:45:14 -080019import static com.android.server.display.DisplayTransformManager.LEVEL_COLOR_MATRIX_DISPLAY_WHITE_BALANCE;
Christine Franks39b03112018-07-03 14:46:07 -070020import static com.android.server.display.DisplayTransformManager.LEVEL_COLOR_MATRIX_NIGHT_DISPLAY;
Christine Franks09c229e2018-12-14 10:37:40 -080021import static com.android.server.display.DisplayTransformManager.LEVEL_COLOR_MATRIX_SATURATION;
Christine Franks39b03112018-07-03 14:46:07 -070022
Christine Franks09c229e2018-12-14 10:37:40 -080023import android.Manifest;
Justin Klaassen639214e2016-07-14 21:00:06 -070024import android.animation.Animator;
25import android.animation.AnimatorListenerAdapter;
26import android.animation.TypeEvaluator;
27import android.animation.ValueAnimator;
Justin Klaassen4346f632016-08-08 15:01:47 -070028import android.annotation.NonNull;
Justin Klaassen908b86c2016-08-08 09:18:42 -070029import android.annotation.Nullable;
Justin Klaassen911e8892016-06-21 18:24:24 -070030import android.app.AlarmManager;
31import android.content.BroadcastReceiver;
32import android.content.ContentResolver;
33import android.content.Context;
34import android.content.Intent;
35import android.content.IntentFilter;
Christine Franks09c229e2018-12-14 10:37:40 -080036import android.content.pm.PackageManager;
Justin Klaassen2696d992016-07-11 21:26:46 -070037import android.database.ContentObserver;
Christine Franks245ffd42018-11-16 13:45:14 -080038import android.hardware.display.ColorDisplayManager;
Christine Franks39b03112018-07-03 14:46:07 -070039import android.hardware.display.IColorDisplayManager;
Justin Klaassen2696d992016-07-11 21:26:46 -070040import android.net.Uri;
Justin Klaassen639214e2016-07-14 21:00:06 -070041import android.opengl.Matrix;
Christine Franks39b03112018-07-03 14:46:07 -070042import android.os.Binder;
Justin Klaassen911e8892016-06-21 18:24:24 -070043import android.os.Handler;
44import android.os.Looper;
Christine Franks09c229e2018-12-14 10:37:40 -080045import android.os.Message;
Justin Klaassen911e8892016-06-21 18:24:24 -070046import android.os.UserHandle;
47import android.provider.Settings.Secure;
Christine Franks57fdde82018-07-03 14:46:07 -070048import android.provider.Settings.System;
Justin Klaassen639214e2016-07-14 21:00:06 -070049import android.util.MathUtils;
Justin Klaassen911e8892016-06-21 18:24:24 -070050import android.util.Slog;
Christine Franks9114f462019-01-04 11:27:30 -080051import android.view.accessibility.AccessibilityManager;
Justin Klaassen639214e2016-07-14 21:00:06 -070052import android.view.animation.AnimationUtils;
Justin Klaassen911e8892016-06-21 18:24:24 -070053
Christine Franks39b03112018-07-03 14:46:07 -070054import com.android.internal.R;
Christine Franks57fdde82018-07-03 14:46:07 -070055import com.android.internal.annotations.VisibleForTesting;
Christine Franks5397f032017-11-01 18:35:16 -070056import com.android.internal.app.ColorDisplayController;
Christine Franks57fdde82018-07-03 14:46:07 -070057import com.android.server.DisplayThread;
Justin Klaassen911e8892016-06-21 18:24:24 -070058import com.android.server.SystemService;
59import com.android.server.twilight.TwilightListener;
60import com.android.server.twilight.TwilightManager;
61import com.android.server.twilight.TwilightState;
62
Christine Franks57fdde82018-07-03 14:46:07 -070063import java.time.DateTimeException;
64import java.time.Instant;
Christine Franks03213462017-08-25 13:57:26 -070065import java.time.LocalDateTime;
66import java.time.LocalTime;
67import java.time.ZoneId;
Christine Franks57fdde82018-07-03 14:46:07 -070068import java.time.format.DateTimeParseException;
Christine Franks09c229e2018-12-14 10:37:40 -080069import java.util.Arrays;
Christine Franks8ad71492017-10-24 19:04:22 -070070
Justin Klaassen911e8892016-06-21 18:24:24 -070071/**
Christine Franks39b03112018-07-03 14:46:07 -070072 * Controls the display's color transforms.
Justin Klaassen911e8892016-06-21 18:24:24 -070073 */
Christine Franks57fdde82018-07-03 14:46:07 -070074public final class ColorDisplayService extends SystemService {
Justin Klaassen911e8892016-06-21 18:24:24 -070075
Christine Franks5397f032017-11-01 18:35:16 -070076 private static final String TAG = "ColorDisplayService";
Justin Klaassen911e8892016-06-21 18:24:24 -070077
78 /**
Christine Franks7b83b4282017-01-18 14:55:00 -080079 * The transition time, in milliseconds, for Night Display to turn on/off.
80 */
81 private static final long TRANSITION_DURATION = 3000L;
82
83 /**
Justin Klaassen639214e2016-07-14 21:00:06 -070084 * The identity matrix, used if one of the given matrices is {@code null}.
85 */
86 private static final float[] MATRIX_IDENTITY = new float[16];
Christine Franks57fdde82018-07-03 14:46:07 -070087
Justin Klaassen639214e2016-07-14 21:00:06 -070088 static {
89 Matrix.setIdentityM(MATRIX_IDENTITY, 0);
90 }
91
Christine Franks09c229e2018-12-14 10:37:40 -080092 private static final int MSG_APPLY_NIGHT_DISPLAY_IMMEDIATE = 0;
93 private static final int MSG_APPLY_NIGHT_DISPLAY_ANIMATED = 1;
94 private static final int MSG_APPLY_GLOBAL_SATURATION = 2;
95
Justin Klaassen639214e2016-07-14 21:00:06 -070096 /**
97 * Evaluator used to animate color matrix transitions.
98 */
99 private static final ColorMatrixEvaluator COLOR_MATRIX_EVALUATOR = new ColorMatrixEvaluator();
100
Christine Franks245ffd42018-11-16 13:45:14 -0800101 private final TintController mNightDisplayTintController = new TintController() {
102
103 private float[] mMatrixNightDisplay = new float[16];
104 private final float[] mColorTempCoefficients = new float[9];
105
106 /**
107 * Set coefficients based on whether the color matrix is linear or not.
108 */
109 @Override
110 public void setUp(Context context, boolean needsLinear) {
111 final String[] coefficients = context.getResources().getStringArray(needsLinear
112 ? R.array.config_nightDisplayColorTemperatureCoefficients
113 : R.array.config_nightDisplayColorTemperatureCoefficientsNative);
114 for (int i = 0; i < 9 && i < coefficients.length; i++) {
115 mColorTempCoefficients[i] = Float.parseFloat(coefficients[i]);
116 }
117 }
118
119 @Override
120 public void setMatrix(int cct) {
121 if (mMatrixNightDisplay.length != 16) {
122 Slog.d(TAG, "The display transformation matrix must be 4x4");
123 return;
124 }
125
126 Matrix.setIdentityM(mMatrixNightDisplay, 0);
127
128 final float squareTemperature = cct * cct;
129 final float red = squareTemperature * mColorTempCoefficients[0]
130 + cct * mColorTempCoefficients[1] + mColorTempCoefficients[2];
131 final float green = squareTemperature * mColorTempCoefficients[3]
132 + cct * mColorTempCoefficients[4] + mColorTempCoefficients[5];
133 final float blue = squareTemperature * mColorTempCoefficients[6]
134 + cct * mColorTempCoefficients[7] + mColorTempCoefficients[8];
135 mMatrixNightDisplay[0] = red;
136 mMatrixNightDisplay[5] = green;
137 mMatrixNightDisplay[10] = blue;
138 }
139
140 @Override
141 public float[] getMatrix() {
142 return isActivated() ? mMatrixNightDisplay : MATRIX_IDENTITY;
143 }
144
145 @Override
146 public int getLevel() {
147 return LEVEL_COLOR_MATRIX_NIGHT_DISPLAY;
148 }
149 };
150
151 private final TintController mDisplayWhiteBalanceTintController = new TintController() {
152
153 private float[] mMatrixDisplayWhiteBalance = new float[16];
154
155 @Override
156 public void setUp(Context context, boolean needsLinear) {
157 }
158
159 @Override
160 public float[] getMatrix() {
161 return isActivated() ? mMatrixDisplayWhiteBalance : MATRIX_IDENTITY;
162 }
163
164 @Override
165 public void setMatrix(int cct) {
166 }
167
168 @Override
169 public int getLevel() {
170 return LEVEL_COLOR_MATRIX_DISPLAY_WHITE_BALANCE;
171 }
172 };
173
Christine Franks09c229e2018-12-14 10:37:40 -0800174 private final TintController mGlobalSaturationTintController = new TintController() {
175
176 private float[] mMatrixGlobalSaturation = new float[16];
177
178 @Override
179 public void setUp(Context context, boolean needsLinear) {
180 }
181
182 @Override
183 public float[] getMatrix() {
184 return Arrays.copyOf(mMatrixGlobalSaturation, mMatrixGlobalSaturation.length);
185 }
186
187 @Override
188 public void setMatrix(int saturationLevel) {
189 if (saturationLevel < 0) {
190 saturationLevel = 0;
191 } else if (saturationLevel > 100) {
192 saturationLevel = 100;
193 }
194 Slog.d(TAG, "Setting saturation level: " + saturationLevel);
195
196 if (saturationLevel == 100) {
197 Matrix.setIdentityM(mMatrixGlobalSaturation, 0);
198 } else {
199 float saturation = saturationLevel * 0.1f;
200 float desaturation = 1.0f - saturation;
201 float[] luminance = {0.231f * desaturation, 0.715f * desaturation,
202 0.072f * desaturation};
203 mMatrixGlobalSaturation[0] = luminance[0] + saturation;
204 mMatrixGlobalSaturation[1] = luminance[0];
205 mMatrixGlobalSaturation[2] = luminance[0];
206 mMatrixGlobalSaturation[4] = luminance[1];
207 mMatrixGlobalSaturation[5] = luminance[1] + saturation;
208 mMatrixGlobalSaturation[6] = luminance[1];
209 mMatrixGlobalSaturation[8] = luminance[2];
210 mMatrixGlobalSaturation[9] = luminance[2];
211 mMatrixGlobalSaturation[10] = luminance[2] + saturation;
212 }
213 }
214
215 @Override
216 public int getLevel() {
217 return LEVEL_COLOR_MATRIX_SATURATION;
218 }
219 };
220
Christine Franks9114f462019-01-04 11:27:30 -0800221 /**
222 * Matrix and offset used for converting color to grayscale.
223 */
224 private static final float[] MATRIX_GRAYSCALE = new float[]{
225 .2126f, .2126f, .2126f, 0f,
226 .7152f, .7152f, .7152f, 0f,
227 .0722f, .0722f, .0722f, 0f,
228 0f, 0f, 0f, 1f
229 };
230
231 /**
232 * Matrix and offset used for luminance inversion. Represents a transform from RGB to YIQ color
233 * space, rotation around the Y axis by 180 degrees, transform back to RGB color space, and
234 * subtraction from 1. The last row represents a non-multiplied addition, see surfaceflinger's
235 * ProgramCache for full implementation details.
236 */
237 private static final float[] MATRIX_INVERT_COLOR = new float[] {
238 0.402f, -0.598f, -0.599f, 0f,
239 -1.174f, -0.174f, -1.175f, 0f,
240 -0.228f, -0.228f, 0.772f, 0f,
241 1f, 1f, 1f, 1f
242 };
243
Justin Klaassen2696d992016-07-11 21:26:46 -0700244 private final Handler mHandler;
245
Justin Klaassen911e8892016-06-21 18:24:24 -0700246 private int mCurrentUser = UserHandle.USER_NULL;
Justin Klaassen2696d992016-07-11 21:26:46 -0700247 private ContentObserver mUserSetupObserver;
Justin Klaassen911e8892016-06-21 18:24:24 -0700248 private boolean mBootCompleted;
249
Christine Franks57fdde82018-07-03 14:46:07 -0700250 private ColorDisplayController mNightDisplayController;
251 private ContentObserver mContentObserver;
Christine Franks57fdde82018-07-03 14:46:07 -0700252
Christine Franks245ffd42018-11-16 13:45:14 -0800253 private DisplayWhiteBalanceListener mDisplayWhiteBalanceListener;
254
Christine Franks57fdde82018-07-03 14:46:07 -0700255 private NightDisplayAutoMode mNightDisplayAutoMode;
Justin Klaassen911e8892016-06-21 18:24:24 -0700256
Christine Franks245ffd42018-11-16 13:45:14 -0800257 private Integer mDisplayWhiteBalanceColorTemperature;
258
Christine Franks5397f032017-11-01 18:35:16 -0700259 public ColorDisplayService(Context context) {
Justin Klaassen911e8892016-06-21 18:24:24 -0700260 super(context);
Christine Franks09c229e2018-12-14 10:37:40 -0800261 mHandler = new TintHandler(Looper.getMainLooper());
Justin Klaassen911e8892016-06-21 18:24:24 -0700262 }
263
264 @Override
265 public void onStart() {
Christine Franks39b03112018-07-03 14:46:07 -0700266 publishBinderService(Context.COLOR_DISPLAY_SERVICE, new BinderService());
Christine Franks245ffd42018-11-16 13:45:14 -0800267 publishLocalService(ColorDisplayServiceInternal.class, new ColorDisplayServiceInternal());
Justin Klaassen911e8892016-06-21 18:24:24 -0700268 }
269
270 @Override
Justin Klaassen2696d992016-07-11 21:26:46 -0700271 public void onBootPhase(int phase) {
Christine Frankse5bb03e2017-02-10 17:36:10 -0800272 if (phase >= PHASE_BOOT_COMPLETED) {
Justin Klaassen2696d992016-07-11 21:26:46 -0700273 mBootCompleted = true;
274
275 // Register listeners now that boot is complete.
276 if (mCurrentUser != UserHandle.USER_NULL && mUserSetupObserver == null) {
277 setUp();
278 }
279 }
280 }
281
282 @Override
Justin Klaassen911e8892016-06-21 18:24:24 -0700283 public void onStartUser(int userHandle) {
284 super.onStartUser(userHandle);
285
Justin Klaassen911e8892016-06-21 18:24:24 -0700286 if (mCurrentUser == UserHandle.USER_NULL) {
Justin Klaassen2696d992016-07-11 21:26:46 -0700287 onUserChanged(userHandle);
Justin Klaassen911e8892016-06-21 18:24:24 -0700288 }
289 }
290
291 @Override
292 public void onSwitchUser(int userHandle) {
293 super.onSwitchUser(userHandle);
294
Justin Klaassen2696d992016-07-11 21:26:46 -0700295 onUserChanged(userHandle);
Justin Klaassen911e8892016-06-21 18:24:24 -0700296 }
297
298 @Override
299 public void onStopUser(int userHandle) {
300 super.onStopUser(userHandle);
301
Justin Klaassen911e8892016-06-21 18:24:24 -0700302 if (mCurrentUser == userHandle) {
Justin Klaassen2696d992016-07-11 21:26:46 -0700303 onUserChanged(UserHandle.USER_NULL);
Justin Klaassen911e8892016-06-21 18:24:24 -0700304 }
305 }
306
Justin Klaassen2696d992016-07-11 21:26:46 -0700307 private void onUserChanged(int userHandle) {
308 final ContentResolver cr = getContext().getContentResolver();
Justin Klaassen911e8892016-06-21 18:24:24 -0700309
Justin Klaassen2696d992016-07-11 21:26:46 -0700310 if (mCurrentUser != UserHandle.USER_NULL) {
311 if (mUserSetupObserver != null) {
312 cr.unregisterContentObserver(mUserSetupObserver);
313 mUserSetupObserver = null;
314 } else if (mBootCompleted) {
315 tearDown();
316 }
317 }
318
319 mCurrentUser = userHandle;
320
321 if (mCurrentUser != UserHandle.USER_NULL) {
322 if (!isUserSetupCompleted(cr, mCurrentUser)) {
323 mUserSetupObserver = new ContentObserver(mHandler) {
324 @Override
325 public void onChange(boolean selfChange, Uri uri) {
326 if (isUserSetupCompleted(cr, mCurrentUser)) {
327 cr.unregisterContentObserver(this);
328 mUserSetupObserver = null;
329
330 if (mBootCompleted) {
331 setUp();
332 }
333 }
334 }
335 };
336 cr.registerContentObserver(Secure.getUriFor(Secure.USER_SETUP_COMPLETE),
Christine Franks39b03112018-07-03 14:46:07 -0700337 false /* notifyForDescendants */, mUserSetupObserver, mCurrentUser);
Justin Klaassen2696d992016-07-11 21:26:46 -0700338 } else if (mBootCompleted) {
339 setUp();
Justin Klaassen911e8892016-06-21 18:24:24 -0700340 }
341 }
342 }
343
Justin Klaassen2696d992016-07-11 21:26:46 -0700344 private static boolean isUserSetupCompleted(ContentResolver cr, int userHandle) {
345 return Secure.getIntForUser(cr, Secure.USER_SETUP_COMPLETE, 0, userHandle) == 1;
346 }
347
348 private void setUp() {
Justin Klaassenec8837a2016-08-23 12:04:42 -0700349 Slog.d(TAG, "setUp: currentUser=" + mCurrentUser);
350
Christine Franks57fdde82018-07-03 14:46:07 -0700351 mNightDisplayController = new ColorDisplayController(getContext(), mCurrentUser);
352
353 // Listen for external changes to any of the settings.
354 if (mContentObserver == null) {
355 mContentObserver = new ContentObserver(new Handler(DisplayThread.get().getLooper())) {
356 @Override
357 public void onChange(boolean selfChange, Uri uri) {
358 super.onChange(selfChange, uri);
359
360 final String setting = uri == null ? null : uri.getLastPathSegment();
361 if (setting != null) {
362 switch (setting) {
363 case Secure.NIGHT_DISPLAY_ACTIVATED:
364 onNightDisplayActivated(mNightDisplayController.isActivated());
365 break;
366 case Secure.NIGHT_DISPLAY_COLOR_TEMPERATURE:
367 onNightDisplayColorTemperatureChanged(
368 mNightDisplayController.getColorTemperature());
369 break;
370 case Secure.NIGHT_DISPLAY_AUTO_MODE:
371 onNightDisplayAutoModeChanged(
372 mNightDisplayController.getAutoMode());
373 break;
374 case Secure.NIGHT_DISPLAY_CUSTOM_START_TIME:
375 onNightDisplayCustomStartTimeChanged(
376 mNightDisplayController.getCustomStartTime());
377 break;
378 case Secure.NIGHT_DISPLAY_CUSTOM_END_TIME:
379 onNightDisplayCustomEndTimeChanged(
380 mNightDisplayController.getCustomEndTime());
381 break;
382 case System.DISPLAY_COLOR_MODE:
383 onDisplayColorModeChanged(mNightDisplayController.getColorMode());
384 break;
Christine Franks57fdde82018-07-03 14:46:07 -0700385 case Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED:
Christine Franks9114f462019-01-04 11:27:30 -0800386 onAccessibilityInversionChanged();
387 onAccessibilityActivated();
388 break;
389 case Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED:
390 onAccessibilityDaltonizerChanged();
391 onAccessibilityActivated();
392 break;
393 case Secure.ACCESSIBILITY_DISPLAY_DALTONIZER:
394 onAccessibilityDaltonizerChanged();
Christine Franks57fdde82018-07-03 14:46:07 -0700395 break;
Christine Franks245ffd42018-11-16 13:45:14 -0800396 case Secure.DISPLAY_WHITE_BALANCE_ENABLED:
397 onDisplayWhiteBalanceEnabled(isDisplayWhiteBalanceSettingEnabled());
398 break;
Christine Franks57fdde82018-07-03 14:46:07 -0700399 }
400 }
401 }
402 };
403 }
404 final ContentResolver cr = getContext().getContentResolver();
405 cr.registerContentObserver(Secure.getUriFor(Secure.NIGHT_DISPLAY_ACTIVATED),
406 false /* notifyForDescendants */, mContentObserver, mCurrentUser);
407 cr.registerContentObserver(Secure.getUriFor(Secure.NIGHT_DISPLAY_COLOR_TEMPERATURE),
408 false /* notifyForDescendants */, mContentObserver, mCurrentUser);
409 cr.registerContentObserver(Secure.getUriFor(Secure.NIGHT_DISPLAY_AUTO_MODE),
410 false /* notifyForDescendants */, mContentObserver, mCurrentUser);
411 cr.registerContentObserver(Secure.getUriFor(Secure.NIGHT_DISPLAY_CUSTOM_START_TIME),
412 false /* notifyForDescendants */, mContentObserver, mCurrentUser);
413 cr.registerContentObserver(Secure.getUriFor(Secure.NIGHT_DISPLAY_CUSTOM_END_TIME),
414 false /* notifyForDescendants */, mContentObserver, mCurrentUser);
415 cr.registerContentObserver(System.getUriFor(System.DISPLAY_COLOR_MODE),
416 false /* notifyForDescendants */, mContentObserver, mCurrentUser);
417 cr.registerContentObserver(
418 Secure.getUriFor(Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED),
419 false /* notifyForDescendants */, mContentObserver, mCurrentUser);
420 cr.registerContentObserver(
421 Secure.getUriFor(Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED),
422 false /* notifyForDescendants */, mContentObserver, mCurrentUser);
Christine Franks9114f462019-01-04 11:27:30 -0800423 cr.registerContentObserver(
424 Secure.getUriFor(Secure.ACCESSIBILITY_DISPLAY_DALTONIZER),
425 false /* notifyForDescendants */, mContentObserver, mCurrentUser);
Christine Franks245ffd42018-11-16 13:45:14 -0800426 cr.registerContentObserver(Secure.getUriFor(Secure.DISPLAY_WHITE_BALANCE_ENABLED),
427 false /* notifyForDescendants */, mContentObserver, mCurrentUser);
Justin Klaassen911e8892016-06-21 18:24:24 -0700428
Christine Frankscf388c22018-05-15 15:48:10 -0700429 // Set the color mode, if valid, and immediately apply the updated tint matrix based on the
430 // existing activated state. This ensures consistency of tint across the color mode change.
Christine Franks57fdde82018-07-03 14:46:07 -0700431 onDisplayColorModeChanged(mNightDisplayController.getColorMode());
Christine Frankscf388c22018-05-15 15:48:10 -0700432
Christine Franks245ffd42018-11-16 13:45:14 -0800433 if (ColorDisplayManager.isNightDisplayAvailable(getContext())) {
434 // Reset the activated state.
435 mNightDisplayTintController.setActivated(null);
Christine Frankscf388c22018-05-15 15:48:10 -0700436
Christine Franks245ffd42018-11-16 13:45:14 -0800437 // Prepare the night display color transformation matrix.
438 mNightDisplayTintController
439 .setUp(getContext(), DisplayTransformManager.needsLinearColorMatrix());
440 mNightDisplayTintController.setMatrix(mNightDisplayController.getColorTemperature());
Christine Franks8ad71492017-10-24 19:04:22 -0700441
Christine Franks245ffd42018-11-16 13:45:14 -0800442 // Initialize the current auto mode.
443 onNightDisplayAutoModeChanged(mNightDisplayController.getAutoMode());
Christine Franks6418d0b2017-02-13 09:48:00 -0800444
Christine Franks245ffd42018-11-16 13:45:14 -0800445 // Force the initialization current activated state.
446 if (mNightDisplayTintController.isActivatedStateNotSet()) {
447 onNightDisplayActivated(mNightDisplayController.isActivated());
448 }
449 }
Justin Klaassen911e8892016-06-21 18:24:24 -0700450
Christine Franks245ffd42018-11-16 13:45:14 -0800451 if (ColorDisplayManager.isDisplayWhiteBalanceAvailable(getContext())) {
452 // Prepare the display white balance transform matrix.
453 mDisplayWhiteBalanceTintController
454 .setUp(getContext(), DisplayTransformManager.needsLinearColorMatrix());
455 if (mDisplayWhiteBalanceColorTemperature != null) {
456 mDisplayWhiteBalanceTintController
457 .setMatrix(mDisplayWhiteBalanceColorTemperature);
458 }
459
460 onDisplayWhiteBalanceEnabled(isDisplayWhiteBalanceSettingEnabled());
Justin Klaassen911e8892016-06-21 18:24:24 -0700461 }
462 }
463
Justin Klaassen2696d992016-07-11 21:26:46 -0700464 private void tearDown() {
Justin Klaassenec8837a2016-08-23 12:04:42 -0700465 Slog.d(TAG, "tearDown: currentUser=" + mCurrentUser);
466
Christine Franks57fdde82018-07-03 14:46:07 -0700467 getContext().getContentResolver().unregisterContentObserver(mContentObserver);
468
469 if (mNightDisplayController != null) {
470 mNightDisplayController = null;
Justin Klaassen2696d992016-07-11 21:26:46 -0700471 }
Justin Klaassen911e8892016-06-21 18:24:24 -0700472
Christine Franks245ffd42018-11-16 13:45:14 -0800473 if (ColorDisplayManager.isNightDisplayAvailable(getContext())) {
474 if (mNightDisplayAutoMode != null) {
475 mNightDisplayAutoMode.onStop();
476 mNightDisplayAutoMode = null;
477 }
478 mNightDisplayTintController.endAnimator();
Justin Klaassen911e8892016-06-21 18:24:24 -0700479 }
480
Christine Franks245ffd42018-11-16 13:45:14 -0800481 if (ColorDisplayManager.isDisplayWhiteBalanceAvailable(getContext())) {
482 mDisplayWhiteBalanceTintController.endAnimator();
Justin Klaassen639214e2016-07-14 21:00:06 -0700483 }
Justin Klaassen911e8892016-06-21 18:24:24 -0700484 }
485
Christine Franks57fdde82018-07-03 14:46:07 -0700486 private void onNightDisplayActivated(boolean activated) {
Christine Franks245ffd42018-11-16 13:45:14 -0800487 if (mNightDisplayTintController.isActivatedStateNotSet()
488 || mNightDisplayTintController.isActivated() != activated) {
Justin Klaassen911e8892016-06-21 18:24:24 -0700489 Slog.i(TAG, activated ? "Turning on night display" : "Turning off night display");
490
Christine Franks245ffd42018-11-16 13:45:14 -0800491 mNightDisplayTintController.setActivated(activated);
Justin Klaassen908b86c2016-08-08 09:18:42 -0700492
Christine Franks57fdde82018-07-03 14:46:07 -0700493 if (mNightDisplayAutoMode != null) {
494 mNightDisplayAutoMode.onActivated(activated);
Christine Frankse5bb03e2017-02-10 17:36:10 -0800495 }
496
Christine Franks245ffd42018-11-16 13:45:14 -0800497 applyTint(mNightDisplayTintController, false);
Justin Klaassen911e8892016-06-21 18:24:24 -0700498 }
499 }
500
Christine Franks57fdde82018-07-03 14:46:07 -0700501 private void onNightDisplayAutoModeChanged(int autoMode) {
502 Slog.d(TAG, "onNightDisplayAutoModeChanged: autoMode=" + autoMode);
Justin Klaassenec8837a2016-08-23 12:04:42 -0700503
Christine Franks57fdde82018-07-03 14:46:07 -0700504 if (mNightDisplayAutoMode != null) {
505 mNightDisplayAutoMode.onStop();
506 mNightDisplayAutoMode = null;
Justin Klaassen911e8892016-06-21 18:24:24 -0700507 }
508
Christine Franks5397f032017-11-01 18:35:16 -0700509 if (autoMode == ColorDisplayController.AUTO_MODE_CUSTOM) {
Christine Franks57fdde82018-07-03 14:46:07 -0700510 mNightDisplayAutoMode = new CustomNightDisplayAutoMode();
Christine Franks5397f032017-11-01 18:35:16 -0700511 } else if (autoMode == ColorDisplayController.AUTO_MODE_TWILIGHT) {
Christine Franks57fdde82018-07-03 14:46:07 -0700512 mNightDisplayAutoMode = new TwilightNightDisplayAutoMode();
Justin Klaassen911e8892016-06-21 18:24:24 -0700513 }
514
Christine Franks57fdde82018-07-03 14:46:07 -0700515 if (mNightDisplayAutoMode != null) {
516 mNightDisplayAutoMode.onStart();
Justin Klaassen911e8892016-06-21 18:24:24 -0700517 }
518 }
519
Christine Franks57fdde82018-07-03 14:46:07 -0700520 private void onNightDisplayCustomStartTimeChanged(LocalTime startTime) {
521 Slog.d(TAG, "onNightDisplayCustomStartTimeChanged: startTime=" + startTime);
Justin Klaassenec8837a2016-08-23 12:04:42 -0700522
Christine Franks57fdde82018-07-03 14:46:07 -0700523 if (mNightDisplayAutoMode != null) {
524 mNightDisplayAutoMode.onCustomStartTimeChanged(startTime);
Justin Klaassen911e8892016-06-21 18:24:24 -0700525 }
526 }
527
Christine Franks57fdde82018-07-03 14:46:07 -0700528 private void onNightDisplayCustomEndTimeChanged(LocalTime endTime) {
529 Slog.d(TAG, "onNightDisplayCustomEndTimeChanged: endTime=" + endTime);
Justin Klaassenec8837a2016-08-23 12:04:42 -0700530
Christine Franks57fdde82018-07-03 14:46:07 -0700531 if (mNightDisplayAutoMode != null) {
532 mNightDisplayAutoMode.onCustomEndTimeChanged(endTime);
Justin Klaassen911e8892016-06-21 18:24:24 -0700533 }
534 }
535
Christine Franks57fdde82018-07-03 14:46:07 -0700536 private void onNightDisplayColorTemperatureChanged(int colorTemperature) {
Christine Franks245ffd42018-11-16 13:45:14 -0800537 mNightDisplayTintController.setMatrix(colorTemperature);
538 applyTint(mNightDisplayTintController, true);
Christine Franks6418d0b2017-02-13 09:48:00 -0800539 }
540
Christine Franks57fdde82018-07-03 14:46:07 -0700541 private void onDisplayColorModeChanged(int mode) {
Christine Frankscf388c22018-05-15 15:48:10 -0700542 if (mode == -1) {
543 return;
544 }
545
Christine Franks245ffd42018-11-16 13:45:14 -0800546 mNightDisplayTintController.cancelAnimator();
547 mDisplayWhiteBalanceTintController.cancelAnimator();
548
549 mNightDisplayTintController
550 .setUp(getContext(), DisplayTransformManager.needsLinearColorMatrix(mode));
551 mNightDisplayTintController.setMatrix(mNightDisplayController.getColorTemperature());
552
553 mDisplayWhiteBalanceTintController
554 .setUp(getContext(), DisplayTransformManager.needsLinearColorMatrix(mode));
555 if (mDisplayWhiteBalanceColorTemperature != null) {
556 mDisplayWhiteBalanceTintController.setMatrix(mDisplayWhiteBalanceColorTemperature);
Christine Franks218e6562017-11-27 10:20:14 -0800557 }
Christine Franks8ad71492017-10-24 19:04:22 -0700558
Christine Franks218e6562017-11-27 10:20:14 -0800559 final DisplayTransformManager dtm = getLocalService(DisplayTransformManager.class);
Christine Franks245ffd42018-11-16 13:45:14 -0800560 dtm.setColorMode(mode, mNightDisplayTintController.getMatrix());
Christine Franks8ad71492017-10-24 19:04:22 -0700561 }
562
Christine Franks9114f462019-01-04 11:27:30 -0800563 private void onAccessibilityActivated() {
Christine Franks57fdde82018-07-03 14:46:07 -0700564 onDisplayColorModeChanged(mNightDisplayController.getColorMode());
Daniel Solomon317a3572018-03-30 18:36:37 -0700565 }
566
Christine Franks9114f462019-01-04 11:27:30 -0800567 /**
568 * Apply the accessibility daltonizer transform based on the settings value.
569 */
570 private void onAccessibilityDaltonizerChanged() {
571 final boolean enabled = Secure.getIntForUser(getContext().getContentResolver(),
572 Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED, 0, mCurrentUser) != 0;
573 final int daltonizerMode = enabled ? Secure.getIntForUser(getContext().getContentResolver(),
574 Secure.ACCESSIBILITY_DISPLAY_DALTONIZER,
575 AccessibilityManager.DALTONIZER_CORRECT_DEUTERANOMALY, mCurrentUser)
576 : AccessibilityManager.DALTONIZER_DISABLED;
577
578 final DisplayTransformManager dtm = getLocalService(DisplayTransformManager.class);
579 if (daltonizerMode == AccessibilityManager.DALTONIZER_SIMULATE_MONOCHROMACY) {
580 // Monochromacy isn't supported by the native Daltonizer implementation; use grayscale.
581 dtm.setColorMatrix(DisplayTransformManager.LEVEL_COLOR_MATRIX_GRAYSCALE,
582 MATRIX_GRAYSCALE);
583 dtm.setDaltonizerMode(AccessibilityManager.DALTONIZER_DISABLED);
584 } else {
585 dtm.setColorMatrix(DisplayTransformManager.LEVEL_COLOR_MATRIX_GRAYSCALE, null);
586 dtm.setDaltonizerMode(daltonizerMode);
587 }
588 }
589
590 /**
591 * Apply the accessibility inversion transform based on the settings value.
592 */
593 private void onAccessibilityInversionChanged() {
594 final boolean enabled = Secure.getIntForUser(getContext().getContentResolver(),
595 Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED, 0, mCurrentUser) != 0;
596 final DisplayTransformManager dtm = getLocalService(DisplayTransformManager.class);
597 dtm.setColorMatrix(DisplayTransformManager.LEVEL_COLOR_MATRIX_INVERT_COLOR,
598 enabled ? MATRIX_INVERT_COLOR : null);
599 }
Christine Franks8ad71492017-10-24 19:04:22 -0700600
Christine Franks6418d0b2017-02-13 09:48:00 -0800601 /**
602 * Applies current color temperature matrix, or removes it if deactivated.
603 *
604 * @param immediate {@code true} skips transition animation
605 */
Christine Franks245ffd42018-11-16 13:45:14 -0800606 private void applyTint(TintController tintController, boolean immediate) {
607 tintController.cancelAnimator();
Christine Franks6418d0b2017-02-13 09:48:00 -0800608
Christine Franks6418d0b2017-02-13 09:48:00 -0800609 final DisplayTransformManager dtm = getLocalService(DisplayTransformManager.class);
Christine Franks245ffd42018-11-16 13:45:14 -0800610 final float[] from = dtm.getColorMatrix(tintController.getLevel());
611 final float[] to = tintController.getMatrix();
Christine Franks6418d0b2017-02-13 09:48:00 -0800612
613 if (immediate) {
Christine Franks245ffd42018-11-16 13:45:14 -0800614 dtm.setColorMatrix(tintController.getLevel(), to);
Christine Franks6418d0b2017-02-13 09:48:00 -0800615 } else {
Christine Franks245ffd42018-11-16 13:45:14 -0800616 tintController.setAnimator(ValueAnimator.ofObject(COLOR_MATRIX_EVALUATOR,
617 from == null ? MATRIX_IDENTITY : from, to));
618 tintController.getAnimator().setDuration(TRANSITION_DURATION);
619 tintController.getAnimator().setInterpolator(AnimationUtils.loadInterpolator(
Christine Franks6418d0b2017-02-13 09:48:00 -0800620 getContext(), android.R.interpolator.fast_out_slow_in));
Christine Franks245ffd42018-11-16 13:45:14 -0800621 tintController.getAnimator().addUpdateListener((ValueAnimator animator) -> {
622 final float[] value = (float[]) animator.getAnimatedValue();
623 dtm.setColorMatrix(tintController.getLevel(), value);
Christine Franks6418d0b2017-02-13 09:48:00 -0800624 });
Christine Franks245ffd42018-11-16 13:45:14 -0800625 tintController.getAnimator().addListener(new AnimatorListenerAdapter() {
Christine Franks6418d0b2017-02-13 09:48:00 -0800626
627 private boolean mIsCancelled;
628
629 @Override
630 public void onAnimationCancel(Animator animator) {
631 mIsCancelled = true;
632 }
633
634 @Override
635 public void onAnimationEnd(Animator animator) {
636 if (!mIsCancelled) {
637 // Ensure final color matrix is set at the end of the animation. If the
638 // animation is cancelled then don't set the final color matrix so the new
639 // animator can pick up from where this one left off.
Christine Franks245ffd42018-11-16 13:45:14 -0800640 dtm.setColorMatrix(tintController.getLevel(), to);
Christine Franks6418d0b2017-02-13 09:48:00 -0800641 }
Christine Franks245ffd42018-11-16 13:45:14 -0800642 tintController.setAnimator(null);
Christine Franks6418d0b2017-02-13 09:48:00 -0800643 }
644 });
Christine Franks245ffd42018-11-16 13:45:14 -0800645 tintController.getAnimator().start();
Christine Franks6418d0b2017-02-13 09:48:00 -0800646 }
647 }
648
649 /**
Christine Franks39b03112018-07-03 14:46:07 -0700650 * Returns the first date time corresponding to the local time that occurs before the provided
651 * date time.
Christine Franks03213462017-08-25 13:57:26 -0700652 *
653 * @param compareTime the LocalDateTime to compare against
654 * @return the prior LocalDateTime corresponding to this local time
655 */
Christine Franks57fdde82018-07-03 14:46:07 -0700656 @VisibleForTesting
657 static LocalDateTime getDateTimeBefore(LocalTime localTime, LocalDateTime compareTime) {
Christine Franks03213462017-08-25 13:57:26 -0700658 final LocalDateTime ldt = LocalDateTime.of(compareTime.getYear(), compareTime.getMonth(),
659 compareTime.getDayOfMonth(), localTime.getHour(), localTime.getMinute());
660
661 // Check if the local time has passed, if so return the same time yesterday.
662 return ldt.isAfter(compareTime) ? ldt.minusDays(1) : ldt;
663 }
664
665 /**
Christine Franks39b03112018-07-03 14:46:07 -0700666 * Returns the first date time corresponding to this local time that occurs after the provided
667 * date time.
Christine Franks03213462017-08-25 13:57:26 -0700668 *
669 * @param compareTime the LocalDateTime to compare against
670 * @return the next LocalDateTime corresponding to this local time
671 */
Christine Franks57fdde82018-07-03 14:46:07 -0700672 @VisibleForTesting
673 static LocalDateTime getDateTimeAfter(LocalTime localTime, LocalDateTime compareTime) {
Christine Franks03213462017-08-25 13:57:26 -0700674 final LocalDateTime ldt = LocalDateTime.of(compareTime.getYear(), compareTime.getMonth(),
675 compareTime.getDayOfMonth(), localTime.getHour(), localTime.getMinute());
676
677 // Check if the local time has passed, if so return the same time tomorrow.
678 return ldt.isBefore(compareTime) ? ldt.plusDays(1) : ldt;
679 }
680
Christine Franks245ffd42018-11-16 13:45:14 -0800681 private void onDisplayWhiteBalanceEnabled(boolean enabled) {
682 mDisplayWhiteBalanceTintController.setActivated(enabled);
683 if (mDisplayWhiteBalanceListener != null) {
684 mDisplayWhiteBalanceListener.onDisplayWhiteBalanceStatusChanged(enabled);
685 }
686 }
687
688 private boolean isDisplayWhiteBalanceSettingEnabled() {
689 return Secure.getIntForUser(getContext().getContentResolver(),
690 Secure.DISPLAY_WHITE_BALANCE_ENABLED, 0, mCurrentUser) == 1;
691 }
692
Christine Franks39b03112018-07-03 14:46:07 -0700693 private boolean isDeviceColorManagedInternal() {
694 final DisplayTransformManager dtm = getLocalService(DisplayTransformManager.class);
695 return dtm.isDeviceColorManaged();
696 }
697
Christine Franks57fdde82018-07-03 14:46:07 -0700698 /**
699 * Returns the last time the night display transform activation state was changed, or {@link
700 * LocalDateTime#MIN} if night display has never been activated.
701 */
Christine Franks245ffd42018-11-16 13:45:14 -0800702 private LocalDateTime getNightDisplayLastActivatedTimeSetting() {
Christine Franks57fdde82018-07-03 14:46:07 -0700703 final ContentResolver cr = getContext().getContentResolver();
704 final String lastActivatedTime = Secure.getStringForUser(
705 cr, Secure.NIGHT_DISPLAY_LAST_ACTIVATED_TIME, getContext().getUserId());
706 if (lastActivatedTime != null) {
707 try {
708 return LocalDateTime.parse(lastActivatedTime);
709 } catch (DateTimeParseException ignored) {
710 }
711 // Uses the old epoch time.
712 try {
713 return LocalDateTime.ofInstant(
714 Instant.ofEpochMilli(Long.parseLong(lastActivatedTime)),
715 ZoneId.systemDefault());
716 } catch (DateTimeException | NumberFormatException ignored) {
717 }
718 }
719 return LocalDateTime.MIN;
720 }
721
722 private abstract class NightDisplayAutoMode {
723
724 public abstract void onActivated(boolean activated);
725
Justin Klaassen911e8892016-06-21 18:24:24 -0700726 public abstract void onStart();
Christine Frankse5bb03e2017-02-10 17:36:10 -0800727
Justin Klaassen911e8892016-06-21 18:24:24 -0700728 public abstract void onStop();
Christine Franks57fdde82018-07-03 14:46:07 -0700729
730 public void onCustomStartTimeChanged(LocalTime startTime) {
731 }
732
733 public void onCustomEndTimeChanged(LocalTime endTime) {
734 }
Justin Klaassen911e8892016-06-21 18:24:24 -0700735 }
736
Christine Franks57fdde82018-07-03 14:46:07 -0700737 private final class CustomNightDisplayAutoMode extends NightDisplayAutoMode implements
738 AlarmManager.OnAlarmListener {
Justin Klaassen911e8892016-06-21 18:24:24 -0700739
740 private final AlarmManager mAlarmManager;
741 private final BroadcastReceiver mTimeChangedReceiver;
742
Christine Franks03213462017-08-25 13:57:26 -0700743 private LocalTime mStartTime;
744 private LocalTime mEndTime;
Justin Klaassen911e8892016-06-21 18:24:24 -0700745
Christine Franks03213462017-08-25 13:57:26 -0700746 private LocalDateTime mLastActivatedTime;
Justin Klaassen911e8892016-06-21 18:24:24 -0700747
Christine Franks57fdde82018-07-03 14:46:07 -0700748 CustomNightDisplayAutoMode() {
Justin Klaassen911e8892016-06-21 18:24:24 -0700749 mAlarmManager = (AlarmManager) getContext().getSystemService(Context.ALARM_SERVICE);
750 mTimeChangedReceiver = new BroadcastReceiver() {
751 @Override
752 public void onReceive(Context context, Intent intent) {
753 updateActivated();
754 }
755 };
756 }
757
758 private void updateActivated() {
Christine Franks03213462017-08-25 13:57:26 -0700759 final LocalDateTime now = LocalDateTime.now();
760 final LocalDateTime start = getDateTimeBefore(mStartTime, now);
761 final LocalDateTime end = getDateTimeAfter(mEndTime, start);
762 boolean activate = now.isBefore(end);
Justin Klaassen911e8892016-06-21 18:24:24 -0700763
Christine Frankse5bb03e2017-02-10 17:36:10 -0800764 if (mLastActivatedTime != null) {
Christine Frankse5bb03e2017-02-10 17:36:10 -0800765 // Maintain the existing activated state if within the current period.
Christine Franks03213462017-08-25 13:57:26 -0700766 if (mLastActivatedTime.isBefore(now) && mLastActivatedTime.isAfter(start)
767 && (mLastActivatedTime.isAfter(end) || now.isBefore(end))) {
Christine Franks57fdde82018-07-03 14:46:07 -0700768 activate = mNightDisplayController.isActivated();
Justin Klaassen911e8892016-06-21 18:24:24 -0700769 }
770 }
771
Christine Franks245ffd42018-11-16 13:45:14 -0800772 if (mNightDisplayTintController.isActivatedStateNotSet() || (
773 mNightDisplayTintController.isActivated() != activate)) {
Christine Franks57fdde82018-07-03 14:46:07 -0700774 mNightDisplayController.setActivated(activate);
Justin Klaassen911e8892016-06-21 18:24:24 -0700775 }
Christine Franks03213462017-08-25 13:57:26 -0700776
Christine Franks245ffd42018-11-16 13:45:14 -0800777 updateNextAlarm(mNightDisplayTintController.isActivated(), now);
Justin Klaassen911e8892016-06-21 18:24:24 -0700778 }
779
Christine Franks03213462017-08-25 13:57:26 -0700780 private void updateNextAlarm(@Nullable Boolean activated, @NonNull LocalDateTime now) {
Justin Klaassen4346f632016-08-08 15:01:47 -0700781 if (activated != null) {
Christine Franks03213462017-08-25 13:57:26 -0700782 final LocalDateTime next = activated ? getDateTimeAfter(mEndTime, now)
783 : getDateTimeAfter(mStartTime, now);
784 final long millis = next.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli();
785 mAlarmManager.setExact(AlarmManager.RTC, millis, TAG, this, null);
Justin Klaassen911e8892016-06-21 18:24:24 -0700786 }
787 }
788
789 @Override
790 public void onStart() {
791 final IntentFilter intentFilter = new IntentFilter(Intent.ACTION_TIME_CHANGED);
792 intentFilter.addAction(Intent.ACTION_TIMEZONE_CHANGED);
793 getContext().registerReceiver(mTimeChangedReceiver, intentFilter);
794
Christine Franks57fdde82018-07-03 14:46:07 -0700795 mStartTime = mNightDisplayController.getCustomStartTime();
796 mEndTime = mNightDisplayController.getCustomEndTime();
Justin Klaassen911e8892016-06-21 18:24:24 -0700797
Christine Franks57fdde82018-07-03 14:46:07 -0700798 mLastActivatedTime = getNightDisplayLastActivatedTimeSetting();
Christine Frankse5bb03e2017-02-10 17:36:10 -0800799
Justin Klaassen911e8892016-06-21 18:24:24 -0700800 // Force an update to initialize state.
801 updateActivated();
802 }
803
804 @Override
805 public void onStop() {
806 getContext().unregisterReceiver(mTimeChangedReceiver);
807
808 mAlarmManager.cancel(this);
809 mLastActivatedTime = null;
810 }
811
812 @Override
813 public void onActivated(boolean activated) {
Christine Franks57fdde82018-07-03 14:46:07 -0700814 mLastActivatedTime = getNightDisplayLastActivatedTimeSetting();
Christine Franks03213462017-08-25 13:57:26 -0700815 updateNextAlarm(activated, LocalDateTime.now());
Justin Klaassen911e8892016-06-21 18:24:24 -0700816 }
817
818 @Override
Christine Franks03213462017-08-25 13:57:26 -0700819 public void onCustomStartTimeChanged(LocalTime startTime) {
Justin Klaassen911e8892016-06-21 18:24:24 -0700820 mStartTime = startTime;
821 mLastActivatedTime = null;
822 updateActivated();
823 }
824
825 @Override
Christine Franks03213462017-08-25 13:57:26 -0700826 public void onCustomEndTimeChanged(LocalTime endTime) {
Justin Klaassen911e8892016-06-21 18:24:24 -0700827 mEndTime = endTime;
828 mLastActivatedTime = null;
829 updateActivated();
830 }
831
832 @Override
833 public void onAlarm() {
Justin Klaassenec8837a2016-08-23 12:04:42 -0700834 Slog.d(TAG, "onAlarm");
Justin Klaassen911e8892016-06-21 18:24:24 -0700835 updateActivated();
836 }
837 }
838
Christine Franks57fdde82018-07-03 14:46:07 -0700839 private final class TwilightNightDisplayAutoMode extends NightDisplayAutoMode implements
840 TwilightListener {
Justin Klaassen911e8892016-06-21 18:24:24 -0700841
842 private final TwilightManager mTwilightManager;
Christine Franks57fdde82018-07-03 14:46:07 -0700843 private LocalDateTime mLastActivatedTime;
Justin Klaassen911e8892016-06-21 18:24:24 -0700844
Christine Franks57fdde82018-07-03 14:46:07 -0700845 TwilightNightDisplayAutoMode() {
Justin Klaassen911e8892016-06-21 18:24:24 -0700846 mTwilightManager = getLocalService(TwilightManager.class);
Justin Klaassen911e8892016-06-21 18:24:24 -0700847 }
848
Justin Klaassen908b86c2016-08-08 09:18:42 -0700849 private void updateActivated(TwilightState state) {
Justin Klaassen3da4c442017-05-05 15:19:33 -0700850 if (state == null) {
851 // If there isn't a valid TwilightState then just keep the current activated
852 // state.
853 return;
854 }
855
856 boolean activate = state.isNight();
Christine Franks57fdde82018-07-03 14:46:07 -0700857 if (mLastActivatedTime != null) {
Christine Franks03213462017-08-25 13:57:26 -0700858 final LocalDateTime now = LocalDateTime.now();
859 final LocalDateTime sunrise = state.sunrise();
860 final LocalDateTime sunset = state.sunset();
Christine Frankse5bb03e2017-02-10 17:36:10 -0800861 // Maintain the existing activated state if within the current period.
Christine Franks57fdde82018-07-03 14:46:07 -0700862 if (mLastActivatedTime.isBefore(now) && (mLastActivatedTime.isBefore(sunrise)
863 ^ mLastActivatedTime.isBefore(sunset))) {
864 activate = mNightDisplayController.isActivated();
Justin Klaassen911e8892016-06-21 18:24:24 -0700865 }
866 }
Justin Klaassen908b86c2016-08-08 09:18:42 -0700867
Christine Franks245ffd42018-11-16 13:45:14 -0800868 if (mNightDisplayTintController.isActivatedStateNotSet() || (
869 mNightDisplayTintController.isActivated() != activate)) {
Christine Franks57fdde82018-07-03 14:46:07 -0700870 mNightDisplayController.setActivated(activate);
Justin Klaassen908b86c2016-08-08 09:18:42 -0700871 }
Justin Klaassen911e8892016-06-21 18:24:24 -0700872 }
873
874 @Override
Christine Franks57fdde82018-07-03 14:46:07 -0700875 public void onActivated(boolean activated) {
876 mLastActivatedTime = getNightDisplayLastActivatedTimeSetting();
877 }
878
879 @Override
Justin Klaassen911e8892016-06-21 18:24:24 -0700880 public void onStart() {
881 mTwilightManager.registerListener(this, mHandler);
Christine Franks57fdde82018-07-03 14:46:07 -0700882 mLastActivatedTime = getNightDisplayLastActivatedTimeSetting();
Justin Klaassen911e8892016-06-21 18:24:24 -0700883
884 // Force an update to initialize state.
Justin Klaassen908b86c2016-08-08 09:18:42 -0700885 updateActivated(mTwilightManager.getLastTwilightState());
Justin Klaassen911e8892016-06-21 18:24:24 -0700886 }
887
888 @Override
889 public void onStop() {
890 mTwilightManager.unregisterListener(this);
Christine Franks57fdde82018-07-03 14:46:07 -0700891 mLastActivatedTime = null;
Justin Klaassen908b86c2016-08-08 09:18:42 -0700892 }
893
894 @Override
895 public void onTwilightStateChanged(@Nullable TwilightState state) {
Justin Klaassenec8837a2016-08-23 12:04:42 -0700896 Slog.d(TAG, "onTwilightStateChanged: isNight="
897 + (state == null ? null : state.isNight()));
Justin Klaassen908b86c2016-08-08 09:18:42 -0700898 updateActivated(state);
Justin Klaassen911e8892016-06-21 18:24:24 -0700899 }
900 }
Justin Klaassen639214e2016-07-14 21:00:06 -0700901
902 /**
903 * Interpolates between two 4x4 color transform matrices (in column-major order).
904 */
905 private static class ColorMatrixEvaluator implements TypeEvaluator<float[]> {
906
907 /**
908 * Result matrix returned by {@link #evaluate(float, float[], float[])}.
909 */
910 private final float[] mResultMatrix = new float[16];
911
912 @Override
913 public float[] evaluate(float fraction, float[] startValue, float[] endValue) {
914 for (int i = 0; i < mResultMatrix.length; i++) {
915 mResultMatrix[i] = MathUtils.lerp(startValue[i], endValue[i], fraction);
916 }
917 return mResultMatrix;
918 }
919 }
Christine Franks39b03112018-07-03 14:46:07 -0700920
Christine Franks245ffd42018-11-16 13:45:14 -0800921 private abstract static class TintController {
922
923 private ValueAnimator mAnimator;
924 private Boolean mIsActivated;
925
926 public ValueAnimator getAnimator() {
927 return mAnimator;
928 }
929
930 public void setAnimator(ValueAnimator animator) {
931 mAnimator = animator;
932 }
933
934 /**
935 * Cancel the animator if it's still running.
936 */
937 public void cancelAnimator() {
938 if (mAnimator != null) {
939 mAnimator.cancel();
940 }
941 }
942
943 /**
944 * End the animator if it's still running, jumping to the end state.
945 */
946 public void endAnimator() {
947 if (mAnimator != null) {
948 mAnimator.end();
949 mAnimator = null;
950 }
951 }
952
953 public void setActivated(Boolean isActivated) {
954 mIsActivated = isActivated;
955 }
956
957 public boolean isActivated() {
958 return mIsActivated != null && mIsActivated;
959 }
960
961 public boolean isActivatedStateNotSet() {
962 return mIsActivated == null;
963 }
964
965 /**
966 * Set up any constants needed for computing the matrix.
967 */
968 public abstract void setUp(Context context, boolean needsLinear);
969
970 /**
971 * Sets the 4x4 matrix to apply.
972 */
973 public abstract void setMatrix(int value);
974
975 /**
976 * Get the 4x4 matrix to apply.
977 */
978 public abstract float[] getMatrix();
979
980 /**
981 * Get the color transform level to apply the matrix.
982 */
983 public abstract int getLevel();
984 }
985
986 /**
987 * Local service that allows color transforms to be enabled from other system services.
988 */
989 public final class ColorDisplayServiceInternal {
990
991 /**
992 * Set the current CCT value for the display white balance transform, and if the transform
993 * is enabled, apply it.
994 *
995 * @param cct the color temperature in Kelvin.
996 */
997 public boolean setDisplayWhiteBalanceColorTemperature(int cct) {
998 // Update the transform matrix even if it can't be applied.
999 mDisplayWhiteBalanceColorTemperature = cct;
1000 mDisplayWhiteBalanceTintController.setMatrix(cct);
1001
1002 if (mDisplayWhiteBalanceTintController.isActivated()) {
1003 applyTint(mDisplayWhiteBalanceTintController, true);
1004 return true;
1005 }
1006 return false;
1007 }
1008
1009 /**
1010 * Sets the listener and returns whether display white balance is currently enabled.
1011 */
1012 public boolean setDisplayWhiteBalanceListener(DisplayWhiteBalanceListener listener) {
1013 mDisplayWhiteBalanceListener = listener;
1014 return mDisplayWhiteBalanceTintController.isActivated();
1015 }
1016 }
1017
1018 /**
1019 * Listener for changes in display white balance status.
1020 */
1021 public interface DisplayWhiteBalanceListener {
1022
1023 /**
1024 * Notify that the display white balance status has changed, either due to preemption by
1025 * another transform or the feature being turned off.
1026 */
1027 void onDisplayWhiteBalanceStatusChanged(boolean enabled);
1028 }
1029
Christine Franks09c229e2018-12-14 10:37:40 -08001030 private final class TintHandler extends Handler {
1031
1032 TintHandler(Looper looper) {
1033 super(looper, null, true /* async */);
1034 }
1035
1036 @Override
1037 public void handleMessage(Message msg) {
1038 switch (msg.what) {
1039 case MSG_APPLY_GLOBAL_SATURATION:
1040 mGlobalSaturationTintController.setMatrix(msg.arg1);
1041 applyTint(mGlobalSaturationTintController, false);
1042 break;
1043 }
1044 }
1045 }
1046
Christine Franks39b03112018-07-03 14:46:07 -07001047 private final class BinderService extends IColorDisplayManager.Stub {
Christine Franks57fdde82018-07-03 14:46:07 -07001048
Christine Franks39b03112018-07-03 14:46:07 -07001049 @Override
1050 public boolean isDeviceColorManaged() {
1051 final long token = Binder.clearCallingIdentity();
1052 try {
1053 return isDeviceColorManagedInternal();
1054 } finally {
1055 Binder.restoreCallingIdentity(token);
1056 }
1057 }
Christine Franks09c229e2018-12-14 10:37:40 -08001058
1059 @Override
1060 public boolean setSaturationLevel(int level) {
1061 final boolean hasTransformsPermission = getContext()
1062 .checkCallingPermission(Manifest.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS)
1063 == PackageManager.PERMISSION_GRANTED;
1064 final boolean hasLegacyPermission = getContext()
1065 .checkCallingPermission(Manifest.permission.CONTROL_DISPLAY_SATURATION)
1066 == PackageManager.PERMISSION_GRANTED;
1067 if (!hasTransformsPermission && !hasLegacyPermission) {
1068 throw new SecurityException("Permission required to set display saturation level");
1069 }
1070 final long token = Binder.clearCallingIdentity();
1071 try {
1072 final Message message = mHandler.obtainMessage(MSG_APPLY_GLOBAL_SATURATION);
1073 message.arg1 = level;
1074 mHandler.sendMessage(message);
1075 } finally {
1076 Binder.restoreCallingIdentity(token);
1077 }
1078 return true;
1079 }
Christine Franks39b03112018-07-03 14:46:07 -07001080 }
Justin Klaassen911e8892016-06-21 18:24:24 -07001081}