diff --git a/res/drawable/ic_battery_saver_accent_24dp.xml b/res/drawable/ic_battery_saver_accent_24dp.xml new file mode 100644 index 00000000000..c8def54883d --- /dev/null +++ b/res/drawable/ic_battery_saver_accent_24dp.xml @@ -0,0 +1,29 @@ + + + + + + diff --git a/res/values/strings.xml b/res/values/strings.xml index c80ba95735f..bc050f8b7a4 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -9094,6 +9094,12 @@ Impacts what you hear and see + + Battery Saver is on + + + Features restricted + Mobile data is off diff --git a/src/com/android/settings/homepage/contextualcards/conditional/BatterySaverConditionController.java b/src/com/android/settings/homepage/contextualcards/conditional/BatterySaverConditionController.java new file mode 100644 index 00000000000..bce7c5dd981 --- /dev/null +++ b/src/com/android/settings/homepage/contextualcards/conditional/BatterySaverConditionController.java @@ -0,0 +1,108 @@ +/* + * 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.homepage.contextualcards.conditional; + +import android.content.Context; +import android.os.PowerManager; + +import com.android.internal.logging.nano.MetricsProto; +import com.android.settings.R; +import com.android.settings.core.SubSettingLauncher; +import com.android.settings.fuelgauge.BatterySaverReceiver; +import com.android.settings.fuelgauge.batterysaver.BatterySaverSettings; +import com.android.settings.homepage.contextualcards.ContextualCard; +import com.android.settingslib.fuelgauge.BatterySaverUtils; + +import java.util.Objects; + +public class BatterySaverConditionController implements ConditionalCardController, + BatterySaverReceiver.BatterySaverListener { + static final int ID = Objects.hash("BatterySaverConditionController"); + + private final Context mAppContext; + private final ConditionManager mConditionManager; + private final BatterySaverReceiver mReceiver; + private final PowerManager mPowerManager; + + public BatterySaverConditionController(Context appContext, ConditionManager conditionManager) { + mAppContext = appContext; + mConditionManager = conditionManager; + mPowerManager = appContext.getSystemService(PowerManager.class); + mReceiver = new BatterySaverReceiver(appContext); + mReceiver.setBatterySaverListener(this); + } + + @Override + public long getId() { + return ID; + } + + @Override + public boolean isDisplayable() { + return mPowerManager.isPowerSaveMode(); + } + + @Override + public void onPrimaryClick(Context context) { + new SubSettingLauncher(context) + .setDestination(BatterySaverSettings.class.getName()) + .setSourceMetricsCategory(MetricsProto.MetricsEvent.DASHBOARD_SUMMARY) + .setTitleRes(R.string.battery_saver) + .launch(); + } + + @Override + public void onActionClick() { + BatterySaverUtils.setPowerSaveMode(mAppContext, false, + /*needFirstTimeWarning*/ false); + } + + @Override + public ContextualCard buildContextualCard() { + return new ConditionalContextualCard.Builder() + .setConditionId(ID) + .setMetricsConstant(MetricsProto.MetricsEvent.SETTINGS_CONDITION_BATTERY_SAVER) + .setActionText(mAppContext.getText(R.string.condition_turn_off)) + .setName(mAppContext.getPackageName() + "/" + + mAppContext.getText(R.string.condition_battery_title)) + .setTitleText(mAppContext.getText(R.string.condition_battery_title).toString()) + .setSummaryText(mAppContext.getText(R.string.condition_battery_summary).toString()) + .setIconDrawable(mAppContext.getDrawable(R.drawable.ic_battery_saver_accent_24dp)) + .setIsHalfWidth(true) + .build(); + } + + @Override + public void startMonitoringStateChange() { + mReceiver.setListening(true); + } + + @Override + public void stopMonitoringStateChange() { + mReceiver.setListening(false); + } + + @Override + public void onPowerSaveModeChanged() { + mConditionManager.onConditionChanged(); + } + + @Override + public void onBatteryChanged(boolean pluggedIn) { + + } +} diff --git a/src/com/android/settings/homepage/contextualcards/conditional/ConditionManager.java b/src/com/android/settings/homepage/contextualcards/conditional/ConditionManager.java index 39f490312cf..c741b98c359 100644 --- a/src/com/android/settings/homepage/contextualcards/conditional/ConditionManager.java +++ b/src/com/android/settings/homepage/contextualcards/conditional/ConditionManager.java @@ -154,6 +154,7 @@ public class ConditionManager { mCardControllers.add(new AirplaneModeConditionController(mAppContext, this /* manager */)); mCardControllers.add( new BackgroundDataConditionController(mAppContext, this /* manager */)); + mCardControllers.add(new BatterySaverConditionController(mAppContext, this /* manager */)); mCardControllers.add(new CellularDataConditionController(mAppContext, this /* manager */)); mCardControllers.add(new DndConditionCardController(mAppContext, this /* manager */)); mCardControllers.add(new HotspotConditionController(mAppContext, this /* manager */)); diff --git a/src/com/android/settings/homepage/contextualcards/deviceinfo/BatterySlice.java b/src/com/android/settings/homepage/contextualcards/deviceinfo/BatteryInfoSlice.java similarity index 96% rename from src/com/android/settings/homepage/contextualcards/deviceinfo/BatterySlice.java rename to src/com/android/settings/homepage/contextualcards/deviceinfo/BatteryInfoSlice.java index 5271e128502..41095a48601 100644 --- a/src/com/android/settings/homepage/contextualcards/deviceinfo/BatterySlice.java +++ b/src/com/android/settings/homepage/contextualcards/deviceinfo/BatteryInfoSlice.java @@ -42,15 +42,15 @@ import com.android.settings.slices.SliceBuilderUtils; /** * Utility class to build a Battery Slice, and handle all associated actions. */ -public class BatterySlice implements CustomSliceable { - private static final String TAG = "BatterySlice"; +public class BatteryInfoSlice implements CustomSliceable { + private static final String TAG = "BatteryInfoSlice"; private final Context mContext; private BatteryInfo mBatteryInfo; private boolean mIsBatteryInfoLoading; - public BatterySlice(Context context) { + public BatteryInfoSlice(Context context) { mContext = context; } diff --git a/src/com/android/settings/slices/CustomSliceManager.java b/src/com/android/settings/slices/CustomSliceManager.java index bb47df28622..24ee680b7c7 100644 --- a/src/com/android/settings/slices/CustomSliceManager.java +++ b/src/com/android/settings/slices/CustomSliceManager.java @@ -23,7 +23,7 @@ import android.util.ArrayMap; import androidx.annotation.VisibleForTesting; import com.android.settings.flashlight.FlashlightSlice; -import com.android.settings.homepage.contextualcards.deviceinfo.BatterySlice; +import com.android.settings.homepage.contextualcards.deviceinfo.BatteryInfoSlice; import com.android.settings.homepage.contextualcards.deviceinfo.DataUsageSlice; import com.android.settings.homepage.contextualcards.deviceinfo.DeviceInfoSlice; import com.android.settings.homepage.contextualcards.deviceinfo.EmergencyInfoSlice; @@ -106,7 +106,7 @@ public class CustomSliceManager { private void addSlices() { mUriMap.put(CustomSliceRegistry.BATTERY_FIX_SLICE_URI, BatteryFixSlice.class); - mUriMap.put(CustomSliceRegistry.BATTERY_INFO_SLICE_URI, BatterySlice.class); + mUriMap.put(CustomSliceRegistry.BATTERY_INFO_SLICE_URI, BatteryInfoSlice.class); mUriMap.put(CustomSliceRegistry.BLUETOOTH_DEVICES_SLICE_URI, BluetoothDevicesSlice.class); mUriMap.put(CustomSliceRegistry.CONTEXTUAL_WIFI_SLICE_URI, ContextualWifiSlice.class); mUriMap.put(CustomSliceRegistry.DATA_USAGE_SLICE_URI, DataUsageSlice.class); diff --git a/tests/robotests/src/com/android/settings/homepage/contextualcards/conditional/BatterySaverConditionControllerTest.java b/tests/robotests/src/com/android/settings/homepage/contextualcards/conditional/BatterySaverConditionControllerTest.java new file mode 100644 index 00000000000..e4ca6c09722 --- /dev/null +++ b/tests/robotests/src/com/android/settings/homepage/contextualcards/conditional/BatterySaverConditionControllerTest.java @@ -0,0 +1,86 @@ +/* + * 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.homepage.contextualcards.conditional; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; + +import android.content.Context; +import android.content.IntentFilter; +import android.os.PowerManager; + +import com.android.settings.fuelgauge.BatterySaverReceiver; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.RuntimeEnvironment; +import org.robolectric.Shadows; +import org.robolectric.shadows.ShadowPowerManager; + +@RunWith(RobolectricTestRunner.class) +public class BatterySaverConditionControllerTest { + @Mock + private ConditionManager mConditionManager; + + private ShadowPowerManager mPowerManager; + private Context mContext; + private BatterySaverConditionController mController; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + mContext = spy(RuntimeEnvironment.application); + mPowerManager = Shadows.shadowOf(mContext.getSystemService(PowerManager.class)); + mController = new BatterySaverConditionController(mContext, mConditionManager); + } + + @Test + public void startMonitor_shouldRegisterReceiver() { + mController.startMonitoringStateChange(); + + verify(mContext).registerReceiver(any(BatterySaverReceiver.class), any(IntentFilter.class)); + } + + @Test + public void stopMonitor_shouldUnregisterReceiver() { + mController.startMonitoringStateChange(); + mController.stopMonitoringStateChange(); + + verify(mContext).unregisterReceiver(any(BatterySaverReceiver.class)); + } + + @Test + public void isDisplayable_PowerSaverOn_true() { + mPowerManager.setIsPowerSaveMode(true); + + assertThat(mController.isDisplayable()).isTrue(); + } + + @Test + public void isDisplayable_PowerSaverOff_false() { + mPowerManager.setIsPowerSaveMode(false); + + assertThat(mController.isDisplayable()).isFalse(); + } +} diff --git a/tests/robotests/src/com/android/settings/homepage/contextualcards/deviceinfo/BatterySliceTest.java b/tests/robotests/src/com/android/settings/homepage/contextualcards/deviceinfo/BatteryInfoSliceTest.java similarity index 84% rename from tests/robotests/src/com/android/settings/homepage/contextualcards/deviceinfo/BatterySliceTest.java rename to tests/robotests/src/com/android/settings/homepage/contextualcards/deviceinfo/BatteryInfoSliceTest.java index 289a57d74c0..ff276d64d54 100644 --- a/tests/robotests/src/com/android/settings/homepage/contextualcards/deviceinfo/BatterySliceTest.java +++ b/tests/robotests/src/com/android/settings/homepage/contextualcards/deviceinfo/BatteryInfoSliceTest.java @@ -40,10 +40,10 @@ import org.robolectric.RobolectricTestRunner; import org.robolectric.RuntimeEnvironment; @RunWith(RobolectricTestRunner.class) -public class BatterySliceTest { +public class BatteryInfoSliceTest { private Context mContext; - private BatterySlice mBatterySlice; + private BatteryInfoSlice mBatteryInfoSlice; @Before public void setUp() { @@ -52,16 +52,16 @@ public class BatterySliceTest { // Set-up specs for SliceMetadata. SliceProvider.setSpecs(SliceLiveData.SUPPORTED_SPECS); - mBatterySlice = spy(new BatterySlice(mContext)); + mBatteryInfoSlice = spy(new BatteryInfoSlice(mContext)); } @Test public void getSlice_shouldBeCorrectSliceContent() { - doNothing().when(mBatterySlice).loadBatteryInfo(); - doReturn("10%").when(mBatterySlice).getBatteryPercentString(); - doReturn("test").when(mBatterySlice).getSummary(); + doNothing().when(mBatteryInfoSlice).loadBatteryInfo(); + doReturn("10%").when(mBatteryInfoSlice).getBatteryPercentString(); + doReturn("test").when(mBatteryInfoSlice).getSummary(); - final Slice slice = mBatterySlice.getSlice(); + final Slice slice = mBatteryInfoSlice.getSlice(); final SliceMetadata metadata = SliceMetadata.from(mContext, slice); assertThat(metadata.getTitle()).isEqualTo(