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
This commit is contained in:
Salvador Martinez
2018-11-08 11:08:52 -08:00
parent 42e9d26635
commit 108faaf1ce
8 changed files with 350 additions and 2 deletions

View File

@@ -5409,11 +5409,23 @@
<!-- Battery saver: Label for preference to indicate there is no battery saver schedule [CHAR_LIMIT=40] --> <!-- Battery saver: Label for preference to indicate there is no battery saver schedule [CHAR_LIMIT=40] -->
<string name="battery_saver_auto_no_schedule">No schedule</string> <string name="battery_saver_auto_no_schedule">No schedule</string>
<!-- Battery saver: Label for preference to indicate there is a routine based schedule [CHAR_LIMIT=40] -->
<string name="battery_saver_auto_routine">Based on your routine</string>
<!-- Battery saver: Label for preference to indicate there is a percentage based schedule [CHAR_LIMIT=40] -->
<string name="battery_saver_auto_percentage">Based on percentage</string>
<!-- Battery saver: Summary for preference to describe what is meant by a routine based schedule [CHAR_LIMIT=NONE] -->
<string name="battery_saver_auto_routine_summary">Battery Saver turns on if your battery is likely to run out before your next typical charge</string>
<!-- Battery saver: Label for seekbar to change battery saver threshold [CHAR_LIMIT=40] -->
<string name="battery_saver_auto_percentage_summary">Will turn on at <xliff:g id="percent" example="52%">%1$s</xliff:g></string>
<!-- Battery saver: Title for battery saver schedule screen [CHAR_LIMIT=40] --> <!-- Battery saver: Title for battery saver schedule screen [CHAR_LIMIT=40] -->
<string name="battery_saver_schedule_settings_title">Set a schedule</string> <string name="battery_saver_schedule_settings_title">Set a schedule</string>
<!-- Battery saver: Label for seekbar to change battery saver threshold [CHAR_LIMIT=40] --> <!-- Battery saver: Label for seekbar to change battery saver threshold [CHAR_LIMIT=40] -->
<string name="battery_saver_seekbar_title">At <xliff:g id="percent">%1$s</xliff:g></string> <string name="battery_saver_seekbar_title"><xliff:g id="percent">%1$s</xliff:g></string>
<!-- Battery saver: Placeholder label for seekbar to change battery saver threshold [CHAR_LIMIT=40] --> <!-- Battery saver: Placeholder label for seekbar to change battery saver threshold [CHAR_LIMIT=40] -->
<string name="battery_saver_seekbar_title_placeholder">Turn on</string> <string name="battery_saver_seekbar_title_placeholder">Turn on</string>

View File

@@ -20,6 +20,12 @@
android:title="@string/battery_saver" android:title="@string/battery_saver"
android:key="battery_saver_page"> android:key="battery_saver_page">
<Preference
android:key="battery_saver_schedule"
android:fragment="com.android.settings.fuelgauge.batterysaver.BatterySaverScheduleSettings"
android:title="@string/battery_saver_schedule_settings_title"
settings:controller="com.android.settings.fuelgauge.batterysaver.BatterySaverSchedulePreferenceController"/>
<!-- Turn on automatically --> <!-- Turn on automatically -->
<SwitchPreference <SwitchPreference
android:key="auto_battery_saver" android:key="auto_battery_saver"

View File

@@ -0,0 +1,75 @@
/*
* 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 android.content.ContentResolver;
import android.content.Context;
import android.os.PowerManager;
import android.provider.Settings;
import android.provider.Settings.Global;
import androidx.annotation.VisibleForTesting;
import androidx.preference.Preference;
import androidx.preference.PreferenceScreen;
import com.android.settings.R;
import com.android.settings.Utils;
import com.android.settings.core.BasePreferenceController;
public class BatterySaverSchedulePreferenceController extends BasePreferenceController {
@VisibleForTesting
Preference mBatterySaverSchedulePreference;
public static final String KEY_BATTERY_SAVER_SCHEDULE = "battery_saver_schedule";
public BatterySaverSchedulePreferenceController(Context context) {
super(context, KEY_BATTERY_SAVER_SCHEDULE);
}
@Override
public String getPreferenceKey() {
return KEY_BATTERY_SAVER_SCHEDULE;
}
@Override
public void displayPreference(PreferenceScreen screen) {
super.displayPreference(screen);
mBatterySaverSchedulePreference = screen.findPreference(KEY_BATTERY_SAVER_SCHEDULE);
}
@Override
public CharSequence getSummary() {
final ContentResolver resolver = mContext.getContentResolver();
final int mode = Settings.Global.getInt(resolver, Global.AUTOMATIC_POWER_SAVER_MODE,
PowerManager.POWER_SAVER_MODE_PERCENTAGE);
if (mode == PowerManager.POWER_SAVER_MODE_PERCENTAGE) {
final int threshold =
Settings.Global.getInt(resolver, Global.LOW_POWER_MODE_TRIGGER_LEVEL, 0);
if (threshold <= 0) {
return mContext.getText(R.string.battery_saver_auto_no_schedule);
} else {
return mContext.getString(R.string.battery_saver_auto_percentage_summary,
Utils.formatPercentage(threshold));
}
} else {
return mContext.getText(R.string.battery_saver_auto_routine);
}
}
@Override
public int getAvailabilityStatus() {
return AVAILABLE;
}
}

View File

@@ -0,0 +1,138 @@
/*
* 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 android.content.Context;
import android.graphics.drawable.Drawable;
import android.view.View;
import androidx.preference.PreferenceScreen;
import com.android.settings.widget.RadioButtonPickerFragment;
import com.android.settings.R;
import com.android.settings.widget.RadioButtonPreference;
import com.android.settings.widget.SeekBarPreference;
import com.android.settingslib.widget.CandidateInfo;
import com.google.common.collect.Lists;
import java.util.List;
public class BatterySaverScheduleSettings extends RadioButtonPickerFragment {
private static final String KEY_NO_SCHEDULE = "key_battery_saver_no_schedule";
private static final String KEY_ROUTINE = "key_battery_saver_routine";
private static final String KEY_PERCENTAGE = "key_battery_saver_percentage";
public static final int MAX_SEEKBAR_VALUE = 15;
public static final int MIN_SEEKBAR_VALUE = 1;
public static final String KEY_BATTERY_SAVER_SEEK_BAR = "battery_saver_seek_bar";
@Override
protected int getPreferenceScreenResId() {
return R.xml.battery_saver_schedule_settings;
}
@Override
protected List<? extends CandidateInfo> getCandidates() {
Context context = getContext();
List<CandidateInfo> 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;
}
}
}

View File

@@ -44,6 +44,8 @@ public class RadioButtonPreference extends CheckBoxPreference {
} }
private OnClickListener mListener = null; private OnClickListener mListener = null;
private View appendix;
private int appendixVisibility = -1;
public RadioButtonPreference(Context context, AttributeSet attrs, int defStyle) { public RadioButtonPreference(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle); super(context, attrs, defStyle);
@@ -81,6 +83,10 @@ public class RadioButtonPreference extends CheckBoxPreference {
if (summaryContainer != null) { if (summaryContainer != null) {
summaryContainer.setVisibility( summaryContainer.setVisibility(
TextUtils.isEmpty(getSummary()) ? View.GONE : View.VISIBLE); 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); TextView title = (TextView) view.findViewById(android.R.id.title);
@@ -89,4 +95,11 @@ public class RadioButtonPreference extends CheckBoxPreference {
title.setMaxLines(3); title.setMaxLines(3);
} }
} }
public void setAppendixVisibility(int visibility) {
if (appendix != null) {
appendix.setVisibility(visibility);
}
appendixVisibility = visibility;
}
} }

View File

@@ -86,7 +86,7 @@ public class AutoBatterySeekBarPreferenceControllerTest {
mController.updateState(mPreference); mController.updateState(mPreference);
assertThat(mPreference.isVisible()).isTrue(); assertThat(mPreference.isVisible()).isTrue();
assertThat(mPreference.getTitle()).isEqualTo("At 20%"); assertThat(mPreference.getTitle()).isEqualTo("20%");
assertThat(mPreference.getProgress()).isEqualTo(TRIGGER_LEVEL / INTERVAL); assertThat(mPreference.getProgress()).isEqualTo(TRIGGER_LEVEL / INTERVAL);
} }

View File

@@ -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");
}
}

View File

@@ -24,6 +24,7 @@ import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
import android.app.Application; import android.app.Application;
import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import androidx.preference.PreferenceViewHolder; import androidx.preference.PreferenceViewHolder;
@@ -93,4 +94,13 @@ public class RadioButtonPreferenceTest {
mPreference.onBindViewHolder(preferenceViewHolder); mPreference.onBindViewHolder(preferenceViewHolder);
assertEquals(View.GONE, summaryContainer.getVisibility()); 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);
}
} }