[BatteryTips] Separate the low battery tips

Bug: 315748218
Test: atest SettingsRoboTestStub:com.android.settings.fuelgauge.batterytip.detectors.LowBatteryDetectorTest
Change-Id: Id973be37012da414d6125a4dd29ec4522ab80e95
This commit is contained in:
pajacechen
2024-01-31 16:11:55 +08:00
parent c7e0649c2e
commit 5dd8460c3e
6 changed files with 81 additions and 47 deletions

View File

@@ -18,6 +18,7 @@ package com.android.settings.fuelgauge;
import android.content.Context; import android.content.Context;
import com.android.settings.fuelgauge.batterytip.BatteryTipPolicy;
import com.android.settings.fuelgauge.batterytip.tips.BatteryTip; import com.android.settings.fuelgauge.batterytip.tips.BatteryTip;
import java.util.List; import java.util.List;
@@ -35,5 +36,9 @@ public interface BatterySettingsFeatureProvider {
boolean isBatteryInfoEnabled(Context context); boolean isBatteryInfoEnabled(Context context);
/** A way to add more battery tip detectors. */ /** A way to add more battery tip detectors. */
void addBatteryTipDetector(Context context, List<BatteryTip> tips, BatteryInfo batteryInfo); void addBatteryTipDetector(
Context context,
List<BatteryTip> batteryTips,
BatteryInfo batteryInfo,
BatteryTipPolicy batteryTipPolicy);
} }

View File

@@ -18,6 +18,8 @@ package com.android.settings.fuelgauge;
import android.content.Context; import android.content.Context;
import com.android.settings.fuelgauge.batterytip.BatteryTipPolicy;
import com.android.settings.fuelgauge.batterytip.detectors.LowBatteryDetector;
import com.android.settings.fuelgauge.batterytip.tips.BatteryTip; import com.android.settings.fuelgauge.batterytip.tips.BatteryTip;
import java.util.List; import java.util.List;
@@ -42,5 +44,10 @@ public class BatterySettingsFeatureProviderImpl implements BatterySettingsFeatur
@Override @Override
public void addBatteryTipDetector( public void addBatteryTipDetector(
Context context, List<BatteryTip> tips, BatteryInfo batteryInfo) {} Context context,
List<BatteryTip> batteryTips,
BatteryInfo batteryInfo,
BatteryTipPolicy batteryTipPolicy) {
batteryTips.add(new LowBatteryDetector(context, batteryTipPolicy, batteryInfo).detect());
}
} }

View File

@@ -18,7 +18,6 @@ package com.android.settings.fuelgauge.batterytip;
import android.content.Context; import android.content.Context;
import android.os.BatteryUsageStats; import android.os.BatteryUsageStats;
import android.os.PowerManager;
import androidx.annotation.VisibleForTesting; import androidx.annotation.VisibleForTesting;
@@ -27,7 +26,6 @@ import com.android.settings.fuelgauge.BatteryUtils;
import com.android.settings.fuelgauge.batterytip.detectors.BatteryDefenderDetector; import com.android.settings.fuelgauge.batterytip.detectors.BatteryDefenderDetector;
import com.android.settings.fuelgauge.batterytip.detectors.HighUsageDetector; import com.android.settings.fuelgauge.batterytip.detectors.HighUsageDetector;
import com.android.settings.fuelgauge.batterytip.detectors.IncompatibleChargerDetector; import com.android.settings.fuelgauge.batterytip.detectors.IncompatibleChargerDetector;
import com.android.settings.fuelgauge.batterytip.detectors.LowBatteryDetector;
import com.android.settings.fuelgauge.batterytip.tips.BatteryTip; import com.android.settings.fuelgauge.batterytip.tips.BatteryTip;
import com.android.settings.overlay.FeatureFactory; import com.android.settings.overlay.FeatureFactory;
import com.android.settingslib.utils.AsyncLoaderCompat; import com.android.settingslib.utils.AsyncLoaderCompat;
@@ -56,19 +54,18 @@ public class BatteryTipLoader extends AsyncLoaderCompat<List<BatteryTip>> {
@Override @Override
public List<BatteryTip> loadInBackground() { public List<BatteryTip> loadInBackground() {
final List<BatteryTip> tips = new ArrayList<>(); final List<BatteryTip> tips = new ArrayList<>();
final BatteryTipPolicy policy = new BatteryTipPolicy(getContext()); final BatteryTipPolicy batteryTipPolicy = new BatteryTipPolicy(getContext());
final BatteryInfo batteryInfo = mBatteryUtils.getBatteryInfo(TAG); final BatteryInfo batteryInfo = mBatteryUtils.getBatteryInfo(TAG);
final Context context = getContext().getApplicationContext(); final Context context = getContext().getApplicationContext();
final boolean isPowerSaveMode =
context.getSystemService(PowerManager.class).isPowerSaveMode();
tips.add(new LowBatteryDetector(context, policy, batteryInfo, isPowerSaveMode).detect()); tips.add(
tips.add(new HighUsageDetector(context, policy, mBatteryUsageStats, batteryInfo).detect()); new HighUsageDetector(context, batteryTipPolicy, mBatteryUsageStats, batteryInfo)
.detect());
tips.add(new BatteryDefenderDetector(batteryInfo, context).detect()); tips.add(new BatteryDefenderDetector(batteryInfo, context).detect());
tips.add(new IncompatibleChargerDetector(context).detect()); tips.add(new IncompatibleChargerDetector(context).detect());
FeatureFactory.getFeatureFactory() FeatureFactory.getFeatureFactory()
.getBatterySettingsFeatureProvider() .getBatterySettingsFeatureProvider()
.addBatteryTipDetector(context, tips, batteryInfo); .addBatteryTipDetector(context, tips, batteryInfo, batteryTipPolicy);
Collections.sort(tips); Collections.sort(tips);
return tips; return tips;
} }

View File

@@ -17,6 +17,7 @@
package com.android.settings.fuelgauge.batterytip.detectors; package com.android.settings.fuelgauge.batterytip.detectors;
import android.content.Context; import android.content.Context;
import android.os.PowerManager;
import com.android.settings.fuelgauge.BatteryInfo; import com.android.settings.fuelgauge.BatteryInfo;
import com.android.settings.fuelgauge.batterytip.BatteryTipPolicy; import com.android.settings.fuelgauge.batterytip.BatteryTipPolicy;
@@ -26,37 +27,33 @@ import com.android.settings.fuelgauge.batterytip.tips.LowBatteryTip;
/** Detect whether the battery is too low */ /** Detect whether the battery is too low */
public class LowBatteryDetector implements BatteryTipDetector { public class LowBatteryDetector implements BatteryTipDetector {
private final BatteryInfo mBatteryInfo; private final BatteryInfo mBatteryInfo;
private final BatteryTipPolicy mPolicy; private final BatteryTipPolicy mBatteryTipPolicy;
private final boolean mIsPowerSaveMode; private final boolean mIsPowerSaveMode;
private final int mWarningLevel; private final int mWarningLevel;
public LowBatteryDetector( public LowBatteryDetector(
Context context, Context context, BatteryTipPolicy batteryTipPolicy, BatteryInfo batteryInfo) {
BatteryTipPolicy policy, mBatteryTipPolicy = batteryTipPolicy;
BatteryInfo batteryInfo,
boolean isPowerSaveMode) {
mPolicy = policy;
mBatteryInfo = batteryInfo; mBatteryInfo = batteryInfo;
mWarningLevel = mWarningLevel =
context.getResources() context.getResources()
.getInteger(com.android.internal.R.integer.config_lowBatteryWarningLevel); .getInteger(com.android.internal.R.integer.config_lowBatteryWarningLevel);
mIsPowerSaveMode = isPowerSaveMode; mIsPowerSaveMode = context.getSystemService(PowerManager.class).isPowerSaveMode();
} }
@Override @Override
public BatteryTip detect() { public BatteryTip detect() {
final boolean lowBattery = mBatteryInfo.batteryLevel <= mWarningLevel; final boolean lowBattery = mBatteryInfo.batteryLevel <= mWarningLevel;
final boolean lowBatteryEnabled = mPolicy.lowBatteryEnabled && !mIsPowerSaveMode; final boolean lowBatteryEnabled = mBatteryTipPolicy.lowBatteryEnabled && !mIsPowerSaveMode;
final boolean dischargingLowBatteryState = final boolean dischargingLowBatteryState =
mPolicy.testLowBatteryTip || (mBatteryInfo.discharging && lowBattery); mBatteryTipPolicy.testLowBatteryTip || (mBatteryInfo.discharging && lowBattery);
int state = BatteryTip.StateType.INVISIBLE;
// Show it as new if in test or in discharging low battery state, // Show it as new if in test or in discharging low battery state,
// dismiss it if battery saver is on or disabled by config. // dismiss it if battery saver is on or disabled by config.
if (lowBatteryEnabled && dischargingLowBatteryState) { final int state =
state = BatteryTip.StateType.NEW; lowBatteryEnabled && dischargingLowBatteryState
} ? BatteryTip.StateType.NEW
: BatteryTip.StateType.INVISIBLE;
return new LowBatteryTip(state, mIsPowerSaveMode); return new LowBatteryTip(state, mIsPowerSaveMode);
} }

View File

@@ -22,11 +22,17 @@ import android.content.Context;
import androidx.test.core.app.ApplicationProvider; import androidx.test.core.app.ApplicationProvider;
import com.android.settings.fuelgauge.batterytip.BatteryTipPolicy;
import com.android.settings.fuelgauge.batterytip.tips.BatteryTip;
import com.android.settings.fuelgauge.batterytip.tips.LowBatteryTip;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner; import org.robolectric.RobolectricTestRunner;
import java.util.ArrayList;
@RunWith(RobolectricTestRunner.class) @RunWith(RobolectricTestRunner.class)
public class BatterySettingsFeatureProviderImplTest { public class BatterySettingsFeatureProviderImplTest {
private BatterySettingsFeatureProviderImpl mImpl; private BatterySettingsFeatureProviderImpl mImpl;
@@ -52,4 +58,15 @@ public class BatterySettingsFeatureProviderImplTest {
public void isBatteryInfoEnabled_returnFalse() { public void isBatteryInfoEnabled_returnFalse() {
assertThat(mImpl.isBatteryInfoEnabled(mContext)).isFalse(); assertThat(mImpl.isBatteryInfoEnabled(mContext)).isFalse();
} }
@Test
public void addBatteryTipDetector_containsLowBatteryTip() {
var tips = new ArrayList<BatteryTip>();
mImpl.addBatteryTipDetector(
mContext, tips, new BatteryInfo(), new BatteryTipPolicy(mContext));
var expectedResult = tips.stream().anyMatch(tip -> tip instanceof LowBatteryTip);
assertThat(expectedResult).isTrue();
}
} }

View File

@@ -19,20 +19,25 @@ package com.android.settings.fuelgauge.batterytip.detectors;
import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.spy; import static org.mockito.Mockito.spy;
import static org.robolectric.Shadows.shadowOf;
import android.content.Context; import android.content.Context;
import android.os.PowerManager;
import androidx.test.core.app.ApplicationProvider;
import com.android.settings.fuelgauge.BatteryInfo; import com.android.settings.fuelgauge.BatteryInfo;
import com.android.settings.fuelgauge.batterytip.BatteryTipPolicy; import com.android.settings.fuelgauge.batterytip.BatteryTipPolicy;
import com.android.settings.fuelgauge.batterytip.tips.BatteryTip; import com.android.settings.fuelgauge.batterytip.tips.BatteryTip;
import org.junit.Before; import org.junit.Before;
import org.junit.Rule;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.mockito.Mock; import org.mockito.Mock;
import org.mockito.MockitoAnnotations; import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
import org.robolectric.RobolectricTestRunner; import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.util.ReflectionHelpers; import org.robolectric.util.ReflectionHelpers;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
@@ -40,73 +45,79 @@ import java.util.concurrent.TimeUnit;
@RunWith(RobolectricTestRunner.class) @RunWith(RobolectricTestRunner.class)
public class LowBatteryDetectorTest { public class LowBatteryDetectorTest {
@Rule public MockitoRule mockitoRule = MockitoJUnit.rule();
@Mock private BatteryInfo mBatteryInfo; @Mock private BatteryInfo mBatteryInfo;
private BatteryTipPolicy mPolicy;
private BatteryTipPolicy mBatteryTipPolicy;
private LowBatteryDetector mLowBatteryDetector; private LowBatteryDetector mLowBatteryDetector;
private Context mContext; private Context mContext;
private PowerManager mPowerManager;
@Before @Before
public void setUp() { public void setUp() {
MockitoAnnotations.initMocks(this); mContext = ApplicationProvider.getApplicationContext();
mBatteryTipPolicy = spy(new BatteryTipPolicy(mContext));
mPolicy = spy(new BatteryTipPolicy(RuntimeEnvironment.application)); mPowerManager = mContext.getSystemService(PowerManager.class);
mContext = RuntimeEnvironment.application; shadowOf(mPowerManager).setIsPowerSaveMode(false);
ReflectionHelpers.setField(mPolicy, "lowBatteryEnabled", true);
ReflectionHelpers.setField(mBatteryTipPolicy, "lowBatteryEnabled", true);
mBatteryInfo.discharging = true; mBatteryInfo.discharging = true;
mLowBatteryDetector = mLowBatteryDetector = new LowBatteryDetector(mContext, mBatteryTipPolicy, mBatteryInfo);
new LowBatteryDetector(
mContext, mPolicy, mBatteryInfo, false /* isPowerSaveMode */);
} }
@Test @Test
public void testDetect_disabledByPolicy_tipInvisible() { public void detect_disabledByPolicy_tipInvisible() {
ReflectionHelpers.setField(mPolicy, "lowBatteryEnabled", false); ReflectionHelpers.setField(mBatteryTipPolicy, "lowBatteryEnabled", false);
mLowBatteryDetector = shadowOf(mPowerManager).setIsPowerSaveMode(true);
new LowBatteryDetector(mContext, mPolicy, mBatteryInfo, true /* isPowerSaveMode */); mLowBatteryDetector = new LowBatteryDetector(mContext, mBatteryTipPolicy, mBatteryInfo);
assertThat(mLowBatteryDetector.detect().isVisible()).isFalse(); assertThat(mLowBatteryDetector.detect().isVisible()).isFalse();
} }
@Test @Test
public void testDetect_enabledByTest_tipNew() { public void detect_enabledByTest_tipNew() {
ReflectionHelpers.setField(mPolicy, "testLowBatteryTip", true); ReflectionHelpers.setField(mBatteryTipPolicy, "testLowBatteryTip", true);
assertThat(mLowBatteryDetector.detect().getState()).isEqualTo(BatteryTip.StateType.NEW); assertThat(mLowBatteryDetector.detect().getState()).isEqualTo(BatteryTip.StateType.NEW);
} }
@Test @Test
public void testDetect_lowBattery_tipNew() { public void detect_lowBattery_tipNew() {
mBatteryInfo.batteryLevel = 20; mBatteryInfo.batteryLevel = 20;
mBatteryInfo.remainingTimeUs = TimeUnit.DAYS.toMillis(1); mBatteryInfo.remainingTimeUs = TimeUnit.DAYS.toMillis(1);
assertThat(mLowBatteryDetector.detect().getState()).isEqualTo(BatteryTip.StateType.NEW); assertThat(mLowBatteryDetector.detect().getState()).isEqualTo(BatteryTip.StateType.NEW);
} }
@Test @Test
public void testDetect_batterySaverOn_tipInvisible() { public void detect_batterySaverOn_tipInvisible() {
mLowBatteryDetector = shadowOf(mPowerManager).setIsPowerSaveMode(true);
new LowBatteryDetector(mContext, mPolicy, mBatteryInfo, true /* isPowerSaveMode */); mLowBatteryDetector = new LowBatteryDetector(mContext, mBatteryTipPolicy, mBatteryInfo);
assertThat(mLowBatteryDetector.detect().getState()) assertThat(mLowBatteryDetector.detect().getState())
.isEqualTo(BatteryTip.StateType.INVISIBLE); .isEqualTo(BatteryTip.StateType.INVISIBLE);
} }
@Test @Test
public void testDetect_charging_tipInvisible() { public void detect_charging_tipInvisible() {
mBatteryInfo.discharging = false; mBatteryInfo.discharging = false;
assertThat(mLowBatteryDetector.detect().isVisible()).isFalse(); assertThat(mLowBatteryDetector.detect().isVisible()).isFalse();
} }
@Test @Test
public void testDetect_lowTimeEstimation_tipInvisible() { public void detect_lowTimeEstimation_tipInvisible() {
mBatteryInfo.batteryLevel = 50; mBatteryInfo.batteryLevel = 50;
mBatteryInfo.remainingTimeUs = TimeUnit.MINUTES.toMillis(1); mBatteryInfo.remainingTimeUs = TimeUnit.MINUTES.toMillis(1);
assertThat(mLowBatteryDetector.detect().isVisible()).isFalse(); assertThat(mLowBatteryDetector.detect().isVisible()).isFalse();
} }
@Test @Test
public void testDetect_noEarlyWarning_tipInvisible() { public void detect_noEarlyWarning_tipInvisible() {
mBatteryInfo.remainingTimeUs = TimeUnit.DAYS.toMicros(1); mBatteryInfo.remainingTimeUs = TimeUnit.DAYS.toMicros(1);
mBatteryInfo.batteryLevel = 100; mBatteryInfo.batteryLevel = 100;