Merge "Add charging string V2 for settings" into 24D1-dev

This commit is contained in:
YK Hung
2024-04-16 01:25:31 +00:00
committed by Android (Google) Code Review
4 changed files with 501 additions and 46 deletions

View File

@@ -88,6 +88,14 @@ public class BatteryHeaderPreferenceController extends BasePreferenceController
return info.statusLabel; return info.statusLabel;
} else if (info.statusLabel != null && !info.discharging) { } else if (info.statusLabel != null && !info.discharging) {
// Charging state // Charging state
if (com.android.settingslib.fuelgauge.BatteryUtils.isChargingStringV2Enabled()) {
return info.isFastCharging
? mContext.getString(
R.string.battery_state_and_duration,
info.statusLabel,
info.remainingLabel)
: info.remainingLabel;
}
return mContext.getString( return mContext.getString(
R.string.battery_state_and_duration, info.statusLabel, info.remainingLabel); R.string.battery_state_and_duration, info.statusLabel, info.remainingLabel);
} else if (mPowerManager.isPowerSaveMode()) { } else if (mPowerManager.isPowerSaveMode()) {

View File

@@ -37,6 +37,8 @@ import com.android.internal.os.BatteryStatsHistoryIterator;
import com.android.settings.Utils; import com.android.settings.Utils;
import com.android.settings.overlay.FeatureFactory; import com.android.settings.overlay.FeatureFactory;
import com.android.settings.widget.UsageView; import com.android.settings.widget.UsageView;
import com.android.settingslib.R;
import com.android.settingslib.fuelgauge.BatteryStatus;
import com.android.settingslib.fuelgauge.Estimate; import com.android.settingslib.fuelgauge.Estimate;
import com.android.settingslib.fuelgauge.EstimateKt; import com.android.settingslib.fuelgauge.EstimateKt;
import com.android.settingslib.utils.PowerUtil; import com.android.settingslib.utils.PowerUtil;
@@ -52,6 +54,7 @@ public class BatteryInfo {
public int pluggedStatus; public int pluggedStatus;
public boolean discharging = true; public boolean discharging = true;
public boolean isBatteryDefender; public boolean isBatteryDefender;
public boolean isFastCharging;
public long remainingTimeUs = 0; public long remainingTimeUs = 0;
public long averageTimeToDischarge = EstimateKt.AVERAGE_TIME_TO_DISCHARGE_UNKNOWN; public long averageTimeToDischarge = EstimateKt.AVERAGE_TIME_TO_DISCHARGE_UNKNOWN;
public String batteryPercentString; public String batteryPercentString;
@@ -143,13 +146,13 @@ public class BatteryInfo {
parseBatteryHistory(parserList); parseBatteryHistory(parserList);
String timeString = String timeString =
context.getString( context.getString(
com.android.settingslib.R.string.charge_length_format, R.string.charge_length_format,
Formatter.formatShortElapsedTime(context, timePeriod)); Formatter.formatShortElapsedTime(context, timePeriod));
String remaining = ""; String remaining = "";
if (remainingTimeUs != 0) { if (remainingTimeUs != 0) {
remaining = remaining =
context.getString( context.getString(
com.android.settingslib.R.string.remaining_length_format, R.string.remaining_length_format,
Formatter.formatShortElapsedTime(context, remainingTimeUs / 1000)); Formatter.formatShortElapsedTime(context, remainingTimeUs / 1000));
} }
view.setBottomLabels(new CharSequence[] {timeString, remaining}); view.setBottomLabels(new CharSequence[] {timeString, remaining});
@@ -291,8 +294,8 @@ public class BatteryInfo {
@NonNull BatteryUsageStats batteryUsageStats, @NonNull BatteryUsageStats batteryUsageStats,
Estimate estimate, Estimate estimate,
long elapsedRealtimeUs, long elapsedRealtimeUs,
boolean shortString) { boolean shortString,
final long startTime = System.currentTimeMillis(); long currentTimeMs) {
final boolean isCompactStatus = final boolean isCompactStatus =
context.getResources() context.getResources()
.getBoolean(com.android.settings.R.bool.config_use_compact_battery_status); .getBoolean(com.android.settings.R.bool.config_use_compact_battery_status);
@@ -313,22 +316,51 @@ public class BatteryInfo {
info.batteryStatus = info.batteryStatus =
batteryBroadcast.getIntExtra( batteryBroadcast.getIntExtra(
BatteryManager.EXTRA_STATUS, BatteryManager.BATTERY_STATUS_UNKNOWN); BatteryManager.EXTRA_STATUS, BatteryManager.BATTERY_STATUS_UNKNOWN);
info.isFastCharging =
BatteryStatus.getChargingSpeed(context, batteryBroadcast)
== BatteryStatus.CHARGING_FAST;
if (!info.mCharging) { if (!info.mCharging) {
updateBatteryInfoDischarging(context, shortString, estimate, info); updateBatteryInfoDischarging(context, shortString, estimate, info);
} else { } else {
updateBatteryInfoCharging( updateBatteryInfoCharging(
context, batteryBroadcast, batteryUsageStats, info, isCompactStatus); context,
batteryBroadcast,
batteryUsageStats,
info,
isCompactStatus,
currentTimeMs);
} }
BatteryUtils.logRuntime(LOG_TAG, "time for getBatteryInfo", startTime); BatteryUtils.logRuntime(LOG_TAG, "time for getBatteryInfo", currentTimeMs);
return info; return info;
} }
/** Returns a {@code BatteryInfo} with battery and charging relative information. */
@WorkerThread
public static BatteryInfo getBatteryInfo(
Context context,
Intent batteryBroadcast,
BatteryUsageStats batteryUsageStats,
Estimate estimate,
long elapsedRealtimeUs,
boolean shortString) {
long currentTimeMs = System.currentTimeMillis();
return getBatteryInfo(
context,
batteryBroadcast,
batteryUsageStats,
estimate,
elapsedRealtimeUs,
shortString,
currentTimeMs);
}
private static void updateBatteryInfoCharging( private static void updateBatteryInfoCharging(
Context context, Context context,
Intent batteryBroadcast, Intent batteryBroadcast,
BatteryUsageStats stats, BatteryUsageStats stats,
BatteryInfo info, BatteryInfo info,
boolean compactStatus) { boolean compactStatus,
long currentTimeMs) {
final Resources resources = context.getResources(); final Resources resources = context.getResources();
final long chargeTimeMs = stats.getChargeTimeRemainingMs(); final long chargeTimeMs = stats.getChargeTimeRemainingMs();
if (getSettingsChargeTimeRemaining(context) != chargeTimeMs) { if (getSettingsChargeTimeRemaining(context) != chargeTimeMs) {
@@ -350,7 +382,7 @@ public class BatteryInfo {
|| dockDefenderMode == BatteryUtils.DockDefenderMode.ACTIVE) { || dockDefenderMode == BatteryUtils.DockDefenderMode.ACTIVE) {
// Battery defender active, battery charging paused // Battery defender active, battery charging paused
info.remainingLabel = null; info.remainingLabel = null;
int chargingLimitedResId = com.android.settingslib.R.string.power_charging_limited; int chargingLimitedResId = R.string.power_charging_limited;
info.chargeLabel = context.getString(chargingLimitedResId, info.batteryPercentString); info.chargeLabel = context.getString(chargingLimitedResId, info.batteryPercentString);
} else if ((chargeTimeMs > 0 } else if ((chargeTimeMs > 0
&& status != BatteryManager.BATTERY_STATUS_FULL && status != BatteryManager.BATTERY_STATUS_FULL
@@ -358,30 +390,29 @@ public class BatteryInfo {
|| dockDefenderMode == BatteryUtils.DockDefenderMode.TEMPORARILY_BYPASSED) { || dockDefenderMode == BatteryUtils.DockDefenderMode.TEMPORARILY_BYPASSED) {
// Battery is charging to full // Battery is charging to full
info.remainingTimeUs = PowerUtil.convertMsToUs(chargeTimeMs); info.remainingTimeUs = PowerUtil.convertMsToUs(chargeTimeMs);
final CharSequence timeString =
StringUtil.formatElapsedTime( int resId = getChargingDurationResId(info.isFastCharging);
context,
(double) PowerUtil.convertUsToMs(info.remainingTimeUs),
false /* withSeconds */,
true /* collapseTimeUnit */);
int resId = com.android.settingslib.R.string.power_charging_duration;
info.remainingLabel = info.remainingLabel =
chargeTimeMs <= 0 chargeTimeMs <= 0
? null ? null
: context.getString( : getPowerRemainingChargingLabel(
com.android.settingslib.R.string context, chargeTimeMs, info.isFastCharging, currentTimeMs);
.power_remaining_charging_duration_only,
timeString);
info.chargeLabel = info.chargeLabel =
chargeTimeMs <= 0 chargeTimeMs <= 0
? info.batteryPercentString ? info.batteryPercentString
: context.getString(resId, info.batteryPercentString, timeString); : getChargeLabelWithTimeToFull(
context,
resId,
info.batteryPercentString,
chargeTimeMs,
info.isFastCharging,
currentTimeMs);
} else if (dockDefenderMode == BatteryUtils.DockDefenderMode.FUTURE_BYPASS) { } else if (dockDefenderMode == BatteryUtils.DockDefenderMode.FUTURE_BYPASS) {
// Dock defender will be triggered in the future, charging will be optimized. // Dock defender will be triggered in the future, charging will be optimized.
info.chargeLabel = info.chargeLabel =
context.getString( context.getString(
com.android.settingslib.R.string.power_charging_future_paused, R.string.power_charging_future_paused, info.batteryPercentString);
info.batteryPercentString);
} else { } else {
final String chargeStatusLabel = final String chargeStatusLabel =
Utils.getBatteryStatus(context, batteryBroadcast, compactStatus); Utils.getBatteryStatus(context, batteryBroadcast, compactStatus);
@@ -390,12 +421,70 @@ public class BatteryInfo {
info.batteryLevel == 100 info.batteryLevel == 100
? info.batteryPercentString ? info.batteryPercentString
: resources.getString( : resources.getString(
com.android.settingslib.R.string.power_charging, R.string.power_charging,
info.batteryPercentString, info.batteryPercentString,
chargeStatusLabel); chargeStatusLabel);
} }
} }
private static CharSequence getPowerRemainingChargingLabel(
Context context, long remainingTimeMs, boolean isFastCharging, long currentTimeMs) {
if (com.android.settingslib.fuelgauge.BatteryUtils.isChargingStringV2Enabled()) {
int chargeLabelResId =
isFastCharging
? R.string.power_remaining_fast_charging_duration_only_v2
: R.string.power_remaining_charging_duration_only_v2;
String timeString =
PowerUtil.getTargetTimeShortString(context, remainingTimeMs, currentTimeMs);
return context.getString(chargeLabelResId, timeString);
}
final CharSequence timeString =
StringUtil.formatElapsedTime(
context,
remainingTimeMs,
/* withSeconds= */ false,
/* collapseTimeUnit= */ true);
return context.getString(R.string.power_remaining_charging_duration_only, timeString);
}
private static CharSequence getChargeLabelWithTimeToFull(
Context context,
int chargeLabelResId,
String batteryPercentString,
long chargeTimeMs,
boolean isFastCharging,
long currentTimeMs) {
if (com.android.settingslib.fuelgauge.BatteryUtils.isChargingStringV2Enabled()) {
var timeString =
PowerUtil.getTargetTimeShortString(context, chargeTimeMs, currentTimeMs);
return isFastCharging
? context.getString(
chargeLabelResId,
batteryPercentString,
context.getString(R.string.battery_info_status_charging_fast_v2),
timeString)
: context.getString(chargeLabelResId, batteryPercentString, timeString);
} else {
var timeString =
StringUtil.formatElapsedTime(
context,
(double) chargeTimeMs,
/* withSeconds= */ false,
/* collapseTimeUnit= */ true);
return context.getString(chargeLabelResId, batteryPercentString, timeString);
}
}
private static int getChargingDurationResId(boolean isFastCharging) {
if (com.android.settingslib.fuelgauge.BatteryUtils.isChargingStringV2Enabled()) {
return isFastCharging
? R.string.power_fast_charging_duration_v2
: R.string.power_charging_duration_v2;
}
return R.string.power_charging_duration;
}
private static void updateBatteryInfoDischarging( private static void updateBatteryInfoDischarging(
Context context, boolean shortString, Estimate estimate, BatteryInfo info) { Context context, boolean shortString, Estimate estimate, BatteryInfo info) {
final long drainTimeUs = PowerUtil.convertMsToUs(estimate.getEstimateMillis()); final long drainTimeUs = PowerUtil.convertMsToUs(estimate.getEstimateMillis());

View File

@@ -44,6 +44,7 @@ import com.android.settings.testutils.BatteryTestUtils;
import com.android.settings.testutils.shadow.ShadowEntityHeaderController; import com.android.settings.testutils.shadow.ShadowEntityHeaderController;
import com.android.settings.testutils.shadow.ShadowUtils; import com.android.settings.testutils.shadow.ShadowUtils;
import com.android.settings.widget.EntityHeaderController; import com.android.settings.widget.EntityHeaderController;
import com.android.settingslib.fuelgauge.BatteryUtils;
import com.android.settingslib.widget.UsageProgressBarPreference; import com.android.settingslib.widget.UsageProgressBarPreference;
import org.junit.After; import org.junit.After;
@@ -107,6 +108,8 @@ public class BatteryHeaderPreferenceControllerTest {
mController = spy(new BatteryHeaderPreferenceController(mContext, PREF_KEY)); mController = spy(new BatteryHeaderPreferenceController(mContext, PREF_KEY));
mController.mBatteryUsageProgressBarPref = mBatteryUsageProgressBarPref; mController.mBatteryUsageProgressBarPref = mBatteryUsageProgressBarPref;
mController.mBatteryStatusFeatureProvider = mBatteryStatusFeatureProvider; mController.mBatteryStatusFeatureProvider = mBatteryStatusFeatureProvider;
BatteryUtils.setChargingStringV2Enabled(null);
} }
@After @After
@@ -225,6 +228,65 @@ public class BatteryHeaderPreferenceControllerTest {
verify(mBatteryUsageProgressBarPref).setBottomSummary(label); verify(mBatteryUsageProgressBarPref).setBottomSummary(label);
} }
@Test
public void updateBatteryStatus_chargingString_statusWithRemainingLabel() {
var batteryInfo =
arrangeUpdateBatteryStatusTestWithRemainingLabel(
/* remainingLabel= */ "1 hr, 40 min left until full",
/* statusLabel= */ "Charging rapidly",
/* isFastCharging= */ true,
/* isChargingStringV2= */ false);
var expectedChargingString = batteryInfo.statusLabel + "" + batteryInfo.remainingLabel;
mController.updateBatteryStatus(/* label= */ null, batteryInfo);
verify(mBatteryUsageProgressBarPref).setBottomSummary(expectedChargingString);
}
@Test
public void updateBatteryStatus_chargingStringV2FastCharging_statusWithRemainingLabel() {
var batteryInfo =
arrangeUpdateBatteryStatusTestWithRemainingLabel(
/* remainingLabel= */ "Full by 1:30 PM",
/* statusLabel= */ "Fast Charging",
/* isFastCharging= */ true,
/* isChargingStringV2= */ true);
var expectedChargingString = batteryInfo.statusLabel + "" + batteryInfo.remainingLabel;
mController.updateBatteryStatus(/* label= */ null, batteryInfo);
verify(mBatteryUsageProgressBarPref).setBottomSummary(expectedChargingString);
}
@Test
public void updateBatteryStatus_chargingStringV2NonFastCharging_remainingLabel() {
var batteryInfo =
arrangeUpdateBatteryStatusTestWithRemainingLabel(
/* remainingLabel= */ "Fully charged by 11:10 PM",
/* statusLabel= */ "Charging",
/* isFastCharging= */ false,
/* isChargingStringV2= */ true);
var expectedChargingString = batteryInfo.remainingLabel;
mController.updateBatteryStatus(/* label= */ null, batteryInfo);
verify(mBatteryUsageProgressBarPref).setBottomSummary(expectedChargingString);
}
private BatteryInfo arrangeUpdateBatteryStatusTestWithRemainingLabel(
String remainingLabel,
String statusLabel,
boolean isFastCharging,
boolean isChargingStringV2) {
BatteryUtils.setChargingStringV2Enabled(isChargingStringV2);
mBatteryInfo.isBatteryDefender = false;
mBatteryInfo.remainingLabel = remainingLabel;
mBatteryInfo.statusLabel = statusLabel;
mBatteryInfo.discharging = false;
mBatteryInfo.isFastCharging = isFastCharging;
return mBatteryInfo;
}
@Test @Test
public void updateHeaderByBatteryTips_lowBatteryTip_showLowBattery() { public void updateHeaderByBatteryTips_lowBatteryTip_showLowBattery() {
setChargingState(/* isDischarging */ true, /* updatedByStatusFeature */ false); setChargingState(/* isDischarging */ true, /* updatedByStatusFeature */ false);

View File

@@ -17,6 +17,7 @@
package com.android.settings.fuelgauge; package com.android.settings.fuelgauge;
import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertWithMessage;
import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.ArgumentMatchers.anyLong;
@@ -30,12 +31,14 @@ import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
import android.app.AlarmManager;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.os.BatteryManager; import android.os.BatteryManager;
import android.os.BatteryStats; import android.os.BatteryStats;
import android.os.BatteryUsageStats; import android.os.BatteryUsageStats;
import android.os.SystemClock; import android.os.SystemClock;
import android.os.SystemProperties;
import android.provider.Settings; import android.provider.Settings;
import android.util.SparseIntArray; import android.util.SparseIntArray;
@@ -56,6 +59,9 @@ import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment; import org.robolectric.RuntimeEnvironment;
import java.time.Duration; import java.time.Duration;
import java.time.Instant;
import java.util.Locale;
import java.util.Map;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
@RunWith(RobolectricTestRunner.class) @RunWith(RobolectricTestRunner.class)
@@ -80,11 +86,23 @@ public class BatteryInfoTest {
1000, /* estimateMillis */ 1000, /* estimateMillis */
false, /* isBasedOnUsage */ false, /* isBasedOnUsage */
1000 /* averageDischargeTime */); 1000 /* averageDischargeTime */);
private static final Map<ChargingType, Integer> CHARGING_TYPE_MAP =
Map.of(
ChargingType.WIRED, BatteryManager.BATTERY_PLUGGED_AC,
ChargingType.WIRELESS, BatteryManager.BATTERY_PLUGGED_WIRELESS,
ChargingType.DOCKED, BatteryManager.BATTERY_PLUGGED_DOCK);
private static final Map<ChargingSpeed, Integer> CHARGING_SPEED_MAP =
Map.of(
ChargingSpeed.FAST, 1501000,
ChargingSpeed.REGULAR, 1500000,
ChargingSpeed.SLOW, 999999);
private static final long UNUSED_TIME_MS = -1L;
private Intent mDisChargingBatteryBroadcast; private Intent mDisChargingBatteryBroadcast;
private Intent mChargingBatteryBroadcast; private Intent mChargingBatteryBroadcast;
private Context mContext; private Context mContext;
private FakeFeatureFactory mFeatureFactory; private FakeFeatureFactory mFeatureFactory;
@Mock private BatteryUsageStats mBatteryUsageStats; @Mock private BatteryUsageStats mBatteryUsageStats;
@Before @Before
@@ -102,10 +120,13 @@ public class BatteryInfoTest {
mContext.getContentResolver(), mContext.getContentResolver(),
BatteryUtils.SETTINGS_GLOBAL_DOCK_DEFENDER_BYPASS, BatteryUtils.SETTINGS_GLOBAL_DOCK_DEFENDER_BYPASS,
0); 0);
// Reset static cache for testing purpose.
com.android.settingslib.fuelgauge.BatteryUtils.setChargingStringV2Enabled(null);
} }
@Test @Test
public void testGetBatteryInfo_hasStatusLabel() { public void getBatteryInfo_hasStatusLabel() {
doReturn(REMAINING_TIME_NULL).when(mBatteryUsageStats).getBatteryTimeRemainingMs(); doReturn(REMAINING_TIME_NULL).when(mBatteryUsageStats).getBatteryTimeRemainingMs();
BatteryInfo info = BatteryInfo info =
BatteryInfo.getBatteryInfoOld( BatteryInfo.getBatteryInfoOld(
@@ -119,7 +140,7 @@ public class BatteryInfoTest {
} }
@Test @Test
public void testGetBatteryInfo_doNotShowChargingMethod_hasRemainingTime() { public void getBatteryInfo_doNotShowChargingMethod_hasRemainingTime() {
doReturn(REMAINING_TIME).when(mBatteryUsageStats).getChargeTimeRemainingMs(); doReturn(REMAINING_TIME).when(mBatteryUsageStats).getChargeTimeRemainingMs();
BatteryInfo info = BatteryInfo info =
BatteryInfo.getBatteryInfoOld( BatteryInfo.getBatteryInfoOld(
@@ -133,7 +154,7 @@ public class BatteryInfoTest {
} }
@Test @Test
public void testGetBatteryInfo_doNotShowChargingMethod_noRemainingTime() { public void getBatteryInfo_doNotShowChargingMethod_noRemainingTime() {
doReturn(REMAINING_TIME_NULL).when(mBatteryUsageStats).getChargeTimeRemainingMs(); doReturn(REMAINING_TIME_NULL).when(mBatteryUsageStats).getChargeTimeRemainingMs();
BatteryInfo info = BatteryInfo info =
BatteryInfo.getBatteryInfoOld( BatteryInfo.getBatteryInfoOld(
@@ -147,7 +168,7 @@ public class BatteryInfoTest {
} }
@Test @Test
public void testGetBatteryInfo_pluggedInUsingShortString_usesCorrectData() { public void getBatteryInfo_pluggedInUsingShortString_usesCorrectData() {
doReturn(TEST_CHARGE_TIME_REMAINING / 1000) doReturn(TEST_CHARGE_TIME_REMAINING / 1000)
.when(mBatteryUsageStats) .when(mBatteryUsageStats)
.getChargeTimeRemainingMs(); .getChargeTimeRemainingMs();
@@ -164,7 +185,7 @@ public class BatteryInfoTest {
} }
@Test @Test
public void testGetBatteryInfo_basedOnUsageTrueMoreThanFifteenMinutes_usesCorrectString() { public void getBatteryInfo_basedOnUsageTrueMoreThanFifteenMinutes_usesCorrectString() {
Estimate estimate = Estimate estimate =
new Estimate( new Estimate(
Duration.ofHours(4).toMillis(), Duration.ofHours(4).toMillis(),
@@ -215,7 +236,7 @@ public class BatteryInfoTest {
} }
@Test @Test
public void testGetBatteryInfo_basedOnUsageFalse_usesDefaultString() { public void getBatteryInfo_basedOnUsageFalse_usesDefaultString() {
BatteryInfo info = BatteryInfo info =
BatteryInfo.getBatteryInfo( BatteryInfo.getBatteryInfo(
mContext, mContext,
@@ -238,7 +259,7 @@ public class BatteryInfoTest {
} }
@Test @Test
public void testGetBatteryInfo_charging_usesChargeTime() { public void getBatteryInfo_charging_usesChargeTime() {
doReturn(TEST_CHARGE_TIME_REMAINING / 1000) doReturn(TEST_CHARGE_TIME_REMAINING / 1000)
.when(mBatteryUsageStats) .when(mBatteryUsageStats)
.getChargeTimeRemainingMs(); .getChargeTimeRemainingMs();
@@ -258,7 +279,7 @@ public class BatteryInfoTest {
} }
@Test @Test
public void testGetBatteryInfo_pluggedInWithFullBattery_onlyShowBatteryLevel() { public void getBatteryInfo_pluggedInWithFullBattery_onlyShowBatteryLevel() {
mChargingBatteryBroadcast.putExtra(BatteryManager.EXTRA_LEVEL, 100); mChargingBatteryBroadcast.putExtra(BatteryManager.EXTRA_LEVEL, 100);
BatteryInfo info = BatteryInfo info =
@@ -274,7 +295,7 @@ public class BatteryInfoTest {
} }
@Test @Test
public void testGetBatteryInfo_chargingWithDefender_updateChargeLabel() { public void getBatteryInfo_chargingWithDefender_updateChargeLabel() {
doReturn(TEST_CHARGE_TIME_REMAINING).when(mBatteryUsageStats).getChargeTimeRemainingMs(); doReturn(TEST_CHARGE_TIME_REMAINING).when(mBatteryUsageStats).getChargeTimeRemainingMs();
mChargingBatteryBroadcast.putExtra( mChargingBatteryBroadcast.putExtra(
BatteryManager.EXTRA_CHARGING_STATUS, BatteryManager.EXTRA_CHARGING_STATUS,
@@ -294,7 +315,7 @@ public class BatteryInfoTest {
} }
@Test @Test
public void testGetBatteryInfo_getChargeTimeRemaining_updateSettingsGlobal() { public void getBatteryInfo_getChargeTimeRemaining_updateSettingsGlobal() {
doReturn(TEST_CHARGE_TIME_REMAINING).when(mBatteryUsageStats).getChargeTimeRemainingMs(); doReturn(TEST_CHARGE_TIME_REMAINING).when(mBatteryUsageStats).getChargeTimeRemainingMs();
BatteryInfo.getBatteryInfo( BatteryInfo.getBatteryInfo(
@@ -310,7 +331,7 @@ public class BatteryInfoTest {
} }
@Test @Test
public void testGetBatteryInfo_differentChargeTimeRemaining_updateSettingsGlobal() { public void getBatteryInfo_differentChargeTimeRemaining_updateSettingsGlobal() {
doReturn(TEST_CHARGE_TIME_REMAINING).when(mBatteryUsageStats).getChargeTimeRemainingMs(); doReturn(TEST_CHARGE_TIME_REMAINING).when(mBatteryUsageStats).getChargeTimeRemainingMs();
final long newTimeToFull = 300L; final long newTimeToFull = 300L;
doReturn(newTimeToFull).when(mBatteryUsageStats).getChargeTimeRemainingMs(); doReturn(newTimeToFull).when(mBatteryUsageStats).getChargeTimeRemainingMs();
@@ -327,16 +348,15 @@ public class BatteryInfoTest {
} }
@Test @Test
public void testGetBatteryInfo_dockDefenderActive_updateChargeString() { public void getBatteryInfo_dockDefenderActive_updateChargeString() {
doReturn(TEST_CHARGE_TIME_REMAINING / 1000) doReturn(TEST_CHARGE_TIME_REMAINING / 1000)
.when(mBatteryUsageStats) .when(mBatteryUsageStats)
.getChargeTimeRemainingMs(); .getChargeTimeRemainingMs();
doReturn(true).when(mFeatureFactory.powerUsageFeatureProvider).isExtraDefend(); doReturn(true).when(mFeatureFactory.powerUsageFeatureProvider).isExtraDefend();
Intent intent = Intent intent =
BatteryTestUtils.getCustomBatteryIntent( createBatteryIntent(
BatteryManager.BATTERY_PLUGGED_DOCK, BatteryManager.BATTERY_PLUGGED_DOCK,
50 /* level */, /* level= */ 50,
100 /* scale */,
BatteryManager.BATTERY_STATUS_CHARGING) BatteryManager.BATTERY_STATUS_CHARGING)
.putExtra( .putExtra(
BatteryManager.EXTRA_CHARGING_STATUS, BatteryManager.EXTRA_CHARGING_STATUS,
@@ -355,7 +375,7 @@ public class BatteryInfoTest {
} }
@Test @Test
public void testGetBatteryInfo_dockDefenderTemporarilyBypassed_updateChargeLabel() { public void getBatteryInfo_dockDefenderTemporarilyBypassed_updateChargeLabel() {
doReturn(REMAINING_TIME).when(mBatteryUsageStats).getChargeTimeRemainingMs(); doReturn(REMAINING_TIME).when(mBatteryUsageStats).getChargeTimeRemainingMs();
mChargingBatteryBroadcast.putExtra( mChargingBatteryBroadcast.putExtra(
BatteryManager.EXTRA_CHARGING_STATUS, BatteryManager.CHARGING_POLICY_DEFAULT); BatteryManager.EXTRA_CHARGING_STATUS, BatteryManager.CHARGING_POLICY_DEFAULT);
@@ -367,10 +387,9 @@ public class BatteryInfoTest {
BatteryInfo info = BatteryInfo info =
BatteryInfo.getBatteryInfo( BatteryInfo.getBatteryInfo(
mContext, mContext,
BatteryTestUtils.getCustomBatteryIntent( createBatteryIntent(
BatteryManager.BATTERY_PLUGGED_DOCK, BatteryManager.BATTERY_PLUGGED_DOCK,
50 /* level */, /* level= */ 50,
100 /* scale */,
BatteryManager.BATTERY_STATUS_CHARGING), BatteryManager.BATTERY_STATUS_CHARGING),
mBatteryUsageStats, mBatteryUsageStats,
MOCK_ESTIMATE, MOCK_ESTIMATE,
@@ -381,7 +400,7 @@ public class BatteryInfoTest {
} }
@Test @Test
public void testGetBatteryInfo_dockDefenderFutureBypass_updateChargeLabel() { public void getBatteryInfo_dockDefenderFutureBypass_updateChargeLabel() {
doReturn(false).when(mFeatureFactory.powerUsageFeatureProvider).isExtraDefend(); doReturn(false).when(mFeatureFactory.powerUsageFeatureProvider).isExtraDefend();
mChargingBatteryBroadcast.putExtra( mChargingBatteryBroadcast.putExtra(
BatteryManager.EXTRA_CHARGING_STATUS, BatteryManager.CHARGING_POLICY_DEFAULT); BatteryManager.EXTRA_CHARGING_STATUS, BatteryManager.CHARGING_POLICY_DEFAULT);
@@ -389,10 +408,9 @@ public class BatteryInfoTest {
BatteryInfo info = BatteryInfo info =
BatteryInfo.getBatteryInfo( BatteryInfo.getBatteryInfo(
mContext, mContext,
BatteryTestUtils.getCustomBatteryIntent( createBatteryIntent(
BatteryManager.BATTERY_PLUGGED_DOCK, BatteryManager.BATTERY_PLUGGED_DOCK,
50 /* level */, /* level= */ 50,
100 /* scale */,
BatteryManager.BATTERY_STATUS_CHARGING), BatteryManager.BATTERY_STATUS_CHARGING),
mBatteryUsageStats, mBatteryUsageStats,
MOCK_ESTIMATE, MOCK_ESTIMATE,
@@ -402,6 +420,284 @@ public class BatteryInfoTest {
assertThat(info.chargeLabel.toString()).contains(STATUS_CHARGING_FUTURE_BYPASS); assertThat(info.chargeLabel.toString()).contains(STATUS_CHARGING_FUTURE_BYPASS);
} }
@Test
public void getBatteryInfo_fastCharging_updateRemainingLabelAndStatusLabel() {
prepareTestGetBatteryInfoEnvironment(
/* remainingTimeMs= */ Duration.ofMinutes(90).toMillis(),
/* chargingStringV2Enabled= */ false);
Intent batteryIntent =
createIntentForGetBatteryInfoTest(
ChargingType.WIRED, ChargingSpeed.FAST, /* batteryLevel= */ 61);
var expectedStatusLabel = "Charging rapidly";
var expectedRemainingLabel = "1 hr, 30 min left until full";
var expectedChargeLabel = "61% - " + expectedRemainingLabel;
assertGetBatteryInfo(
batteryIntent,
/* currentTimeMillis= */ UNUSED_TIME_MS,
expectedStatusLabel,
expectedRemainingLabel,
expectedChargeLabel);
}
@Test
public void getBatteryInfo_regularCharging_updateRemainingLabelAndStatusLabel() {
prepareTestGetBatteryInfoEnvironment(
/* remainingTimeMs= */ Duration.ofMinutes(80).toMillis(),
/* chargingStringV2Enabled= */ false);
Intent batteryIntent =
createIntentForGetBatteryInfoTest(
ChargingType.WIRED, ChargingSpeed.REGULAR, /* batteryLevel= */ 33);
var expectedStatusLabel = "Charging";
var expectedRemainingLabel = "1 hr, 20 min left until full";
var expectedChargeLabel = "33% - " + expectedRemainingLabel;
assertGetBatteryInfo(
batteryIntent,
/* currentTimeMillis= */ UNUSED_TIME_MS,
expectedStatusLabel,
expectedRemainingLabel,
expectedChargeLabel);
}
@Test
public void getBatteryInfo_slowCharging_updateRemainingLabelAndStatusLabel() {
prepareTestGetBatteryInfoEnvironment(
/* remainingTimeMs= */ Duration.ofMinutes(100).toMillis(),
/* chargingStringV2Enabled= */ false);
Intent batteryIntent =
createIntentForGetBatteryInfoTest(
ChargingType.WIRED, ChargingSpeed.SLOW, /* batteryLevel= */ 53);
var expectedStatusLabel = "Charging slowly";
var expectedRemainingLabel = "1 hr, 40 min left until full";
var expectedChargeLabel = "53% - " + expectedRemainingLabel;
assertGetBatteryInfo(
batteryIntent,
/* currentTimeMillis= */ UNUSED_TIME_MS,
expectedStatusLabel,
expectedRemainingLabel,
expectedChargeLabel);
}
@Test
public void getBatteryInfo_wirelessCharging_updateRemainingLabelAndStatusLabel() {
prepareTestGetBatteryInfoEnvironment(
/* remainingTimeMs= */ Duration.ofMinutes(130).toMillis(),
/* chargingStringV2Enabled= */ false);
Intent batteryIntent =
createIntentForGetBatteryInfoTest(
ChargingType.WIRELESS, ChargingSpeed.REGULAR, /* batteryLevel= */ 10);
var expectedStatusLabel = "Charging wirelessly";
var expectedRemainingLabel = "2 hr, 10 min left until full";
var expectedChargeLabel = "10% - " + expectedRemainingLabel;
assertGetBatteryInfo(
batteryIntent,
/* currentTimeMillis= */ UNUSED_TIME_MS,
expectedStatusLabel,
expectedRemainingLabel,
expectedChargeLabel);
}
@Test
public void getBatteryInfo_dockedCharging_updateRemainingLabelAndStatusLabel() {
prepareTestGetBatteryInfoEnvironment(
/* remainingTimeMs= */ Duration.ofMinutes(30).toMillis(),
/* chargingStringV2Enabled= */ false);
Intent batteryIntent =
createIntentForGetBatteryInfoTest(
ChargingType.DOCKED, ChargingSpeed.REGULAR, /* batteryLevel= */ 51);
var expectedStatusLabel = "Charging";
var expectedRemainingLabel = "30 min left until full";
var expectedChargeLabel = "51% - " + expectedRemainingLabel;
assertGetBatteryInfo(
batteryIntent,
/* currentTimeMillis= */ UNUSED_TIME_MS,
expectedStatusLabel,
expectedRemainingLabel,
expectedChargeLabel);
}
@Test
public void getBatteryInfo_fastChargingV2_updateRemainingLabelAndStatusLabel() {
prepareTestGetBatteryInfoEnvironment(
/* remainingTimeMs= */ Duration.ofMinutes(30).toMillis(),
/* chargingStringV2Enabled= */ true);
Intent batteryIntent =
createIntentForGetBatteryInfoTest(
ChargingType.WIRED, ChargingSpeed.FAST, /* batteryLevel= */ 56);
var expectedStatusLabel = "Fast charging";
var expectedRemainingLabel = "Full by 1:30PM";
var expectedChargeLabel = "56% - " + expectedStatusLabel + " - " + expectedRemainingLabel;
var currentTimeMillis = Instant.parse("2024-04-01T13:00:00Z").toEpochMilli();
assertGetBatteryInfo(
batteryIntent,
currentTimeMillis,
expectedStatusLabel,
expectedRemainingLabel,
expectedChargeLabel);
}
@Test
public void getBatteryInfo_regularChargingV2_updateRemainingLabelAndStatusLabel() {
prepareTestGetBatteryInfoEnvironment(
/* remainingTimeMs= */ Duration.ofHours(1).toMillis(),
/* chargingStringV2Enabled= */ true);
Intent batteryIntent =
createIntentForGetBatteryInfoTest(
ChargingType.WIRED, ChargingSpeed.REGULAR, /* batteryLevel= */ 12);
var expectedStatusLabel = "Charging";
var expectedRemainingLabel = "Fully charged by 2:00PM";
var expectedChargeLabel = "12% - " + expectedRemainingLabel;
var currentTimeMillis = Instant.parse("2024-04-01T13:00:00Z").toEpochMilli();
assertGetBatteryInfo(
batteryIntent,
currentTimeMillis,
expectedStatusLabel,
expectedRemainingLabel,
expectedChargeLabel);
}
@Test
public void getBatteryInfo_slowChargingV2_updateRemainingLabelAndStatusLabel() {
prepareTestGetBatteryInfoEnvironment(
/* remainingTimeMs= */ Duration.ofHours(2).toMillis(),
/* chargingStringV2Enabled= */ true);
Intent batteryIntent =
createIntentForGetBatteryInfoTest(
ChargingType.WIRED, ChargingSpeed.SLOW, /* batteryLevel= */ 18);
var expectedStatusLabel = "Charging";
var expectedRemainingLabel = "Fully charged by 3:00PM";
var expectedChargeLabel = "18% - " + expectedRemainingLabel;
var currentTimeMillis = Instant.parse("2024-04-01T13:00:00Z").toEpochMilli();
assertGetBatteryInfo(
batteryIntent,
currentTimeMillis,
expectedStatusLabel,
expectedRemainingLabel,
expectedChargeLabel);
}
@Test
public void getBatteryInfo_wirelessChargingV2_updateRemainingLabelAndStatusLabel() {
prepareTestGetBatteryInfoEnvironment(
/* remainingTimeMs= */ Duration.ofHours(1).toMillis(),
/* chargingStringV2Enabled= */ true);
Intent batteryIntent =
createIntentForGetBatteryInfoTest(
ChargingType.WIRELESS, ChargingSpeed.REGULAR, /* batteryLevel= */ 45);
var expectedStatusLabel = "Charging";
var expectedRemainingLabel = "Fully charged by 4:00PM";
var expectedChargeLabel = "45% - " + expectedRemainingLabel;
var currentTimeMillis = Instant.parse("2024-04-01T15:00:00Z").toEpochMilli();
assertGetBatteryInfo(
batteryIntent,
currentTimeMillis,
expectedStatusLabel,
expectedRemainingLabel,
expectedChargeLabel);
}
@Test
public void getBatteryInfo_dockedChargingV2_updateRemainingLabelAndStatusLabel() {
prepareTestGetBatteryInfoEnvironment(
/* remainingTimeMs= */ Duration.ofHours(1).toMillis(),
/* chargingStringV2Enabled= */ true);
Intent batteryIntent =
createIntentForGetBatteryInfoTest(
ChargingType.DOCKED, ChargingSpeed.REGULAR, /* batteryLevel= */ 66);
var expectedStatusLabel = "Charging";
var expectedRemainingLabel = "Fully charged by 2:00PM";
var expectedChargeLabel = "66% - " + expectedRemainingLabel;
var currentTimeMillis = Instant.parse("2021-02-09T13:00:00.00Z").toEpochMilli();
assertGetBatteryInfo(
batteryIntent,
currentTimeMillis,
expectedStatusLabel,
expectedRemainingLabel,
expectedChargeLabel);
}
private enum ChargingSpeed {
FAST,
REGULAR,
SLOW
}
private enum ChargingType {
WIRED,
WIRELESS,
DOCKED
}
private Intent createIntentForGetBatteryInfoTest(
ChargingType chargingType, ChargingSpeed chargingSpeed, int batteryLevel) {
return createBatteryIntent(
CHARGING_TYPE_MAP.get(chargingType),
batteryLevel,
BatteryManager.BATTERY_STATUS_CHARGING)
.putExtra(
BatteryManager.EXTRA_MAX_CHARGING_CURRENT,
CHARGING_SPEED_MAP.get(chargingSpeed))
.putExtra(BatteryManager.EXTRA_MAX_CHARGING_VOLTAGE, 5000000);
}
private void prepareTestGetBatteryInfoEnvironment(
long remainingTimeMs, boolean chargingStringV2Enabled) {
when(mBatteryUsageStats.getChargeTimeRemainingMs()).thenReturn(remainingTimeMs);
SystemProperties.set(
com.android.settingslib.fuelgauge.BatteryUtils.PROPERTY_CHARGING_STRING_V2_KEY,
String.valueOf(chargingStringV2Enabled));
Settings.Global.putInt(
mContext.getContentResolver(),
BatteryUtils.SETTINGS_GLOBAL_DOCK_DEFENDER_BYPASS,
1);
}
private void assertGetBatteryInfo(
Intent batteryIntent,
long currentTimeMillis,
String expectedStatusLabel,
String expectedRemainingLabel,
String expectedChargeLabel) {
mContext.getResources().getConfiguration().setLocale(Locale.US);
mContext.getSystemService(AlarmManager.class).setTimeZone("UTC");
var info =
BatteryInfo.getBatteryInfo(
mContext,
batteryIntent,
mBatteryUsageStats,
MOCK_ESTIMATE,
/* elapsedRealtimeUs= */ UNUSED_TIME_MS,
/* shortString= */ false,
/* currentTimeMillis= */ currentTimeMillis);
assertWithMessage("statusLabel is incorrect")
.that(info.statusLabel)
.isEqualTo(expectedStatusLabel);
assertWithMessage("remainingLabel is incorrect")
.that(info.remainingLabel.toString())
.isEqualTo(expectedRemainingLabel);
assertWithMessage("chargeLabel is incorrect")
.that(info.chargeLabel.toString())
.isEqualTo(expectedChargeLabel);
}
private static Intent createBatteryIntent(int plugged, int level, int status) {
return new Intent()
.putExtra(BatteryManager.EXTRA_PLUGGED, plugged)
.putExtra(BatteryManager.EXTRA_LEVEL, level)
.putExtra(BatteryManager.EXTRA_SCALE, 100)
.putExtra(BatteryManager.EXTRA_STATUS, status);
}
// Make our battery stats return a sequence of battery events. // Make our battery stats return a sequence of battery events.
private void mockBatteryStatsHistory() { private void mockBatteryStatsHistory() {
// Mock out new data every time iterateBatteryStatsHistory is called. // Mock out new data every time iterateBatteryStatsHistory is called.