From 0896fa3beee1c389f771643184074b4a4a485f60 Mon Sep 17 00:00:00 2001 From: Fan Zhang Date: Wed, 9 Jan 2019 23:14:34 +0000 Subject: [PATCH 1/2] Revert "Remove battery saver condition." This reverts commit 44b28f4a2b5cea5abeb603fe81a38f6f8a666bc5. Bug: 121115306 Test: robotests Reason for revert: Change of design Change-Id: Ia2b9131595d582fd8300367f729fde2b3de81b6e --- res/drawable/ic_battery_saver_accent_24dp.xml | 29 +++++ res/values/strings.xml | 6 + .../BatterySaverConditionController.java | 108 ++++++++++++++++++ .../conditional/ConditionManager.java | 1 + ...atterySlice.java => BatteryInfoSlice.java} | 6 +- .../settings/slices/CustomSliceManager.java | 4 +- .../BatterySaverConditionControllerTest.java | 86 ++++++++++++++ ...iceTest.java => BatteryInfoSliceTest.java} | 14 +-- 8 files changed, 242 insertions(+), 12 deletions(-) create mode 100644 res/drawable/ic_battery_saver_accent_24dp.xml create mode 100644 src/com/android/settings/homepage/contextualcards/conditional/BatterySaverConditionController.java rename src/com/android/settings/homepage/contextualcards/deviceinfo/{BatterySlice.java => BatteryInfoSlice.java} (96%) create mode 100644 tests/robotests/src/com/android/settings/homepage/contextualcards/conditional/BatterySaverConditionControllerTest.java rename tests/robotests/src/com/android/settings/homepage/contextualcards/deviceinfo/{BatterySliceTest.java => BatteryInfoSliceTest.java} (84%) 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( From 75c143cba9bc2a46f3990a300035bcfc484da050 Mon Sep 17 00:00:00 2001 From: Fan Zhang Date: Wed, 9 Jan 2019 16:17:18 -0800 Subject: [PATCH 2/2] Filter out unimportant battery tips from slice card. Change-Id: I2d087f345e44a7dc643715f67c9f516f5541e05e Fixes: 121115306 Test: robotests --- .../slices/BatteryFixSlice.java | 35 +++++++++++-------- .../slices/BatteryFixSliceTest.java | 25 +++++++++++-- 2 files changed, 44 insertions(+), 16 deletions(-) diff --git a/src/com/android/settings/homepage/contextualcards/slices/BatteryFixSlice.java b/src/com/android/settings/homepage/contextualcards/slices/BatteryFixSlice.java index 531501b3b5e..c0cfb3f1430 100644 --- a/src/com/android/settings/homepage/contextualcards/slices/BatteryFixSlice.java +++ b/src/com/android/settings/homepage/contextualcards/slices/BatteryFixSlice.java @@ -25,7 +25,6 @@ import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; import android.net.Uri; -import android.util.Log; import androidx.annotation.VisibleForTesting; import androidx.annotation.WorkerThread; @@ -49,6 +48,7 @@ import com.android.settings.slices.SliceBackgroundWorker; import com.android.settings.slices.SliceBuilderUtils; import com.android.settingslib.utils.ThreadUtils; +import java.util.Arrays; import java.util.List; public class BatteryFixSlice implements CustomSliceable { @@ -58,6 +58,11 @@ public class BatteryFixSlice implements CustomSliceable { @VisibleForTesting static final String KEY_CURRENT_TIPS_TYPE = "current_tip_type"; + private static final List UNIMPORTANT_BATTERY_TIPS = Arrays.asList( + BatteryTip.TipType.SUMMARY, + BatteryTip.TipType.BATTERY_SAVER + ); + private static final String TAG = "BatteryFixSlice"; private final Context mContext; @@ -78,7 +83,7 @@ public class BatteryFixSlice implements CustomSliceable { .setAccentColor(-1); // TipType.SUMMARY is battery good - if (readBatteryTipAvailabilityCache(mContext) == BatteryTip.TipType.SUMMARY) { + if (UNIMPORTANT_BATTERY_TIPS.contains(readBatteryTipAvailabilityCache(mContext))) { return buildBatteryGoodSlice(sliceBuilder, true); } @@ -91,19 +96,21 @@ public class BatteryFixSlice implements CustomSliceable { } for (BatteryTip batteryTip : batteryTips) { - if (batteryTip.getState() != BatteryTip.StateType.INVISIBLE) { - final IconCompat icon = IconCompat.createWithResource(mContext, batteryTip.getIconId()); - final SliceAction primaryAction = SliceAction.createDeeplink(getPrimaryAction(), - icon, - ListBuilder.ICON_IMAGE, - batteryTip.getTitle(mContext)); - sliceBuilder.addRow(new RowBuilder() - .setTitleItem(icon, ListBuilder.ICON_IMAGE) - .setTitle(batteryTip.getTitle(mContext)) - .setSubtitle(batteryTip.getSummary(mContext)) - .setPrimaryAction(primaryAction)); - break; + if (batteryTip.getState() == BatteryTip.StateType.INVISIBLE) { + continue; } + final IconCompat icon = IconCompat.createWithResource(mContext, + batteryTip.getIconId()); + final SliceAction primaryAction = SliceAction.createDeeplink(getPrimaryAction(), + icon, + ListBuilder.ICON_IMAGE, + batteryTip.getTitle(mContext)); + sliceBuilder.addRow(new RowBuilder() + .setTitleItem(icon, ListBuilder.ICON_IMAGE) + .setTitle(batteryTip.getTitle(mContext)) + .setSubtitle(batteryTip.getSummary(mContext)) + .setPrimaryAction(primaryAction)); + break; } return sliceBuilder.build(); } diff --git a/tests/robotests/src/com/android/settings/homepage/contextualcards/slices/BatteryFixSliceTest.java b/tests/robotests/src/com/android/settings/homepage/contextualcards/slices/BatteryFixSliceTest.java index ff08c6aa312..1c299cb6c40 100644 --- a/tests/robotests/src/com/android/settings/homepage/contextualcards/slices/BatteryFixSliceTest.java +++ b/tests/robotests/src/com/android/settings/homepage/contextualcards/slices/BatteryFixSliceTest.java @@ -26,6 +26,8 @@ import static com.google.common.truth.Truth.assertThat; import android.content.Context; import android.content.SharedPreferences; +import androidx.slice.Slice; +import androidx.slice.SliceMetadata; import androidx.slice.SliceProvider; import androidx.slice.widget.SliceLiveData; @@ -55,11 +57,13 @@ import java.util.List; public class BatteryFixSliceTest { private Context mContext; + private BatteryFixSlice mSlice; @Before public void setUp() { MockitoAnnotations.initMocks(this); mContext = RuntimeEnvironment.application; + mSlice = new BatteryFixSlice(mContext); // Set-up specs for SliceMetadata. SliceProvider.setSpecs(SliceLiveData.SUPPORTED_SPECS); @@ -71,7 +75,7 @@ public class BatteryFixSliceTest { } @Test - public void readBatteryTipfromPref_readCorrectValue() { + public void readBatteryTipFromPref_readCorrectValue() { int target = 111; final SharedPreferences.Editor editor = mContext.getSharedPreferences(PREFS, MODE_PRIVATE).edit(); @@ -90,7 +94,7 @@ public class BatteryFixSliceTest { public void updateBatteryTipAvailabilityCache_writeCorrectValue() { final List tips = new ArrayList<>(); tips.add(new LowBatteryTip(BatteryTip.StateType.INVISIBLE, false, "")); - tips.add(new EarlyWarningTip(BatteryTip.StateType.HANDLED, false)); + tips.add(new EarlyWarningTip(BatteryTip.StateType.NEW, false)); ShadowBatteryTipLoader.setBatteryTips(tips); BatteryFixSlice.updateBatteryTipAvailabilityCache(mContext); @@ -99,6 +103,23 @@ public class BatteryFixSliceTest { BatteryTip.TipType.BATTERY_SAVER); } + @Test + @Config(shadows = { + ShadowBatteryStatsHelperLoader.class, + ShadowBatteryTipLoader.class + }) + public void getSlice_unimportantSlice_shouldSkip() { + final List tips = new ArrayList<>(); + tips.add(new LowBatteryTip(BatteryTip.StateType.INVISIBLE, false, "")); + tips.add(new EarlyWarningTip(BatteryTip.StateType.NEW, false)); + ShadowBatteryTipLoader.setBatteryTips(tips); + + BatteryFixSlice.updateBatteryTipAvailabilityCache(mContext); + final Slice slice = mSlice.getSlice(); + + assertThat(SliceMetadata.from(mContext, slice).isErrorSlice()).isTrue(); + } + @Implements(BatteryStatsHelperLoader.class) public static class ShadowBatteryStatsHelperLoader {