blob: b332c47ce28176eee81d29cbdd793bebfd1f2378 [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;
Justin Klaassen639214e2016-07-14 21:00:06 -070051import android.view.animation.AnimationUtils;
Justin Klaassen911e8892016-06-21 18:24:24 -070052
Christine Franks39b03112018-07-03 14:46:07 -070053import com.android.internal.R;
Christine Franks57fdde82018-07-03 14:46:07 -070054import com.android.internal.annotations.VisibleForTesting;
Christine Franks5397f032017-11-01 18:35:16 -070055import com.android.internal.app.ColorDisplayController;
Christine Franks57fdde82018-07-03 14:46:07 -070056import com.android.server.DisplayThread;
Justin Klaassen911e8892016-06-21 18:24:24 -070057import com.android.server.SystemService;
58import com.android.server.twilight.TwilightListener;
59import com.android.server.twilight.TwilightManager;
60import com.android.server.twilight.TwilightState;
61
Christine Franks57fdde82018-07-03 14:46:07 -070062import java.time.DateTimeException;
63import java.time.Instant;
Christine Franks03213462017-08-25 13:57:26 -070064import java.time.LocalDateTime;
65import java.time.LocalTime;
66import java.time.ZoneId;
Christine Franks57fdde82018-07-03 14:46:07 -070067import java.time.format.DateTimeParseException;
Christine Franks09c229e2018-12-14 10:37:40 -080068import java.util.Arrays;
Christine Franks8ad71492017-10-24 19:04:22 -070069
Justin Klaassen911e8892016-06-21 18:24:24 -070070/**
Christine Franks39b03112018-07-03 14:46:07 -070071 * Controls the display's color transforms.
Justin Klaassen911e8892016-06-21 18:24:24 -070072 */
Christine Franks57fdde82018-07-03 14:46:07 -070073public final class ColorDisplayService extends SystemService {
Justin Klaassen911e8892016-06-21 18:24:24 -070074
Christine Franks5397f032017-11-01 18:35:16 -070075 private static final String TAG = "ColorDisplayService";
Justin Klaassen911e8892016-06-21 18:24:24 -070076
77 /**
Christine Franks7b83b4282017-01-18 14:55:00 -080078 * The transition time, in milliseconds, for Night Display to turn on/off.
79 */
80 private static final long TRANSITION_DURATION = 3000L;
81
82 /**
Justin Klaassen639214e2016-07-14 21:00:06 -070083 * The identity matrix, used if one of the given matrices is {@code null}.
84 */
85 private static final float[] MATRIX_IDENTITY = new float[16];
Christine Franks57fdde82018-07-03 14:46:07 -070086
Justin Klaassen639214e2016-07-14 21:00:06 -070087 static {
88 Matrix.setIdentityM(MATRIX_IDENTITY, 0);
89 }
90
Christine Franks09c229e2018-12-14 10:37:40 -080091 private static final int MSG_APPLY_NIGHT_DISPLAY_IMMEDIATE = 0;
92 private static final int MSG_APPLY_NIGHT_DISPLAY_ANIMATED = 1;
93 private static final int MSG_APPLY_GLOBAL_SATURATION = 2;
94
Justin Klaassen639214e2016-07-14 21:00:06 -070095 /**
96 * Evaluator used to animate color matrix transitions.
97 */
98 private static final ColorMatrixEvaluator COLOR_MATRIX_EVALUATOR = new ColorMatrixEvaluator();
99
Christine Franks245ffd42018-11-16 13:45:14 -0800100 private final TintController mNightDisplayTintController = new TintController() {
101
102 private float[] mMatrixNightDisplay = new float[16];
103 private final float[] mColorTempCoefficients = new float[9];
104
105 /**
106 * Set coefficients based on whether the color matrix is linear or not.
107 */
108 @Override
109 public void setUp(Context context, boolean needsLinear) {
110 final String[] coefficients = context.getResources().getStringArray(needsLinear
111 ? R.array.config_nightDisplayColorTemperatureCoefficients
112 : R.array.config_nightDisplayColorTemperatureCoefficientsNative);
113 for (int i = 0; i < 9 && i < coefficients.length; i++) {
114 mColorTempCoefficients[i] = Float.parseFloat(coefficients[i]);
115 }
116 }
117
118 @Override
119 public void setMatrix(int cct) {
120 if (mMatrixNightDisplay.length != 16) {
121 Slog.d(TAG, "The display transformation matrix must be 4x4");
122 return;
123 }
124
125 Matrix.setIdentityM(mMatrixNightDisplay, 0);
126
127 final float squareTemperature = cct * cct;
128 final float red = squareTemperature * mColorTempCoefficients[0]
129 + cct * mColorTempCoefficients[1] + mColorTempCoefficients[2];
130 final float green = squareTemperature * mColorTempCoefficients[3]
131 + cct * mColorTempCoefficients[4] + mColorTempCoefficients[5];
132 final float blue = squareTemperature * mColorTempCoefficients[6]
133 + cct * mColorTempCoefficients[7] + mColorTempCoefficients[8];
134 mMatrixNightDisplay[0] = red;
135 mMatrixNightDisplay[5] = green;
136 mMatrixNightDisplay[10] = blue;
137 }
138
139 @Override
140 public float[] getMatrix() {
141 return isActivated() ? mMatrixNightDisplay : MATRIX_IDENTITY;
142 }
143
144 @Override
145 public int getLevel() {
146 return LEVEL_COLOR_MATRIX_NIGHT_DISPLAY;
147 }
148 };
149
150 private final TintController mDisplayWhiteBalanceTintController = new TintController() {
151
152 private float[] mMatrixDisplayWhiteBalance = new float[16];
153
154 @Override
155 public void setUp(Context context, boolean needsLinear) {
156 }
157
158 @Override
159 public float[] getMatrix() {
160 return isActivated() ? mMatrixDisplayWhiteBalance : MATRIX_IDENTITY;
161 }
162
163 @Override
164 public void setMatrix(int cct) {
165 }
166
167 @Override
168 public int getLevel() {
169 return LEVEL_COLOR_MATRIX_DISPLAY_WHITE_BALANCE;
170 }
171 };
172
Christine Franks09c229e2018-12-14 10:37:40 -0800173 private final TintController mGlobalSaturationTintController = new TintController() {
174
175 private float[] mMatrixGlobalSaturation = new float[16];
176
177 @Override
178 public void setUp(Context context, boolean needsLinear) {
179 }
180
181 @Override
182 public float[] getMatrix() {
183 return Arrays.copyOf(mMatrixGlobalSaturation, mMatrixGlobalSaturation.length);
184 }
185
186 @Override
187 public void setMatrix(int saturationLevel) {
188 if (saturationLevel < 0) {
189 saturationLevel = 0;
190 } else if (saturationLevel > 100) {
191 saturationLevel = 100;
192 }
193 Slog.d(TAG, "Setting saturation level: " + saturationLevel);
194
195 if (saturationLevel == 100) {
196 Matrix.setIdentityM(mMatrixGlobalSaturation, 0);
197 } else {
198 float saturation = saturationLevel * 0.1f;
199 float desaturation = 1.0f - saturation;
200 float[] luminance = {0.231f * desaturation, 0.715f * desaturation,
201 0.072f * desaturation};
202 mMatrixGlobalSaturation[0] = luminance[0] + saturation;
203 mMatrixGlobalSaturation[1] = luminance[0];
204 mMatrixGlobalSaturation[2] = luminance[0];
205 mMatrixGlobalSaturation[4] = luminance[1];
206 mMatrixGlobalSaturation[5] = luminance[1] + saturation;
207 mMatrixGlobalSaturation[6] = luminance[1];
208 mMatrixGlobalSaturation[8] = luminance[2];
209 mMatrixGlobalSaturation[9] = luminance[2];
210 mMatrixGlobalSaturation[10] = luminance[2] + saturation;
211 }
212 }
213
214 @Override
215 public int getLevel() {
216 return LEVEL_COLOR_MATRIX_SATURATION;
217 }
218 };
219
Justin Klaassen2696d992016-07-11 21:26:46 -0700220 private final Handler mHandler;
221
Justin Klaassen911e8892016-06-21 18:24:24 -0700222 private int mCurrentUser = UserHandle.USER_NULL;
Justin Klaassen2696d992016-07-11 21:26:46 -0700223 private ContentObserver mUserSetupObserver;
Justin Klaassen911e8892016-06-21 18:24:24 -0700224 private boolean mBootCompleted;
225
Christine Franks57fdde82018-07-03 14:46:07 -0700226 private ColorDisplayController mNightDisplayController;
227 private ContentObserver mContentObserver;
Christine Franks57fdde82018-07-03 14:46:07 -0700228
Christine Franks245ffd42018-11-16 13:45:14 -0800229 private DisplayWhiteBalanceListener mDisplayWhiteBalanceListener;
230
Christine Franks57fdde82018-07-03 14:46:07 -0700231 private NightDisplayAutoMode mNightDisplayAutoMode;
Justin Klaassen911e8892016-06-21 18:24:24 -0700232
Christine Franks245ffd42018-11-16 13:45:14 -0800233 private Integer mDisplayWhiteBalanceColorTemperature;
234
Christine Franks5397f032017-11-01 18:35:16 -0700235 public ColorDisplayService(Context context) {
Justin Klaassen911e8892016-06-21 18:24:24 -0700236 super(context);
Christine Franks09c229e2018-12-14 10:37:40 -0800237 mHandler = new TintHandler(Looper.getMainLooper());
Justin Klaassen911e8892016-06-21 18:24:24 -0700238 }
239
240 @Override
241 public void onStart() {
Christine Franks39b03112018-07-03 14:46:07 -0700242 publishBinderService(Context.COLOR_DISPLAY_SERVICE, new BinderService());
Christine Franks245ffd42018-11-16 13:45:14 -0800243 publishLocalService(ColorDisplayServiceInternal.class, new ColorDisplayServiceInternal());
Justin Klaassen911e8892016-06-21 18:24:24 -0700244 }
245
246 @Override
Justin Klaassen2696d992016-07-11 21:26:46 -0700247 public void onBootPhase(int phase) {
Christine Frankse5bb03e2017-02-10 17:36:10 -0800248 if (phase >= PHASE_BOOT_COMPLETED) {
Justin Klaassen2696d992016-07-11 21:26:46 -0700249 mBootCompleted = true;
250
251 // Register listeners now that boot is complete.
252 if (mCurrentUser != UserHandle.USER_NULL && mUserSetupObserver == null) {
253 setUp();
254 }
255 }
256 }
257
258 @Override
Justin Klaassen911e8892016-06-21 18:24:24 -0700259 public void onStartUser(int userHandle) {
260 super.onStartUser(userHandle);
261
Justin Klaassen911e8892016-06-21 18:24:24 -0700262 if (mCurrentUser == UserHandle.USER_NULL) {
Justin Klaassen2696d992016-07-11 21:26:46 -0700263 onUserChanged(userHandle);
Justin Klaassen911e8892016-06-21 18:24:24 -0700264 }
265 }
266
267 @Override
268 public void onSwitchUser(int userHandle) {
269 super.onSwitchUser(userHandle);
270
Justin Klaassen2696d992016-07-11 21:26:46 -0700271 onUserChanged(userHandle);
Justin Klaassen911e8892016-06-21 18:24:24 -0700272 }
273
274 @Override
275 public void onStopUser(int userHandle) {
276 super.onStopUser(userHandle);
277
Justin Klaassen911e8892016-06-21 18:24:24 -0700278 if (mCurrentUser == userHandle) {
Justin Klaassen2696d992016-07-11 21:26:46 -0700279 onUserChanged(UserHandle.USER_NULL);
Justin Klaassen911e8892016-06-21 18:24:24 -0700280 }
281 }
282
Justin Klaassen2696d992016-07-11 21:26:46 -0700283 private void onUserChanged(int userHandle) {
284 final ContentResolver cr = getContext().getContentResolver();
Justin Klaassen911e8892016-06-21 18:24:24 -0700285
Justin Klaassen2696d992016-07-11 21:26:46 -0700286 if (mCurrentUser != UserHandle.USER_NULL) {
287 if (mUserSetupObserver != null) {
288 cr.unregisterContentObserver(mUserSetupObserver);
289 mUserSetupObserver = null;
290 } else if (mBootCompleted) {
291 tearDown();
292 }
293 }
294
295 mCurrentUser = userHandle;
296
297 if (mCurrentUser != UserHandle.USER_NULL) {
298 if (!isUserSetupCompleted(cr, mCurrentUser)) {
299 mUserSetupObserver = new ContentObserver(mHandler) {
300 @Override
301 public void onChange(boolean selfChange, Uri uri) {
302 if (isUserSetupCompleted(cr, mCurrentUser)) {
303 cr.unregisterContentObserver(this);
304 mUserSetupObserver = null;
305
306 if (mBootCompleted) {
307 setUp();
308 }
309 }
310 }
311 };
312 cr.registerContentObserver(Secure.getUriFor(Secure.USER_SETUP_COMPLETE),
Christine Franks39b03112018-07-03 14:46:07 -0700313 false /* notifyForDescendants */, mUserSetupObserver, mCurrentUser);
Justin Klaassen2696d992016-07-11 21:26:46 -0700314 } else if (mBootCompleted) {
315 setUp();
Justin Klaassen911e8892016-06-21 18:24:24 -0700316 }
317 }
318 }
319
Justin Klaassen2696d992016-07-11 21:26:46 -0700320 private static boolean isUserSetupCompleted(ContentResolver cr, int userHandle) {
321 return Secure.getIntForUser(cr, Secure.USER_SETUP_COMPLETE, 0, userHandle) == 1;
322 }
323
324 private void setUp() {
Justin Klaassenec8837a2016-08-23 12:04:42 -0700325 Slog.d(TAG, "setUp: currentUser=" + mCurrentUser);
326
Christine Franks57fdde82018-07-03 14:46:07 -0700327 mNightDisplayController = new ColorDisplayController(getContext(), mCurrentUser);
328
329 // Listen for external changes to any of the settings.
330 if (mContentObserver == null) {
331 mContentObserver = new ContentObserver(new Handler(DisplayThread.get().getLooper())) {
332 @Override
333 public void onChange(boolean selfChange, Uri uri) {
334 super.onChange(selfChange, uri);
335
336 final String setting = uri == null ? null : uri.getLastPathSegment();
337 if (setting != null) {
338 switch (setting) {
339 case Secure.NIGHT_DISPLAY_ACTIVATED:
340 onNightDisplayActivated(mNightDisplayController.isActivated());
341 break;
342 case Secure.NIGHT_DISPLAY_COLOR_TEMPERATURE:
343 onNightDisplayColorTemperatureChanged(
344 mNightDisplayController.getColorTemperature());
345 break;
346 case Secure.NIGHT_DISPLAY_AUTO_MODE:
347 onNightDisplayAutoModeChanged(
348 mNightDisplayController.getAutoMode());
349 break;
350 case Secure.NIGHT_DISPLAY_CUSTOM_START_TIME:
351 onNightDisplayCustomStartTimeChanged(
352 mNightDisplayController.getCustomStartTime());
353 break;
354 case Secure.NIGHT_DISPLAY_CUSTOM_END_TIME:
355 onNightDisplayCustomEndTimeChanged(
356 mNightDisplayController.getCustomEndTime());
357 break;
358 case System.DISPLAY_COLOR_MODE:
359 onDisplayColorModeChanged(mNightDisplayController.getColorMode());
360 break;
361 case Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED:
362 case Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED:
363 onAccessibilityTransformChanged();
364 break;
Christine Franks245ffd42018-11-16 13:45:14 -0800365 case Secure.DISPLAY_WHITE_BALANCE_ENABLED:
366 onDisplayWhiteBalanceEnabled(isDisplayWhiteBalanceSettingEnabled());
367 break;
Christine Franks57fdde82018-07-03 14:46:07 -0700368 }
369 }
370 }
371 };
372 }
373 final ContentResolver cr = getContext().getContentResolver();
374 cr.registerContentObserver(Secure.getUriFor(Secure.NIGHT_DISPLAY_ACTIVATED),
375 false /* notifyForDescendants */, mContentObserver, mCurrentUser);
376 cr.registerContentObserver(Secure.getUriFor(Secure.NIGHT_DISPLAY_COLOR_TEMPERATURE),
377 false /* notifyForDescendants */, mContentObserver, mCurrentUser);
378 cr.registerContentObserver(Secure.getUriFor(Secure.NIGHT_DISPLAY_AUTO_MODE),
379 false /* notifyForDescendants */, mContentObserver, mCurrentUser);
380 cr.registerContentObserver(Secure.getUriFor(Secure.NIGHT_DISPLAY_CUSTOM_START_TIME),
381 false /* notifyForDescendants */, mContentObserver, mCurrentUser);
382 cr.registerContentObserver(Secure.getUriFor(Secure.NIGHT_DISPLAY_CUSTOM_END_TIME),
383 false /* notifyForDescendants */, mContentObserver, mCurrentUser);
384 cr.registerContentObserver(System.getUriFor(System.DISPLAY_COLOR_MODE),
385 false /* notifyForDescendants */, mContentObserver, mCurrentUser);
386 cr.registerContentObserver(
387 Secure.getUriFor(Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED),
388 false /* notifyForDescendants */, mContentObserver, mCurrentUser);
389 cr.registerContentObserver(
390 Secure.getUriFor(Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED),
391 false /* notifyForDescendants */, mContentObserver, mCurrentUser);
Christine Franks245ffd42018-11-16 13:45:14 -0800392 cr.registerContentObserver(Secure.getUriFor(Secure.DISPLAY_WHITE_BALANCE_ENABLED),
393 false /* notifyForDescendants */, mContentObserver, mCurrentUser);
Justin Klaassen911e8892016-06-21 18:24:24 -0700394
Christine Frankscf388c22018-05-15 15:48:10 -0700395 // Set the color mode, if valid, and immediately apply the updated tint matrix based on the
396 // existing activated state. This ensures consistency of tint across the color mode change.
Christine Franks57fdde82018-07-03 14:46:07 -0700397 onDisplayColorModeChanged(mNightDisplayController.getColorMode());
Christine Frankscf388c22018-05-15 15:48:10 -0700398
Christine Franks245ffd42018-11-16 13:45:14 -0800399 if (ColorDisplayManager.isNightDisplayAvailable(getContext())) {
400 // Reset the activated state.
401 mNightDisplayTintController.setActivated(null);
Christine Frankscf388c22018-05-15 15:48:10 -0700402
Christine Franks245ffd42018-11-16 13:45:14 -0800403 // Prepare the night display color transformation matrix.
404 mNightDisplayTintController
405 .setUp(getContext(), DisplayTransformManager.needsLinearColorMatrix());
406 mNightDisplayTintController.setMatrix(mNightDisplayController.getColorTemperature());
Christine Franks8ad71492017-10-24 19:04:22 -0700407
Christine Franks245ffd42018-11-16 13:45:14 -0800408 // Initialize the current auto mode.
409 onNightDisplayAutoModeChanged(mNightDisplayController.getAutoMode());
Christine Franks6418d0b2017-02-13 09:48:00 -0800410
Christine Franks245ffd42018-11-16 13:45:14 -0800411 // Force the initialization current activated state.
412 if (mNightDisplayTintController.isActivatedStateNotSet()) {
413 onNightDisplayActivated(mNightDisplayController.isActivated());
414 }
415 }
Justin Klaassen911e8892016-06-21 18:24:24 -0700416
Christine Franks245ffd42018-11-16 13:45:14 -0800417 if (ColorDisplayManager.isDisplayWhiteBalanceAvailable(getContext())) {
418 // Prepare the display white balance transform matrix.
419 mDisplayWhiteBalanceTintController
420 .setUp(getContext(), DisplayTransformManager.needsLinearColorMatrix());
421 if (mDisplayWhiteBalanceColorTemperature != null) {
422 mDisplayWhiteBalanceTintController
423 .setMatrix(mDisplayWhiteBalanceColorTemperature);
424 }
425
426 onDisplayWhiteBalanceEnabled(isDisplayWhiteBalanceSettingEnabled());
Justin Klaassen911e8892016-06-21 18:24:24 -0700427 }
428 }
429
Justin Klaassen2696d992016-07-11 21:26:46 -0700430 private void tearDown() {
Justin Klaassenec8837a2016-08-23 12:04:42 -0700431 Slog.d(TAG, "tearDown: currentUser=" + mCurrentUser);
432
Christine Franks57fdde82018-07-03 14:46:07 -0700433 getContext().getContentResolver().unregisterContentObserver(mContentObserver);
434
435 if (mNightDisplayController != null) {
436 mNightDisplayController = null;
Justin Klaassen2696d992016-07-11 21:26:46 -0700437 }
Justin Klaassen911e8892016-06-21 18:24:24 -0700438
Christine Franks245ffd42018-11-16 13:45:14 -0800439 if (ColorDisplayManager.isNightDisplayAvailable(getContext())) {
440 if (mNightDisplayAutoMode != null) {
441 mNightDisplayAutoMode.onStop();
442 mNightDisplayAutoMode = null;
443 }
444 mNightDisplayTintController.endAnimator();
Justin Klaassen911e8892016-06-21 18:24:24 -0700445 }
446
Christine Franks245ffd42018-11-16 13:45:14 -0800447 if (ColorDisplayManager.isDisplayWhiteBalanceAvailable(getContext())) {
448 mDisplayWhiteBalanceTintController.endAnimator();
Justin Klaassen639214e2016-07-14 21:00:06 -0700449 }
Justin Klaassen911e8892016-06-21 18:24:24 -0700450 }
451
Christine Franks57fdde82018-07-03 14:46:07 -0700452 private void onNightDisplayActivated(boolean activated) {
Christine Franks245ffd42018-11-16 13:45:14 -0800453 if (mNightDisplayTintController.isActivatedStateNotSet()
454 || mNightDisplayTintController.isActivated() != activated) {
Justin Klaassen911e8892016-06-21 18:24:24 -0700455 Slog.i(TAG, activated ? "Turning on night display" : "Turning off night display");
456
Christine Franks245ffd42018-11-16 13:45:14 -0800457 mNightDisplayTintController.setActivated(activated);
Justin Klaassen908b86c2016-08-08 09:18:42 -0700458
Christine Franks57fdde82018-07-03 14:46:07 -0700459 if (mNightDisplayAutoMode != null) {
460 mNightDisplayAutoMode.onActivated(activated);
Christine Frankse5bb03e2017-02-10 17:36:10 -0800461 }
462
Christine Franks245ffd42018-11-16 13:45:14 -0800463 applyTint(mNightDisplayTintController, false);
Justin Klaassen911e8892016-06-21 18:24:24 -0700464 }
465 }
466
Christine Franks57fdde82018-07-03 14:46:07 -0700467 private void onNightDisplayAutoModeChanged(int autoMode) {
468 Slog.d(TAG, "onNightDisplayAutoModeChanged: autoMode=" + autoMode);
Justin Klaassenec8837a2016-08-23 12:04:42 -0700469
Christine Franks57fdde82018-07-03 14:46:07 -0700470 if (mNightDisplayAutoMode != null) {
471 mNightDisplayAutoMode.onStop();
472 mNightDisplayAutoMode = null;
Justin Klaassen911e8892016-06-21 18:24:24 -0700473 }
474
Christine Franks5397f032017-11-01 18:35:16 -0700475 if (autoMode == ColorDisplayController.AUTO_MODE_CUSTOM) {
Christine Franks57fdde82018-07-03 14:46:07 -0700476 mNightDisplayAutoMode = new CustomNightDisplayAutoMode();
Christine Franks5397f032017-11-01 18:35:16 -0700477 } else if (autoMode == ColorDisplayController.AUTO_MODE_TWILIGHT) {
Christine Franks57fdde82018-07-03 14:46:07 -0700478 mNightDisplayAutoMode = new TwilightNightDisplayAutoMode();
Justin Klaassen911e8892016-06-21 18:24:24 -0700479 }
480
Christine Franks57fdde82018-07-03 14:46:07 -0700481 if (mNightDisplayAutoMode != null) {
482 mNightDisplayAutoMode.onStart();
Justin Klaassen911e8892016-06-21 18:24:24 -0700483 }
484 }
485
Christine Franks57fdde82018-07-03 14:46:07 -0700486 private void onNightDisplayCustomStartTimeChanged(LocalTime startTime) {
487 Slog.d(TAG, "onNightDisplayCustomStartTimeChanged: startTime=" + startTime);
Justin Klaassenec8837a2016-08-23 12:04:42 -0700488
Christine Franks57fdde82018-07-03 14:46:07 -0700489 if (mNightDisplayAutoMode != null) {
490 mNightDisplayAutoMode.onCustomStartTimeChanged(startTime);
Justin Klaassen911e8892016-06-21 18:24:24 -0700491 }
492 }
493
Christine Franks57fdde82018-07-03 14:46:07 -0700494 private void onNightDisplayCustomEndTimeChanged(LocalTime endTime) {
495 Slog.d(TAG, "onNightDisplayCustomEndTimeChanged: endTime=" + endTime);
Justin Klaassenec8837a2016-08-23 12:04:42 -0700496
Christine Franks57fdde82018-07-03 14:46:07 -0700497 if (mNightDisplayAutoMode != null) {
498 mNightDisplayAutoMode.onCustomEndTimeChanged(endTime);
Justin Klaassen911e8892016-06-21 18:24:24 -0700499 }
500 }
501
Christine Franks57fdde82018-07-03 14:46:07 -0700502 private void onNightDisplayColorTemperatureChanged(int colorTemperature) {
Christine Franks245ffd42018-11-16 13:45:14 -0800503 mNightDisplayTintController.setMatrix(colorTemperature);
504 applyTint(mNightDisplayTintController, true);
Christine Franks6418d0b2017-02-13 09:48:00 -0800505 }
506
Christine Franks57fdde82018-07-03 14:46:07 -0700507 private void onDisplayColorModeChanged(int mode) {
Christine Frankscf388c22018-05-15 15:48:10 -0700508 if (mode == -1) {
509 return;
510 }
511
Christine Franks245ffd42018-11-16 13:45:14 -0800512 mNightDisplayTintController.cancelAnimator();
513 mDisplayWhiteBalanceTintController.cancelAnimator();
514
515 mNightDisplayTintController
516 .setUp(getContext(), DisplayTransformManager.needsLinearColorMatrix(mode));
517 mNightDisplayTintController.setMatrix(mNightDisplayController.getColorTemperature());
518
519 mDisplayWhiteBalanceTintController
520 .setUp(getContext(), DisplayTransformManager.needsLinearColorMatrix(mode));
521 if (mDisplayWhiteBalanceColorTemperature != null) {
522 mDisplayWhiteBalanceTintController.setMatrix(mDisplayWhiteBalanceColorTemperature);
Christine Franks218e6562017-11-27 10:20:14 -0800523 }
Christine Franks8ad71492017-10-24 19:04:22 -0700524
Christine Franks218e6562017-11-27 10:20:14 -0800525 final DisplayTransformManager dtm = getLocalService(DisplayTransformManager.class);
Christine Franks245ffd42018-11-16 13:45:14 -0800526 dtm.setColorMode(mode, mNightDisplayTintController.getMatrix());
Christine Franks8ad71492017-10-24 19:04:22 -0700527 }
528
Christine Franks57fdde82018-07-03 14:46:07 -0700529 private void onAccessibilityTransformChanged() {
530 onDisplayColorModeChanged(mNightDisplayController.getColorMode());
Daniel Solomon317a3572018-03-30 18:36:37 -0700531 }
532
Christine Franks8ad71492017-10-24 19:04:22 -0700533
Christine Franks6418d0b2017-02-13 09:48:00 -0800534 /**
535 * Applies current color temperature matrix, or removes it if deactivated.
536 *
537 * @param immediate {@code true} skips transition animation
538 */
Christine Franks245ffd42018-11-16 13:45:14 -0800539 private void applyTint(TintController tintController, boolean immediate) {
540 tintController.cancelAnimator();
Christine Franks6418d0b2017-02-13 09:48:00 -0800541
Christine Franks6418d0b2017-02-13 09:48:00 -0800542 final DisplayTransformManager dtm = getLocalService(DisplayTransformManager.class);
Christine Franks245ffd42018-11-16 13:45:14 -0800543 final float[] from = dtm.getColorMatrix(tintController.getLevel());
544 final float[] to = tintController.getMatrix();
Christine Franks6418d0b2017-02-13 09:48:00 -0800545
546 if (immediate) {
Christine Franks245ffd42018-11-16 13:45:14 -0800547 dtm.setColorMatrix(tintController.getLevel(), to);
Christine Franks6418d0b2017-02-13 09:48:00 -0800548 } else {
Christine Franks245ffd42018-11-16 13:45:14 -0800549 tintController.setAnimator(ValueAnimator.ofObject(COLOR_MATRIX_EVALUATOR,
550 from == null ? MATRIX_IDENTITY : from, to));
551 tintController.getAnimator().setDuration(TRANSITION_DURATION);
552 tintController.getAnimator().setInterpolator(AnimationUtils.loadInterpolator(
Christine Franks6418d0b2017-02-13 09:48:00 -0800553 getContext(), android.R.interpolator.fast_out_slow_in));
Christine Franks245ffd42018-11-16 13:45:14 -0800554 tintController.getAnimator().addUpdateListener((ValueAnimator animator) -> {
555 final float[] value = (float[]) animator.getAnimatedValue();
556 dtm.setColorMatrix(tintController.getLevel(), value);
Christine Franks6418d0b2017-02-13 09:48:00 -0800557 });
Christine Franks245ffd42018-11-16 13:45:14 -0800558 tintController.getAnimator().addListener(new AnimatorListenerAdapter() {
Christine Franks6418d0b2017-02-13 09:48:00 -0800559
560 private boolean mIsCancelled;
561
562 @Override
563 public void onAnimationCancel(Animator animator) {
564 mIsCancelled = true;
565 }
566
567 @Override
568 public void onAnimationEnd(Animator animator) {
569 if (!mIsCancelled) {
570 // Ensure final color matrix is set at the end of the animation. If the
571 // animation is cancelled then don't set the final color matrix so the new
572 // animator can pick up from where this one left off.
Christine Franks245ffd42018-11-16 13:45:14 -0800573 dtm.setColorMatrix(tintController.getLevel(), to);
Christine Franks6418d0b2017-02-13 09:48:00 -0800574 }
Christine Franks245ffd42018-11-16 13:45:14 -0800575 tintController.setAnimator(null);
Christine Franks6418d0b2017-02-13 09:48:00 -0800576 }
577 });
Christine Franks245ffd42018-11-16 13:45:14 -0800578 tintController.getAnimator().start();
Christine Franks6418d0b2017-02-13 09:48:00 -0800579 }
580 }
581
582 /**
Christine Franks39b03112018-07-03 14:46:07 -0700583 * Returns the first date time corresponding to the local time that occurs before the provided
584 * date time.
Christine Franks03213462017-08-25 13:57:26 -0700585 *
586 * @param compareTime the LocalDateTime to compare against
587 * @return the prior LocalDateTime corresponding to this local time
588 */
Christine Franks57fdde82018-07-03 14:46:07 -0700589 @VisibleForTesting
590 static LocalDateTime getDateTimeBefore(LocalTime localTime, LocalDateTime compareTime) {
Christine Franks03213462017-08-25 13:57:26 -0700591 final LocalDateTime ldt = LocalDateTime.of(compareTime.getYear(), compareTime.getMonth(),
592 compareTime.getDayOfMonth(), localTime.getHour(), localTime.getMinute());
593
594 // Check if the local time has passed, if so return the same time yesterday.
595 return ldt.isAfter(compareTime) ? ldt.minusDays(1) : ldt;
596 }
597
598 /**
Christine Franks39b03112018-07-03 14:46:07 -0700599 * Returns the first date time corresponding to this local time that occurs after the provided
600 * date time.
Christine Franks03213462017-08-25 13:57:26 -0700601 *
602 * @param compareTime the LocalDateTime to compare against
603 * @return the next LocalDateTime corresponding to this local time
604 */
Christine Franks57fdde82018-07-03 14:46:07 -0700605 @VisibleForTesting
606 static LocalDateTime getDateTimeAfter(LocalTime localTime, LocalDateTime compareTime) {
Christine Franks03213462017-08-25 13:57:26 -0700607 final LocalDateTime ldt = LocalDateTime.of(compareTime.getYear(), compareTime.getMonth(),
608 compareTime.getDayOfMonth(), localTime.getHour(), localTime.getMinute());
609
610 // Check if the local time has passed, if so return the same time tomorrow.
611 return ldt.isBefore(compareTime) ? ldt.plusDays(1) : ldt;
612 }
613
Christine Franks245ffd42018-11-16 13:45:14 -0800614 private void onDisplayWhiteBalanceEnabled(boolean enabled) {
615 mDisplayWhiteBalanceTintController.setActivated(enabled);
616 if (mDisplayWhiteBalanceListener != null) {
617 mDisplayWhiteBalanceListener.onDisplayWhiteBalanceStatusChanged(enabled);
618 }
619 }
620
621 private boolean isDisplayWhiteBalanceSettingEnabled() {
622 return Secure.getIntForUser(getContext().getContentResolver(),
623 Secure.DISPLAY_WHITE_BALANCE_ENABLED, 0, mCurrentUser) == 1;
624 }
625
Christine Franks39b03112018-07-03 14:46:07 -0700626 private boolean isDeviceColorManagedInternal() {
627 final DisplayTransformManager dtm = getLocalService(DisplayTransformManager.class);
628 return dtm.isDeviceColorManaged();
629 }
630
Christine Franks57fdde82018-07-03 14:46:07 -0700631 /**
632 * Returns the last time the night display transform activation state was changed, or {@link
633 * LocalDateTime#MIN} if night display has never been activated.
634 */
Christine Franks245ffd42018-11-16 13:45:14 -0800635 private LocalDateTime getNightDisplayLastActivatedTimeSetting() {
Christine Franks57fdde82018-07-03 14:46:07 -0700636 final ContentResolver cr = getContext().getContentResolver();
637 final String lastActivatedTime = Secure.getStringForUser(
638 cr, Secure.NIGHT_DISPLAY_LAST_ACTIVATED_TIME, getContext().getUserId());
639 if (lastActivatedTime != null) {
640 try {
641 return LocalDateTime.parse(lastActivatedTime);
642 } catch (DateTimeParseException ignored) {
643 }
644 // Uses the old epoch time.
645 try {
646 return LocalDateTime.ofInstant(
647 Instant.ofEpochMilli(Long.parseLong(lastActivatedTime)),
648 ZoneId.systemDefault());
649 } catch (DateTimeException | NumberFormatException ignored) {
650 }
651 }
652 return LocalDateTime.MIN;
653 }
654
655 private abstract class NightDisplayAutoMode {
656
657 public abstract void onActivated(boolean activated);
658
Justin Klaassen911e8892016-06-21 18:24:24 -0700659 public abstract void onStart();
Christine Frankse5bb03e2017-02-10 17:36:10 -0800660
Justin Klaassen911e8892016-06-21 18:24:24 -0700661 public abstract void onStop();
Christine Franks57fdde82018-07-03 14:46:07 -0700662
663 public void onCustomStartTimeChanged(LocalTime startTime) {
664 }
665
666 public void onCustomEndTimeChanged(LocalTime endTime) {
667 }
Justin Klaassen911e8892016-06-21 18:24:24 -0700668 }
669
Christine Franks57fdde82018-07-03 14:46:07 -0700670 private final class CustomNightDisplayAutoMode extends NightDisplayAutoMode implements
671 AlarmManager.OnAlarmListener {
Justin Klaassen911e8892016-06-21 18:24:24 -0700672
673 private final AlarmManager mAlarmManager;
674 private final BroadcastReceiver mTimeChangedReceiver;
675
Christine Franks03213462017-08-25 13:57:26 -0700676 private LocalTime mStartTime;
677 private LocalTime mEndTime;
Justin Klaassen911e8892016-06-21 18:24:24 -0700678
Christine Franks03213462017-08-25 13:57:26 -0700679 private LocalDateTime mLastActivatedTime;
Justin Klaassen911e8892016-06-21 18:24:24 -0700680
Christine Franks57fdde82018-07-03 14:46:07 -0700681 CustomNightDisplayAutoMode() {
Justin Klaassen911e8892016-06-21 18:24:24 -0700682 mAlarmManager = (AlarmManager) getContext().getSystemService(Context.ALARM_SERVICE);
683 mTimeChangedReceiver = new BroadcastReceiver() {
684 @Override
685 public void onReceive(Context context, Intent intent) {
686 updateActivated();
687 }
688 };
689 }
690
691 private void updateActivated() {
Christine Franks03213462017-08-25 13:57:26 -0700692 final LocalDateTime now = LocalDateTime.now();
693 final LocalDateTime start = getDateTimeBefore(mStartTime, now);
694 final LocalDateTime end = getDateTimeAfter(mEndTime, start);
695 boolean activate = now.isBefore(end);
Justin Klaassen911e8892016-06-21 18:24:24 -0700696
Christine Frankse5bb03e2017-02-10 17:36:10 -0800697 if (mLastActivatedTime != null) {
Christine Frankse5bb03e2017-02-10 17:36:10 -0800698 // Maintain the existing activated state if within the current period.
Christine Franks03213462017-08-25 13:57:26 -0700699 if (mLastActivatedTime.isBefore(now) && mLastActivatedTime.isAfter(start)
700 && (mLastActivatedTime.isAfter(end) || now.isBefore(end))) {
Christine Franks57fdde82018-07-03 14:46:07 -0700701 activate = mNightDisplayController.isActivated();
Justin Klaassen911e8892016-06-21 18:24:24 -0700702 }
703 }
704
Christine Franks245ffd42018-11-16 13:45:14 -0800705 if (mNightDisplayTintController.isActivatedStateNotSet() || (
706 mNightDisplayTintController.isActivated() != activate)) {
Christine Franks57fdde82018-07-03 14:46:07 -0700707 mNightDisplayController.setActivated(activate);
Justin Klaassen911e8892016-06-21 18:24:24 -0700708 }
Christine Franks03213462017-08-25 13:57:26 -0700709
Christine Franks245ffd42018-11-16 13:45:14 -0800710 updateNextAlarm(mNightDisplayTintController.isActivated(), now);
Justin Klaassen911e8892016-06-21 18:24:24 -0700711 }
712
Christine Franks03213462017-08-25 13:57:26 -0700713 private void updateNextAlarm(@Nullable Boolean activated, @NonNull LocalDateTime now) {
Justin Klaassen4346f632016-08-08 15:01:47 -0700714 if (activated != null) {
Christine Franks03213462017-08-25 13:57:26 -0700715 final LocalDateTime next = activated ? getDateTimeAfter(mEndTime, now)
716 : getDateTimeAfter(mStartTime, now);
717 final long millis = next.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli();
718 mAlarmManager.setExact(AlarmManager.RTC, millis, TAG, this, null);
Justin Klaassen911e8892016-06-21 18:24:24 -0700719 }
720 }
721
722 @Override
723 public void onStart() {
724 final IntentFilter intentFilter = new IntentFilter(Intent.ACTION_TIME_CHANGED);
725 intentFilter.addAction(Intent.ACTION_TIMEZONE_CHANGED);
726 getContext().registerReceiver(mTimeChangedReceiver, intentFilter);
727
Christine Franks57fdde82018-07-03 14:46:07 -0700728 mStartTime = mNightDisplayController.getCustomStartTime();
729 mEndTime = mNightDisplayController.getCustomEndTime();
Justin Klaassen911e8892016-06-21 18:24:24 -0700730
Christine Franks57fdde82018-07-03 14:46:07 -0700731 mLastActivatedTime = getNightDisplayLastActivatedTimeSetting();
Christine Frankse5bb03e2017-02-10 17:36:10 -0800732
Justin Klaassen911e8892016-06-21 18:24:24 -0700733 // Force an update to initialize state.
734 updateActivated();
735 }
736
737 @Override
738 public void onStop() {
739 getContext().unregisterReceiver(mTimeChangedReceiver);
740
741 mAlarmManager.cancel(this);
742 mLastActivatedTime = null;
743 }
744
745 @Override
746 public void onActivated(boolean activated) {
Christine Franks57fdde82018-07-03 14:46:07 -0700747 mLastActivatedTime = getNightDisplayLastActivatedTimeSetting();
Christine Franks03213462017-08-25 13:57:26 -0700748 updateNextAlarm(activated, LocalDateTime.now());
Justin Klaassen911e8892016-06-21 18:24:24 -0700749 }
750
751 @Override
Christine Franks03213462017-08-25 13:57:26 -0700752 public void onCustomStartTimeChanged(LocalTime startTime) {
Justin Klaassen911e8892016-06-21 18:24:24 -0700753 mStartTime = startTime;
754 mLastActivatedTime = null;
755 updateActivated();
756 }
757
758 @Override
Christine Franks03213462017-08-25 13:57:26 -0700759 public void onCustomEndTimeChanged(LocalTime endTime) {
Justin Klaassen911e8892016-06-21 18:24:24 -0700760 mEndTime = endTime;
761 mLastActivatedTime = null;
762 updateActivated();
763 }
764
765 @Override
766 public void onAlarm() {
Justin Klaassenec8837a2016-08-23 12:04:42 -0700767 Slog.d(TAG, "onAlarm");
Justin Klaassen911e8892016-06-21 18:24:24 -0700768 updateActivated();
769 }
770 }
771
Christine Franks57fdde82018-07-03 14:46:07 -0700772 private final class TwilightNightDisplayAutoMode extends NightDisplayAutoMode implements
773 TwilightListener {
Justin Klaassen911e8892016-06-21 18:24:24 -0700774
775 private final TwilightManager mTwilightManager;
Christine Franks57fdde82018-07-03 14:46:07 -0700776 private LocalDateTime mLastActivatedTime;
Justin Klaassen911e8892016-06-21 18:24:24 -0700777
Christine Franks57fdde82018-07-03 14:46:07 -0700778 TwilightNightDisplayAutoMode() {
Justin Klaassen911e8892016-06-21 18:24:24 -0700779 mTwilightManager = getLocalService(TwilightManager.class);
Justin Klaassen911e8892016-06-21 18:24:24 -0700780 }
781
Justin Klaassen908b86c2016-08-08 09:18:42 -0700782 private void updateActivated(TwilightState state) {
Justin Klaassen3da4c442017-05-05 15:19:33 -0700783 if (state == null) {
784 // If there isn't a valid TwilightState then just keep the current activated
785 // state.
786 return;
787 }
788
789 boolean activate = state.isNight();
Christine Franks57fdde82018-07-03 14:46:07 -0700790 if (mLastActivatedTime != null) {
Christine Franks03213462017-08-25 13:57:26 -0700791 final LocalDateTime now = LocalDateTime.now();
792 final LocalDateTime sunrise = state.sunrise();
793 final LocalDateTime sunset = state.sunset();
Christine Frankse5bb03e2017-02-10 17:36:10 -0800794 // Maintain the existing activated state if within the current period.
Christine Franks57fdde82018-07-03 14:46:07 -0700795 if (mLastActivatedTime.isBefore(now) && (mLastActivatedTime.isBefore(sunrise)
796 ^ mLastActivatedTime.isBefore(sunset))) {
797 activate = mNightDisplayController.isActivated();
Justin Klaassen911e8892016-06-21 18:24:24 -0700798 }
799 }
Justin Klaassen908b86c2016-08-08 09:18:42 -0700800
Christine Franks245ffd42018-11-16 13:45:14 -0800801 if (mNightDisplayTintController.isActivatedStateNotSet() || (
802 mNightDisplayTintController.isActivated() != activate)) {
Christine Franks57fdde82018-07-03 14:46:07 -0700803 mNightDisplayController.setActivated(activate);
Justin Klaassen908b86c2016-08-08 09:18:42 -0700804 }
Justin Klaassen911e8892016-06-21 18:24:24 -0700805 }
806
807 @Override
Christine Franks57fdde82018-07-03 14:46:07 -0700808 public void onActivated(boolean activated) {
809 mLastActivatedTime = getNightDisplayLastActivatedTimeSetting();
810 }
811
812 @Override
Justin Klaassen911e8892016-06-21 18:24:24 -0700813 public void onStart() {
814 mTwilightManager.registerListener(this, mHandler);
Christine Franks57fdde82018-07-03 14:46:07 -0700815 mLastActivatedTime = getNightDisplayLastActivatedTimeSetting();
Justin Klaassen911e8892016-06-21 18:24:24 -0700816
817 // Force an update to initialize state.
Justin Klaassen908b86c2016-08-08 09:18:42 -0700818 updateActivated(mTwilightManager.getLastTwilightState());
Justin Klaassen911e8892016-06-21 18:24:24 -0700819 }
820
821 @Override
822 public void onStop() {
823 mTwilightManager.unregisterListener(this);
Christine Franks57fdde82018-07-03 14:46:07 -0700824 mLastActivatedTime = null;
Justin Klaassen908b86c2016-08-08 09:18:42 -0700825 }
826
827 @Override
828 public void onTwilightStateChanged(@Nullable TwilightState state) {
Justin Klaassenec8837a2016-08-23 12:04:42 -0700829 Slog.d(TAG, "onTwilightStateChanged: isNight="
830 + (state == null ? null : state.isNight()));
Justin Klaassen908b86c2016-08-08 09:18:42 -0700831 updateActivated(state);
Justin Klaassen911e8892016-06-21 18:24:24 -0700832 }
833 }
Justin Klaassen639214e2016-07-14 21:00:06 -0700834
835 /**
836 * Interpolates between two 4x4 color transform matrices (in column-major order).
837 */
838 private static class ColorMatrixEvaluator implements TypeEvaluator<float[]> {
839
840 /**
841 * Result matrix returned by {@link #evaluate(float, float[], float[])}.
842 */
843 private final float[] mResultMatrix = new float[16];
844
845 @Override
846 public float[] evaluate(float fraction, float[] startValue, float[] endValue) {
847 for (int i = 0; i < mResultMatrix.length; i++) {
848 mResultMatrix[i] = MathUtils.lerp(startValue[i], endValue[i], fraction);
849 }
850 return mResultMatrix;
851 }
852 }
Christine Franks39b03112018-07-03 14:46:07 -0700853
Christine Franks245ffd42018-11-16 13:45:14 -0800854 private abstract static class TintController {
855
856 private ValueAnimator mAnimator;
857 private Boolean mIsActivated;
858
859 public ValueAnimator getAnimator() {
860 return mAnimator;
861 }
862
863 public void setAnimator(ValueAnimator animator) {
864 mAnimator = animator;
865 }
866
867 /**
868 * Cancel the animator if it's still running.
869 */
870 public void cancelAnimator() {
871 if (mAnimator != null) {
872 mAnimator.cancel();
873 }
874 }
875
876 /**
877 * End the animator if it's still running, jumping to the end state.
878 */
879 public void endAnimator() {
880 if (mAnimator != null) {
881 mAnimator.end();
882 mAnimator = null;
883 }
884 }
885
886 public void setActivated(Boolean isActivated) {
887 mIsActivated = isActivated;
888 }
889
890 public boolean isActivated() {
891 return mIsActivated != null && mIsActivated;
892 }
893
894 public boolean isActivatedStateNotSet() {
895 return mIsActivated == null;
896 }
897
898 /**
899 * Set up any constants needed for computing the matrix.
900 */
901 public abstract void setUp(Context context, boolean needsLinear);
902
903 /**
904 * Sets the 4x4 matrix to apply.
905 */
906 public abstract void setMatrix(int value);
907
908 /**
909 * Get the 4x4 matrix to apply.
910 */
911 public abstract float[] getMatrix();
912
913 /**
914 * Get the color transform level to apply the matrix.
915 */
916 public abstract int getLevel();
917 }
918
919 /**
920 * Local service that allows color transforms to be enabled from other system services.
921 */
922 public final class ColorDisplayServiceInternal {
923
924 /**
925 * Set the current CCT value for the display white balance transform, and if the transform
926 * is enabled, apply it.
927 *
928 * @param cct the color temperature in Kelvin.
929 */
930 public boolean setDisplayWhiteBalanceColorTemperature(int cct) {
931 // Update the transform matrix even if it can't be applied.
932 mDisplayWhiteBalanceColorTemperature = cct;
933 mDisplayWhiteBalanceTintController.setMatrix(cct);
934
935 if (mDisplayWhiteBalanceTintController.isActivated()) {
936 applyTint(mDisplayWhiteBalanceTintController, true);
937 return true;
938 }
939 return false;
940 }
941
942 /**
943 * Sets the listener and returns whether display white balance is currently enabled.
944 */
945 public boolean setDisplayWhiteBalanceListener(DisplayWhiteBalanceListener listener) {
946 mDisplayWhiteBalanceListener = listener;
947 return mDisplayWhiteBalanceTintController.isActivated();
948 }
949 }
950
951 /**
952 * Listener for changes in display white balance status.
953 */
954 public interface DisplayWhiteBalanceListener {
955
956 /**
957 * Notify that the display white balance status has changed, either due to preemption by
958 * another transform or the feature being turned off.
959 */
960 void onDisplayWhiteBalanceStatusChanged(boolean enabled);
961 }
962
Christine Franks09c229e2018-12-14 10:37:40 -0800963 private final class TintHandler extends Handler {
964
965 TintHandler(Looper looper) {
966 super(looper, null, true /* async */);
967 }
968
969 @Override
970 public void handleMessage(Message msg) {
971 switch (msg.what) {
972 case MSG_APPLY_GLOBAL_SATURATION:
973 mGlobalSaturationTintController.setMatrix(msg.arg1);
974 applyTint(mGlobalSaturationTintController, false);
975 break;
976 }
977 }
978 }
979
Christine Franks39b03112018-07-03 14:46:07 -0700980 private final class BinderService extends IColorDisplayManager.Stub {
Christine Franks57fdde82018-07-03 14:46:07 -0700981
Christine Franks39b03112018-07-03 14:46:07 -0700982 @Override
983 public boolean isDeviceColorManaged() {
984 final long token = Binder.clearCallingIdentity();
985 try {
986 return isDeviceColorManagedInternal();
987 } finally {
988 Binder.restoreCallingIdentity(token);
989 }
990 }
Christine Franks09c229e2018-12-14 10:37:40 -0800991
992 @Override
993 public boolean setSaturationLevel(int level) {
994 final boolean hasTransformsPermission = getContext()
995 .checkCallingPermission(Manifest.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS)
996 == PackageManager.PERMISSION_GRANTED;
997 final boolean hasLegacyPermission = getContext()
998 .checkCallingPermission(Manifest.permission.CONTROL_DISPLAY_SATURATION)
999 == PackageManager.PERMISSION_GRANTED;
1000 if (!hasTransformsPermission && !hasLegacyPermission) {
1001 throw new SecurityException("Permission required to set display saturation level");
1002 }
1003 final long token = Binder.clearCallingIdentity();
1004 try {
1005 final Message message = mHandler.obtainMessage(MSG_APPLY_GLOBAL_SATURATION);
1006 message.arg1 = level;
1007 mHandler.sendMessage(message);
1008 } finally {
1009 Binder.restoreCallingIdentity(token);
1010 }
1011 return true;
1012 }
Christine Franks39b03112018-07-03 14:46:07 -07001013 }
Justin Klaassen911e8892016-06-21 18:24:24 -07001014}