diff --git a/res/values/strings.xml b/res/values/strings.xml
index 7cd9ae4d892..b997914c059 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -4285,8 +4285,17 @@
%1$s to charge
-
- Use since last full charge
+
+ Screen usage
+
+ Screen consumption
+
+ Mobile network scanning
+
+
+ App usage since last full charge
+
+ Device usage since last full charge
Battery use since unplugged
@@ -4483,6 +4492,8 @@
Used for %1$s
+
+ %1$s of overall battery
Usage breakdown since last full charge
diff --git a/res/xml/power_usage_summary.xml b/res/xml/power_usage_summary.xml
index b6808c000f4..58cab77aa71 100644
--- a/res/xml/power_usage_summary.xml
+++ b/res/xml/power_usage_summary.xml
@@ -55,4 +55,22 @@
android:key="app_list"
android:title="@string/power_usage_list_summary"/>
+
+
+
+
+
+
+
+
+
+
diff --git a/src/com/android/settings/fuelgauge/PowerGaugePreference.java b/src/com/android/settings/fuelgauge/PowerGaugePreference.java
index b067edf6b07..fe7ef6eea5a 100644
--- a/src/com/android/settings/fuelgauge/PowerGaugePreference.java
+++ b/src/com/android/settings/fuelgauge/PowerGaugePreference.java
@@ -59,7 +59,7 @@ public class PowerGaugePreference extends TintablePreference {
}
public void setPercent(double percentOfTotal) {
- mProgress = Utils.formatPercentage((int) (percentOfTotal + 0.5));
+ mProgress = Utils.formatPercentage(percentOfTotal, true);
notifyChanged();
}
diff --git a/src/com/android/settings/fuelgauge/PowerUsageSummary.java b/src/com/android/settings/fuelgauge/PowerUsageSummary.java
index 44499109809..c44a3735d1f 100644
--- a/src/com/android/settings/fuelgauge/PowerUsageSummary.java
+++ b/src/com/android/settings/fuelgauge/PowerUsageSummary.java
@@ -78,11 +78,17 @@ public class PowerUsageSummary extends PowerUsageBase {
private static final boolean USE_FAKE_DATA = false;
private static final String KEY_APP_LIST = "app_list";
private static final String KEY_BATTERY_HEADER = "battery_header";
+
private static final int MIN_POWER_THRESHOLD_MILLI_AMP = 5;
private static final int MAX_ITEMS_TO_LIST = USE_FAKE_DATA ? 30 : 10;
private static final int MIN_AVERAGE_POWER_THRESHOLD_MILLI_AMP = 10;
private static final int SECONDS_IN_HOUR = 60 * 60;
+ private static final String KEY_SCREEN_USAGE = "screen_usage";
+ private static final String KEY_SCREEN_CONSUMPTION = "screen_consumption";
+ private static final String KEY_CELLULAR_NETWORK = "cellular_network";
+
+
private static final int MENU_STATS_TYPE = Menu.FIRST;
private static final int MENU_HIGH_POWER_APPS = Menu.FIRST + 3;
@VisibleForTesting
@@ -93,6 +99,13 @@ public class PowerUsageSummary extends PowerUsageBase {
@VisibleForTesting
boolean mShowAllApps = false;
+
+ Preference mScreenUsagePref;
+ @VisibleForTesting
+ Preference mScreenConsumptionPref;
+ @VisibleForTesting
+ Preference mCellularNetworkPref;
+
private LayoutPreference mBatteryLayoutPref;
private PreferenceGroup mAppListGroup;
@@ -105,6 +118,9 @@ public class PowerUsageSummary extends PowerUsageBase {
mBatteryLayoutPref = (LayoutPreference) findPreference(KEY_BATTERY_HEADER);
mAppListGroup = (PreferenceGroup) findPreference(KEY_APP_LIST);
+ mScreenUsagePref = findPreference(KEY_SCREEN_USAGE);
+ mScreenConsumptionPref = findPreference(KEY_SCREEN_CONSUMPTION);
+ mCellularNetworkPref = findPreference(KEY_CELLULAR_NETWORK);
}
@Override
@@ -384,6 +400,11 @@ public class PowerUsageSummary extends PowerUsageBase {
context.getTheme().resolveAttribute(android.R.attr.colorControlNormal, value, true);
final int colorControl = context.getColor(value.resourceId);
final String usedTime = context.getString(R.string.battery_used_for);
+ final int dischargeAmount = USE_FAKE_DATA ? 5000
+ : stats != null ? stats.getDischargeAmount(mStatsType) : 0;
+
+ updateScreenPreference(dischargeAmount);
+ updateCellularPreference(dischargeAmount);
if (averagePower >= MIN_AVERAGE_POWER_THRESHOLD_MILLI_AMP || USE_FAKE_DATA) {
final List usageList = getCoalescedUsageList(
@@ -391,8 +412,6 @@ public class PowerUsageSummary extends PowerUsageBase {
double hiddenPowerMah = mShowAllApps ? 0 : removeHiddenBatterySippers(usageList);
- final int dischargeAmount = USE_FAKE_DATA ? 5000
- : stats != null ? stats.getDischargeAmount(mStatsType) : 0;
final int numSippers = usageList.size();
for (int i = 0; i < numSippers; i++) {
final BatterySipper sipper = usageList.get(i);
@@ -478,6 +497,39 @@ public class PowerUsageSummary extends PowerUsageBase {
BatteryEntry.startRequestQueue();
}
+ @VisibleForTesting
+ BatterySipper findBatterySipperByType(List usageList, DrainType type) {
+ for (int i = 0, size = usageList.size(); i < size; i++) {
+ final BatterySipper sipper = usageList.get(i);
+ if (sipper.drainType == type) {
+ return sipper;
+ }
+ }
+ return null;
+ }
+
+ @VisibleForTesting
+ void updateScreenPreference(final int dischargeAmount) {
+ final BatterySipper sipper = findBatterySipperByType(
+ mStatsHelper.getUsageList(), DrainType.SCREEN);
+ final Context context = getContext();
+ final double percentOfTotal = calculatePercentage(sipper.totalPowerMah, dischargeAmount);
+
+ mScreenUsagePref.setSummary(getString(R.string.battery_used_for,
+ Utils.formatElapsedTime(context, sipper.usageTimeMs, false)));
+ mScreenConsumptionPref.setSummary(getString(R.string.battery_overall_usage,
+ Utils.formatPercentage(percentOfTotal, true)));
+ }
+
+ @VisibleForTesting
+ void updateCellularPreference(final int dischargeAmount) {
+ final BatterySipper sipper = findBatterySipperByType(
+ mStatsHelper.getUsageList(), DrainType.CELL);
+ final double percentOfTotal = calculatePercentage(sipper.totalPowerMah, dischargeAmount);
+ mCellularNetworkPref.setSummary(getString(R.string.battery_overall_usage,
+ Utils.formatPercentage(percentOfTotal, true)));
+ }
+
@VisibleForTesting
void updateHeaderPreference(BatteryInfo info) {
final BatteryMeterView batteryView = (BatteryMeterView) mBatteryLayoutPref
@@ -502,6 +554,11 @@ public class PowerUsageSummary extends PowerUsageBase {
batteryView.setBatteryInfo(info.mBatteryLevel);
}
+ @VisibleForTesting
+ double calculatePercentage(double powerUsage, double dischargeAmount) {
+ return ((powerUsage / mStatsHelper.getTotalPower()) * dischargeAmount);
+ }
+
@VisibleForTesting
void setUsageSummary(Preference preference, String usedTimePrefix, long usageTimeMs) {
// Only show summary when usage time is longer than one minute
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/PowerUsageSummaryTest.java b/tests/robotests/src/com/android/settings/fuelgauge/PowerUsageSummaryTest.java
index dd849737cbd..72f6a87992d 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/PowerUsageSummaryTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/PowerUsageSummaryTest.java
@@ -18,6 +18,7 @@ package com.android.settings.fuelgauge;
import android.content.Context;
import android.content.Intent;
import android.os.Process;
+import android.support.v7.preference.Preference;
import android.text.TextUtils;
import android.text.format.DateUtils;
import android.view.Menu;
@@ -26,9 +27,12 @@ import android.view.MenuItem;
import android.view.View;
import android.widget.TextView;
import com.android.internal.os.BatterySipper;
+import com.android.internal.os.BatteryStatsHelper;
import com.android.internal.os.BatteryStatsImpl;
import com.android.settings.R;
+import com.android.settings.SettingsRobolectricTestRunner;
import com.android.settings.TestConfig;
+import com.android.settings.Utils;
import com.android.settings.applications.LayoutPreference;
import com.android.settings.testutils.FakeFeatureFactory;
import com.android.settingslib.BatteryInfo;
@@ -38,9 +42,8 @@ import org.junit.runner.RunWith;
import org.mockito.Answers;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
-import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config;
-import org.robolectric.util.ReflectionHelpers;
import java.util.ArrayList;
import java.util.List;
@@ -49,7 +52,10 @@ import static com.android.settings.fuelgauge.PowerUsageBase.MENU_STATS_REFRESH;
import static com.android.settings.fuelgauge.PowerUsageSummary.MENU_ADDITIONAL_BATTERY_INFO;
import static com.android.settings.fuelgauge.PowerUsageSummary.MENU_TOGGLE_APPS;
import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.Matchers.anyInt;
import static org.mockito.Matchers.anyString;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
@@ -59,7 +65,7 @@ import static org.mockito.Mockito.when;
* Unit tests for {@link PowerUsageSummary}.
*/
// TODO: Improve this test class so that it starts up the real activity and fragment.
-@RunWith(RobolectricTestRunner.class)
+@RunWith(SettingsRobolectricTestRunner.class)
@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
public class PowerUsageSummaryTest {
private static final String[] PACKAGE_NAMES = {"com.app1", "com.app2"};
@@ -67,9 +73,13 @@ public class PowerUsageSummaryTest {
private static final int UID = 123;
private static final int POWER_MAH = 100;
private static final long REMAINING_TIME_US = 100000;
+ private static final int DISCHARGE_AMOUNT = 100;
+ private static final long USAGE_TIME_MS = 10000;
+ private static final double TOTAL_POWER = 200;
private static final double BATTERY_SCREEN_USAGE = 300;
private static final double BATTERY_SYSTEM_USAGE = 600;
private static final double PRECISION = 0.001;
+ private static final double POWER_USAGE_PERCENTAGE = 50;
private static final Intent ADDITIONAL_BATTERY_INFO_INTENT =
new Intent("com.example.app.ADDITIONAL_BATTERY_INFO");
@@ -92,6 +102,8 @@ public class PowerUsageSummaryTest {
@Mock
private BatterySipper mSystemBatterySipper;
@Mock
+ private BatterySipper mCellBatterySipper;
+ @Mock
private PowerGaugePreference mPreference;
@Mock
private LayoutPreference mBatteryLayoutPref;
@@ -105,15 +117,26 @@ public class PowerUsageSummaryTest {
private TextView mSummary2;
@Mock
private BatteryInfo mBatteryInfo;
+ @Mock
+ private Preference mScreenUsagePref;
+ @Mock
+ private Preference mScreenConsumptionPref;
+ @Mock
+ private Preference mCellularNetworkPref;
+ @Mock
+ private BatteryStatsHelper mBatteryHelper;
+ private Context mRealContext;
private TestFragment mFragment;
private FakeFeatureFactory mFeatureFactory;
private PowerUsageSummary mPowerUsageSummary;
+ private List mUsageList;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
+ mRealContext = RuntimeEnvironment.application;
FakeFeatureFactory.setupForTest(mContext);
mFeatureFactory = (FakeFeatureFactory) FakeFeatureFactory.getFactory(mContext);
@@ -129,13 +152,19 @@ public class PowerUsageSummaryTest {
when(mToggleAppsMenu.getItemId()).thenReturn(MENU_TOGGLE_APPS);
when(mFeatureFactory.powerUsageFeatureProvider.getAdditionalBatteryInfoIntent())
.thenReturn(ADDITIONAL_BATTERY_INFO_INTENT);
+ when(mBatteryHelper.getTotalPower()).thenReturn(TOTAL_POWER);
mPowerUsageSummary = spy(new PowerUsageSummary());
- when(mPowerUsageSummary.getContext()).thenReturn(mContext);
+ when(mPowerUsageSummary.getContext()).thenReturn(mRealContext);
when(mNormalBatterySipper.getPackages()).thenReturn(PACKAGE_NAMES);
when(mNormalBatterySipper.getUid()).thenReturn(UID);
mNormalBatterySipper.totalPowerMah = POWER_MAH;
+ mNormalBatterySipper.drainType = BatterySipper.DrainType.APP;
+
+ mCellBatterySipper.drainType = BatterySipper.DrainType.CELL;
+ mCellBatterySipper.totalPowerMah = POWER_MAH;
+
when(mBatteryLayoutPref.findViewById(R.id.summary1)).thenReturn(mSummary1);
when(mBatteryLayoutPref.findViewById(R.id.summary2)).thenReturn(mSummary2);
when(mBatteryLayoutPref.findViewById(R.id.time)).thenReturn(mTimeText);
@@ -145,9 +174,19 @@ public class PowerUsageSummaryTest {
mScreenBatterySipper.drainType = BatterySipper.DrainType.SCREEN;
mScreenBatterySipper.totalPowerMah = BATTERY_SCREEN_USAGE;
+ mScreenBatterySipper.usageTimeMs = USAGE_TIME_MS;
+
mSystemBatterySipper.drainType = BatterySipper.DrainType.APP;
mSystemBatterySipper.totalPowerMah = BATTERY_SYSTEM_USAGE;
when(mSystemBatterySipper.getUid()).thenReturn(Process.SYSTEM_UID);
+
+ mUsageList = new ArrayList<>();
+ mUsageList.add(mNormalBatterySipper);
+ mUsageList.add(mScreenBatterySipper);
+ mUsageList.add(mCellBatterySipper);
+
+ mPowerUsageSummary.mStatsHelper = mBatteryHelper;
+ when(mBatteryHelper.getUsageList()).thenReturn(mUsageList);
}
@Test
@@ -326,6 +365,7 @@ public class PowerUsageSummaryTest {
verify(mSummary1).setText(R.string.estimated_time_left);
}
+
private void testToggleAllApps(final boolean isShowApps) {
mFragment.mShowAllApps = isShowApps;
@@ -333,6 +373,61 @@ public class PowerUsageSummaryTest {
assertThat(mFragment.mShowAllApps).isEqualTo(!isShowApps);
}
+ @Test
+ public void testFindBatterySipperByType_findTypeScreen() {
+ BatterySipper sipper = mPowerUsageSummary.findBatterySipperByType(mUsageList,
+ BatterySipper.DrainType.SCREEN);
+
+ assertThat(sipper).isSameAs(mScreenBatterySipper);
+ }
+
+ @Test
+ public void testFindBatterySipperByType_findTypeApp() {
+ BatterySipper sipper = mPowerUsageSummary.findBatterySipperByType(mUsageList,
+ BatterySipper.DrainType.APP);
+
+ assertThat(sipper).isSameAs(mNormalBatterySipper);
+ }
+
+ @Test
+ public void testUpdateCellularPreference_ShowCorrectSummary() {
+ mPowerUsageSummary.mCellularNetworkPref = mCellularNetworkPref;
+ final double percent = POWER_MAH / TOTAL_POWER * DISCHARGE_AMOUNT;
+ final String expectedSummary = mRealContext.getString(R.string.battery_overall_usage,
+ Utils.formatPercentage((int) percent));
+ doReturn(expectedSummary).when(mPowerUsageSummary)
+ .getString(eq(R.string.battery_overall_usage), anyInt());
+ mPowerUsageSummary.updateCellularPreference(DISCHARGE_AMOUNT);
+
+ verify(mCellularNetworkPref).setSummary(expectedSummary);
+ }
+
+ @Test
+ public void testUpdateScreenPreference_ShowCorrectSummary() {
+ mPowerUsageSummary.mScreenUsagePref = mScreenUsagePref;
+ mPowerUsageSummary.mScreenConsumptionPref = mScreenConsumptionPref;
+ final String expectedUsedTime = mRealContext.getString(R.string.battery_used_for,
+ Utils.formatElapsedTime(mRealContext, USAGE_TIME_MS, false));
+ final double percent = BATTERY_SCREEN_USAGE / TOTAL_POWER * DISCHARGE_AMOUNT;
+ final String expectedOverallUsage = mRealContext.getString(R.string.battery_overall_usage,
+ Utils.formatPercentage((int) percent));
+ doReturn(expectedUsedTime).when(mPowerUsageSummary).getString(
+ eq(R.string.battery_used_for), anyInt());
+ doReturn(expectedOverallUsage).when(mPowerUsageSummary).getString(
+ eq(R.string.battery_overall_usage), anyInt());
+
+ mPowerUsageSummary.updateScreenPreference(DISCHARGE_AMOUNT);
+
+ verify(mScreenUsagePref).setSummary(expectedUsedTime);
+ verify(mScreenConsumptionPref).setSummary(expectedOverallUsage);
+ }
+
+ @Test
+ public void testCalculatePercentage() {
+ final double percent = mPowerUsageSummary.calculatePercentage(POWER_MAH, DISCHARGE_AMOUNT);
+ assertThat(percent).isWithin(PRECISION).of(POWER_USAGE_PERCENTAGE);
+ }
+
public static class TestFragment extends PowerUsageSummary {
private Context mContext;