blob: b6c82d3a66e4f3c4f61cac1556b08593ba4ba888 [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 Franks39b03112018-07-03 14:46:07 -070019import static com.android.server.display.DisplayTransformManager.LEVEL_COLOR_MATRIX_NIGHT_DISPLAY;
20
Justin Klaassen639214e2016-07-14 21:00:06 -070021import android.animation.Animator;
22import android.animation.AnimatorListenerAdapter;
23import android.animation.TypeEvaluator;
24import android.animation.ValueAnimator;
Justin Klaassen4346f632016-08-08 15:01:47 -070025import android.annotation.NonNull;
Justin Klaassen908b86c2016-08-08 09:18:42 -070026import android.annotation.Nullable;
Justin Klaassen911e8892016-06-21 18:24:24 -070027import android.app.AlarmManager;
28import android.content.BroadcastReceiver;
29import android.content.ContentResolver;
30import android.content.Context;
31import android.content.Intent;
32import android.content.IntentFilter;
Justin Klaassen2696d992016-07-11 21:26:46 -070033import android.database.ContentObserver;
Christine Franks39b03112018-07-03 14:46:07 -070034import android.hardware.display.IColorDisplayManager;
Justin Klaassen2696d992016-07-11 21:26:46 -070035import android.net.Uri;
Justin Klaassen639214e2016-07-14 21:00:06 -070036import android.opengl.Matrix;
Christine Franks39b03112018-07-03 14:46:07 -070037import android.os.Binder;
Justin Klaassen911e8892016-06-21 18:24:24 -070038import android.os.Handler;
39import android.os.Looper;
40import android.os.UserHandle;
41import android.provider.Settings.Secure;
Christine Franks57fdde82018-07-03 14:46:07 -070042import android.provider.Settings.System;
Justin Klaassen639214e2016-07-14 21:00:06 -070043import android.util.MathUtils;
Justin Klaassen911e8892016-06-21 18:24:24 -070044import android.util.Slog;
Justin Klaassen639214e2016-07-14 21:00:06 -070045import android.view.animation.AnimationUtils;
Justin Klaassen911e8892016-06-21 18:24:24 -070046
Christine Franks39b03112018-07-03 14:46:07 -070047import com.android.internal.R;
Christine Franks57fdde82018-07-03 14:46:07 -070048import com.android.internal.annotations.VisibleForTesting;
Christine Franks5397f032017-11-01 18:35:16 -070049import com.android.internal.app.ColorDisplayController;
Christine Franks57fdde82018-07-03 14:46:07 -070050import com.android.server.DisplayThread;
Justin Klaassen911e8892016-06-21 18:24:24 -070051import com.android.server.SystemService;
52import com.android.server.twilight.TwilightListener;
53import com.android.server.twilight.TwilightManager;
54import com.android.server.twilight.TwilightState;
55
Christine Franks57fdde82018-07-03 14:46:07 -070056import java.time.DateTimeException;
57import java.time.Instant;
Christine Franks03213462017-08-25 13:57:26 -070058import java.time.LocalDateTime;
59import java.time.LocalTime;
60import java.time.ZoneId;
Christine Franks57fdde82018-07-03 14:46:07 -070061import java.time.format.DateTimeParseException;
Christine Franks8ad71492017-10-24 19:04:22 -070062
Justin Klaassen911e8892016-06-21 18:24:24 -070063/**
Christine Franks39b03112018-07-03 14:46:07 -070064 * Controls the display's color transforms.
Justin Klaassen911e8892016-06-21 18:24:24 -070065 */
Christine Franks57fdde82018-07-03 14:46:07 -070066public final class ColorDisplayService extends SystemService {
Justin Klaassen911e8892016-06-21 18:24:24 -070067
Christine Franks5397f032017-11-01 18:35:16 -070068 private static final String TAG = "ColorDisplayService";
Justin Klaassen911e8892016-06-21 18:24:24 -070069
70 /**
Christine Franks7b83b4282017-01-18 14:55:00 -080071 * The transition time, in milliseconds, for Night Display to turn on/off.
72 */
73 private static final long TRANSITION_DURATION = 3000L;
74
75 /**
Justin Klaassen639214e2016-07-14 21:00:06 -070076 * The identity matrix, used if one of the given matrices is {@code null}.
77 */
78 private static final float[] MATRIX_IDENTITY = new float[16];
Christine Franks57fdde82018-07-03 14:46:07 -070079
Justin Klaassen639214e2016-07-14 21:00:06 -070080 static {
81 Matrix.setIdentityM(MATRIX_IDENTITY, 0);
82 }
83
84 /**
85 * Evaluator used to animate color matrix transitions.
86 */
87 private static final ColorMatrixEvaluator COLOR_MATRIX_EVALUATOR = new ColorMatrixEvaluator();
88
Justin Klaassen2696d992016-07-11 21:26:46 -070089 private final Handler mHandler;
90
Christine Franks6418d0b2017-02-13 09:48:00 -080091 private float[] mMatrixNight = new float[16];
92
Christine Franks2a125472017-07-26 17:51:16 -070093 private final float[] mColorTempCoefficients = new float[9];
Christine Franks6418d0b2017-02-13 09:48:00 -080094
Justin Klaassen911e8892016-06-21 18:24:24 -070095 private int mCurrentUser = UserHandle.USER_NULL;
Justin Klaassen2696d992016-07-11 21:26:46 -070096 private ContentObserver mUserSetupObserver;
Justin Klaassen911e8892016-06-21 18:24:24 -070097 private boolean mBootCompleted;
98
Christine Franks57fdde82018-07-03 14:46:07 -070099 private ColorDisplayController mNightDisplayController;
100 private ContentObserver mContentObserver;
Justin Klaassen639214e2016-07-14 21:00:06 -0700101 private ValueAnimator mColorMatrixAnimator;
Christine Franks57fdde82018-07-03 14:46:07 -0700102
103 private Boolean mIsNightDisplayActivated;
104 private NightDisplayAutoMode mNightDisplayAutoMode;
Justin Klaassen911e8892016-06-21 18:24:24 -0700105
Christine Franks5397f032017-11-01 18:35:16 -0700106 public ColorDisplayService(Context context) {
Justin Klaassen911e8892016-06-21 18:24:24 -0700107 super(context);
Justin Klaassen2696d992016-07-11 21:26:46 -0700108 mHandler = new Handler(Looper.getMainLooper());
Justin Klaassen911e8892016-06-21 18:24:24 -0700109 }
110
111 @Override
112 public void onStart() {
Christine Franks39b03112018-07-03 14:46:07 -0700113 publishBinderService(Context.COLOR_DISPLAY_SERVICE, new BinderService());
Justin Klaassen911e8892016-06-21 18:24:24 -0700114 }
115
116 @Override
Justin Klaassen2696d992016-07-11 21:26:46 -0700117 public void onBootPhase(int phase) {
Christine Frankse5bb03e2017-02-10 17:36:10 -0800118 if (phase >= PHASE_BOOT_COMPLETED) {
Justin Klaassen2696d992016-07-11 21:26:46 -0700119 mBootCompleted = true;
120
121 // Register listeners now that boot is complete.
122 if (mCurrentUser != UserHandle.USER_NULL && mUserSetupObserver == null) {
123 setUp();
124 }
125 }
126 }
127
128 @Override
Justin Klaassen911e8892016-06-21 18:24:24 -0700129 public void onStartUser(int userHandle) {
130 super.onStartUser(userHandle);
131
Justin Klaassen911e8892016-06-21 18:24:24 -0700132 if (mCurrentUser == UserHandle.USER_NULL) {
Justin Klaassen2696d992016-07-11 21:26:46 -0700133 onUserChanged(userHandle);
Justin Klaassen911e8892016-06-21 18:24:24 -0700134 }
135 }
136
137 @Override
138 public void onSwitchUser(int userHandle) {
139 super.onSwitchUser(userHandle);
140
Justin Klaassen2696d992016-07-11 21:26:46 -0700141 onUserChanged(userHandle);
Justin Klaassen911e8892016-06-21 18:24:24 -0700142 }
143
144 @Override
145 public void onStopUser(int userHandle) {
146 super.onStopUser(userHandle);
147
Justin Klaassen911e8892016-06-21 18:24:24 -0700148 if (mCurrentUser == userHandle) {
Justin Klaassen2696d992016-07-11 21:26:46 -0700149 onUserChanged(UserHandle.USER_NULL);
Justin Klaassen911e8892016-06-21 18:24:24 -0700150 }
151 }
152
Justin Klaassen2696d992016-07-11 21:26:46 -0700153 private void onUserChanged(int userHandle) {
154 final ContentResolver cr = getContext().getContentResolver();
Justin Klaassen911e8892016-06-21 18:24:24 -0700155
Justin Klaassen2696d992016-07-11 21:26:46 -0700156 if (mCurrentUser != UserHandle.USER_NULL) {
157 if (mUserSetupObserver != null) {
158 cr.unregisterContentObserver(mUserSetupObserver);
159 mUserSetupObserver = null;
160 } else if (mBootCompleted) {
161 tearDown();
162 }
163 }
164
165 mCurrentUser = userHandle;
166
167 if (mCurrentUser != UserHandle.USER_NULL) {
168 if (!isUserSetupCompleted(cr, mCurrentUser)) {
169 mUserSetupObserver = new ContentObserver(mHandler) {
170 @Override
171 public void onChange(boolean selfChange, Uri uri) {
172 if (isUserSetupCompleted(cr, mCurrentUser)) {
173 cr.unregisterContentObserver(this);
174 mUserSetupObserver = null;
175
176 if (mBootCompleted) {
177 setUp();
178 }
179 }
180 }
181 };
182 cr.registerContentObserver(Secure.getUriFor(Secure.USER_SETUP_COMPLETE),
Christine Franks39b03112018-07-03 14:46:07 -0700183 false /* notifyForDescendants */, mUserSetupObserver, mCurrentUser);
Justin Klaassen2696d992016-07-11 21:26:46 -0700184 } else if (mBootCompleted) {
185 setUp();
Justin Klaassen911e8892016-06-21 18:24:24 -0700186 }
187 }
188 }
189
Justin Klaassen2696d992016-07-11 21:26:46 -0700190 private static boolean isUserSetupCompleted(ContentResolver cr, int userHandle) {
191 return Secure.getIntForUser(cr, Secure.USER_SETUP_COMPLETE, 0, userHandle) == 1;
192 }
193
194 private void setUp() {
Justin Klaassenec8837a2016-08-23 12:04:42 -0700195 Slog.d(TAG, "setUp: currentUser=" + mCurrentUser);
196
Christine Franks57fdde82018-07-03 14:46:07 -0700197 mNightDisplayController = new ColorDisplayController(getContext(), mCurrentUser);
198
199 // Listen for external changes to any of the settings.
200 if (mContentObserver == null) {
201 mContentObserver = new ContentObserver(new Handler(DisplayThread.get().getLooper())) {
202 @Override
203 public void onChange(boolean selfChange, Uri uri) {
204 super.onChange(selfChange, uri);
205
206 final String setting = uri == null ? null : uri.getLastPathSegment();
207 if (setting != null) {
208 switch (setting) {
209 case Secure.NIGHT_DISPLAY_ACTIVATED:
210 onNightDisplayActivated(mNightDisplayController.isActivated());
211 break;
212 case Secure.NIGHT_DISPLAY_COLOR_TEMPERATURE:
213 onNightDisplayColorTemperatureChanged(
214 mNightDisplayController.getColorTemperature());
215 break;
216 case Secure.NIGHT_DISPLAY_AUTO_MODE:
217 onNightDisplayAutoModeChanged(
218 mNightDisplayController.getAutoMode());
219 break;
220 case Secure.NIGHT_DISPLAY_CUSTOM_START_TIME:
221 onNightDisplayCustomStartTimeChanged(
222 mNightDisplayController.getCustomStartTime());
223 break;
224 case Secure.NIGHT_DISPLAY_CUSTOM_END_TIME:
225 onNightDisplayCustomEndTimeChanged(
226 mNightDisplayController.getCustomEndTime());
227 break;
228 case System.DISPLAY_COLOR_MODE:
229 onDisplayColorModeChanged(mNightDisplayController.getColorMode());
230 break;
231 case Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED:
232 case Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED:
233 onAccessibilityTransformChanged();
234 break;
235 }
236 }
237 }
238 };
239 }
240 final ContentResolver cr = getContext().getContentResolver();
241 cr.registerContentObserver(Secure.getUriFor(Secure.NIGHT_DISPLAY_ACTIVATED),
242 false /* notifyForDescendants */, mContentObserver, mCurrentUser);
243 cr.registerContentObserver(Secure.getUriFor(Secure.NIGHT_DISPLAY_COLOR_TEMPERATURE),
244 false /* notifyForDescendants */, mContentObserver, mCurrentUser);
245 cr.registerContentObserver(Secure.getUriFor(Secure.NIGHT_DISPLAY_AUTO_MODE),
246 false /* notifyForDescendants */, mContentObserver, mCurrentUser);
247 cr.registerContentObserver(Secure.getUriFor(Secure.NIGHT_DISPLAY_CUSTOM_START_TIME),
248 false /* notifyForDescendants */, mContentObserver, mCurrentUser);
249 cr.registerContentObserver(Secure.getUriFor(Secure.NIGHT_DISPLAY_CUSTOM_END_TIME),
250 false /* notifyForDescendants */, mContentObserver, mCurrentUser);
251 cr.registerContentObserver(System.getUriFor(System.DISPLAY_COLOR_MODE),
252 false /* notifyForDescendants */, mContentObserver, mCurrentUser);
253 cr.registerContentObserver(
254 Secure.getUriFor(Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED),
255 false /* notifyForDescendants */, mContentObserver, mCurrentUser);
256 cr.registerContentObserver(
257 Secure.getUriFor(Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED),
258 false /* notifyForDescendants */, mContentObserver, mCurrentUser);
Justin Klaassen911e8892016-06-21 18:24:24 -0700259
Christine Frankscf388c22018-05-15 15:48:10 -0700260 // Set the color mode, if valid, and immediately apply the updated tint matrix based on the
261 // existing activated state. This ensures consistency of tint across the color mode change.
Christine Franks57fdde82018-07-03 14:46:07 -0700262 onDisplayColorModeChanged(mNightDisplayController.getColorMode());
Christine Frankscf388c22018-05-15 15:48:10 -0700263
264 // Reset the activated state.
Christine Franks57fdde82018-07-03 14:46:07 -0700265 mIsNightDisplayActivated = null;
Christine Frankscf388c22018-05-15 15:48:10 -0700266
Chia-I Wu9ec9ea72018-05-07 13:17:04 -0700267 setCoefficientMatrix(getContext(), DisplayTransformManager.needsLinearColorMatrix());
Christine Franks8ad71492017-10-24 19:04:22 -0700268
Christine Franks6418d0b2017-02-13 09:48:00 -0800269 // Prepare color transformation matrix.
Christine Franks57fdde82018-07-03 14:46:07 -0700270 setMatrix(mNightDisplayController.getColorTemperature(), mMatrixNight);
Christine Franks6418d0b2017-02-13 09:48:00 -0800271
Justin Klaassen911e8892016-06-21 18:24:24 -0700272 // Initialize the current auto mode.
Christine Franks57fdde82018-07-03 14:46:07 -0700273 onNightDisplayAutoModeChanged(mNightDisplayController.getAutoMode());
Justin Klaassen911e8892016-06-21 18:24:24 -0700274
275 // Force the initialization current activated state.
Christine Franks57fdde82018-07-03 14:46:07 -0700276 if (mIsNightDisplayActivated == null) {
277 onNightDisplayActivated(mNightDisplayController.isActivated());
Justin Klaassen911e8892016-06-21 18:24:24 -0700278 }
279 }
280
Justin Klaassen2696d992016-07-11 21:26:46 -0700281 private void tearDown() {
Justin Klaassenec8837a2016-08-23 12:04:42 -0700282 Slog.d(TAG, "tearDown: currentUser=" + mCurrentUser);
283
Christine Franks57fdde82018-07-03 14:46:07 -0700284 getContext().getContentResolver().unregisterContentObserver(mContentObserver);
285
286 if (mNightDisplayController != null) {
287 mNightDisplayController = null;
Justin Klaassen2696d992016-07-11 21:26:46 -0700288 }
Justin Klaassen911e8892016-06-21 18:24:24 -0700289
Christine Franks57fdde82018-07-03 14:46:07 -0700290 if (mNightDisplayAutoMode != null) {
291 mNightDisplayAutoMode.onStop();
292 mNightDisplayAutoMode = null;
Justin Klaassen911e8892016-06-21 18:24:24 -0700293 }
294
Justin Klaassen639214e2016-07-14 21:00:06 -0700295 if (mColorMatrixAnimator != null) {
296 mColorMatrixAnimator.end();
297 mColorMatrixAnimator = null;
298 }
Justin Klaassen911e8892016-06-21 18:24:24 -0700299 }
300
Christine Franks57fdde82018-07-03 14:46:07 -0700301 private void onNightDisplayActivated(boolean activated) {
302 if (mIsNightDisplayActivated == null || mIsNightDisplayActivated != activated) {
Justin Klaassen911e8892016-06-21 18:24:24 -0700303 Slog.i(TAG, activated ? "Turning on night display" : "Turning off night display");
304
Christine Franks57fdde82018-07-03 14:46:07 -0700305 mIsNightDisplayActivated = activated;
Justin Klaassen908b86c2016-08-08 09:18:42 -0700306
Christine Franks57fdde82018-07-03 14:46:07 -0700307 if (mNightDisplayAutoMode != null) {
308 mNightDisplayAutoMode.onActivated(activated);
Christine Frankse5bb03e2017-02-10 17:36:10 -0800309 }
310
Christine Franks6418d0b2017-02-13 09:48:00 -0800311 applyTint(false);
Justin Klaassen911e8892016-06-21 18:24:24 -0700312 }
313 }
314
Christine Franks57fdde82018-07-03 14:46:07 -0700315 private void onNightDisplayAutoModeChanged(int autoMode) {
316 Slog.d(TAG, "onNightDisplayAutoModeChanged: autoMode=" + autoMode);
Justin Klaassenec8837a2016-08-23 12:04:42 -0700317
Christine Franks57fdde82018-07-03 14:46:07 -0700318 if (mNightDisplayAutoMode != null) {
319 mNightDisplayAutoMode.onStop();
320 mNightDisplayAutoMode = null;
Justin Klaassen911e8892016-06-21 18:24:24 -0700321 }
322
Christine Franks5397f032017-11-01 18:35:16 -0700323 if (autoMode == ColorDisplayController.AUTO_MODE_CUSTOM) {
Christine Franks57fdde82018-07-03 14:46:07 -0700324 mNightDisplayAutoMode = new CustomNightDisplayAutoMode();
Christine Franks5397f032017-11-01 18:35:16 -0700325 } else if (autoMode == ColorDisplayController.AUTO_MODE_TWILIGHT) {
Christine Franks57fdde82018-07-03 14:46:07 -0700326 mNightDisplayAutoMode = new TwilightNightDisplayAutoMode();
Justin Klaassen911e8892016-06-21 18:24:24 -0700327 }
328
Christine Franks57fdde82018-07-03 14:46:07 -0700329 if (mNightDisplayAutoMode != null) {
330 mNightDisplayAutoMode.onStart();
Justin Klaassen911e8892016-06-21 18:24:24 -0700331 }
332 }
333
Christine Franks57fdde82018-07-03 14:46:07 -0700334 private void onNightDisplayCustomStartTimeChanged(LocalTime startTime) {
335 Slog.d(TAG, "onNightDisplayCustomStartTimeChanged: startTime=" + startTime);
Justin Klaassenec8837a2016-08-23 12:04:42 -0700336
Christine Franks57fdde82018-07-03 14:46:07 -0700337 if (mNightDisplayAutoMode != null) {
338 mNightDisplayAutoMode.onCustomStartTimeChanged(startTime);
Justin Klaassen911e8892016-06-21 18:24:24 -0700339 }
340 }
341
Christine Franks57fdde82018-07-03 14:46:07 -0700342 private void onNightDisplayCustomEndTimeChanged(LocalTime endTime) {
343 Slog.d(TAG, "onNightDisplayCustomEndTimeChanged: endTime=" + endTime);
Justin Klaassenec8837a2016-08-23 12:04:42 -0700344
Christine Franks57fdde82018-07-03 14:46:07 -0700345 if (mNightDisplayAutoMode != null) {
346 mNightDisplayAutoMode.onCustomEndTimeChanged(endTime);
Justin Klaassen911e8892016-06-21 18:24:24 -0700347 }
348 }
349
Christine Franks57fdde82018-07-03 14:46:07 -0700350 private void onNightDisplayColorTemperatureChanged(int colorTemperature) {
Christine Franks6418d0b2017-02-13 09:48:00 -0800351 setMatrix(colorTemperature, mMatrixNight);
352 applyTint(true);
353 }
354
Christine Franks57fdde82018-07-03 14:46:07 -0700355 private void onDisplayColorModeChanged(int mode) {
Christine Frankscf388c22018-05-15 15:48:10 -0700356 if (mode == -1) {
357 return;
358 }
359
Christine Franks218e6562017-11-27 10:20:14 -0800360 // Cancel the night display tint animator if it's running.
361 if (mColorMatrixAnimator != null) {
362 mColorMatrixAnimator.cancel();
363 }
Christine Franks8ad71492017-10-24 19:04:22 -0700364
Chia-I Wu9ec9ea72018-05-07 13:17:04 -0700365 setCoefficientMatrix(getContext(), DisplayTransformManager.needsLinearColorMatrix(mode));
Christine Franks57fdde82018-07-03 14:46:07 -0700366 setMatrix(mNightDisplayController.getColorTemperature(), mMatrixNight);
Christine Franks218e6562017-11-27 10:20:14 -0800367
368 final DisplayTransformManager dtm = getLocalService(DisplayTransformManager.class);
Christine Franks57fdde82018-07-03 14:46:07 -0700369 dtm.setColorMode(mode, (mIsNightDisplayActivated != null && mIsNightDisplayActivated)
370 ? mMatrixNight : MATRIX_IDENTITY);
Christine Franks8ad71492017-10-24 19:04:22 -0700371 }
372
Christine Franks57fdde82018-07-03 14:46:07 -0700373 private void onAccessibilityTransformChanged() {
374 onDisplayColorModeChanged(mNightDisplayController.getColorMode());
Daniel Solomon317a3572018-03-30 18:36:37 -0700375 }
376
Christine Franks218e6562017-11-27 10:20:14 -0800377 /**
Chia-I Wu9ec9ea72018-05-07 13:17:04 -0700378 * Set coefficients based on whether the color matrix is linear or not.
Christine Franks218e6562017-11-27 10:20:14 -0800379 */
Chia-I Wu9ec9ea72018-05-07 13:17:04 -0700380 private void setCoefficientMatrix(Context context, boolean needsLinear) {
381 final String[] coefficients = context.getResources().getStringArray(needsLinear
382 ? R.array.config_nightDisplayColorTemperatureCoefficients
383 : R.array.config_nightDisplayColorTemperatureCoefficientsNative);
Christine Franks8ad71492017-10-24 19:04:22 -0700384 for (int i = 0; i < 9 && i < coefficients.length; i++) {
385 mColorTempCoefficients[i] = Float.parseFloat(coefficients[i]);
386 }
387 }
388
Christine Franks6418d0b2017-02-13 09:48:00 -0800389 /**
390 * Applies current color temperature matrix, or removes it if deactivated.
391 *
392 * @param immediate {@code true} skips transition animation
393 */
394 private void applyTint(boolean immediate) {
395 // Cancel the old animator if still running.
396 if (mColorMatrixAnimator != null) {
397 mColorMatrixAnimator.cancel();
398 }
399
Christine Franks6418d0b2017-02-13 09:48:00 -0800400 final DisplayTransformManager dtm = getLocalService(DisplayTransformManager.class);
401 final float[] from = dtm.getColorMatrix(LEVEL_COLOR_MATRIX_NIGHT_DISPLAY);
Christine Franks57fdde82018-07-03 14:46:07 -0700402 final float[] to = mIsNightDisplayActivated ? mMatrixNight : MATRIX_IDENTITY;
Christine Franks6418d0b2017-02-13 09:48:00 -0800403
404 if (immediate) {
405 dtm.setColorMatrix(LEVEL_COLOR_MATRIX_NIGHT_DISPLAY, to);
406 } else {
407 mColorMatrixAnimator = ValueAnimator.ofObject(COLOR_MATRIX_EVALUATOR,
408 from == null ? MATRIX_IDENTITY : from, to);
409 mColorMatrixAnimator.setDuration(TRANSITION_DURATION);
410 mColorMatrixAnimator.setInterpolator(AnimationUtils.loadInterpolator(
411 getContext(), android.R.interpolator.fast_out_slow_in));
412 mColorMatrixAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
413 @Override
414 public void onAnimationUpdate(ValueAnimator animator) {
415 final float[] value = (float[]) animator.getAnimatedValue();
416 dtm.setColorMatrix(LEVEL_COLOR_MATRIX_NIGHT_DISPLAY, value);
417 }
418 });
419 mColorMatrixAnimator.addListener(new AnimatorListenerAdapter() {
420
421 private boolean mIsCancelled;
422
423 @Override
424 public void onAnimationCancel(Animator animator) {
425 mIsCancelled = true;
426 }
427
428 @Override
429 public void onAnimationEnd(Animator animator) {
430 if (!mIsCancelled) {
431 // Ensure final color matrix is set at the end of the animation. If the
432 // animation is cancelled then don't set the final color matrix so the new
433 // animator can pick up from where this one left off.
434 dtm.setColorMatrix(LEVEL_COLOR_MATRIX_NIGHT_DISPLAY, to);
435 }
436 mColorMatrixAnimator = null;
437 }
438 });
439 mColorMatrixAnimator.start();
440 }
441 }
442
443 /**
444 * Set the color transformation {@code MATRIX_NIGHT} to the given color temperature.
445 *
446 * @param colorTemperature color temperature in Kelvin
Christine Franks57fdde82018-07-03 14:46:07 -0700447 * @param outTemp the 4x4 display transformation matrix for that color temperature
Christine Franks6418d0b2017-02-13 09:48:00 -0800448 */
449 private void setMatrix(int colorTemperature, float[] outTemp) {
450 if (outTemp.length != 16) {
451 Slog.d(TAG, "The display transformation matrix must be 4x4");
452 return;
453 }
454
455 Matrix.setIdentityM(mMatrixNight, 0);
456
457 final float squareTemperature = colorTemperature * colorTemperature;
458 final float red = squareTemperature * mColorTempCoefficients[0]
Christine Franks2a125472017-07-26 17:51:16 -0700459 + colorTemperature * mColorTempCoefficients[1] + mColorTempCoefficients[2];
460 final float green = squareTemperature * mColorTempCoefficients[3]
461 + colorTemperature * mColorTempCoefficients[4] + mColorTempCoefficients[5];
462 final float blue = squareTemperature * mColorTempCoefficients[6]
463 + colorTemperature * mColorTempCoefficients[7] + mColorTempCoefficients[8];
Christine Franks6418d0b2017-02-13 09:48:00 -0800464 outTemp[0] = red;
465 outTemp[5] = green;
466 outTemp[10] = blue;
467 }
468
Christine Franks03213462017-08-25 13:57:26 -0700469 /**
Christine Franks39b03112018-07-03 14:46:07 -0700470 * Returns the first date time corresponding to the local time that occurs before the provided
471 * date time.
Christine Franks03213462017-08-25 13:57:26 -0700472 *
473 * @param compareTime the LocalDateTime to compare against
474 * @return the prior LocalDateTime corresponding to this local time
475 */
Christine Franks57fdde82018-07-03 14:46:07 -0700476 @VisibleForTesting
477 static LocalDateTime getDateTimeBefore(LocalTime localTime, LocalDateTime compareTime) {
Christine Franks03213462017-08-25 13:57:26 -0700478 final LocalDateTime ldt = LocalDateTime.of(compareTime.getYear(), compareTime.getMonth(),
479 compareTime.getDayOfMonth(), localTime.getHour(), localTime.getMinute());
480
481 // Check if the local time has passed, if so return the same time yesterday.
482 return ldt.isAfter(compareTime) ? ldt.minusDays(1) : ldt;
483 }
484
485 /**
Christine Franks39b03112018-07-03 14:46:07 -0700486 * Returns the first date time corresponding to this local time that occurs after the provided
487 * date time.
Christine Franks03213462017-08-25 13:57:26 -0700488 *
489 * @param compareTime the LocalDateTime to compare against
490 * @return the next LocalDateTime corresponding to this local time
491 */
Christine Franks57fdde82018-07-03 14:46:07 -0700492 @VisibleForTesting
493 static LocalDateTime getDateTimeAfter(LocalTime localTime, LocalDateTime compareTime) {
Christine Franks03213462017-08-25 13:57:26 -0700494 final LocalDateTime ldt = LocalDateTime.of(compareTime.getYear(), compareTime.getMonth(),
495 compareTime.getDayOfMonth(), localTime.getHour(), localTime.getMinute());
496
497 // Check if the local time has passed, if so return the same time tomorrow.
498 return ldt.isBefore(compareTime) ? ldt.plusDays(1) : ldt;
499 }
500
Christine Franks39b03112018-07-03 14:46:07 -0700501 private boolean isDeviceColorManagedInternal() {
502 final DisplayTransformManager dtm = getLocalService(DisplayTransformManager.class);
503 return dtm.isDeviceColorManaged();
504 }
505
Christine Franks57fdde82018-07-03 14:46:07 -0700506 /**
507 * Returns the last time the night display transform activation state was changed, or {@link
508 * LocalDateTime#MIN} if night display has never been activated.
509 */
510 private @NonNull LocalDateTime getNightDisplayLastActivatedTimeSetting() {
511 final ContentResolver cr = getContext().getContentResolver();
512 final String lastActivatedTime = Secure.getStringForUser(
513 cr, Secure.NIGHT_DISPLAY_LAST_ACTIVATED_TIME, getContext().getUserId());
514 if (lastActivatedTime != null) {
515 try {
516 return LocalDateTime.parse(lastActivatedTime);
517 } catch (DateTimeParseException ignored) {
518 }
519 // Uses the old epoch time.
520 try {
521 return LocalDateTime.ofInstant(
522 Instant.ofEpochMilli(Long.parseLong(lastActivatedTime)),
523 ZoneId.systemDefault());
524 } catch (DateTimeException | NumberFormatException ignored) {
525 }
526 }
527 return LocalDateTime.MIN;
528 }
529
530 private abstract class NightDisplayAutoMode {
531
532 public abstract void onActivated(boolean activated);
533
Justin Klaassen911e8892016-06-21 18:24:24 -0700534 public abstract void onStart();
Christine Frankse5bb03e2017-02-10 17:36:10 -0800535
Justin Klaassen911e8892016-06-21 18:24:24 -0700536 public abstract void onStop();
Christine Franks57fdde82018-07-03 14:46:07 -0700537
538 public void onCustomStartTimeChanged(LocalTime startTime) {
539 }
540
541 public void onCustomEndTimeChanged(LocalTime endTime) {
542 }
Justin Klaassen911e8892016-06-21 18:24:24 -0700543 }
544
Christine Franks57fdde82018-07-03 14:46:07 -0700545 private final class CustomNightDisplayAutoMode extends NightDisplayAutoMode implements
546 AlarmManager.OnAlarmListener {
Justin Klaassen911e8892016-06-21 18:24:24 -0700547
548 private final AlarmManager mAlarmManager;
549 private final BroadcastReceiver mTimeChangedReceiver;
550
Christine Franks03213462017-08-25 13:57:26 -0700551 private LocalTime mStartTime;
552 private LocalTime mEndTime;
Justin Klaassen911e8892016-06-21 18:24:24 -0700553
Christine Franks03213462017-08-25 13:57:26 -0700554 private LocalDateTime mLastActivatedTime;
Justin Klaassen911e8892016-06-21 18:24:24 -0700555
Christine Franks57fdde82018-07-03 14:46:07 -0700556 CustomNightDisplayAutoMode() {
Justin Klaassen911e8892016-06-21 18:24:24 -0700557 mAlarmManager = (AlarmManager) getContext().getSystemService(Context.ALARM_SERVICE);
558 mTimeChangedReceiver = new BroadcastReceiver() {
559 @Override
560 public void onReceive(Context context, Intent intent) {
561 updateActivated();
562 }
563 };
564 }
565
566 private void updateActivated() {
Christine Franks03213462017-08-25 13:57:26 -0700567 final LocalDateTime now = LocalDateTime.now();
568 final LocalDateTime start = getDateTimeBefore(mStartTime, now);
569 final LocalDateTime end = getDateTimeAfter(mEndTime, start);
570 boolean activate = now.isBefore(end);
Justin Klaassen911e8892016-06-21 18:24:24 -0700571
Christine Frankse5bb03e2017-02-10 17:36:10 -0800572 if (mLastActivatedTime != null) {
Christine Frankse5bb03e2017-02-10 17:36:10 -0800573 // Maintain the existing activated state if within the current period.
Christine Franks03213462017-08-25 13:57:26 -0700574 if (mLastActivatedTime.isBefore(now) && mLastActivatedTime.isAfter(start)
575 && (mLastActivatedTime.isAfter(end) || now.isBefore(end))) {
Christine Franks57fdde82018-07-03 14:46:07 -0700576 activate = mNightDisplayController.isActivated();
Justin Klaassen911e8892016-06-21 18:24:24 -0700577 }
578 }
579
Christine Franks57fdde82018-07-03 14:46:07 -0700580 if (mIsNightDisplayActivated == null || mIsNightDisplayActivated != activate) {
581 mNightDisplayController.setActivated(activate);
Justin Klaassen911e8892016-06-21 18:24:24 -0700582 }
Christine Franks03213462017-08-25 13:57:26 -0700583
Christine Franks57fdde82018-07-03 14:46:07 -0700584 updateNextAlarm(mIsNightDisplayActivated, now);
Justin Klaassen911e8892016-06-21 18:24:24 -0700585 }
586
Christine Franks03213462017-08-25 13:57:26 -0700587 private void updateNextAlarm(@Nullable Boolean activated, @NonNull LocalDateTime now) {
Justin Klaassen4346f632016-08-08 15:01:47 -0700588 if (activated != null) {
Christine Franks03213462017-08-25 13:57:26 -0700589 final LocalDateTime next = activated ? getDateTimeAfter(mEndTime, now)
590 : getDateTimeAfter(mStartTime, now);
591 final long millis = next.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli();
592 mAlarmManager.setExact(AlarmManager.RTC, millis, TAG, this, null);
Justin Klaassen911e8892016-06-21 18:24:24 -0700593 }
594 }
595
596 @Override
597 public void onStart() {
598 final IntentFilter intentFilter = new IntentFilter(Intent.ACTION_TIME_CHANGED);
599 intentFilter.addAction(Intent.ACTION_TIMEZONE_CHANGED);
600 getContext().registerReceiver(mTimeChangedReceiver, intentFilter);
601
Christine Franks57fdde82018-07-03 14:46:07 -0700602 mStartTime = mNightDisplayController.getCustomStartTime();
603 mEndTime = mNightDisplayController.getCustomEndTime();
Justin Klaassen911e8892016-06-21 18:24:24 -0700604
Christine Franks57fdde82018-07-03 14:46:07 -0700605 mLastActivatedTime = getNightDisplayLastActivatedTimeSetting();
Christine Frankse5bb03e2017-02-10 17:36:10 -0800606
Justin Klaassen911e8892016-06-21 18:24:24 -0700607 // Force an update to initialize state.
608 updateActivated();
609 }
610
611 @Override
612 public void onStop() {
613 getContext().unregisterReceiver(mTimeChangedReceiver);
614
615 mAlarmManager.cancel(this);
616 mLastActivatedTime = null;
617 }
618
619 @Override
620 public void onActivated(boolean activated) {
Christine Franks57fdde82018-07-03 14:46:07 -0700621 mLastActivatedTime = getNightDisplayLastActivatedTimeSetting();
Christine Franks03213462017-08-25 13:57:26 -0700622 updateNextAlarm(activated, LocalDateTime.now());
Justin Klaassen911e8892016-06-21 18:24:24 -0700623 }
624
625 @Override
Christine Franks03213462017-08-25 13:57:26 -0700626 public void onCustomStartTimeChanged(LocalTime startTime) {
Justin Klaassen911e8892016-06-21 18:24:24 -0700627 mStartTime = startTime;
628 mLastActivatedTime = null;
629 updateActivated();
630 }
631
632 @Override
Christine Franks03213462017-08-25 13:57:26 -0700633 public void onCustomEndTimeChanged(LocalTime endTime) {
Justin Klaassen911e8892016-06-21 18:24:24 -0700634 mEndTime = endTime;
635 mLastActivatedTime = null;
636 updateActivated();
637 }
638
639 @Override
640 public void onAlarm() {
Justin Klaassenec8837a2016-08-23 12:04:42 -0700641 Slog.d(TAG, "onAlarm");
Justin Klaassen911e8892016-06-21 18:24:24 -0700642 updateActivated();
643 }
644 }
645
Christine Franks57fdde82018-07-03 14:46:07 -0700646 private final class TwilightNightDisplayAutoMode extends NightDisplayAutoMode implements
647 TwilightListener {
Justin Klaassen911e8892016-06-21 18:24:24 -0700648
649 private final TwilightManager mTwilightManager;
Christine Franks57fdde82018-07-03 14:46:07 -0700650 private LocalDateTime mLastActivatedTime;
Justin Klaassen911e8892016-06-21 18:24:24 -0700651
Christine Franks57fdde82018-07-03 14:46:07 -0700652 TwilightNightDisplayAutoMode() {
Justin Klaassen911e8892016-06-21 18:24:24 -0700653 mTwilightManager = getLocalService(TwilightManager.class);
Justin Klaassen911e8892016-06-21 18:24:24 -0700654 }
655
Justin Klaassen908b86c2016-08-08 09:18:42 -0700656 private void updateActivated(TwilightState state) {
Justin Klaassen3da4c442017-05-05 15:19:33 -0700657 if (state == null) {
658 // If there isn't a valid TwilightState then just keep the current activated
659 // state.
660 return;
661 }
662
663 boolean activate = state.isNight();
Christine Franks57fdde82018-07-03 14:46:07 -0700664 if (mLastActivatedTime != null) {
Christine Franks03213462017-08-25 13:57:26 -0700665 final LocalDateTime now = LocalDateTime.now();
666 final LocalDateTime sunrise = state.sunrise();
667 final LocalDateTime sunset = state.sunset();
Christine Frankse5bb03e2017-02-10 17:36:10 -0800668 // Maintain the existing activated state if within the current period.
Christine Franks57fdde82018-07-03 14:46:07 -0700669 if (mLastActivatedTime.isBefore(now) && (mLastActivatedTime.isBefore(sunrise)
670 ^ mLastActivatedTime.isBefore(sunset))) {
671 activate = mNightDisplayController.isActivated();
Justin Klaassen911e8892016-06-21 18:24:24 -0700672 }
673 }
Justin Klaassen908b86c2016-08-08 09:18:42 -0700674
Christine Franks57fdde82018-07-03 14:46:07 -0700675 if (mIsNightDisplayActivated == null || mIsNightDisplayActivated != activate) {
676 mNightDisplayController.setActivated(activate);
Justin Klaassen908b86c2016-08-08 09:18:42 -0700677 }
Justin Klaassen911e8892016-06-21 18:24:24 -0700678 }
679
680 @Override
Christine Franks57fdde82018-07-03 14:46:07 -0700681 public void onActivated(boolean activated) {
682 mLastActivatedTime = getNightDisplayLastActivatedTimeSetting();
683 }
684
685 @Override
Justin Klaassen911e8892016-06-21 18:24:24 -0700686 public void onStart() {
687 mTwilightManager.registerListener(this, mHandler);
Christine Franks57fdde82018-07-03 14:46:07 -0700688 mLastActivatedTime = getNightDisplayLastActivatedTimeSetting();
Justin Klaassen911e8892016-06-21 18:24:24 -0700689
690 // Force an update to initialize state.
Justin Klaassen908b86c2016-08-08 09:18:42 -0700691 updateActivated(mTwilightManager.getLastTwilightState());
Justin Klaassen911e8892016-06-21 18:24:24 -0700692 }
693
694 @Override
695 public void onStop() {
696 mTwilightManager.unregisterListener(this);
Christine Franks57fdde82018-07-03 14:46:07 -0700697 mLastActivatedTime = null;
Justin Klaassen908b86c2016-08-08 09:18:42 -0700698 }
699
700 @Override
701 public void onTwilightStateChanged(@Nullable TwilightState state) {
Justin Klaassenec8837a2016-08-23 12:04:42 -0700702 Slog.d(TAG, "onTwilightStateChanged: isNight="
703 + (state == null ? null : state.isNight()));
Justin Klaassen908b86c2016-08-08 09:18:42 -0700704 updateActivated(state);
Justin Klaassen911e8892016-06-21 18:24:24 -0700705 }
706 }
Justin Klaassen639214e2016-07-14 21:00:06 -0700707
708 /**
709 * Interpolates between two 4x4 color transform matrices (in column-major order).
710 */
711 private static class ColorMatrixEvaluator implements TypeEvaluator<float[]> {
712
713 /**
714 * Result matrix returned by {@link #evaluate(float, float[], float[])}.
715 */
716 private final float[] mResultMatrix = new float[16];
717
718 @Override
719 public float[] evaluate(float fraction, float[] startValue, float[] endValue) {
720 for (int i = 0; i < mResultMatrix.length; i++) {
721 mResultMatrix[i] = MathUtils.lerp(startValue[i], endValue[i], fraction);
722 }
723 return mResultMatrix;
724 }
725 }
Christine Franks39b03112018-07-03 14:46:07 -0700726
727 private final class BinderService extends IColorDisplayManager.Stub {
Christine Franks57fdde82018-07-03 14:46:07 -0700728
Christine Franks39b03112018-07-03 14:46:07 -0700729 @Override
730 public boolean isDeviceColorManaged() {
731 final long token = Binder.clearCallingIdentity();
732 try {
733 return isDeviceColorManagedInternal();
734 } finally {
735 Binder.restoreCallingIdentity(token);
736 }
737 }
738 }
Justin Klaassen911e8892016-06-21 18:24:24 -0700739}