Add color temperature preference for Night Display
Bug: 32463283
Test: adb shell settings put secure night_display_color_temperature
XXXX, where XXXX is {0, 2900, 4000, 7000}, and the temperatures
outside the valid range are capped at the min/max, respectively.
Change-Id: I322c0a907b30742fc312a9938fd0c47f679e580b
diff --git a/services/core/java/com/android/server/display/NightDisplayService.java b/services/core/java/com/android/server/display/NightDisplayService.java
index cba694c..d1275bb 100644
--- a/services/core/java/com/android/server/display/NightDisplayService.java
+++ b/services/core/java/com/android/server/display/NightDisplayService.java
@@ -65,16 +65,6 @@
private static final boolean DEBUG = false;
/**
- * Night display ~= 3400 K.
- */
- private static final float[] MATRIX_NIGHT = new float[] {
- 1, 0, 0, 0,
- 0, 0.754f, 0, 0,
- 0, 0, 0.516f, 0,
- 0, 0, 0, 1
- };
-
- /**
* The transition time, in milliseconds, for Night Display to turn on/off.
*/
private static final long TRANSITION_DURATION = 3000L;
@@ -112,13 +102,34 @@
if (enabled) {
dtm.setColorMatrix(LEVEL_COLOR_MATRIX_NIGHT_DISPLAY, MATRIX_IDENTITY);
} else if (mController != null && mController.isActivated()) {
- dtm.setColorMatrix(LEVEL_COLOR_MATRIX_NIGHT_DISPLAY, MATRIX_NIGHT);
+ setMatrix(mController.getColorTemperature(), mMatrixNight);
+ dtm.setColorMatrix(LEVEL_COLOR_MATRIX_NIGHT_DISPLAY, mMatrixNight);
}
}
});
}
};
+ private float[] mMatrixNight = new float[16];
+
+ /**
+ * These coefficients were generated by an LLS quadratic regression fitted to the
+ * overdetermined system based on experimental readings (and subsequent conversion from xy
+ * chromaticity coordinates to gamma-corrected RGB values): { (temperature, R, G, B) } ->
+ * { (7304, 1.0, 1.0, 1.0), (4082, 1.0, 0.857, 0.719), (2850, 1.0, .754, .516),
+ * (2596, 1.0, 0.722, 0.454) }. The 3x3 matrix is formatted like so:
+ * <table>
+ * <tr><td>R: a coefficient</td><td>G: a coefficient</td><td>B: a coefficient</td></tr>
+ * <tr><td>R: b coefficient</td><td>G: b coefficient</td><td>B: b coefficient</td></tr>
+ * <tr><td>R: y-intercept</td><td>G: y-intercept</td><td>B: y-intercept</td></tr>
+ * </table>
+ */
+ private static final float[] mColorTempCoefficients = new float[] {
+ 0.0f, -0.00000000962353339f, -0.0000000189359041f,
+ 0.0f, 0.000153045476f, 0.000302412211f,
+ 1.0f, 0.390782778f, -0.198650895f
+ };
+
private int mCurrentUser = UserHandle.USER_NULL;
private ContentObserver mUserSetupObserver;
private boolean mBootCompleted;
@@ -232,6 +243,9 @@
mController = new NightDisplayController(getContext(), mCurrentUser);
mController.setListener(this);
+ // Prepare color transformation matrix.
+ setMatrix(mController.getColorTemperature(), mMatrixNight);
+
// Initialize the current auto mode.
onAutoModeChanged(mController.getAutoMode());
@@ -239,6 +253,9 @@
if (mIsActivated == null) {
onActivated(mController.isActivated());
}
+
+ // Transition the screen to the current temperature.
+ applyTint(false);
}
private void tearDown() {
@@ -273,53 +290,7 @@
mIsActivated = activated;
- // Cancel the old animator if still running.
- if (mColorMatrixAnimator != null) {
- mColorMatrixAnimator.cancel();
- }
-
- // Don't do any color matrix change animations if we are ignoring them anyway.
- if (mIgnoreAllColorMatrixChanges.get()) {
- return;
- }
-
- final DisplayTransformManager dtm = getLocalService(DisplayTransformManager.class);
- final float[] from = dtm.getColorMatrix(LEVEL_COLOR_MATRIX_NIGHT_DISPLAY);
- final float[] to = mIsActivated ? MATRIX_NIGHT : null;
-
- mColorMatrixAnimator = ValueAnimator.ofObject(COLOR_MATRIX_EVALUATOR,
- from == null ? MATRIX_IDENTITY : from, to == null ? MATRIX_IDENTITY : to);
- mColorMatrixAnimator.setDuration(TRANSITION_DURATION);
- mColorMatrixAnimator.setInterpolator(AnimationUtils.loadInterpolator(
- getContext(), android.R.interpolator.fast_out_slow_in));
- mColorMatrixAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
- @Override
- public void onAnimationUpdate(ValueAnimator animator) {
- final float[] value = (float[]) animator.getAnimatedValue();
- dtm.setColorMatrix(LEVEL_COLOR_MATRIX_NIGHT_DISPLAY, value);
- }
- });
- mColorMatrixAnimator.addListener(new AnimatorListenerAdapter() {
-
- private boolean mIsCancelled;
-
- @Override
- public void onAnimationCancel(Animator animator) {
- mIsCancelled = true;
- }
-
- @Override
- public void onAnimationEnd(Animator animator) {
- if (!mIsCancelled) {
- // Ensure final color matrix is set at the end of the animation. If the
- // animation is cancelled then don't set the final color matrix so the new
- // animator can pick up from where this one left off.
- dtm.setColorMatrix(LEVEL_COLOR_MATRIX_NIGHT_DISPLAY, to);
- }
- mColorMatrixAnimator = null;
- }
- });
- mColorMatrixAnimator.start();
+ applyTint(false);
}
}
@@ -361,6 +332,97 @@
}
}
+ @Override
+ public void onColorTemperatureChanged(int colorTemperature) {
+ setMatrix(colorTemperature, mMatrixNight);
+ applyTint(true);
+ }
+
+ /**
+ * Applies current color temperature matrix, or removes it if deactivated.
+ *
+ * @param immediate {@code true} skips transition animation
+ */
+ private void applyTint(boolean immediate) {
+ // Cancel the old animator if still running.
+ if (mColorMatrixAnimator != null) {
+ mColorMatrixAnimator.cancel();
+ }
+
+ // Don't do any color matrix change animations if we are ignoring them anyway.
+ if (mIgnoreAllColorMatrixChanges.get()) {
+ return;
+ }
+
+ final DisplayTransformManager dtm = getLocalService(DisplayTransformManager.class);
+ final float[] from = dtm.getColorMatrix(LEVEL_COLOR_MATRIX_NIGHT_DISPLAY);
+ final float[] to = mIsActivated ? mMatrixNight : MATRIX_IDENTITY;
+
+ if (immediate) {
+ dtm.setColorMatrix(LEVEL_COLOR_MATRIX_NIGHT_DISPLAY, to);
+ } else {
+ mColorMatrixAnimator = ValueAnimator.ofObject(COLOR_MATRIX_EVALUATOR,
+ from == null ? MATRIX_IDENTITY : from, to);
+ mColorMatrixAnimator.setDuration(TRANSITION_DURATION);
+ mColorMatrixAnimator.setInterpolator(AnimationUtils.loadInterpolator(
+ getContext(), android.R.interpolator.fast_out_slow_in));
+ mColorMatrixAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+ @Override
+ public void onAnimationUpdate(ValueAnimator animator) {
+ final float[] value = (float[]) animator.getAnimatedValue();
+ dtm.setColorMatrix(LEVEL_COLOR_MATRIX_NIGHT_DISPLAY, value);
+ }
+ });
+ mColorMatrixAnimator.addListener(new AnimatorListenerAdapter() {
+
+ private boolean mIsCancelled;
+
+ @Override
+ public void onAnimationCancel(Animator animator) {
+ mIsCancelled = true;
+ }
+
+ @Override
+ public void onAnimationEnd(Animator animator) {
+ if (!mIsCancelled) {
+ // Ensure final color matrix is set at the end of the animation. If the
+ // animation is cancelled then don't set the final color matrix so the new
+ // animator can pick up from where this one left off.
+ dtm.setColorMatrix(LEVEL_COLOR_MATRIX_NIGHT_DISPLAY, to);
+ }
+ mColorMatrixAnimator = null;
+ }
+ });
+ mColorMatrixAnimator.start();
+ }
+ }
+
+ /**
+ * Set the color transformation {@code MATRIX_NIGHT} to the given color temperature.
+ *
+ * @param colorTemperature color temperature in Kelvin
+ * @param outTemp the 4x4 display transformation matrix for that color temperature
+ */
+ private void setMatrix(int colorTemperature, float[] outTemp) {
+ if (outTemp.length != 16) {
+ Slog.d(TAG, "The display transformation matrix must be 4x4");
+ return;
+ }
+
+ Matrix.setIdentityM(mMatrixNight, 0);
+
+ final float squareTemperature = colorTemperature * colorTemperature;
+ final float red = squareTemperature * mColorTempCoefficients[0]
+ + colorTemperature * mColorTempCoefficients[3] + mColorTempCoefficients[6];
+ final float green = squareTemperature * mColorTempCoefficients[1]
+ + colorTemperature * mColorTempCoefficients[4] + mColorTempCoefficients[7];
+ final float blue = squareTemperature * mColorTempCoefficients[2]
+ + colorTemperature * mColorTempCoefficients[5] + mColorTempCoefficients[8];
+ outTemp[0] = red;
+ outTemp[5] = green;
+ outTemp[10] = blue;
+ }
+
private abstract class AutoMode implements NightDisplayController.Callback {
public abstract void onStart();
public abstract void onStop();