From 66cce1f9e9db79703e798f853c146869cefd5324 Mon Sep 17 00:00:00 2001 From: jackqdyulei Date: Mon, 23 Jan 2017 17:12:03 -0800 Subject: [PATCH] Making "Battery Saver" use MasterSwitchPreference. By using MasterSwitchPreference, we make "battery saver" to have two parts: left part is the clickable summary jumping to other fragment while right part is a switch toggle. Also remove the previous battery saver preference. Bug: 34279051 Test: RunSettingsRoboTests Change-Id: If36fa9741e413a9bbdd57fa67a2c15b6e457afd7 --- res/xml/power_usage_summary.xml | 3 +- .../fuelgauge/BatterySaverController.java | 176 ++++++++++++++++++ .../fuelgauge/BatterySaverPreference.java | 79 -------- .../settings/fuelgauge/PowerUsageSummary.java | 1 + .../fuelgauge/BatterySaverControllerTest.java | 94 ++++++++++ 5 files changed, 273 insertions(+), 80 deletions(-) create mode 100644 src/com/android/settings/fuelgauge/BatterySaverController.java delete mode 100644 src/com/android/settings/fuelgauge/BatterySaverPreference.java create mode 100644 tests/robotests/src/com/android/settings/fuelgauge/BatterySaverControllerTest.java diff --git a/res/xml/power_usage_summary.xml b/res/xml/power_usage_summary.xml index 4607c482462..b4ac7e2c08d 100644 --- a/res/xml/power_usage_summary.xml +++ b/res/xml/power_usage_summary.xml @@ -27,7 +27,8 @@ android:key="power_management" android:title="@string/battery_power_management"> - diff --git a/src/com/android/settings/fuelgauge/BatterySaverController.java b/src/com/android/settings/fuelgauge/BatterySaverController.java new file mode 100644 index 00000000000..08d570a625a --- /dev/null +++ b/src/com/android/settings/fuelgauge/BatterySaverController.java @@ -0,0 +1,176 @@ +/* + * 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.fuelgauge; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.database.ContentObserver; +import android.os.BatteryManager; +import android.os.Handler; +import android.os.PowerManager; +import android.provider.Settings; +import android.support.annotation.VisibleForTesting; +import android.support.v7.preference.Preference; +import android.support.v7.preference.PreferenceScreen; +import android.util.Log; +import com.android.settings.R; +import com.android.settings.Utils; +import com.android.settings.core.PreferenceController; +import com.android.settings.core.lifecycle.Lifecycle; +import com.android.settings.core.lifecycle.LifecycleObserver; +import com.android.settings.core.lifecycle.events.OnStart; +import com.android.settings.core.lifecycle.events.OnStop; +import com.android.settings.dashboard.conditional.BatterySaverCondition; +import com.android.settings.dashboard.conditional.ConditionManager; +import com.android.settings.widget.MasterSwitchPreference; + +import static android.os.PowerManager.ACTION_POWER_SAVE_MODE_CHANGING; + +public class BatterySaverController extends PreferenceController implements + Preference.OnPreferenceChangeListener, LifecycleObserver, OnStart, OnStop { + private static final String KEY_BATTERY_SAVER = "battery_saver"; + private static final String TAG = "BatterySaverController"; + private static final boolean DEBUG = false; + + private final BatteryStateChangeReceiver mBatteryStateChangeReceiver; + private final PowerManager mPowerManager; + private MasterSwitchPreference mBatterySaverPref; + + public BatterySaverController(Context context, Lifecycle lifecycle) { + super(context); + + lifecycle.addObserver(this); + mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE); + mBatteryStateChangeReceiver = new BatteryStateChangeReceiver(); + } + + @Override + public boolean isAvailable() { + return true; + } + + @Override + public String getPreferenceKey() { + return KEY_BATTERY_SAVER; + } + + @Override + public void displayPreference(PreferenceScreen screen) { + super.displayPreference(screen); + mBatterySaverPref = (MasterSwitchPreference) screen.findPreference(KEY_BATTERY_SAVER); + } + + @Override + public void updateState(Preference preference) { + mBatterySaverPref.setChecked(mPowerManager.isPowerSaveMode()); + updateSummary(); + } + + @Override + public boolean onPreferenceChange(Preference preference, Object newValue) { + final boolean saverOn = (Boolean) newValue; + if (saverOn != mPowerManager.isPowerSaveMode() + && !mPowerManager.setPowerSaveMode(saverOn)) { + // Do nothing if power save mode doesn't set correctly + return false; + } + + refreshConditionManager(); + updateSummary(); + return true; + } + + @Override + public void onStart() { + mContext.getContentResolver().registerContentObserver( + Settings.Global.getUriFor(Settings.Global.LOW_POWER_MODE_TRIGGER_LEVEL) + , true, mObserver); + + mBatteryStateChangeReceiver.setListening(true); + } + + @Override + public void onStop() { + mContext.getContentResolver().unregisterContentObserver(mObserver); + mBatteryStateChangeReceiver.setListening(false); + } + + @VisibleForTesting + void refreshConditionManager() { + ConditionManager.get(mContext).getCondition(BatterySaverCondition.class).refreshState(); + } + + private void updateSummary() { + final boolean mode = mPowerManager.isPowerSaveMode(); + final int format = mode ? R.string.battery_saver_on_summary + : R.string.battery_saver_off_summary; + final int percent = Settings.Global.getInt(mContext.getContentResolver(), + Settings.Global.LOW_POWER_MODE_TRIGGER_LEVEL, 0); + final int percentFormat = percent > 0 ? R.string.battery_saver_desc_turn_on_auto_pct + : R.string.battery_saver_desc_turn_on_auto_never; + + final String summary = mContext.getString(format, mContext.getString(percentFormat, + Utils.formatPercentage(percent))); + + mBatterySaverPref.setSummary(summary); + } + + private final ContentObserver mObserver = new ContentObserver(new Handler()) { + @Override + public void onChange(boolean selfChange) { + updateSummary(); + } + }; + + private final class BatteryStateChangeReceiver extends BroadcastReceiver { + private boolean mRegistered; + + @Override + public void onReceive(Context context, Intent intent) { + if (DEBUG) { + Log.d(TAG, "Received " + intent.getAction()); + } + final String action = intent.getAction(); + if (action.equals(ACTION_POWER_SAVE_MODE_CHANGING)) { + mBatterySaverPref.setChecked(mPowerManager.isPowerSaveMode()); + updateSummary(); + } else if (action.equals(Intent.ACTION_BATTERY_CHANGED)) { + final int status = intent.getIntExtra(BatteryManager.EXTRA_STATUS, -1); + // disable BSM switch if phone is plugged in or at 100% while plugged in + final boolean enabled = !(status == BatteryManager.BATTERY_STATUS_CHARGING + || status == BatteryManager.BATTERY_STATUS_FULL); + + mBatterySaverPref.setSwitchEnabled(enabled); + } + } + + public void setListening(boolean listening) { + if (listening && !mRegistered) { + final IntentFilter ifilter = new IntentFilter(); + ifilter.addAction(Intent.ACTION_BATTERY_CHANGED); + ifilter.addAction(ACTION_POWER_SAVE_MODE_CHANGING); + mContext.registerReceiver(this, ifilter); + mRegistered = true; + } else if (!listening && mRegistered) { + mContext.unregisterReceiver(this); + mRegistered = false; + } + } + + } +} diff --git a/src/com/android/settings/fuelgauge/BatterySaverPreference.java b/src/com/android/settings/fuelgauge/BatterySaverPreference.java deleted file mode 100644 index 9e0f39b4858..00000000000 --- a/src/com/android/settings/fuelgauge/BatterySaverPreference.java +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Copyright (C) 2016 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; - -import android.content.Context; -import android.database.ContentObserver; -import android.os.Handler; -import android.os.PowerManager; -import android.provider.Settings; -import android.provider.Settings.Global; -import android.support.v7.preference.Preference; -import android.util.AttributeSet; -import android.view.View; -import com.android.settings.R; -import com.android.settings.Utils; - -public class BatterySaverPreference extends Preference { - - private PowerManager mPowerManager; - - public BatterySaverPreference(Context context, AttributeSet attrs) { - super(context, attrs); - } - - @Override - protected void performClick(View view) { - Utils.startWithFragment(getContext(), getFragment(), null, null, 0, 0, getTitle()); - } - - @Override - public void onAttached() { - super.onAttached(); - mPowerManager = (PowerManager) getContext().getSystemService(Context.POWER_SERVICE); - mObserver.onChange(true); - getContext().getContentResolver().registerContentObserver( - Settings.Global.getUriFor(Global.LOW_POWER_MODE_TRIGGER_LEVEL), true, mObserver); - getContext().getContentResolver().registerContentObserver( - Settings.Global.getUriFor(Global.LOW_POWER_MODE), true, mObserver); - } - - @Override - public void onDetached() { - super.onDetached(); - getContext().getContentResolver().unregisterContentObserver(mObserver); - } - - private void updateSwitch() { - final Context context = getContext(); - final boolean mode = mPowerManager.isPowerSaveMode(); - int format = mode ? R.string.battery_saver_on_summary - : R.string.battery_saver_off_summary; - int percent = Settings.Global.getInt(context.getContentResolver(), - Global.LOW_POWER_MODE_TRIGGER_LEVEL, 0); - int percentFormat = percent > 0 ? R.string.battery_saver_desc_turn_on_auto_pct - : R.string.battery_saver_desc_turn_on_auto_never; - setSummary(context.getString(format, context.getString(percentFormat, - Utils.formatPercentage(percent)))); - } - - private final ContentObserver mObserver = new ContentObserver(new Handler()) { - @Override - public void onChange(boolean selfChange) { - updateSwitch(); - } - }; - -} diff --git a/src/com/android/settings/fuelgauge/PowerUsageSummary.java b/src/com/android/settings/fuelgauge/PowerUsageSummary.java index 4a25e3ef395..24218b022a8 100644 --- a/src/com/android/settings/fuelgauge/PowerUsageSummary.java +++ b/src/com/android/settings/fuelgauge/PowerUsageSummary.java @@ -158,6 +158,7 @@ public class PowerUsageSummary extends PowerUsageBase { final List controllers = new ArrayList<>(); controllers.add(new AutoBrightnessPreferenceController(context)); controllers.add(new TimeoutPreferenceController(context)); + controllers.add(new BatterySaverController(context, getLifecycle())); return controllers; } diff --git a/tests/robotests/src/com/android/settings/fuelgauge/BatterySaverControllerTest.java b/tests/robotests/src/com/android/settings/fuelgauge/BatterySaverControllerTest.java new file mode 100644 index 00000000000..96563814e94 --- /dev/null +++ b/tests/robotests/src/com/android/settings/fuelgauge/BatterySaverControllerTest.java @@ -0,0 +1,94 @@ +/* + * 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.fuelgauge; + +import android.content.Context; +import android.os.PowerManager; +import com.android.settings.SettingsRobolectricTestRunner; +import com.android.settings.TestConfig; +import com.android.settings.core.lifecycle.Lifecycle; +import com.android.settings.widget.MasterSwitchPreference; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.robolectric.annotation.Config; +import org.robolectric.util.ReflectionHelpers; + +import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +@RunWith(SettingsRobolectricTestRunner.class) +@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION) +public class BatterySaverControllerTest { + @Mock + private MasterSwitchPreference mBatterySaverPref; + @Mock + private PowerManager mPowerManager; + @Mock + private Context mContext; + @Mock + private Lifecycle mLifecycle; + private BatterySaverController mBatterySaverController; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + + mBatterySaverController = spy(new BatterySaverController(mContext, mLifecycle)); + ReflectionHelpers.setField(mBatterySaverController, "mPowerManager", mPowerManager); + ReflectionHelpers.setField(mBatterySaverController, "mBatterySaverPref", mBatterySaverPref); + doNothing().when(mBatterySaverController).refreshConditionManager(); + } + + @Test + public void testOnPreferenceChange_TurnOnBatterySaver_BatterySaverOn() { + testOnPreferenceChangeInner(true); + } + + @Test + public void testOnPreferenceChange_TurnOffBatterySaver_BatterySaverOff() { + testOnPreferenceChangeInner(false); + } + + @Test + public void testUpdateState_SaverModeOn_PreferenceChecked() { + testUpdateStateInner(true); + } + + @Test + public void testUpdateState_SaverModeOff_PreferenceUnChecked() { + testUpdateStateInner(false); + } + + private void testOnPreferenceChangeInner(final boolean saverOn) { + when(mPowerManager.setPowerSaveMode(saverOn)).thenReturn(true); + when(mPowerManager.isPowerSaveMode()).thenReturn(!saverOn); + + mBatterySaverController.onPreferenceChange(mBatterySaverPref, saverOn); + verify(mPowerManager).setPowerSaveMode(saverOn); + } + + private void testUpdateStateInner(final boolean saverOn) { + when(mPowerManager.isPowerSaveMode()).thenReturn(saverOn); + + mBatterySaverController.updateState(mBatterySaverPref); + verify(mBatterySaverPref).setChecked(saverOn); + } +}