Merge "Add color temperature preference to Night Display"
diff --git a/res/layout/preference_widget_seekbar_settings.xml b/res/layout/preference_widget_seekbar_settings.xml
index 77c27a4..87fea99 100644
--- a/res/layout/preference_widget_seekbar_settings.xml
+++ b/res/layout/preference_widget_seekbar_settings.xml
@@ -15,81 +15,49 @@
 -->
 
 <!-- Layout used by SeekBarPreference for the seekbar widget style. -->
-<!-- Same as frameworks/base/core/res/res/layout/preference_widget_seekbar_material.xml with
-     reserved icon space -->
-<LinearLayout
+<RelativeLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
     android:minHeight="?android:attr/listPreferredItemHeight"
     android:gravity="center_vertical"
     android:paddingStart="?android:attr/listPreferredItemPaddingStart"
-    android:paddingEnd="?android:attr/listPreferredItemPaddingEnd">
+    android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
+    android:paddingBottom="6dp"
+    android:paddingTop="6dp">
 
-    <LinearLayout
-        android:id="@+id/icon_container"
+    <TextView
+        android:id="@android:id/title"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
-        android:minWidth="60dp"
-        android:gravity="start|center_vertical"
-        android:orientation="horizontal"
-        android:paddingEnd="12dp"
-        android:paddingTop="4dp"
-        android:paddingBottom="4dp">
-        <com.android.internal.widget.PreferenceImageView
-            android:id="@android:id/icon"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:maxWidth="48dp"
-            android:maxHeight="48dp"/>
-    </LinearLayout>
+        android:layout_marginStart="60dp"
+        android:layout_marginEnd="8dp"
+        android:singleLine="true"
+        android:textAppearance="@android:style/TextAppearance.Material.Subhead"
+        android:textColor="?android:attr/textColorPrimary"
+        android:ellipsize="marquee"
+        android:fadingEdge="horizontal" />
 
-    <RelativeLayout
+    <TextView
+        android:id="@android:id/summary"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
-        android:layout_marginEnd="8dip"
-        android:layout_marginTop="6dip"
-        android:layout_marginBottom="6dip"
-        android:layout_weight="1">
+        android:layout_marginStart="60dp"
+        android:layout_marginEnd="8dp"
+        android:layout_below="@android:id/title"
+        android:layout_alignStart="@android:id/title"
+        android:textAppearance="?android:attr/textAppearanceListItemSecondary"
+        android:textColor="?android:attr/textColorSecondary"
+        android:maxLines="4" />
 
-        <TextView
-            android:id="@android:id/title"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:singleLine="true"
-            android:textAppearance="@android:style/TextAppearance.Material.Subhead"
-            android:textColor="?android:attr/textColorPrimary"
-            android:ellipsize="marquee"
-            android:fadingEdge="horizontal"/>
+    <com.android.settings.widget.DefaultIndicatorSeekBar
+        android:id="@*android:id/seekbar"
+        style="@android:style/Widget.Material.SeekBar.Discrete"
+        android:layout_width="match_parent"
+        android:layout_height="48dp"
+        android:layout_alignParentEnd="true"
+        android:layout_below="@android:id/summary"
+        android:layout_marginEnd="8dp"
+        android:layout_marginStart="44dp" />
 
-        <TextView
-            android:id="@android:id/summary"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:layout_below="@android:id/title"
-            android:layout_alignStart="@android:id/title"
-            android:textAppearance="?android:attr/textAppearanceListItemSecondary"
-            android:textColor="?android:attr/textColorSecondary"
-            android:maxLines="4"/>
-
-        <!-- Preference should place its actual preference widget here. -->
-        <LinearLayout
-            android:id="@android:id/widget_frame"
-            android:layout_width="wrap_content"
-            android:layout_height="match_parent"
-            android:layout_below="@android:id/summary"
-            android:layout_alignStart="@android:id/title"
-            android:gravity="center"
-            android:orientation="vertical"/>
-
-        <SeekBar
-            android:id="@*android:id/seekbar"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:layout_below="@android:id/summary"
-            android:layout_toEndOf="@android:id/widget_frame"
-            android:layout_alignParentEnd="true"/>
-
-    </RelativeLayout>
-
-</LinearLayout>
+</RelativeLayout>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index ddb1bf1..2cf7b55 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -2230,6 +2230,8 @@
     <string name="night_display_start_time_title">Start time</string>
     <!-- Night display screen, setting option name to configure time to automatically turn off night display. [CHAR LIMIT=30] -->
     <string name="night_display_end_time_title">End time</string>
+    <!-- Night display screen, setting the color temperature of the display. [CHAR LIMIT=30] -->
+    <string name="night_display_temperature_title">Intensity</string>
     <!-- Display settings screen, summary format of night display when off. [CHAR LIMIT=NONE] -->
     <string name="night_display_summary_off">Off. <xliff:g name="auto_mode_summary" example="Never turn on automatically">%1$s</xliff:g></string>
     <!-- Display settings screen, summary of night display when off and will *never* turn on automatically. [CHAR LIMIT=NONE] -->
diff --git a/res/xml/night_display_settings.xml b/res/xml/night_display_settings.xml
index c0b91ae..f78a769 100644
--- a/res/xml/night_display_settings.xml
+++ b/res/xml/night_display_settings.xml
@@ -16,7 +16,6 @@
 
 <PreferenceScreen
         xmlns:android="http://schemas.android.com/apk/res/android"
-        xmlns:settings="http://schemas.android.com/apk/res-auto"
         android:title="@string/night_display_title">
 
     <PreferenceCategory
@@ -44,6 +43,10 @@
                 android:key="night_display_activated"
                 android:title="@string/night_display_title" />
 
+        <com.android.settings.SeekBarPreference
+            android:key="night_display_temperature"
+            android:title="@string/night_display_temperature_title"/>
+
     </PreferenceCategory>
 
 </PreferenceScreen>
\ No newline at end of file
diff --git a/src/com/android/settings/SeekBarPreference.java b/src/com/android/settings/SeekBarPreference.java
index 26b69db..a8d5fbf 100644
--- a/src/com/android/settings/SeekBarPreference.java
+++ b/src/com/android/settings/SeekBarPreference.java
@@ -21,7 +21,6 @@
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.support.v4.content.res.TypedArrayUtils;
-import android.support.v7.preference.Preference;
 import android.support.v7.preference.PreferenceViewHolder;
 import android.util.AttributeSet;
 import android.view.KeyEvent;
@@ -29,6 +28,7 @@
 import android.widget.SeekBar;
 import android.widget.SeekBar.OnSeekBarChangeListener;
 
+import com.android.settings.widget.DefaultIndicatorSeekBar;
 import com.android.settingslib.RestrictedPreference;
 
 /**
@@ -41,6 +41,11 @@
     private int mMax;
     private boolean mTrackingTouch;
 
+    private boolean mContinuousUpdates;
+    private int mDefaultProgress = -1;
+
+    private SeekBar mSeekBar;
+
     public SeekBarPreference(
             Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
         super(context, attrs, defStyleAttr, defStyleRes);
@@ -78,12 +83,15 @@
     public void onBindViewHolder(PreferenceViewHolder view) {
         super.onBindViewHolder(view);
         view.itemView.setOnKeyListener(this);
-        SeekBar seekBar = (SeekBar) view.findViewById(
+        mSeekBar = (SeekBar) view.findViewById(
                 com.android.internal.R.id.seekbar);
-        seekBar.setOnSeekBarChangeListener(this);
-        seekBar.setMax(mMax);
-        seekBar.setProgress(mProgress);
-        seekBar.setEnabled(isEnabled());
+        mSeekBar.setOnSeekBarChangeListener(this);
+        mSeekBar.setMax(mMax);
+        mSeekBar.setProgress(mProgress);
+        mSeekBar.setEnabled(isEnabled());
+        if (mSeekBar instanceof DefaultIndicatorSeekBar) {
+            ((DefaultIndicatorSeekBar) mSeekBar).setDefaultProgress(mDefaultProgress);
+        }
     }
 
     @Override
@@ -126,6 +134,27 @@
         setProgress(progress, true);
     }
 
+    /**
+     * Sets the progress point to draw a single tick mark representing a default value.
+     */
+    public void setDefaultProgress(int defaultProgress) {
+        if (mDefaultProgress != defaultProgress) {
+            mDefaultProgress = defaultProgress;
+            if (mSeekBar instanceof DefaultIndicatorSeekBar) {
+                ((DefaultIndicatorSeekBar) mSeekBar).setDefaultProgress(mDefaultProgress);
+            }
+        }
+    }
+
+    /**
+     * When {@code continuousUpdates} is true, update the persisted setting immediately as the thumb
+     * is dragged along the SeekBar. Otherwise, only update the value of the setting when the thumb
+     * is dropped.
+     */
+    public void setContinuousUpdates(boolean continuousUpdates) {
+        mContinuousUpdates = continuousUpdates;
+    }
+
     private void setProgress(int progress, boolean notifyChanged) {
         if (progress > mMax) {
             progress = mMax;
@@ -164,7 +193,7 @@
     @Override
     public void onProgressChanged(
             SeekBar seekBar, int progress, boolean fromUser) {
-        if (fromUser && !mTrackingTouch) {
+        if (fromUser && (mContinuousUpdates || !mTrackingTouch)) {
             syncProgress(seekBar);
         }
     }
diff --git a/src/com/android/settings/display/NightDisplaySettings.java b/src/com/android/settings/display/NightDisplaySettings.java
index 619c0d4..bc90eb9 100644
--- a/src/com/android/settings/display/NightDisplaySettings.java
+++ b/src/com/android/settings/display/NightDisplaySettings.java
@@ -28,6 +28,7 @@
 import com.android.internal.app.NightDisplayController;
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.settings.R;
+import com.android.settings.SeekBarPreference;
 import com.android.settings.SettingsPreferenceFragment;
 
 import java.text.DateFormat;
@@ -44,6 +45,7 @@
     private static final String KEY_NIGHT_DISPLAY_START_TIME = "night_display_start_time";
     private static final String KEY_NIGHT_DISPLAY_END_TIME = "night_display_end_time";
     private static final String KEY_NIGHT_DISPLAY_ACTIVATED = "night_display_activated";
+    private static final String KEY_NIGHT_DISPLAY_TEMPERATURE = "night_display_temperature";
 
     private static final int DIALOG_START_TIME = 0;
     private static final int DIALOG_END_TIME = 1;
@@ -55,6 +57,7 @@
     private Preference mStartTimePreference;
     private Preference mEndTimePreference;
     private TwoStatePreference mActivatedPreference;
+    private SeekBarPreference mTemperaturePreference;
 
     @Override
     public void onCreate(Bundle savedInstanceState) {
@@ -65,6 +68,11 @@
 
         mTimeFormatter = android.text.format.DateFormat.getTimeFormat(context);
         mTimeFormatter.setTimeZone(TimeZone.getTimeZone("UTC"));
+
+        mTemperaturePreference.setMax(convertTemperature(mController.getMinimumColorTemperature()));
+        mTemperaturePreference.setDefaultProgress(convertTemperature(
+                mController.getDefaultColorTemperature()));
+        mTemperaturePreference.setContinuousUpdates(true);
     }
 
     @Override
@@ -78,6 +86,7 @@
         mStartTimePreference = findPreference(KEY_NIGHT_DISPLAY_START_TIME);
         mEndTimePreference = findPreference(KEY_NIGHT_DISPLAY_END_TIME);
         mActivatedPreference = (TwoStatePreference) findPreference(KEY_NIGHT_DISPLAY_ACTIVATED);
+        mTemperaturePreference = (SeekBarPreference) findPreference(KEY_NIGHT_DISPLAY_TEMPERATURE);
 
         mAutoModePreference.setEntries(new CharSequence[] {
                 getString(R.string.night_display_auto_mode_never),
@@ -91,6 +100,7 @@
         });
         mAutoModePreference.setOnPreferenceChangeListener(this);
         mActivatedPreference.setOnPreferenceChangeListener(this);
+        mTemperaturePreference.setOnPreferenceChangeListener(this);
     }
 
     @Override
@@ -105,6 +115,7 @@
         onAutoModeChanged(mController.getAutoMode());
         onCustomStartTimeChanged(mController.getCustomStartTime());
         onCustomEndTimeChanged(mController.getCustomEndTime());
+        onColorTemperatureChanged(mController.getColorTemperature());
     }
 
     @Override
@@ -166,9 +177,11 @@
                 return 0;
         }
     }
+
     @Override
     public void onActivated(boolean activated) {
         mActivatedPreference.setChecked(activated);
+        mTemperaturePreference.setEnabled(activated);
     }
 
     @Override
@@ -180,6 +193,11 @@
         mEndTimePreference.setVisible(showCustomSchedule);
     }
 
+    @Override
+    public void onColorTemperatureChanged(int colorTemperature) {
+        mTemperaturePreference.setProgress(convertTemperature(colorTemperature));
+    }
+
     private String getFormattedTimeString(NightDisplayController.LocalTime localTime) {
         final Calendar c = Calendar.getInstance();
         c.setTimeZone(mTimeFormatter.getTimeZone());
@@ -190,6 +208,15 @@
         return mTimeFormatter.format(c.getTime());
     }
 
+    /**
+     * Inverts and range-adjusts a raw value from the SeekBar (i.e. [0, maxTemp-minTemp]), or
+     * converts an inverted and range-adjusted value to the raw SeekBar value, depending on the
+     * adjustment status of the input.
+     */
+    private int convertTemperature(int temperature) {
+        return mController.getMaximumColorTemperature() - temperature;
+    }
+
     @Override
     public void onCustomStartTimeChanged(NightDisplayController.LocalTime startTime) {
         mStartTimePreference.setSummary(getFormattedTimeString(startTime));
@@ -206,6 +233,8 @@
             return mController.setAutoMode(Integer.parseInt((String) newValue));
         } else if (preference == mActivatedPreference) {
             return mController.setActivated((Boolean) newValue);
+        } else if (preference == mTemperaturePreference) {
+            return mController.setColorTemperature(convertTemperature((Integer) newValue));
         }
         return false;
     }
diff --git a/src/com/android/settings/widget/DefaultIndicatorSeekBar.java b/src/com/android/settings/widget/DefaultIndicatorSeekBar.java
new file mode 100644
index 0000000..d8edf03
--- /dev/null
+++ b/src/com/android/settings/widget/DefaultIndicatorSeekBar.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.widget;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.drawable.Drawable;
+import android.util.AttributeSet;
+import android.widget.SeekBar;
+
+public class DefaultIndicatorSeekBar extends SeekBar {
+
+    private int mDefaultProgress = -1;
+
+    public DefaultIndicatorSeekBar(Context context) {
+        super(context);
+    }
+
+    public DefaultIndicatorSeekBar(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    public DefaultIndicatorSeekBar(Context context, AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+    }
+
+    public DefaultIndicatorSeekBar(Context context, AttributeSet attrs, int defStyleAttr,
+            int defStyleRes) {
+        super(context, attrs, defStyleAttr, defStyleRes);
+    }
+
+    /**
+     * N.B. Only draws the default indicator tick mark, NOT equally spaced tick marks.
+     */
+    @Override
+    protected void drawTickMarks(Canvas canvas) {
+        if (isEnabled() && mDefaultProgress <= getMax() && mDefaultProgress >= getMin()) {
+            final Drawable defaultIndicator = getTickMark();
+
+            // Adjust the drawable's bounds to center it at the point where it's drawn.
+            final int w = defaultIndicator.getIntrinsicWidth();
+            final int h = defaultIndicator.getIntrinsicHeight();
+            final int halfW = w >= 0 ? w / 2 : 1;
+            final int halfH = h >= 0 ? h / 2 : 1;
+            defaultIndicator.setBounds(-halfW, -halfH, halfW, halfH);
+
+            // This mimics the computation of the thumb position, to get the true "default."
+            final int availableWidth = getWidth() - mPaddingLeft - mPaddingRight;
+            final int range = getMax() - getMin();
+            final float scale = range > 0f ? mDefaultProgress / (float) range : 0f;
+            final int offset = (int) ((scale * availableWidth) + 0.5f);
+            final int indicatorPosition = isLayoutRtl() && getMirrorForRtl()
+                    ? availableWidth - offset + mPaddingRight : offset + mPaddingLeft;
+
+            final int saveCount = canvas.save();
+            canvas.translate(indicatorPosition, getHeight() / 2);
+            defaultIndicator.draw(canvas);
+            canvas.restoreToCount(saveCount);
+        }
+    }
+
+    /**
+     * N.B. This sets the default *unadjusted* progress, i.e. in the SeekBar's [0 - max] terms.
+     */
+    public void setDefaultProgress(int defaultProgress) {
+        if (mDefaultProgress != defaultProgress) {
+            mDefaultProgress = defaultProgress;
+            invalidate();
+        }
+    }
+
+    public int getDefaultProgress() {
+        return mDefaultProgress;
+    }
+}
diff --git a/tests/robotests/src/com/android/settings/widget/DefaultIndicatorSeekBarTest.java b/tests/robotests/src/com/android/settings/widget/DefaultIndicatorSeekBarTest.java
new file mode 100644
index 0000000..cf4be56
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/widget/DefaultIndicatorSeekBarTest.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.widget;
+
+import com.android.settings.SettingsRobolectricTestRunner;
+import com.android.settings.TestConfig;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+
+import static junit.framework.Assert.assertEquals;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
+public class DefaultIndicatorSeekBarTest {
+
+    private DefaultIndicatorSeekBar mDefaultIndicatorSeekBar;
+
+    @Before
+    public void setUp() {
+        mDefaultIndicatorSeekBar = new DefaultIndicatorSeekBar(RuntimeEnvironment.application);
+        mDefaultIndicatorSeekBar.setMax(100);
+    }
+
+    @After
+    public void tearDown() {
+        mDefaultIndicatorSeekBar = null;
+    }
+
+    @Test
+    public void defaultProgress_setSucceeds() {
+        mDefaultIndicatorSeekBar.setDefaultProgress(40);
+        assertEquals(40, mDefaultIndicatorSeekBar.getDefaultProgress());
+    }
+
+}