From 108faaf1ce8f5e07de02967746106074fc4addda Mon Sep 17 00:00:00 2001 From: Salvador Martinez Date: Thu, 8 Nov 2018 11:08:52 -0800 Subject: [PATCH] Create shell UI for smart battery saver This CL creates the new screen WITHOUT any of the behaviors (changing the global settings/calling power manager apis) and hooks it up to the preference in BatterySaverSettings for navigation. The logic for the preference in BatterySaver Settings is also added so that it shows the correct summary when the schedule settings are modified. Additionally, a small tweak is made to the radio preference widget to make it possible to hide the appendix view. It didn't seem to do anything except get in the way and potentially ruin your day, much like its biological counterpart. Test: Overriding power mode via adb, robotests Bug: 111450127 Change-Id: Ic681aaf565ce1caf7d00d314e14ae6c4779fe8f6 --- res/values/strings.xml | 14 +- res/xml/battery_saver_settings.xml | 6 + ...terySaverSchedulePreferenceController.java | 75 ++++++++++ .../BatterySaverScheduleSettings.java | 138 ++++++++++++++++++ .../widget/RadioButtonPreference.java | 13 ++ ...atterySeekBarPreferenceControllerTest.java | 2 +- ...SaverSchedulePreferenceControllerTest.java | 94 ++++++++++++ .../widget/RadioButtonPreferenceTest.java | 10 ++ 8 files changed, 350 insertions(+), 2 deletions(-) create mode 100644 src/com/android/settings/fuelgauge/batterysaver/BatterySaverSchedulePreferenceController.java create mode 100644 src/com/android/settings/fuelgauge/batterysaver/BatterySaverScheduleSettings.java create mode 100644 tests/robotests/src/com/android/settings/fuelgauge/batterysaver/BatterySaverSchedulePreferenceControllerTest.java diff --git a/res/values/strings.xml b/res/values/strings.xml index 4aab4e0fceb..3285fe7da32 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -5409,11 +5409,23 @@ No schedule + + Based on your routine + + + Based on percentage + + + Battery Saver turns on if your battery is likely to run out before your next typical charge + + + Will turn on at %1$s + Set a schedule - At %1$s + %1$s Turn on diff --git a/res/xml/battery_saver_settings.xml b/res/xml/battery_saver_settings.xml index eb4954f8ca8..a0860182f41 100644 --- a/res/xml/battery_saver_settings.xml +++ b/res/xml/battery_saver_settings.xml @@ -20,6 +20,12 @@ android:title="@string/battery_saver" android:key="battery_saver_page"> + + getCandidates() { + Context context = getContext(); + List candidates = Lists.newArrayList(); + candidates.add(new BatterySaverScheduleCandidateInfo( + context.getText(R.string.battery_saver_auto_no_schedule), + /* summary */ null, + KEY_NO_SCHEDULE, + /* enabled */ true)); + candidates.add(new BatterySaverScheduleCandidateInfo( + context.getText(R.string.battery_saver_auto_routine), + context.getText(R.string.battery_saver_auto_routine_summary), + KEY_ROUTINE, + /* enabled */ true)); + candidates.add(new BatterySaverScheduleCandidateInfo( + context.getText(R.string.battery_saver_auto_percentage), + /* summary */ null, + KEY_PERCENTAGE, + /* enabled */ true)); + + return candidates; + } + + @Override + public void bindPreferenceExtra(RadioButtonPreference pref, String key, CandidateInfo info, + String defaultKey, String systemDefaultKey) { + final BatterySaverScheduleCandidateInfo candidateInfo = + (BatterySaverScheduleCandidateInfo) info; + final CharSequence summary = candidateInfo.getSummary(); + if (summary != null) { + pref.setSummary(summary); + pref.setAppendixVisibility(View.GONE); + } + } + + @Override + protected void addStaticPreferences(PreferenceScreen screen) { + SeekBarPreference seekbar = new SeekBarPreference(getContext()); + seekbar.setMax(MAX_SEEKBAR_VALUE); + seekbar.setMin(MIN_SEEKBAR_VALUE); + seekbar.setTitle(R.string.battery_saver_seekbar_title_placeholder); + seekbar.setKey(KEY_BATTERY_SAVER_SEEK_BAR); + screen.addPreference(seekbar); + } + + @Override + protected String getDefaultKey() { + return null; + } + + @Override + protected boolean setDefaultKey(String key) { + return false; + } + + @Override + public int getMetricsCategory() { + return 0; + } + + static class BatterySaverScheduleCandidateInfo extends CandidateInfo { + + private final CharSequence mLabel; + private final CharSequence mSummary; + private final String mKey; + + BatterySaverScheduleCandidateInfo(CharSequence label, CharSequence summary, String key, + boolean enabled) { + super(enabled); + mLabel = label; + mKey = key; + mSummary = summary; + } + + @Override + public CharSequence loadLabel() { + return mLabel; + } + + @Override + public Drawable loadIcon() { + return null; + } + + @Override + public String getKey() { + return mKey; + } + + public CharSequence getSummary() { + return mSummary; + } + } +} \ No newline at end of file diff --git a/src/com/android/settings/widget/RadioButtonPreference.java b/src/com/android/settings/widget/RadioButtonPreference.java index ed7f9053121..512fe4e5f71 100644 --- a/src/com/android/settings/widget/RadioButtonPreference.java +++ b/src/com/android/settings/widget/RadioButtonPreference.java @@ -44,6 +44,8 @@ public class RadioButtonPreference extends CheckBoxPreference { } private OnClickListener mListener = null; + private View appendix; + private int appendixVisibility = -1; public RadioButtonPreference(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); @@ -81,6 +83,10 @@ public class RadioButtonPreference extends CheckBoxPreference { if (summaryContainer != null) { summaryContainer.setVisibility( TextUtils.isEmpty(getSummary()) ? View.GONE : View.VISIBLE); + appendix = view.findViewById(R.id.appendix); + if (appendix != null && appendixVisibility != -1) { + appendix.setVisibility(appendixVisibility); + } } TextView title = (TextView) view.findViewById(android.R.id.title); @@ -89,4 +95,11 @@ public class RadioButtonPreference extends CheckBoxPreference { title.setMaxLines(3); } } + + public void setAppendixVisibility(int visibility) { + if (appendix != null) { + appendix.setVisibility(visibility); + } + appendixVisibility = visibility; + } } diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batterysaver/AutoBatterySeekBarPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batterysaver/AutoBatterySeekBarPreferenceControllerTest.java index 5a92ce7ee2b..5d2ac62c3b6 100644 --- a/tests/robotests/src/com/android/settings/fuelgauge/batterysaver/AutoBatterySeekBarPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/fuelgauge/batterysaver/AutoBatterySeekBarPreferenceControllerTest.java @@ -86,7 +86,7 @@ public class AutoBatterySeekBarPreferenceControllerTest { mController.updateState(mPreference); assertThat(mPreference.isVisible()).isTrue(); - assertThat(mPreference.getTitle()).isEqualTo("At 20%"); + assertThat(mPreference.getTitle()).isEqualTo("20%"); assertThat(mPreference.getProgress()).isEqualTo(TRIGGER_LEVEL / INTERVAL); } diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batterysaver/BatterySaverSchedulePreferenceControllerTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batterysaver/BatterySaverSchedulePreferenceControllerTest.java new file mode 100644 index 00000000000..c4d4c87b625 --- /dev/null +++ b/tests/robotests/src/com/android/settings/fuelgauge/batterysaver/BatterySaverSchedulePreferenceControllerTest.java @@ -0,0 +1,94 @@ +/* + * Copyright (C) 2018 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.fuelgauge.batterysaver; + +import static com.google.common.truth.Truth.assertThat; + +import android.content.Context; +import android.os.PowerManager; +import android.provider.Settings; +import android.provider.Settings.Global; +import androidx.preference.Preference; +import com.android.settings.R; +import com.android.settings.testutils.SettingsRobolectricTestRunner; +import com.android.settings.testutils.shadow.SettingsShadowResources; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.MockitoAnnotations; +import org.robolectric.RuntimeEnvironment; +import org.robolectric.annotation.Config; + +@RunWith(SettingsRobolectricTestRunner.class) +@Config(shadows = SettingsShadowResources.class) +public class BatterySaverSchedulePreferenceControllerTest { + + private static final int TRIGGER_LEVEL = 20; + private static final int DEFAULT_LEVEL = 15; + + private BatterySaverSchedulePreferenceController mController; + private Context mContext; + private Preference mPreference; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + + SettingsShadowResources.overrideResource( + com.android.internal.R.integer.config_lowBatteryWarningLevel, DEFAULT_LEVEL); + mContext = RuntimeEnvironment.application; + mController = new BatterySaverSchedulePreferenceController(mContext); + mPreference = new Preference(mContext); + mController.mBatterySaverSchedulePreference = mPreference; + } + + @Test + public void testPreference_lowPowerLevelZero_percentageMode_summaryNoSchedule() { + Settings.Global.putInt(mContext.getContentResolver(), + Settings.Global.LOW_POWER_MODE_TRIGGER_LEVEL, 0); + Settings.Global.putInt(mContext.getContentResolver(), + Global.AUTOMATIC_POWER_SAVER_MODE, PowerManager.POWER_SAVER_MODE_PERCENTAGE); + + mController.updateState(mPreference); + + assertThat(mPreference.getSummary()).isEqualTo("No schedule"); + } + + @Test + public void testPreference_lowPowerLevelNonZero_percentageMode_summaryPercentage() { + Settings.Global.putInt(mContext.getContentResolver(), + Settings.Global.LOW_POWER_MODE_TRIGGER_LEVEL, TRIGGER_LEVEL); + Settings.Global.putInt(mContext.getContentResolver(), + Global.AUTOMATIC_POWER_SAVER_MODE, PowerManager.POWER_SAVER_MODE_PERCENTAGE); + + mController.updateState(mPreference); + + assertThat(mPreference.getSummary()).isEqualTo("Will turn on at 20%"); + } + + @Test + public void testPreference_percentageRoutine_summaryRoutine() { + // It doesn't matter what this is set to for routine mode + Settings.Global.putInt(mContext.getContentResolver(), + Settings.Global.LOW_POWER_MODE_TRIGGER_LEVEL, TRIGGER_LEVEL); + Settings.Global.putInt(mContext.getContentResolver(), + Global.AUTOMATIC_POWER_SAVER_MODE, PowerManager.POWER_SAVER_MODE_DYNAMIC); + + mController.updateState(mPreference); + + assertThat(mPreference.getSummary()).isEqualTo("Based on your routine"); + } +} diff --git a/tests/robotests/src/com/android/settings/widget/RadioButtonPreferenceTest.java b/tests/robotests/src/com/android/settings/widget/RadioButtonPreferenceTest.java index 5d914a2f273..8a6aabf5d4a 100644 --- a/tests/robotests/src/com/android/settings/widget/RadioButtonPreferenceTest.java +++ b/tests/robotests/src/com/android/settings/widget/RadioButtonPreferenceTest.java @@ -24,6 +24,7 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import android.app.Application; +import android.view.LayoutInflater; import android.view.View; import androidx.preference.PreferenceViewHolder; @@ -93,4 +94,13 @@ public class RadioButtonPreferenceTest { mPreference.onBindViewHolder(preferenceViewHolder); assertEquals(View.GONE, summaryContainer.getVisibility()); } + + @Test + public void hideAppendix_shouldBeGone() { + mPreference.setAppendixVisibility(View.GONE); + View view = LayoutInflater.from(mContext).inflate(R.layout.preference_radio, null); + PreferenceViewHolder holder = PreferenceViewHolder.createInstanceForTests(view); + mPreference.onBindViewHolder(holder); + assertThat(holder.findViewById(R.id.appendix).getVisibility()).isEqualTo(View.GONE); + } }