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:
@@ -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,
|
||||
|
||||
@@ -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>
|
||||
@@ -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"
|
||||
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user