OSDN Git Service

Add color temperature preference for Night Display
authorChristine Franks <christyfranks@google.com>
Mon, 13 Feb 2017 17:48:00 +0000 (09:48 -0800)
committerChristine Franks <christyfranks@google.com>
Mon, 20 Mar 2017 16:07:17 +0000 (09:07 -0700)
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

core/java/android/provider/Settings.java
core/java/android/widget/AbsSeekBar.java
core/java/android/widget/ProgressBar.java
core/java/com/android/internal/app/NightDisplayController.java
core/res/res/values/config.xml
core/res/res/values/symbols.xml
services/core/java/com/android/server/display/NightDisplayService.java

index 7b84f68..8fc54a0 100755 (executable)
@@ -6788,6 +6788,13 @@ public final class Settings {
         public static final String NIGHT_DISPLAY_AUTO_MODE = "night_display_auto_mode";
 
         /**
+         * Control the color temperature of Night Display, represented in Kelvin.
+         * @hide
+         */
+        public static final String NIGHT_DISPLAY_COLOR_TEMPERATURE =
+                "night_display_color_temperature";
+
+        /**
          * Custom time when Night display is scheduled to activate.
          * Represented as milliseconds from midnight (e.g. 79200000 == 10pm).
          * @hide
@@ -7022,6 +7029,7 @@ public final class Settings {
             INCALL_POWER_BUTTON_BEHAVIOR,
             NIGHT_DISPLAY_CUSTOM_START_TIME,
             NIGHT_DISPLAY_CUSTOM_END_TIME,
+            NIGHT_DISPLAY_COLOR_TEMPERATURE,
             NIGHT_DISPLAY_AUTO_MODE,
             NIGHT_DISPLAY_ACTIVATED,
             SYNC_PARENT_SOUNDS,
index 5d136dc..1d1fcc9 100644 (file)
@@ -677,7 +677,6 @@ public abstract class AbsSeekBar extends ProgressBar {
     protected synchronized void onDraw(Canvas canvas) {
         super.onDraw(canvas);
         drawThumb(canvas);
-
     }
 
     @Override
@@ -703,9 +702,9 @@ public abstract class AbsSeekBar extends ProgressBar {
     }
 
     /**
-     * Draw the tick marks.
+     * @hide
      */
-    void drawTickMarks(Canvas canvas) {
+    protected void drawTickMarks(Canvas canvas) {
         if (mTickMark != null) {
             final int count = getMax() - getMin();
             if (count > 1) {
index ec2adfb..cabf8ea 100644 (file)
@@ -851,6 +851,13 @@ public class ProgressBar extends View {
     }
 
     /**
+     * @hide
+     */
+    public boolean getMirrorForRtl() {
+        return mMirrorForRtl;
+    }
+
+    /**
      * Applies the progress tints in order of increasing specificity.
      */
     private void applyProgressTints() {
index 68afe02..d19f1ec 100644 (file)
@@ -46,7 +46,6 @@ public final class NightDisplayController {
     private static final String TAG = "NightDisplayController";
     private static final boolean DEBUG = false;
 
-    /** @hide */
     @Retention(RetentionPolicy.SOURCE)
     @IntDef({ AUTO_MODE_DISABLED, AUTO_MODE_CUSTOM, AUTO_MODE_TWILIGHT })
     public @interface AutoMode {}
@@ -233,6 +232,65 @@ public final class NightDisplayController {
                 Secure.NIGHT_DISPLAY_CUSTOM_END_TIME, endTime.toMillis(), mUserId);
     }
 
+    /**
+     * Returns the color temperature (in Kelvin) to tint the display when activated.
+     */
+    public int getColorTemperature() {
+        int colorTemperature = Secure.getIntForUser(mContext.getContentResolver(),
+                Secure.NIGHT_DISPLAY_COLOR_TEMPERATURE, -1, mUserId);
+        if (colorTemperature == -1) {
+            if (DEBUG) {
+                Slog.d(TAG, "Using default value for setting: "
+                    + Secure.NIGHT_DISPLAY_COLOR_TEMPERATURE);
+            }
+            colorTemperature = getDefaultColorTemperature();
+        }
+        final int minimumTemperature = getMinimumColorTemperature();
+        final int maximumTemperature = getMaximumColorTemperature();
+        if (colorTemperature < minimumTemperature) {
+            colorTemperature = minimumTemperature;
+        } else if (colorTemperature > maximumTemperature) {
+            colorTemperature = maximumTemperature;
+        }
+
+        return colorTemperature;
+    }
+
+    /**
+     * Sets the current temperature.
+     *
+     * @param colorTemperature the temperature, in Kelvin.
+     * @return {@code true} if new temperature was set successfully.
+     */
+    public boolean setColorTemperature(int colorTemperature) {
+        return Secure.putIntForUser(mContext.getContentResolver(),
+            Secure.NIGHT_DISPLAY_COLOR_TEMPERATURE, colorTemperature, mUserId);
+    }
+
+    /**
+     * Returns the minimum allowed color temperature (in Kelvin) to tint the display when activated.
+     */
+    public int getMinimumColorTemperature() {
+        return mContext.getResources().getInteger(
+                R.integer.config_nightDisplayColorTemperatureMin);
+    }
+
+    /**
+     * Returns the maximum allowed color temperature (in Kelvin) to tint the display when activated.
+     */
+    public int getMaximumColorTemperature() {
+        return mContext.getResources().getInteger(
+                R.integer.config_nightDisplayColorTemperatureMax);
+    }
+
+    /**
+     * Returns the default color temperature (in Kelvin) to tint the display when activated.
+     */
+    public int getDefaultColorTemperature() {
+        return mContext.getResources().getInteger(
+                R.integer.config_nightDisplayColorTemperatureDefault);
+    }
+
     private void onSettingChanged(@NonNull String setting) {
         if (DEBUG) {
             Slog.d(TAG, "onSettingChanged: " + setting);
@@ -252,6 +310,9 @@ public final class NightDisplayController {
                 case Secure.NIGHT_DISPLAY_CUSTOM_END_TIME:
                     mCallback.onCustomEndTimeChanged(getCustomEndTime());
                     break;
+                case Secure.NIGHT_DISPLAY_COLOR_TEMPERATURE:
+                    mCallback.onColorTemperatureChanged(getColorTemperature());
+                    break;
             }
         }
     }
@@ -278,6 +339,8 @@ public final class NightDisplayController {
                         false /* notifyForDescendants */, mContentObserver, mUserId);
                 cr.registerContentObserver(Secure.getUriFor(Secure.NIGHT_DISPLAY_CUSTOM_END_TIME),
                         false /* notifyForDescendants */, mContentObserver, mUserId);
+                cr.registerContentObserver(Secure.getUriFor(Secure.NIGHT_DISPLAY_COLOR_TEMPERATURE),
+                        false /* notifyForDescendants */, mContentObserver, mUserId);
             }
         }
     }
@@ -417,5 +480,12 @@ public final class NightDisplayController {
          * @param endTime the local time to automatically deactivate Night display
          */
         default void onCustomEndTimeChanged(LocalTime endTime) {}
+
+        /**
+         * Callback invoked when the color temperature changes.
+         *
+         * @param colorTemperature the color temperature to tint the screen
+         */
+        default void onColorTemperatureChanged(int colorTemperature) {}
     }
 }
index 385f256..c6cd746 100644 (file)
          Represented as milliseconds from midnight (e.g. 21600000 == 6am). -->
     <integer name="config_defaultNightDisplayCustomEndTime">21600000</integer>
 
+    <!-- Minimum color temperature, in Kelvin, supported by Night display. -->
+    <integer name="config_nightDisplayColorTemperatureMin">2596</integer>
+
+    <!-- Default color temperature, in Kelvin, to tint the screen when Night display is
+         activated. -->
+    <integer name="config_nightDisplayColorTemperatureDefault">2850</integer>
+
+    <!-- Maximum color temperature, in Kelvin, supported by Night display. -->
+    <integer name="config_nightDisplayColorTemperatureMax">4082</integer>
+
     <!-- Indicate whether to allow the device to suspend when the screen is off
          due to the proximity sensor.  This resource should only be set to true
          if the sensor HAL correctly handles the proximity sensor as a wake-up source.
index 7201eae..f6f29d9 100644 (file)
   <java-symbol type="integer" name="config_defaultNightDisplayAutoMode" />
   <java-symbol type="integer" name="config_defaultNightDisplayCustomStartTime" />
   <java-symbol type="integer" name="config_defaultNightDisplayCustomEndTime" />
+  <java-symbol type="integer" name="config_nightDisplayColorTemperatureDefault" />
+  <java-symbol type="integer" name="config_nightDisplayColorTemperatureMin" />
+  <java-symbol type="integer" name="config_nightDisplayColorTemperatureMax" />
 
   <!-- Default first user restrictions -->
   <java-symbol type="array" name="config_defaultFirstUserRestrictions" />
index cba694c..d1275bb 100644 (file)
@@ -65,16 +65,6 @@ public final class NightDisplayService extends SystemService
     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 @@ public final class NightDisplayService extends SystemService
                     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 @@ public final class NightDisplayService extends SystemService
         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 @@ public final class NightDisplayService extends SystemService
         if (mIsActivated == null) {
             onActivated(mController.isActivated());
         }
+
+        // Transition the screen to the current temperature.
+        applyTint(false);
     }
 
     private void tearDown() {
@@ -273,53 +290,7 @@ public final class NightDisplayService extends SystemService
 
             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 @@ public final class NightDisplayService extends SystemService
         }
     }
 
+    @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();