blob: 521fa236cf7db2615ef5ca4889c718ad9cef0d38 [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;
Justin Klaassen639214e2016-07-14 21:00:06 -070042import android.util.MathUtils;
Justin Klaassen911e8892016-06-21 18:24:24 -070043import android.util.Slog;
Justin Klaassen639214e2016-07-14 21:00:06 -070044import android.view.animation.AnimationUtils;
Justin Klaassen911e8892016-06-21 18:24:24 -070045
Christine Franks39b03112018-07-03 14:46:07 -070046import com.android.internal.R;
Christine Franks5397f032017-11-01 18:35:16 -070047import com.android.internal.app.ColorDisplayController;
Justin Klaassen911e8892016-06-21 18:24:24 -070048import com.android.server.SystemService;
49import com.android.server.twilight.TwilightListener;
50import com.android.server.twilight.TwilightManager;
51import com.android.server.twilight.TwilightState;
52
Christine Franks03213462017-08-25 13:57:26 -070053import java.time.LocalDateTime;
54import java.time.LocalTime;
55import java.time.ZoneId;
Christine Franks8ad71492017-10-24 19:04:22 -070056
Justin Klaassen911e8892016-06-21 18:24:24 -070057/**
Christine Franks39b03112018-07-03 14:46:07 -070058 * Controls the display's color transforms.
Justin Klaassen911e8892016-06-21 18:24:24 -070059 */
Christine Franks5397f032017-11-01 18:35:16 -070060public final class ColorDisplayService extends SystemService
61 implements ColorDisplayController.Callback {
Justin Klaassen911e8892016-06-21 18:24:24 -070062
Christine Franks5397f032017-11-01 18:35:16 -070063 private static final String TAG = "ColorDisplayService";
Justin Klaassen911e8892016-06-21 18:24:24 -070064
65 /**
Christine Franks7b83b4282017-01-18 14:55:00 -080066 * The transition time, in milliseconds, for Night Display to turn on/off.
67 */
68 private static final long TRANSITION_DURATION = 3000L;
69
70 /**
Justin Klaassen639214e2016-07-14 21:00:06 -070071 * The identity matrix, used if one of the given matrices is {@code null}.
72 */
73 private static final float[] MATRIX_IDENTITY = new float[16];
74 static {
75 Matrix.setIdentityM(MATRIX_IDENTITY, 0);
76 }
77
78 /**
79 * Evaluator used to animate color matrix transitions.
80 */
81 private static final ColorMatrixEvaluator COLOR_MATRIX_EVALUATOR = new ColorMatrixEvaluator();
82
Justin Klaassen2696d992016-07-11 21:26:46 -070083 private final Handler mHandler;
84
Christine Franks6418d0b2017-02-13 09:48:00 -080085 private float[] mMatrixNight = new float[16];
86
Christine Franks2a125472017-07-26 17:51:16 -070087 private final float[] mColorTempCoefficients = new float[9];
Christine Franks6418d0b2017-02-13 09:48:00 -080088
Justin Klaassen911e8892016-06-21 18:24:24 -070089 private int mCurrentUser = UserHandle.USER_NULL;
Justin Klaassen2696d992016-07-11 21:26:46 -070090 private ContentObserver mUserSetupObserver;
Justin Klaassen911e8892016-06-21 18:24:24 -070091 private boolean mBootCompleted;
92
Christine Franks5397f032017-11-01 18:35:16 -070093 private ColorDisplayController mController;
Justin Klaassen639214e2016-07-14 21:00:06 -070094 private ValueAnimator mColorMatrixAnimator;
Justin Klaassen911e8892016-06-21 18:24:24 -070095 private Boolean mIsActivated;
96 private AutoMode mAutoMode;
97
Christine Franks5397f032017-11-01 18:35:16 -070098 public ColorDisplayService(Context context) {
Justin Klaassen911e8892016-06-21 18:24:24 -070099 super(context);
Justin Klaassen2696d992016-07-11 21:26:46 -0700100 mHandler = new Handler(Looper.getMainLooper());
Justin Klaassen911e8892016-06-21 18:24:24 -0700101 }
102
103 @Override
104 public void onStart() {
Christine Franks39b03112018-07-03 14:46:07 -0700105 publishBinderService(Context.COLOR_DISPLAY_SERVICE, new BinderService());
Justin Klaassen911e8892016-06-21 18:24:24 -0700106 }
107
108 @Override
Justin Klaassen2696d992016-07-11 21:26:46 -0700109 public void onBootPhase(int phase) {
Christine Frankse5bb03e2017-02-10 17:36:10 -0800110 if (phase >= PHASE_BOOT_COMPLETED) {
Justin Klaassen2696d992016-07-11 21:26:46 -0700111 mBootCompleted = true;
112
113 // Register listeners now that boot is complete.
114 if (mCurrentUser != UserHandle.USER_NULL && mUserSetupObserver == null) {
115 setUp();
116 }
117 }
118 }
119
120 @Override
Justin Klaassen911e8892016-06-21 18:24:24 -0700121 public void onStartUser(int userHandle) {
122 super.onStartUser(userHandle);
123
Justin Klaassen911e8892016-06-21 18:24:24 -0700124 if (mCurrentUser == UserHandle.USER_NULL) {
Justin Klaassen2696d992016-07-11 21:26:46 -0700125 onUserChanged(userHandle);
Justin Klaassen911e8892016-06-21 18:24:24 -0700126 }
127 }
128
129 @Override
130 public void onSwitchUser(int userHandle) {
131 super.onSwitchUser(userHandle);
132
Justin Klaassen2696d992016-07-11 21:26:46 -0700133 onUserChanged(userHandle);
Justin Klaassen911e8892016-06-21 18:24:24 -0700134 }
135
136 @Override
137 public void onStopUser(int userHandle) {
138 super.onStopUser(userHandle);
139
Justin Klaassen911e8892016-06-21 18:24:24 -0700140 if (mCurrentUser == userHandle) {
Justin Klaassen2696d992016-07-11 21:26:46 -0700141 onUserChanged(UserHandle.USER_NULL);
Justin Klaassen911e8892016-06-21 18:24:24 -0700142 }
143 }
144
Justin Klaassen2696d992016-07-11 21:26:46 -0700145 private void onUserChanged(int userHandle) {
146 final ContentResolver cr = getContext().getContentResolver();
Justin Klaassen911e8892016-06-21 18:24:24 -0700147
Justin Klaassen2696d992016-07-11 21:26:46 -0700148 if (mCurrentUser != UserHandle.USER_NULL) {
149 if (mUserSetupObserver != null) {
150 cr.unregisterContentObserver(mUserSetupObserver);
151 mUserSetupObserver = null;
152 } else if (mBootCompleted) {
153 tearDown();
154 }
155 }
156
157 mCurrentUser = userHandle;
158
159 if (mCurrentUser != UserHandle.USER_NULL) {
160 if (!isUserSetupCompleted(cr, mCurrentUser)) {
161 mUserSetupObserver = new ContentObserver(mHandler) {
162 @Override
163 public void onChange(boolean selfChange, Uri uri) {
164 if (isUserSetupCompleted(cr, mCurrentUser)) {
165 cr.unregisterContentObserver(this);
166 mUserSetupObserver = null;
167
168 if (mBootCompleted) {
169 setUp();
170 }
171 }
172 }
173 };
174 cr.registerContentObserver(Secure.getUriFor(Secure.USER_SETUP_COMPLETE),
Christine Franks39b03112018-07-03 14:46:07 -0700175 false /* notifyForDescendants */, mUserSetupObserver, mCurrentUser);
Justin Klaassen2696d992016-07-11 21:26:46 -0700176 } else if (mBootCompleted) {
177 setUp();
Justin Klaassen911e8892016-06-21 18:24:24 -0700178 }
179 }
180 }
181
Justin Klaassen2696d992016-07-11 21:26:46 -0700182 private static boolean isUserSetupCompleted(ContentResolver cr, int userHandle) {
183 return Secure.getIntForUser(cr, Secure.USER_SETUP_COMPLETE, 0, userHandle) == 1;
184 }
185
186 private void setUp() {
Justin Klaassenec8837a2016-08-23 12:04:42 -0700187 Slog.d(TAG, "setUp: currentUser=" + mCurrentUser);
188
Justin Klaassen911e8892016-06-21 18:24:24 -0700189 // Create a new controller for the current user and start listening for changes.
Christine Franks5397f032017-11-01 18:35:16 -0700190 mController = new ColorDisplayController(getContext(), mCurrentUser);
Justin Klaassen911e8892016-06-21 18:24:24 -0700191 mController.setListener(this);
192
Christine Frankscf388c22018-05-15 15:48:10 -0700193 // Set the color mode, if valid, and immediately apply the updated tint matrix based on the
194 // existing activated state. This ensures consistency of tint across the color mode change.
195 onDisplayColorModeChanged(mController.getColorMode());
196
197 // Reset the activated state.
198 mIsActivated = null;
199
Chia-I Wu9ec9ea72018-05-07 13:17:04 -0700200 setCoefficientMatrix(getContext(), DisplayTransformManager.needsLinearColorMatrix());
Christine Franks8ad71492017-10-24 19:04:22 -0700201
Christine Franks6418d0b2017-02-13 09:48:00 -0800202 // Prepare color transformation matrix.
203 setMatrix(mController.getColorTemperature(), mMatrixNight);
204
Justin Klaassen911e8892016-06-21 18:24:24 -0700205 // Initialize the current auto mode.
206 onAutoModeChanged(mController.getAutoMode());
207
208 // Force the initialization current activated state.
209 if (mIsActivated == null) {
210 onActivated(mController.isActivated());
211 }
212 }
213
Justin Klaassen2696d992016-07-11 21:26:46 -0700214 private void tearDown() {
Justin Klaassenec8837a2016-08-23 12:04:42 -0700215 Slog.d(TAG, "tearDown: currentUser=" + mCurrentUser);
216
Justin Klaassen2696d992016-07-11 21:26:46 -0700217 if (mController != null) {
218 mController.setListener(null);
219 mController = null;
220 }
Justin Klaassen911e8892016-06-21 18:24:24 -0700221
222 if (mAutoMode != null) {
223 mAutoMode.onStop();
224 mAutoMode = null;
225 }
226
Justin Klaassen639214e2016-07-14 21:00:06 -0700227 if (mColorMatrixAnimator != null) {
228 mColorMatrixAnimator.end();
229 mColorMatrixAnimator = null;
230 }
Justin Klaassen911e8892016-06-21 18:24:24 -0700231 }
232
233 @Override
234 public void onActivated(boolean activated) {
235 if (mIsActivated == null || mIsActivated != activated) {
236 Slog.i(TAG, activated ? "Turning on night display" : "Turning off night display");
237
Justin Klaassen908b86c2016-08-08 09:18:42 -0700238 mIsActivated = activated;
239
Christine Frankse5bb03e2017-02-10 17:36:10 -0800240 if (mAutoMode != null) {
241 mAutoMode.onActivated(activated);
242 }
243
Christine Franks6418d0b2017-02-13 09:48:00 -0800244 applyTint(false);
Justin Klaassen911e8892016-06-21 18:24:24 -0700245 }
246 }
247
248 @Override
249 public void onAutoModeChanged(int autoMode) {
Justin Klaassenec8837a2016-08-23 12:04:42 -0700250 Slog.d(TAG, "onAutoModeChanged: autoMode=" + autoMode);
251
Justin Klaassen911e8892016-06-21 18:24:24 -0700252 if (mAutoMode != null) {
253 mAutoMode.onStop();
254 mAutoMode = null;
255 }
256
Christine Franks5397f032017-11-01 18:35:16 -0700257 if (autoMode == ColorDisplayController.AUTO_MODE_CUSTOM) {
Justin Klaassen911e8892016-06-21 18:24:24 -0700258 mAutoMode = new CustomAutoMode();
Christine Franks5397f032017-11-01 18:35:16 -0700259 } else if (autoMode == ColorDisplayController.AUTO_MODE_TWILIGHT) {
Justin Klaassen911e8892016-06-21 18:24:24 -0700260 mAutoMode = new TwilightAutoMode();
261 }
262
263 if (mAutoMode != null) {
264 mAutoMode.onStart();
265 }
266 }
267
268 @Override
Christine Franks03213462017-08-25 13:57:26 -0700269 public void onCustomStartTimeChanged(LocalTime startTime) {
Justin Klaassenec8837a2016-08-23 12:04:42 -0700270 Slog.d(TAG, "onCustomStartTimeChanged: startTime=" + startTime);
271
Justin Klaassen911e8892016-06-21 18:24:24 -0700272 if (mAutoMode != null) {
273 mAutoMode.onCustomStartTimeChanged(startTime);
274 }
275 }
276
277 @Override
Christine Franks03213462017-08-25 13:57:26 -0700278 public void onCustomEndTimeChanged(LocalTime endTime) {
Justin Klaassenec8837a2016-08-23 12:04:42 -0700279 Slog.d(TAG, "onCustomEndTimeChanged: endTime=" + endTime);
280
Justin Klaassen911e8892016-06-21 18:24:24 -0700281 if (mAutoMode != null) {
282 mAutoMode.onCustomEndTimeChanged(endTime);
283 }
284 }
285
Christine Franks6418d0b2017-02-13 09:48:00 -0800286 @Override
287 public void onColorTemperatureChanged(int colorTemperature) {
288 setMatrix(colorTemperature, mMatrixNight);
289 applyTint(true);
290 }
291
Christine Franks8ad71492017-10-24 19:04:22 -0700292 @Override
Christine Franks218e6562017-11-27 10:20:14 -0800293 public void onDisplayColorModeChanged(int mode) {
Christine Frankscf388c22018-05-15 15:48:10 -0700294 if (mode == -1) {
295 return;
296 }
297
Christine Franks218e6562017-11-27 10:20:14 -0800298 // Cancel the night display tint animator if it's running.
299 if (mColorMatrixAnimator != null) {
300 mColorMatrixAnimator.cancel();
301 }
Christine Franks8ad71492017-10-24 19:04:22 -0700302
Chia-I Wu9ec9ea72018-05-07 13:17:04 -0700303 setCoefficientMatrix(getContext(), DisplayTransformManager.needsLinearColorMatrix(mode));
Christine Franks8ad71492017-10-24 19:04:22 -0700304 setMatrix(mController.getColorTemperature(), mMatrixNight);
Christine Franks218e6562017-11-27 10:20:14 -0800305
306 final DisplayTransformManager dtm = getLocalService(DisplayTransformManager.class);
Christine Frankscf388c22018-05-15 15:48:10 -0700307 dtm.setColorMode(mode, (mIsActivated != null && mIsActivated) ? mMatrixNight
308 : MATRIX_IDENTITY);
Christine Franks8ad71492017-10-24 19:04:22 -0700309 }
310
Daniel Solomon317a3572018-03-30 18:36:37 -0700311 @Override
312 public void onAccessibilityTransformChanged(boolean state) {
313 onDisplayColorModeChanged(mController.getColorMode());
314 }
315
Christine Franks218e6562017-11-27 10:20:14 -0800316 /**
Chia-I Wu9ec9ea72018-05-07 13:17:04 -0700317 * Set coefficients based on whether the color matrix is linear or not.
Christine Franks218e6562017-11-27 10:20:14 -0800318 */
Chia-I Wu9ec9ea72018-05-07 13:17:04 -0700319 private void setCoefficientMatrix(Context context, boolean needsLinear) {
320 final String[] coefficients = context.getResources().getStringArray(needsLinear
321 ? R.array.config_nightDisplayColorTemperatureCoefficients
322 : R.array.config_nightDisplayColorTemperatureCoefficientsNative);
Christine Franks8ad71492017-10-24 19:04:22 -0700323 for (int i = 0; i < 9 && i < coefficients.length; i++) {
324 mColorTempCoefficients[i] = Float.parseFloat(coefficients[i]);
325 }
326 }
327
Christine Franks6418d0b2017-02-13 09:48:00 -0800328 /**
329 * Applies current color temperature matrix, or removes it if deactivated.
330 *
331 * @param immediate {@code true} skips transition animation
332 */
333 private void applyTint(boolean immediate) {
334 // Cancel the old animator if still running.
335 if (mColorMatrixAnimator != null) {
336 mColorMatrixAnimator.cancel();
337 }
338
Christine Franks6418d0b2017-02-13 09:48:00 -0800339 final DisplayTransformManager dtm = getLocalService(DisplayTransformManager.class);
340 final float[] from = dtm.getColorMatrix(LEVEL_COLOR_MATRIX_NIGHT_DISPLAY);
341 final float[] to = mIsActivated ? mMatrixNight : MATRIX_IDENTITY;
342
343 if (immediate) {
344 dtm.setColorMatrix(LEVEL_COLOR_MATRIX_NIGHT_DISPLAY, to);
345 } else {
346 mColorMatrixAnimator = ValueAnimator.ofObject(COLOR_MATRIX_EVALUATOR,
347 from == null ? MATRIX_IDENTITY : from, to);
348 mColorMatrixAnimator.setDuration(TRANSITION_DURATION);
349 mColorMatrixAnimator.setInterpolator(AnimationUtils.loadInterpolator(
350 getContext(), android.R.interpolator.fast_out_slow_in));
351 mColorMatrixAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
352 @Override
353 public void onAnimationUpdate(ValueAnimator animator) {
354 final float[] value = (float[]) animator.getAnimatedValue();
355 dtm.setColorMatrix(LEVEL_COLOR_MATRIX_NIGHT_DISPLAY, value);
356 }
357 });
358 mColorMatrixAnimator.addListener(new AnimatorListenerAdapter() {
359
360 private boolean mIsCancelled;
361
362 @Override
363 public void onAnimationCancel(Animator animator) {
364 mIsCancelled = true;
365 }
366
367 @Override
368 public void onAnimationEnd(Animator animator) {
369 if (!mIsCancelled) {
370 // Ensure final color matrix is set at the end of the animation. If the
371 // animation is cancelled then don't set the final color matrix so the new
372 // animator can pick up from where this one left off.
373 dtm.setColorMatrix(LEVEL_COLOR_MATRIX_NIGHT_DISPLAY, to);
374 }
375 mColorMatrixAnimator = null;
376 }
377 });
378 mColorMatrixAnimator.start();
379 }
380 }
381
382 /**
383 * Set the color transformation {@code MATRIX_NIGHT} to the given color temperature.
384 *
385 * @param colorTemperature color temperature in Kelvin
Christine Frankse5bb03e2017-02-10 17:36:10 -0800386 * @param outTemp the 4x4 display transformation matrix for that color temperature
Christine Franks6418d0b2017-02-13 09:48:00 -0800387 */
388 private void setMatrix(int colorTemperature, float[] outTemp) {
389 if (outTemp.length != 16) {
390 Slog.d(TAG, "The display transformation matrix must be 4x4");
391 return;
392 }
393
394 Matrix.setIdentityM(mMatrixNight, 0);
395
396 final float squareTemperature = colorTemperature * colorTemperature;
397 final float red = squareTemperature * mColorTempCoefficients[0]
Christine Franks2a125472017-07-26 17:51:16 -0700398 + colorTemperature * mColorTempCoefficients[1] + mColorTempCoefficients[2];
399 final float green = squareTemperature * mColorTempCoefficients[3]
400 + colorTemperature * mColorTempCoefficients[4] + mColorTempCoefficients[5];
401 final float blue = squareTemperature * mColorTempCoefficients[6]
402 + colorTemperature * mColorTempCoefficients[7] + mColorTempCoefficients[8];
Christine Franks6418d0b2017-02-13 09:48:00 -0800403 outTemp[0] = red;
404 outTemp[5] = green;
405 outTemp[10] = blue;
406 }
407
Christine Franks03213462017-08-25 13:57:26 -0700408 /**
Christine Franks39b03112018-07-03 14:46:07 -0700409 * Returns the first date time corresponding to the local time that occurs before the provided
410 * date time.
Christine Franks03213462017-08-25 13:57:26 -0700411 *
412 * @param compareTime the LocalDateTime to compare against
413 * @return the prior LocalDateTime corresponding to this local time
414 */
415 public static LocalDateTime getDateTimeBefore(LocalTime localTime, LocalDateTime compareTime) {
416 final LocalDateTime ldt = LocalDateTime.of(compareTime.getYear(), compareTime.getMonth(),
417 compareTime.getDayOfMonth(), localTime.getHour(), localTime.getMinute());
418
419 // Check if the local time has passed, if so return the same time yesterday.
420 return ldt.isAfter(compareTime) ? ldt.minusDays(1) : ldt;
421 }
422
423 /**
Christine Franks39b03112018-07-03 14:46:07 -0700424 * Returns the first date time corresponding to this local time that occurs after the provided
425 * date time.
Christine Franks03213462017-08-25 13:57:26 -0700426 *
427 * @param compareTime the LocalDateTime to compare against
428 * @return the next LocalDateTime corresponding to this local time
429 */
430 public static LocalDateTime getDateTimeAfter(LocalTime localTime, LocalDateTime compareTime) {
431 final LocalDateTime ldt = LocalDateTime.of(compareTime.getYear(), compareTime.getMonth(),
432 compareTime.getDayOfMonth(), localTime.getHour(), localTime.getMinute());
433
434 // Check if the local time has passed, if so return the same time tomorrow.
435 return ldt.isBefore(compareTime) ? ldt.plusDays(1) : ldt;
436 }
437
Christine Franks39b03112018-07-03 14:46:07 -0700438 private boolean isDeviceColorManagedInternal() {
439 final DisplayTransformManager dtm = getLocalService(DisplayTransformManager.class);
440 return dtm.isDeviceColorManaged();
441 }
442
Christine Franks5397f032017-11-01 18:35:16 -0700443 private abstract class AutoMode implements ColorDisplayController.Callback {
Justin Klaassen911e8892016-06-21 18:24:24 -0700444 public abstract void onStart();
Christine Frankse5bb03e2017-02-10 17:36:10 -0800445
Justin Klaassen911e8892016-06-21 18:24:24 -0700446 public abstract void onStop();
447 }
448
449 private class CustomAutoMode extends AutoMode implements AlarmManager.OnAlarmListener {
450
451 private final AlarmManager mAlarmManager;
452 private final BroadcastReceiver mTimeChangedReceiver;
453
Christine Franks03213462017-08-25 13:57:26 -0700454 private LocalTime mStartTime;
455 private LocalTime mEndTime;
Justin Klaassen911e8892016-06-21 18:24:24 -0700456
Christine Franks03213462017-08-25 13:57:26 -0700457 private LocalDateTime mLastActivatedTime;
Justin Klaassen911e8892016-06-21 18:24:24 -0700458
Christine Frankse5bb03e2017-02-10 17:36:10 -0800459 CustomAutoMode() {
Justin Klaassen911e8892016-06-21 18:24:24 -0700460 mAlarmManager = (AlarmManager) getContext().getSystemService(Context.ALARM_SERVICE);
461 mTimeChangedReceiver = new BroadcastReceiver() {
462 @Override
463 public void onReceive(Context context, Intent intent) {
464 updateActivated();
465 }
466 };
467 }
468
469 private void updateActivated() {
Christine Franks03213462017-08-25 13:57:26 -0700470 final LocalDateTime now = LocalDateTime.now();
471 final LocalDateTime start = getDateTimeBefore(mStartTime, now);
472 final LocalDateTime end = getDateTimeAfter(mEndTime, start);
473 boolean activate = now.isBefore(end);
Justin Klaassen911e8892016-06-21 18:24:24 -0700474
Christine Frankse5bb03e2017-02-10 17:36:10 -0800475 if (mLastActivatedTime != null) {
Christine Frankse5bb03e2017-02-10 17:36:10 -0800476 // Maintain the existing activated state if within the current period.
Christine Franks03213462017-08-25 13:57:26 -0700477 if (mLastActivatedTime.isBefore(now) && mLastActivatedTime.isAfter(start)
478 && (mLastActivatedTime.isAfter(end) || now.isBefore(end))) {
Christine Frankse5bb03e2017-02-10 17:36:10 -0800479 activate = mController.isActivated();
Justin Klaassen911e8892016-06-21 18:24:24 -0700480 }
481 }
482
Christine Frankse5bb03e2017-02-10 17:36:10 -0800483 if (mIsActivated == null || mIsActivated != activate) {
484 mController.setActivated(activate);
Justin Klaassen911e8892016-06-21 18:24:24 -0700485 }
Christine Franks03213462017-08-25 13:57:26 -0700486
Justin Klaassen4346f632016-08-08 15:01:47 -0700487 updateNextAlarm(mIsActivated, now);
Justin Klaassen911e8892016-06-21 18:24:24 -0700488 }
489
Christine Franks03213462017-08-25 13:57:26 -0700490 private void updateNextAlarm(@Nullable Boolean activated, @NonNull LocalDateTime now) {
Justin Klaassen4346f632016-08-08 15:01:47 -0700491 if (activated != null) {
Christine Franks03213462017-08-25 13:57:26 -0700492 final LocalDateTime next = activated ? getDateTimeAfter(mEndTime, now)
493 : getDateTimeAfter(mStartTime, now);
494 final long millis = next.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli();
495 mAlarmManager.setExact(AlarmManager.RTC, millis, TAG, this, null);
Justin Klaassen911e8892016-06-21 18:24:24 -0700496 }
497 }
498
499 @Override
500 public void onStart() {
501 final IntentFilter intentFilter = new IntentFilter(Intent.ACTION_TIME_CHANGED);
502 intentFilter.addAction(Intent.ACTION_TIMEZONE_CHANGED);
503 getContext().registerReceiver(mTimeChangedReceiver, intentFilter);
504
505 mStartTime = mController.getCustomStartTime();
506 mEndTime = mController.getCustomEndTime();
507
Christine Franks1454eae2017-05-31 10:52:22 -0700508 mLastActivatedTime = mController.getLastActivatedTime();
Christine Frankse5bb03e2017-02-10 17:36:10 -0800509
Justin Klaassen911e8892016-06-21 18:24:24 -0700510 // Force an update to initialize state.
511 updateActivated();
512 }
513
514 @Override
515 public void onStop() {
516 getContext().unregisterReceiver(mTimeChangedReceiver);
517
518 mAlarmManager.cancel(this);
519 mLastActivatedTime = null;
520 }
521
522 @Override
523 public void onActivated(boolean activated) {
Christine Franks1454eae2017-05-31 10:52:22 -0700524 mLastActivatedTime = mController.getLastActivatedTime();
Christine Franks03213462017-08-25 13:57:26 -0700525 updateNextAlarm(activated, LocalDateTime.now());
Justin Klaassen911e8892016-06-21 18:24:24 -0700526 }
527
528 @Override
Christine Franks03213462017-08-25 13:57:26 -0700529 public void onCustomStartTimeChanged(LocalTime startTime) {
Justin Klaassen911e8892016-06-21 18:24:24 -0700530 mStartTime = startTime;
531 mLastActivatedTime = null;
532 updateActivated();
533 }
534
535 @Override
Christine Franks03213462017-08-25 13:57:26 -0700536 public void onCustomEndTimeChanged(LocalTime endTime) {
Justin Klaassen911e8892016-06-21 18:24:24 -0700537 mEndTime = endTime;
538 mLastActivatedTime = null;
539 updateActivated();
540 }
541
542 @Override
543 public void onAlarm() {
Justin Klaassenec8837a2016-08-23 12:04:42 -0700544 Slog.d(TAG, "onAlarm");
Justin Klaassen911e8892016-06-21 18:24:24 -0700545 updateActivated();
546 }
547 }
548
549 private class TwilightAutoMode extends AutoMode implements TwilightListener {
550
551 private final TwilightManager mTwilightManager;
Justin Klaassen911e8892016-06-21 18:24:24 -0700552
Christine Frankse5bb03e2017-02-10 17:36:10 -0800553 TwilightAutoMode() {
Justin Klaassen911e8892016-06-21 18:24:24 -0700554 mTwilightManager = getLocalService(TwilightManager.class);
Justin Klaassen911e8892016-06-21 18:24:24 -0700555 }
556
Justin Klaassen908b86c2016-08-08 09:18:42 -0700557 private void updateActivated(TwilightState state) {
Justin Klaassen3da4c442017-05-05 15:19:33 -0700558 if (state == null) {
559 // If there isn't a valid TwilightState then just keep the current activated
560 // state.
561 return;
562 }
563
564 boolean activate = state.isNight();
Christine Franks03213462017-08-25 13:57:26 -0700565 final LocalDateTime lastActivatedTime = mController.getLastActivatedTime();
Justin Klaassen3da4c442017-05-05 15:19:33 -0700566 if (lastActivatedTime != null) {
Christine Franks03213462017-08-25 13:57:26 -0700567 final LocalDateTime now = LocalDateTime.now();
568 final LocalDateTime sunrise = state.sunrise();
569 final LocalDateTime sunset = state.sunset();
Christine Frankse5bb03e2017-02-10 17:36:10 -0800570 // Maintain the existing activated state if within the current period.
Christine Franks03213462017-08-25 13:57:26 -0700571 if (lastActivatedTime.isBefore(now) && (lastActivatedTime.isBefore(sunrise)
572 ^ lastActivatedTime.isBefore(sunset))) {
Christine Frankse5bb03e2017-02-10 17:36:10 -0800573 activate = mController.isActivated();
Justin Klaassen911e8892016-06-21 18:24:24 -0700574 }
575 }
Justin Klaassen908b86c2016-08-08 09:18:42 -0700576
Christine Frankse5bb03e2017-02-10 17:36:10 -0800577 if (mIsActivated == null || mIsActivated != activate) {
578 mController.setActivated(activate);
Justin Klaassen908b86c2016-08-08 09:18:42 -0700579 }
Justin Klaassen911e8892016-06-21 18:24:24 -0700580 }
581
582 @Override
583 public void onStart() {
584 mTwilightManager.registerListener(this, mHandler);
585
586 // Force an update to initialize state.
Justin Klaassen908b86c2016-08-08 09:18:42 -0700587 updateActivated(mTwilightManager.getLastTwilightState());
Justin Klaassen911e8892016-06-21 18:24:24 -0700588 }
589
590 @Override
591 public void onStop() {
592 mTwilightManager.unregisterListener(this);
593 }
594
595 @Override
Justin Klaassen908b86c2016-08-08 09:18:42 -0700596 public void onActivated(boolean activated) {
Justin Klaassen908b86c2016-08-08 09:18:42 -0700597 }
598
599 @Override
600 public void onTwilightStateChanged(@Nullable TwilightState state) {
Justin Klaassenec8837a2016-08-23 12:04:42 -0700601 Slog.d(TAG, "onTwilightStateChanged: isNight="
602 + (state == null ? null : state.isNight()));
Justin Klaassen908b86c2016-08-08 09:18:42 -0700603 updateActivated(state);
Justin Klaassen911e8892016-06-21 18:24:24 -0700604 }
605 }
Justin Klaassen639214e2016-07-14 21:00:06 -0700606
607 /**
608 * Interpolates between two 4x4 color transform matrices (in column-major order).
609 */
610 private static class ColorMatrixEvaluator implements TypeEvaluator<float[]> {
611
612 /**
613 * Result matrix returned by {@link #evaluate(float, float[], float[])}.
614 */
615 private final float[] mResultMatrix = new float[16];
616
617 @Override
618 public float[] evaluate(float fraction, float[] startValue, float[] endValue) {
619 for (int i = 0; i < mResultMatrix.length; i++) {
620 mResultMatrix[i] = MathUtils.lerp(startValue[i], endValue[i], fraction);
621 }
622 return mResultMatrix;
623 }
624 }
Christine Franks39b03112018-07-03 14:46:07 -0700625
626 private final class BinderService extends IColorDisplayManager.Stub {
627 @Override
628 public boolean isDeviceColorManaged() {
629 final long token = Binder.clearCallingIdentity();
630 try {
631 return isDeviceColorManagedInternal();
632 } finally {
633 Binder.restoreCallingIdentity(token);
634 }
635 }
636 }
Justin Klaassen911e8892016-06-21 18:24:24 -0700637}