From c53ab43d94a7dec16e253eb97b532712ec279fb9 Mon Sep 17 00:00:00 2001 From: Christine Franks Date: Tue, 24 Jan 2017 16:12:24 -0800 Subject: [PATCH] Add color temperature preference to Night Display Bug: 32463283 Test: make RunSettingsRoboTests Change-Id: Ibf3cf9af05b44a071603d3d7efa6e90ae00c823a --- .../preference_widget_seekbar_settings.xml | 96 +++++++------------ res/values/strings.xml | 2 + res/xml/night_display_settings.xml | 5 +- .../android/settings/SeekBarPreference.java | 43 +++++++-- .../display/NightDisplaySettings.java | 29 ++++++ .../widget/DefaultIndicatorSeekBar.java | 89 +++++++++++++++++ .../widget/DefaultIndicatorSeekBarTest.java | 54 +++++++++++ 7 files changed, 246 insertions(+), 72 deletions(-) create mode 100644 src/com/android/settings/widget/DefaultIndicatorSeekBar.java create mode 100644 tests/robotests/src/com/android/settings/widget/DefaultIndicatorSeekBarTest.java diff --git a/res/layout/preference_widget_seekbar_settings.xml b/res/layout/preference_widget_seekbar_settings.xml index 77c27a4c71b..87fea99823b 100644 --- a/res/layout/preference_widget_seekbar_settings.xml +++ b/res/layout/preference_widget_seekbar_settings.xml @@ -15,81 +15,49 @@ --> - - + android:paddingEnd="?android:attr/listPreferredItemPaddingEnd" + android:paddingBottom="6dp" + android:paddingTop="6dp"> - - - + 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" /> - + 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" /> - + - - - - - - - - - - + diff --git a/res/values/strings.xml b/res/values/strings.xml index ddb1bf17ac5..2cf7b551fc3 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -2230,6 +2230,8 @@ Start time End time + + Intensity Off. %1$s diff --git a/res/xml/night_display_settings.xml b/res/xml/night_display_settings.xml index c0b91ae60ae..f78a7694de6 100644 --- a/res/xml/night_display_settings.xml +++ b/res/xml/night_display_settings.xml @@ -16,7 +16,6 @@ + + \ No newline at end of file diff --git a/src/com/android/settings/SeekBarPreference.java b/src/com/android/settings/SeekBarPreference.java index 26b69db0d08..a8d5fbf6729 100644 --- a/src/com/android/settings/SeekBarPreference.java +++ b/src/com/android/settings/SeekBarPreference.java @@ -21,7 +21,6 @@ import android.content.res.TypedArray; 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.view.View; import android.widget.SeekBar; import android.widget.SeekBar.OnSeekBarChangeListener; +import com.android.settings.widget.DefaultIndicatorSeekBar; import com.android.settingslib.RestrictedPreference; /** @@ -41,6 +41,11 @@ public class SeekBarPreference extends RestrictedPreference 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 class SeekBarPreference extends RestrictedPreference 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 @@ public class SeekBarPreference extends RestrictedPreference 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 @@ public class SeekBarPreference extends RestrictedPreference @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 619c0d4d1cd..bc90eb93b34 100644 --- a/src/com/android/settings/display/NightDisplaySettings.java +++ b/src/com/android/settings/display/NightDisplaySettings.java @@ -28,6 +28,7 @@ import android.widget.TimePicker; 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 @@ public class NightDisplaySettings extends SettingsPreferenceFragment 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 @@ public class NightDisplaySettings extends SettingsPreferenceFragment private Preference mStartTimePreference; private Preference mEndTimePreference; private TwoStatePreference mActivatedPreference; + private SeekBarPreference mTemperaturePreference; @Override public void onCreate(Bundle savedInstanceState) { @@ -65,6 +68,11 @@ public class NightDisplaySettings extends SettingsPreferenceFragment 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 @@ public class NightDisplaySettings extends SettingsPreferenceFragment 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 @@ public class NightDisplaySettings extends SettingsPreferenceFragment }); mAutoModePreference.setOnPreferenceChangeListener(this); mActivatedPreference.setOnPreferenceChangeListener(this); + mTemperaturePreference.setOnPreferenceChangeListener(this); } @Override @@ -105,6 +115,7 @@ public class NightDisplaySettings extends SettingsPreferenceFragment onAutoModeChanged(mController.getAutoMode()); onCustomStartTimeChanged(mController.getCustomStartTime()); onCustomEndTimeChanged(mController.getCustomEndTime()); + onColorTemperatureChanged(mController.getColorTemperature()); } @Override @@ -166,9 +177,11 @@ public class NightDisplaySettings extends SettingsPreferenceFragment return 0; } } + @Override public void onActivated(boolean activated) { mActivatedPreference.setChecked(activated); + mTemperaturePreference.setEnabled(activated); } @Override @@ -180,6 +193,11 @@ public class NightDisplaySettings extends SettingsPreferenceFragment 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 @@ public class NightDisplaySettings extends SettingsPreferenceFragment 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 @@ public class NightDisplaySettings extends SettingsPreferenceFragment 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 00000000000..d8edf030fba --- /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 00000000000..cf4be56745e --- /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()); + } + +}