BatteryStatsViewer UI improvements:

1. DEVICE and ALL_APPS are now combined in one screen
2. Totals are displayed at the top
3. Column headers are shown conditionally
4. New icon for totals
5. Swipe-to-refresh

Bug: 187370721
Test: mp :BatteryStatsViewer && adb shell am start -n com.android.frameworks.core.batterystatsviewer/.BatteryConsumerPickerActivity

Change-Id: Ic250fe54b3fe3c54ff461218f126fc83f9921fd9
This commit is contained in:
Dmitri Plotnikov
2021-05-05 14:19:05 -07:00
parent 1ebf0cf006
commit 00b667a64e
10 changed files with 533 additions and 334 deletions

View File

@@ -15,6 +15,7 @@ android_app {
"androidx.appcompat_appcompat",
"androidx.cardview_cardview",
"androidx.recyclerview_recyclerview",
"androidx.swiperefreshlayout_swiperefreshlayout",
"com.google.android.material_material",
],
platform_apis: true,

View File

@@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="#d14d2c">
<path
android:fillColor="@android:color/white"
android:pathData="M19,4H5v2l6,6 -6,6v2h14v-3h-9l5,-5 -5,-5h9V4z"/>
</vector>

View File

@@ -30,7 +30,8 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginEnd="8dp"/>
android:layout_marginEnd="8dp"
android:paddingBottom="8dp"/>
<TextView
android:id="@+id/title"
@@ -40,7 +41,7 @@
android:textAppearance="@style/TextAppearanceBody"/>
<TextView
android:id="@+id/amount"
android:id="@+id/value1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
@@ -49,7 +50,7 @@
android:textAppearance="@style/TextAppearanceBody"/>
<TextView
android:id="@+id/percent"
android:id="@+id/value2"
android:layout_width="76dp"
android:layout_height="wrap_content"
android:gravity="right"

View File

@@ -1,33 +0,0 @@
<!--
~ Copyright (C) 2020 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.
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<com.google.android.material.tabs.TabLayout
android:id="@+id/tab_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
<androidx.viewpager.widget.ViewPager
android:id="@+id/pager"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1" />
</LinearLayout>

View File

@@ -14,22 +14,15 @@
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<FrameLayout
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/swipe_refresh"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/list_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="gone"/>
android:layout_height="match_parent"/>
<ProgressBar
style="?android:attr/progressBarStyleLarge"
android:id="@+id/loading_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:indeterminate="true"/>
</FrameLayout>
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>

View File

@@ -14,94 +14,89 @@
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<FrameLayout
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/swipe_refresh"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.cardview.widget.CardView
style="@style/LoadTestCardView"
<LinearLayout
android:id="@+id/app_card"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:layout_marginEnd="10dp"
android:layout_marginBottom="10dp"
android:layout_marginStart="10dp"
android:padding="20dp">
android:visibility="invisible">
<LinearLayout
<androidx.cardview.widget.CardView
style="@style/LoadTestCardView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:minHeight="80dp"
android:paddingStart="10dp"
android:paddingEnd="10dp">
android:layout_marginTop="10dp"
android:layout_marginEnd="10dp"
android:layout_marginBottom="10dp"
android:layout_marginStart="10dp"
android:padding="20dp">
<include layout="@layout/battery_consumer_info_layout"/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:minHeight="80dp"
android:paddingStart="10dp"
android:paddingEnd="10dp">
<include layout="@layout/battery_consumer_info_layout"/>
</LinearLayout>
</androidx.cardview.widget.CardView>
<LinearLayout
android:id="@+id/headings"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingTop="2dp"
android:paddingBottom="4dp">
<FrameLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"/>
<TextView
android:layout_width="76dp"
android:layout_height="wrap_content"
android:gravity="end"
android:paddingEnd="20dp"
android:text="Total"/>
<TextView
android:layout_width="76dp"
android:layout_height="wrap_content"
android:gravity="end"
android:paddingEnd="20dp"
android:text="Apps"/>
</LinearLayout>
</androidx.cardview.widget.CardView>
<LinearLayout
android:id="@+id/headings"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingTop="2dp"
android:paddingBottom="4dp">
<FrameLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"/>
<TextView
android:layout_width="100dp"
android:layout_height="wrap_content"
android:gravity="end"
android:paddingEnd="10dp"
android:text="Total"/>
<TextView
android:layout_width="100dp"
android:layout_height="wrap_content"
android:gravity="end"
android:paddingEnd="30dp"
android:text="Apps"/>
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="@android:color/darker_gray"/>
</LinearLayout>
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="@android:color/darker_gray"/>
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/battery_consumer_data_view"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"/>
<TextView
android:id="@+id/empty_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:visibility="gone"
android:text="No battery stats available"/>
</LinearLayout>
<FrameLayout
android:id="@+id/loading_view"
<TextView
android:id="@+id/empty_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#AAFFFFFF">
<ProgressBar
style="?android:attr/progressBarStyleLarge"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:indeterminate="true"/>
</FrameLayout>
</FrameLayout>
android:gravity="center"
android:visibility="gone"
android:text="No battery stats available"/>
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>

View File

@@ -28,29 +28,54 @@ import java.util.List;
public class BatteryConsumerData {
public static final String UID_BATTERY_CONSUMER_ID_PREFIX = "APP|";
public static final String AGGREGATE_BATTERY_CONSUMER_ID = "SYS|";
enum EntryType {
POWER_MODELED,
POWER_MEASURED,
POWER_CUSTOM,
DURATION,
UID_TOTAL_POWER,
UID_POWER_MODELED,
UID_POWER_MEASURED,
UID_POWER_CUSTOM,
UID_DURATION,
DEVICE_TOTAL_POWER,
DEVICE_POWER_MODELED,
DEVICE_POWER_MEASURED,
DEVICE_POWER_CUSTOM,
DEVICE_DURATION,
}
enum ConsumerType {
UID_BATTERY_CONSUMER,
DEVICE_POWER_COMPONENT,
}
public static class Entry {
public String title;
public EntryType entryType;
public double value;
public double total;
public boolean isSystemBatteryConsumer;
public String title;
public double value1;
public double value2;
}
private final BatteryConsumerInfoHelper.BatteryConsumerInfo mBatteryConsumerInfo;
private BatteryConsumerInfoHelper.BatteryConsumerInfo mBatteryConsumerInfo;
private final List<Entry> mEntries = new ArrayList<>();
public BatteryConsumerData(Context context,
List<BatteryUsageStats> batteryUsageStatsList, String batteryConsumerId) {
switch (getConsumerType(batteryConsumerId)) {
case UID_BATTERY_CONSUMER:
populateForUidBatteryConsumer(context, batteryUsageStatsList, batteryConsumerId);
break;
case DEVICE_POWER_COMPONENT:
populateForAggregateBatteryConsumer(context, batteryUsageStatsList);
break;
}
}
private void populateForUidBatteryConsumer(
Context context, List<BatteryUsageStats> batteryUsageStatsList,
String batteryConsumerId) {
BatteryUsageStats batteryUsageStats = batteryUsageStatsList.get(0);
BatteryUsageStats modeledBatteryUsageStats = batteryUsageStatsList.get(1);
BatteryConsumer requestedBatteryConsumer = getRequestedBatteryConsumer(batteryUsageStats,
batteryConsumerId);
BatteryConsumer requestedModeledBatteryConsumer = getRequestedBatteryConsumer(
@@ -62,7 +87,7 @@ public class BatteryConsumerData {
}
mBatteryConsumerInfo = BatteryConsumerInfoHelper.makeBatteryConsumerInfo(
requestedBatteryConsumer, batteryConsumerId, context.getPackageManager());
batteryUsageStats, batteryConsumerId, context.getPackageManager());
double[] totalPowerByComponentMah = new double[BatteryConsumer.POWER_COMPONENT_COUNT];
double[] totalModeledPowerByComponentMah =
@@ -77,56 +102,152 @@ public class BatteryConsumerData {
computeTotalPowerForCustomComponent(batteryUsageStats, totalCustomPowerByComponentMah);
computeTotalDuration(batteryUsageStats, totalDurationByComponentMs);
if (isPowerProfileModelsOnly(requestedBatteryConsumer)) {
addEntry("Consumed", EntryType.UID_TOTAL_POWER,
requestedBatteryConsumer.getConsumedPower(),
batteryUsageStats.getAggregateBatteryConsumer(
BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_ALL_APPS)
.getConsumedPower());
} else {
addEntry("Consumed (measured)", EntryType.UID_TOTAL_POWER,
requestedBatteryConsumer.getConsumedPower(),
batteryUsageStats.getAggregateBatteryConsumer(
BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_ALL_APPS)
.getConsumedPower());
addEntry("Consumed (modeled)", EntryType.UID_TOTAL_POWER,
requestedModeledBatteryConsumer.getConsumedPower(),
modeledBatteryUsageStats.getAggregateBatteryConsumer(
BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_ALL_APPS)
.getConsumedPower());
}
for (int component = 0; component < BatteryConsumer.POWER_COMPONENT_COUNT; component++) {
final String metricTitle = getPowerMetricTitle(component);
final int powerModel = requestedBatteryConsumer.getPowerModel(component);
if (powerModel == BatteryConsumer.POWER_MODEL_POWER_PROFILE) {
addEntry(metricTitle, EntryType.POWER_MODELED,
addEntry(metricTitle, EntryType.UID_POWER_MODELED,
requestedBatteryConsumer.getConsumedPower(component),
totalPowerByComponentMah[component],
mBatteryConsumerInfo.isSystemBatteryConsumer);
totalPowerByComponentMah[component]
);
} else {
addEntry(metricTitle + " (measured)", EntryType.POWER_MEASURED,
addEntry(metricTitle + " (measured)", EntryType.UID_POWER_MEASURED,
requestedBatteryConsumer.getConsumedPower(component),
totalPowerByComponentMah[component],
mBatteryConsumerInfo.isSystemBatteryConsumer);
addEntry(metricTitle + " (modeled)", EntryType.POWER_MODELED,
totalPowerByComponentMah[component]
);
addEntry(metricTitle + " (modeled)", EntryType.UID_POWER_MODELED,
requestedModeledBatteryConsumer.getConsumedPower(component),
totalModeledPowerByComponentMah[component],
mBatteryConsumerInfo.isSystemBatteryConsumer);
totalModeledPowerByComponentMah[component]
);
}
}
for (int component = 0; component < customComponentCount; component++) {
final String name = requestedBatteryConsumer.getCustomPowerComponentName(
BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID + component);
addEntry(name + " (custom)", EntryType.POWER_CUSTOM,
addEntry(name + " (custom)", EntryType.UID_POWER_CUSTOM,
requestedBatteryConsumer.getConsumedPowerForCustomComponent(
BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID + component),
totalCustomPowerByComponentMah[component],
mBatteryConsumerInfo.isSystemBatteryConsumer);
totalCustomPowerByComponentMah[component]
);
}
for (int component = 0; component < BatteryConsumer.POWER_COMPONENT_COUNT; component++) {
final String metricTitle = getTimeMetricTitle(component);
addEntry(metricTitle, EntryType.DURATION,
addEntry(metricTitle, EntryType.UID_DURATION,
requestedBatteryConsumer.getUsageDurationMillis(component),
totalDurationByComponentMs[component],
mBatteryConsumerInfo.isSystemBatteryConsumer);
totalDurationByComponentMs[component]
);
}
mBatteryConsumerInfo = BatteryConsumerInfoHelper.makeBatteryConsumerInfo(batteryUsageStats,
batteryConsumerId, context.getPackageManager());
}
private void populateForAggregateBatteryConsumer(Context context,
List<BatteryUsageStats> batteryUsageStatsList) {
BatteryUsageStats batteryUsageStats = batteryUsageStatsList.get(0);
BatteryUsageStats modeledBatteryUsageStats = batteryUsageStatsList.get(1);
final BatteryConsumer deviceBatteryConsumer =
batteryUsageStats.getAggregateBatteryConsumer(
BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_DEVICE);
BatteryConsumer appsBatteryConsumer =
batteryUsageStats.getAggregateBatteryConsumer(
BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_ALL_APPS);
BatteryConsumer modeledDeviceBatteryConsumer =
modeledBatteryUsageStats.getAggregateBatteryConsumer(
BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_DEVICE);
BatteryConsumer modeledAppsBatteryConsumer =
modeledBatteryUsageStats.getAggregateBatteryConsumer(
BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_ALL_APPS);
if (isPowerProfileModelsOnly(deviceBatteryConsumer)) {
addEntry("Consumed", EntryType.DEVICE_TOTAL_POWER,
deviceBatteryConsumer.getConsumedPower(),
appsBatteryConsumer.getConsumedPower());
} else {
addEntry("Consumed (measured)", EntryType.DEVICE_TOTAL_POWER,
deviceBatteryConsumer.getConsumedPower(),
appsBatteryConsumer.getConsumedPower());
addEntry("Consumed (modeled)", EntryType.DEVICE_TOTAL_POWER,
modeledDeviceBatteryConsumer.getConsumedPower(),
modeledAppsBatteryConsumer.getConsumedPower());
}
mBatteryConsumerInfo = BatteryConsumerInfoHelper.makeBatteryConsumerInfo(batteryUsageStats,
AGGREGATE_BATTERY_CONSUMER_ID, context.getPackageManager());
for (int component = 0; component < BatteryConsumer.POWER_COMPONENT_COUNT; component++) {
final String metricTitle = getPowerMetricTitle(component);
final int powerModel = deviceBatteryConsumer.getPowerModel(component);
if (powerModel == BatteryConsumer.POWER_MODEL_POWER_PROFILE) {
addEntry(metricTitle, EntryType.DEVICE_POWER_MODELED,
deviceBatteryConsumer.getConsumedPower(component),
appsBatteryConsumer.getConsumedPower(component));
} else {
addEntry(metricTitle + " (measured)", EntryType.DEVICE_POWER_MEASURED,
deviceBatteryConsumer.getConsumedPower(component),
appsBatteryConsumer.getConsumedPower(component));
addEntry(metricTitle + " (modeled)", EntryType.DEVICE_POWER_MODELED,
modeledDeviceBatteryConsumer.getConsumedPower(component),
modeledAppsBatteryConsumer.getConsumedPower(component));
}
}
final int customComponentCount =
deviceBatteryConsumer.getCustomPowerComponentCount();
for (int component = 0; component < customComponentCount; component++) {
final String name = deviceBatteryConsumer.getCustomPowerComponentName(
BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID + component);
addEntry(name + " (custom)", EntryType.DEVICE_POWER_CUSTOM,
deviceBatteryConsumer.getConsumedPowerForCustomComponent(
BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID + component),
appsBatteryConsumer.getConsumedPowerForCustomComponent(
BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID + component));
}
for (int component = 0; component < BatteryConsumer.POWER_COMPONENT_COUNT; component++) {
final String metricTitle = getTimeMetricTitle(component);
addEntry(metricTitle, EntryType.DEVICE_DURATION,
deviceBatteryConsumer.getUsageDurationMillis(component), 0);
}
}
private boolean isPowerProfileModelsOnly(BatteryConsumer batteryConsumer) {
for (int component = 0; component < BatteryConsumer.POWER_COMPONENT_COUNT; component++) {
if (batteryConsumer.getPowerModel(component)
!= BatteryConsumer.POWER_MODEL_POWER_PROFILE) {
return false;
}
}
return true;
}
private BatteryConsumer getRequestedBatteryConsumer(BatteryUsageStats batteryUsageStats,
String batteryConsumerId) {
for (int scope = 0;
scope < BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_COUNT;
scope++) {
if (batteryConsumerId(scope).equals(batteryConsumerId)) {
return batteryUsageStats.getAggregateBatteryConsumer(scope);
}
}
for (BatteryConsumer consumer : batteryUsageStats.getUidBatteryConsumers()) {
for (UidBatteryConsumer consumer : batteryUsageStats.getUidBatteryConsumers()) {
if (batteryConsumerId(consumer).equals(batteryConsumerId)) {
return consumer;
}
@@ -136,17 +257,27 @@ public class BatteryConsumerData {
}
static String getPowerMetricTitle(int componentId) {
final String componentName = DebugUtils.constantToString(BatteryConsumer.class,
"POWER_COMPONENT_", componentId);
return componentName.charAt(0) + componentName.substring(1).toLowerCase().replace('_', ' ')
+ " power";
return getPowerComponentName(componentId);
}
static String getTimeMetricTitle(int componentId) {
final String componentName = DebugUtils.constantToString(BatteryConsumer.class,
"POWER_COMPONENT_", componentId);
return componentName.charAt(0) + componentName.substring(1).toLowerCase().replace('_', ' ')
+ " time";
return getPowerComponentName(componentId) + " time";
}
private static String getPowerComponentName(int componentId) {
switch (componentId) {
case BatteryConsumer.POWER_COMPONENT_CPU:
return "CPU";
case BatteryConsumer.POWER_COMPONENT_GNSS:
return "GNSS";
case BatteryConsumer.POWER_COMPONENT_WIFI:
return "Wi-Fi";
default:
String componentName = DebugUtils.constantToString(BatteryConsumer.class,
"POWER_COMPONENT_", componentId);
return componentName.charAt(0) + componentName.substring(1).toLowerCase()
.replace('_', ' ');
}
}
private void computeTotalPower(BatteryUsageStats batteryUsageStats,
@@ -183,14 +314,12 @@ public class BatteryConsumerData {
}
}
private void addEntry(String title, EntryType entryType, double amount, double totalAmount,
boolean isSystemBatteryConsumer) {
private void addEntry(String title, EntryType entryType, double value1, double value2) {
Entry entry = new Entry();
entry.title = title;
entry.entryType = entryType;
entry.value = amount;
entry.total = totalAmount;
entry.isSystemBatteryConsumer = isSystemBatteryConsumer;
entry.value1 = value1;
entry.value2 = value2;
mEntries.add(entry);
}
@@ -202,18 +331,15 @@ public class BatteryConsumerData {
return mEntries;
}
public static String batteryConsumerId(BatteryConsumer consumer) {
if (consumer instanceof UidBatteryConsumer) {
return "APP|"
+ UserHandle.getUserId(((UidBatteryConsumer) consumer).getUid()) + "|"
+ ((UidBatteryConsumer) consumer).getUid();
} else {
return "";
public static ConsumerType getConsumerType(String batteryConsumerId) {
if (batteryConsumerId.startsWith(UID_BATTERY_CONSUMER_ID_PREFIX)) {
return ConsumerType.UID_BATTERY_CONSUMER;
}
return ConsumerType.DEVICE_POWER_COMPONENT;
}
public static String batteryConsumerId(
@BatteryUsageStats.AggregateBatteryConsumerScope int scope) {
return "SYS|" + scope;
public static String batteryConsumerId(UidBatteryConsumer consumer) {
return UID_BATTERY_CONSUMER_ID_PREFIX + UserHandle.getUserId(consumer.getUid()) + "|"
+ consumer.getUid();
}
}

View File

@@ -16,18 +16,16 @@
package com.android.frameworks.core.batterystatsviewer;
import static com.android.frameworks.core.batterystatsviewer.BatteryConsumerData.batteryConsumerId;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.os.BatteryConsumer;
import android.os.BatteryUsageStats;
import android.os.Process;
import android.os.UidBatteryConsumer;
import android.util.DebugUtils;
import androidx.annotation.NonNull;
import java.util.List;
class BatteryConsumerInfoHelper {
private static final String SYSTEM_SERVER_PACKAGE_NAME = "android";
@@ -39,93 +37,110 @@ class BatteryConsumerInfoHelper {
public ApplicationInfo iconInfo;
public CharSequence packages;
public CharSequence details;
public boolean isSystemBatteryConsumer;
public BatteryConsumerData.ConsumerType consumerType;
}
@NonNull
public static BatteryConsumerInfo makeBatteryConsumerInfo(
@NonNull BatteryConsumer batteryConsumer, String batteryConsumerId,
@NonNull BatteryUsageStats batteryUsageStats, String batteryConsumerId,
PackageManager packageManager) {
BatteryConsumerInfo info = new BatteryConsumerInfo();
info.id = batteryConsumerId;
info.powerMah = batteryConsumer.getConsumedPower();
if (batteryConsumer instanceof UidBatteryConsumer) {
final UidBatteryConsumer uidBatteryConsumer = (UidBatteryConsumer) batteryConsumer;
int uid = uidBatteryConsumer.getUid();
info.details = String.format("UID: %d", uid);
String packageWithHighestDrain = uidBatteryConsumer.getPackageWithHighestDrain();
if (uid == Process.ROOT_UID) {
info.label = "<root>";
} else {
String[] packages = packageManager.getPackagesForUid(uid);
String primaryPackageName = null;
if (uid == Process.SYSTEM_UID) {
primaryPackageName = SYSTEM_SERVER_PACKAGE_NAME;
} else if (packages != null) {
for (String name : packages) {
primaryPackageName = name;
if (name.equals(packageWithHighestDrain)) {
break;
}
BatteryConsumerData.ConsumerType consumerType = BatteryConsumerData.getConsumerType(
batteryConsumerId);
switch (consumerType) {
case UID_BATTERY_CONSUMER:
final List<UidBatteryConsumer> consumers =
batteryUsageStats.getUidBatteryConsumers();
for (UidBatteryConsumer consumer : consumers) {
if (BatteryConsumerData.batteryConsumerId(consumer).equals(batteryConsumerId)) {
return makeBatteryConsumerInfo(consumer, packageManager);
}
}
if (primaryPackageName != null) {
try {
ApplicationInfo applicationInfo =
packageManager.getApplicationInfo(primaryPackageName, 0);
info.label = applicationInfo.loadLabel(packageManager);
info.iconInfo = applicationInfo;
} catch (PackageManager.NameNotFoundException e) {
info.label = primaryPackageName;
}
} else if (packageWithHighestDrain != null) {
info.label = packageWithHighestDrain;
}
if (packages != null && packages.length > 0) {
StringBuilder sb = new StringBuilder();
if (primaryPackageName != null) {
sb.append(primaryPackageName);
}
for (String packageName : packages) {
if (packageName.equals(primaryPackageName)) {
continue;
}
if (sb.length() != 0) {
sb.append(", ");
}
sb.append(packageName);
}
info.packages = sb;
}
}
// Default the app icon to System Server. This includes root, dex2oat and other UIDs.
if (info.iconInfo == null) {
try {
info.iconInfo =
packageManager.getApplicationInfo(SYSTEM_SERVER_PACKAGE_NAME, 0);
} catch (PackageManager.NameNotFoundException nameNotFoundException) {
// Won't happen
}
}
} else {
for (int scope = 0;
scope < BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_COUNT;
scope++) {
if (batteryConsumerId(scope).equals(batteryConsumerId)) {
final String name = DebugUtils.constantToString(BatteryUsageStats.class,
"AGGREGATE_BATTERY_CONSUMER_SCOPE_", scope)
.replace('_', ' ');
info.label = name;
break;
}
}
break;
case DEVICE_POWER_COMPONENT:
return makeAggregateBatteryConsumerInfo(batteryUsageStats);
}
BatteryConsumerInfo info = new BatteryConsumerInfo();
info.id = batteryConsumerId;
return info;
}
private static BatteryConsumerInfo makeBatteryConsumerInfo(
UidBatteryConsumer uidBatteryConsumer, PackageManager packageManager) {
BatteryConsumerInfo info = new BatteryConsumerInfo();
info.consumerType = BatteryConsumerData.ConsumerType.UID_BATTERY_CONSUMER;
info.id = BatteryConsumerData.batteryConsumerId(uidBatteryConsumer);
info.powerMah = uidBatteryConsumer.getConsumedPower();
int uid = uidBatteryConsumer.getUid();
info.details = String.format("UID: %d", uid);
String packageWithHighestDrain = uidBatteryConsumer.getPackageWithHighestDrain();
if (uid == Process.ROOT_UID) {
info.label = "<root>";
} else {
String[] packages = packageManager.getPackagesForUid(uid);
String primaryPackageName = null;
if (uid == Process.SYSTEM_UID) {
primaryPackageName = SYSTEM_SERVER_PACKAGE_NAME;
} else if (packages != null) {
for (String name : packages) {
primaryPackageName = name;
if (name.equals(packageWithHighestDrain)) {
break;
}
}
}
if (primaryPackageName != null) {
try {
ApplicationInfo applicationInfo =
packageManager.getApplicationInfo(primaryPackageName, 0);
info.label = applicationInfo.loadLabel(packageManager);
info.iconInfo = applicationInfo;
} catch (PackageManager.NameNotFoundException e) {
info.label = primaryPackageName;
}
} else if (packageWithHighestDrain != null) {
info.label = packageWithHighestDrain;
}
if (packages != null && packages.length > 0) {
StringBuilder sb = new StringBuilder();
if (primaryPackageName != null) {
sb.append(primaryPackageName);
}
for (String packageName : packages) {
if (packageName.equals(primaryPackageName)) {
continue;
}
if (sb.length() != 0) {
sb.append(", ");
}
sb.append(packageName);
}
info.packages = sb;
}
}
// Default the app icon to System Server. This includes root, dex2oat and other UIDs.
if (info.iconInfo == null) {
try {
info.iconInfo =
packageManager.getApplicationInfo(SYSTEM_SERVER_PACKAGE_NAME, 0);
} catch (PackageManager.NameNotFoundException nameNotFoundException) {
// Won't happen
}
}
return info;
}
private static BatteryConsumerInfo makeAggregateBatteryConsumerInfo(
BatteryUsageStats batteryUsageStats) {
BatteryConsumerInfo info = new BatteryConsumerInfo();
info.consumerType = BatteryConsumerData.ConsumerType.DEVICE_POWER_COMPONENT;
info.id = BatteryConsumerData.AGGREGATE_BATTERY_CONSUMER_ID;
info.powerMah = batteryUsageStats.getConsumedPower();
info.label = "Device";
return info;
}
}

View File

@@ -21,6 +21,7 @@ import android.content.Intent;
import android.content.pm.PackageManager;
import android.os.BatteryStatsManager;
import android.os.BatteryUsageStats;
import android.os.BatteryUsageStatsQuery;
import android.os.Bundle;
import android.os.UidBatteryConsumer;
import android.view.LayoutInflater;
@@ -35,10 +36,12 @@ import androidx.loader.app.LoaderManager;
import androidx.loader.content.Loader;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
import com.android.settingslib.utils.AsyncLoaderCompat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Locale;
@@ -50,10 +53,11 @@ import java.util.Locale;
public class BatteryConsumerPickerActivity extends ComponentActivity {
private static final String PREF_SELECTED_BATTERY_CONSUMER = "batteryConsumerId";
private static final int BATTERY_STATS_REFRESH_RATE_MILLIS = 60 * 1000;
private static final String FORCE_FRESH_STATS = "force_fresh_stats";
private BatteryConsumerListAdapter mBatteryConsumerListAdapter;
private RecyclerView mAppList;
private View mLoadingView;
private final Runnable mBatteryStatsRefresh = this::loadBatteryStats;
private SwipeRefreshLayout mSwipeRefreshLayout;
private final Runnable mBatteryStatsRefresh = this::refreshPeriodically;
private interface OnBatteryConsumerSelectedListener {
void onBatteryConsumerSelected(String batteryConsumerId);
@@ -64,8 +68,11 @@ public class BatteryConsumerPickerActivity extends ComponentActivity {
super.onCreate(icicle);
setContentView(R.layout.battery_consumer_picker_layout);
mLoadingView = findViewById(R.id.loading_view);
mSwipeRefreshLayout = findViewById(R.id.swipe_refresh);
mSwipeRefreshLayout.setColorSchemeResources(android.R.color.holo_green_light);
mSwipeRefreshLayout.setRefreshing(true);
mSwipeRefreshLayout.setOnRefreshListener(this::onRefresh);
mAppList = findViewById(R.id.list_view);
mAppList.setLayoutManager(new LinearLayoutManager(this));
mBatteryConsumerListAdapter =
@@ -97,7 +104,7 @@ public class BatteryConsumerPickerActivity extends ComponentActivity {
@Override
protected void onResume() {
super.onResume();
loadBatteryStats();
refreshPeriodically();
}
@Override
@@ -106,42 +113,54 @@ public class BatteryConsumerPickerActivity extends ComponentActivity {
getMainThreadHandler().removeCallbacks(mBatteryStatsRefresh);
}
private void loadBatteryStats() {
LoaderManager.getInstance(this).restartLoader(0, null,
new BatteryConsumerListLoaderCallbacks());
private void refreshPeriodically() {
loadBatteryUsageStats(false);
getMainThreadHandler().postDelayed(mBatteryStatsRefresh, BATTERY_STATS_REFRESH_RATE_MILLIS);
}
private void onRefresh() {
loadBatteryUsageStats(true);
}
private void loadBatteryUsageStats(boolean forceFreshStats) {
Bundle args = new Bundle();
args.putBoolean(FORCE_FRESH_STATS, forceFreshStats);
LoaderManager.getInstance(this).restartLoader(0, args,
new BatteryConsumerListLoaderCallbacks());
}
private static class BatteryConsumerListLoader extends
AsyncLoaderCompat<List<BatteryConsumerInfoHelper.BatteryConsumerInfo>> {
private final BatteryStatsManager mBatteryStatsManager;
private final PackageManager mPackageManager;
private final boolean mForceFreshStats;
BatteryConsumerListLoader(Context context) {
BatteryConsumerListLoader(Context context, boolean forceFreshStats) {
super(context);
mBatteryStatsManager = context.getSystemService(BatteryStatsManager.class);
mPackageManager = context.getPackageManager();
mForceFreshStats = forceFreshStats;
}
@Override
public List<BatteryConsumerInfoHelper.BatteryConsumerInfo> loadInBackground() {
final BatteryUsageStats batteryUsageStats = mBatteryStatsManager.getBatteryUsageStats();
final BatteryUsageStatsQuery query = mForceFreshStats
? new BatteryUsageStatsQuery.Builder().setMaxStatsAgeMs(0).build()
: BatteryUsageStatsQuery.DEFAULT;
final BatteryUsageStats batteryUsageStats =
mBatteryStatsManager.getBatteryUsageStats(query);
List<BatteryConsumerInfoHelper.BatteryConsumerInfo> batteryConsumerList =
new ArrayList<>();
for (int scope = 0;
scope < BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_COUNT;
scope++) {
batteryConsumerList.add(
BatteryConsumerInfoHelper.makeBatteryConsumerInfo(
batteryUsageStats.getAggregateBatteryConsumer(scope),
BatteryConsumerData.batteryConsumerId(scope),
mPackageManager));
}
batteryConsumerList.add(
BatteryConsumerInfoHelper.makeBatteryConsumerInfo(
batteryUsageStats,
BatteryConsumerData.AGGREGATE_BATTERY_CONSUMER_ID,
mPackageManager));
for (UidBatteryConsumer consumer : batteryUsageStats.getUidBatteryConsumers()) {
batteryConsumerList.add(
BatteryConsumerInfoHelper.makeBatteryConsumerInfo(consumer,
BatteryConsumerInfoHelper.makeBatteryConsumerInfo(batteryUsageStats,
BatteryConsumerData.batteryConsumerId(consumer),
mPackageManager));
}
@@ -166,7 +185,8 @@ public class BatteryConsumerPickerActivity extends ComponentActivity {
@Override
public Loader<List<BatteryConsumerInfoHelper.BatteryConsumerInfo>> onCreateLoader(int id,
Bundle args) {
return new BatteryConsumerListLoader(BatteryConsumerPickerActivity.this);
return new BatteryConsumerListLoader(BatteryConsumerPickerActivity.this,
args.getBoolean(FORCE_FRESH_STATS));
}
@Override
@@ -174,8 +194,7 @@ public class BatteryConsumerPickerActivity extends ComponentActivity {
@NonNull Loader<List<BatteryConsumerInfoHelper.BatteryConsumerInfo>> loader,
List<BatteryConsumerInfoHelper.BatteryConsumerInfo> batteryConsumerList) {
mBatteryConsumerListAdapter.setBatteryConsumerList(batteryConsumerList);
mAppList.setVisibility(View.VISIBLE);
mLoadingView.setVisibility(View.GONE);
mSwipeRefreshLayout.setRefreshing(false);
}
@Override
@@ -187,7 +206,8 @@ public class BatteryConsumerPickerActivity extends ComponentActivity {
public class BatteryConsumerListAdapter
extends RecyclerView.Adapter<BatteryConsumerViewHolder> {
private final OnBatteryConsumerSelectedListener mListener;
private List<BatteryConsumerInfoHelper.BatteryConsumerInfo> mBatteryConsumerList;
private List<BatteryConsumerInfoHelper.BatteryConsumerInfo> mBatteryConsumerList =
Collections.emptyList();
public BatteryConsumerListAdapter(OnBatteryConsumerSelectedListener listener) {
mListener = listener;

View File

@@ -36,6 +36,7 @@ import androidx.loader.app.LoaderManager.LoaderCallbacks;
import androidx.loader.content.Loader;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
import com.android.settingslib.utils.AsyncLoaderCompat;
@@ -48,10 +49,10 @@ public class BatteryStatsViewerActivity extends ComponentActivity {
private static final int BATTERY_STATS_REFRESH_RATE_MILLIS = 60 * 1000;
private static final int MILLIS_IN_MINUTE = 60000;
private static final int LOADER_BATTERY_USAGE_STATS = 1;
private static final String FORCE_FRESH_STATS = "force_fresh_stats";
private BatteryStatsDataAdapter mBatteryStatsDataAdapter;
private final Runnable mBatteryStatsRefresh = this::loadBatteryStats;
private final Runnable mBatteryStatsRefresh = this::refreshPeriodically;
private String mBatteryConsumerId;
private TextView mTitleView;
private TextView mDetailsView;
@@ -59,7 +60,8 @@ public class BatteryStatsViewerActivity extends ComponentActivity {
private TextView mPackagesView;
private View mHeadingsView;
private RecyclerView mBatteryConsumerDataView;
private View mLoadingView;
private SwipeRefreshLayout mSwipeRefreshLayout;
private View mCardView;
private View mEmptyView;
private List<BatteryUsageStats> mBatteryUsageStats;
@@ -71,6 +73,12 @@ public class BatteryStatsViewerActivity extends ComponentActivity {
setContentView(R.layout.battery_stats_viewer_layout);
mSwipeRefreshLayout = findViewById(R.id.swipe_refresh);
mSwipeRefreshLayout.setColorSchemeResources(android.R.color.holo_green_light);
mSwipeRefreshLayout.setRefreshing(true);
mSwipeRefreshLayout.setOnRefreshListener(this::onRefresh);
mCardView = findViewById(R.id.app_card);
mTitleView = findViewById(android.R.id.title);
mDetailsView = findViewById(R.id.details);
mIconView = findViewById(android.R.id.icon);
@@ -82,18 +90,13 @@ public class BatteryStatsViewerActivity extends ComponentActivity {
mBatteryStatsDataAdapter = new BatteryStatsDataAdapter();
mBatteryConsumerDataView.setAdapter(mBatteryStatsDataAdapter);
mLoadingView = findViewById(R.id.loading_view);
mEmptyView = findViewById(R.id.empty_view);
LoaderManager loaderManager = LoaderManager.getInstance(this);
loaderManager.restartLoader(LOADER_BATTERY_USAGE_STATS, null,
new BatteryUsageStatsLoaderCallbacks());
}
@Override
protected void onResume() {
super.onResume();
loadBatteryStats();
refreshPeriodically();
}
@Override
@@ -102,32 +105,46 @@ public class BatteryStatsViewerActivity extends ComponentActivity {
getMainThreadHandler().removeCallbacks(mBatteryStatsRefresh);
}
private void loadBatteryStats() {
LoaderManager loaderManager = LoaderManager.getInstance(this);
loaderManager.restartLoader(LOADER_BATTERY_USAGE_STATS, null,
new BatteryUsageStatsLoaderCallbacks());
private void refreshPeriodically() {
loadBatteryUsageStats(false);
getMainThreadHandler().postDelayed(mBatteryStatsRefresh, BATTERY_STATS_REFRESH_RATE_MILLIS);
}
private void onRefresh() {
loadBatteryUsageStats(true);
}
private void loadBatteryUsageStats(boolean forceFreshStats) {
Bundle args = new Bundle();
args.putBoolean(FORCE_FRESH_STATS, forceFreshStats);
LoaderManager.getInstance(this).restartLoader(0, args,
new BatteryUsageStatsLoaderCallbacks());
}
private static class BatteryUsageStatsLoader extends
AsyncLoaderCompat<List<BatteryUsageStats>> {
private final BatteryStatsManager mBatteryStatsManager;
private final boolean mForceFreshStats;
BatteryUsageStatsLoader(Context context) {
BatteryUsageStatsLoader(Context context, boolean forceFreshStats) {
super(context);
mBatteryStatsManager = context.getSystemService(BatteryStatsManager.class);
mForceFreshStats = forceFreshStats;
}
@Override
public List<BatteryUsageStats> loadInBackground() {
final int maxStatsAgeMs = mForceFreshStats ? 0 : BATTERY_STATS_REFRESH_RATE_MILLIS;
final BatteryUsageStatsQuery queryDefault =
new BatteryUsageStatsQuery.Builder()
.includePowerModels()
.setMaxStatsAgeMs(maxStatsAgeMs)
.build();
final BatteryUsageStatsQuery queryPowerProfileModeledOnly =
new BatteryUsageStatsQuery.Builder()
.powerProfileModeledOnly()
.includePowerModels()
.setMaxStatsAgeMs(maxStatsAgeMs)
.build();
return mBatteryStatsManager.getBatteryUsageStats(
List.of(queryDefault, queryPowerProfileModeledOnly));
@@ -143,7 +160,8 @@ public class BatteryStatsViewerActivity extends ComponentActivity {
@NonNull
@Override
public Loader<List<BatteryUsageStats>> onCreateLoader(int id, Bundle args) {
return new BatteryUsageStatsLoader(BatteryStatsViewerActivity.this);
return new BatteryUsageStatsLoader(BatteryStatsViewerActivity.this,
args.getBoolean(FORCE_FRESH_STATS));
}
@Override
@@ -194,7 +212,8 @@ public class BatteryStatsViewerActivity extends ComponentActivity {
mPackagesView.setVisibility(View.GONE);
}
if (batteryConsumerInfo.isSystemBatteryConsumer) {
if (batteryConsumerInfo.consumerType
== BatteryConsumerData.ConsumerType.DEVICE_POWER_COMPONENT) {
mHeadingsView.setVisibility(View.VISIBLE);
} else {
mHeadingsView.setVisibility(View.GONE);
@@ -210,7 +229,8 @@ public class BatteryStatsViewerActivity extends ComponentActivity {
mBatteryConsumerDataView.setVisibility(View.VISIBLE);
}
mLoadingView.setVisibility(View.GONE);
mCardView.setVisibility(View.VISIBLE);
mSwipeRefreshLayout.setRefreshing(false);
}
private static class BatteryStatsDataAdapter extends
@@ -218,16 +238,16 @@ public class BatteryStatsViewerActivity extends ComponentActivity {
public static class ViewHolder extends RecyclerView.ViewHolder {
public ImageView iconImageView;
public TextView titleTextView;
public TextView amountTextView;
public TextView percentTextView;
public TextView value1TextView;
public TextView value2TextView;
ViewHolder(View itemView) {
super(itemView);
iconImageView = itemView.findViewById(R.id.icon);
titleTextView = itemView.findViewById(R.id.title);
amountTextView = itemView.findViewById(R.id.amount);
percentTextView = itemView.findViewById(R.id.percent);
value1TextView = itemView.findViewById(R.id.value1);
value2TextView = itemView.findViewById(R.id.value2);
}
}
@@ -255,57 +275,108 @@ public class BatteryStatsViewerActivity extends ComponentActivity {
@Override
public void onBindViewHolder(@NonNull ViewHolder viewHolder, int position) {
BatteryConsumerData.Entry entry = mEntries.get(position);
switch (entry.entryType) {
case POWER_MODELED:
viewHolder.titleTextView.setText(entry.title);
viewHolder.amountTextView.setText(
String.format(Locale.getDefault(), "%.1f mAh", entry.value));
viewHolder.iconImageView.setImageResource(R.drawable.gm_calculate_24);
viewHolder.itemView.setBackgroundResource(
case UID_TOTAL_POWER:
setTitleIconAndBackground(viewHolder, entry.title,
R.drawable.gm_sum_24, 0);
setPowerText(viewHolder.value1TextView, entry.value1);
setProportionText(viewHolder.value2TextView, entry);
break;
case UID_POWER_MODELED:
setTitleIconAndBackground(viewHolder, entry.title,
R.drawable.gm_calculate_24,
R.color.battery_consumer_bg_power_profile);
setPowerText(viewHolder.value1TextView, entry.value1);
setProportionText(viewHolder.value2TextView, entry);
break;
case POWER_MEASURED:
viewHolder.titleTextView.setText(entry.title);
viewHolder.amountTextView.setText(
String.format(Locale.getDefault(), "%.1f mAh", entry.value));
viewHolder.iconImageView.setImageResource(R.drawable.gm_amp_24);
viewHolder.itemView.setBackgroundResource(
case UID_POWER_MEASURED:
setTitleIconAndBackground(viewHolder, entry.title,
R.drawable.gm_amp_24,
R.color.battery_consumer_bg_measured_energy);
setPowerText(viewHolder.value1TextView, entry.value1);
setProportionText(viewHolder.value2TextView, entry);
break;
case POWER_CUSTOM:
viewHolder.titleTextView.setText(entry.title);
viewHolder.amountTextView.setText(
String.format(Locale.getDefault(), "%.1f mAh", entry.value));
viewHolder.iconImageView.setImageResource(R.drawable.gm_custom_24);
viewHolder.itemView.setBackgroundResource(
case UID_POWER_CUSTOM:
setTitleIconAndBackground(viewHolder, entry.title,
R.drawable.gm_custom_24,
R.color.battery_consumer_bg_measured_energy);
setPowerText(viewHolder.value1TextView, entry.value1);
setProportionText(viewHolder.value2TextView, entry);
break;
case DURATION:
viewHolder.titleTextView.setText(entry.title);
final long durationMs = (long) entry.value;
CharSequence text;
if (durationMs < MILLIS_IN_MINUTE) {
text = String.format(Locale.getDefault(), "%,d ms", durationMs);
} else {
text = String.format(Locale.getDefault(), "%,d m %d s",
durationMs / MILLIS_IN_MINUTE,
(durationMs % MILLIS_IN_MINUTE) / 1000);
}
viewHolder.amountTextView.setText(text);
viewHolder.iconImageView.setImageResource(R.drawable.gm_timer_24);
viewHolder.itemView.setBackground(null);
case UID_DURATION:
setTitleIconAndBackground(viewHolder, entry.title,
R.drawable.gm_timer_24, 0);
setDurationText(viewHolder.value1TextView, (long) entry.value1);
setProportionText(viewHolder.value2TextView, entry);
break;
case DEVICE_TOTAL_POWER:
setTitleIconAndBackground(viewHolder, entry.title,
R.drawable.gm_sum_24, 0);
setPowerText(viewHolder.value1TextView, entry.value1);
setPowerText(viewHolder.value2TextView, entry.value2);
break;
case DEVICE_POWER_MODELED:
setTitleIconAndBackground(viewHolder, entry.title,
R.drawable.gm_calculate_24,
R.color.battery_consumer_bg_power_profile);
setPowerText(viewHolder.value1TextView, entry.value1);
setPowerText(viewHolder.value2TextView, entry.value2);
break;
case DEVICE_POWER_MEASURED:
setTitleIconAndBackground(viewHolder, entry.title,
R.drawable.gm_amp_24,
R.color.battery_consumer_bg_measured_energy);
setPowerText(viewHolder.value1TextView, entry.value1);
setPowerText(viewHolder.value2TextView, entry.value2);
break;
case DEVICE_POWER_CUSTOM:
setTitleIconAndBackground(viewHolder, entry.title,
R.drawable.gm_custom_24,
R.color.battery_consumer_bg_measured_energy);
setPowerText(viewHolder.value1TextView, entry.value1);
setPowerText(viewHolder.value2TextView, entry.value2);
break;
case DEVICE_DURATION:
setTitleIconAndBackground(viewHolder, entry.title,
R.drawable.gm_timer_24, 0);
setDurationText(viewHolder.value1TextView, (long) entry.value1);
viewHolder.value2TextView.setVisibility(View.GONE);
break;
}
}
double proportion;
if (entry.isSystemBatteryConsumer) {
proportion = entry.value != 0 ? entry.total * 100 / entry.value : 0;
} else {
proportion = entry.total != 0 ? entry.value * 100 / entry.total : 0;
}
viewHolder.percentTextView.setText(
private void setTitleIconAndBackground(ViewHolder viewHolder, String title, int icon,
int background) {
viewHolder.titleTextView.setText(title);
viewHolder.iconImageView.setImageResource(icon);
viewHolder.itemView.setBackgroundResource(background);
}
private void setProportionText(TextView textView, BatteryConsumerData.Entry entry) {
final double proportion = entry.value2 != 0 ? entry.value1 * 100 / entry.value2 : 0;
textView.setText(
String.format(Locale.getDefault(), "%.1f%%", proportion));
textView.setVisibility(View.VISIBLE);
}
private void setPowerText(TextView textView, double powerMah) {
textView.setText(String.format(Locale.getDefault(), "%.1f", powerMah));
textView.setVisibility(View.VISIBLE);
}
private void setDurationText(TextView textView, long durationMs) {
CharSequence text;
if (durationMs < MILLIS_IN_MINUTE) {
text = String.format(Locale.getDefault(), "%,d ms", durationMs);
} else {
text = String.format(Locale.getDefault(), "%,d m %d s",
durationMs / MILLIS_IN_MINUTE,
(durationMs % MILLIS_IN_MINUTE) / 1000);
}
textView.setText(text);
textView.setVisibility(View.VISIBLE);
}
}
}