Battery chart fixes.

When there is no data to show, don't make a crazy collapsed chart.

Also avoid re-creating the chart when scrolling.

And fix various other things.

Change-Id: Ia1895bc7c46e6d830e66f66e66f17726e1e23e3f
This commit is contained in:
Dianne Hackborn
2014-07-10 10:40:58 -07:00
parent 5427b9a799
commit c19eb361a4
8 changed files with 171 additions and 41 deletions

View File

@@ -75,6 +75,21 @@
</LinearLayout> </LinearLayout>
<LinearLayout
android:id="@+id/messages"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:id="@+id/messages_title"
style="?android:attr/listSeparatorTextViewStyle"
android:layout_marginTop="6dip" />
<!-- Messages go here ... -->
</LinearLayout>
<TextView <TextView
android:id="@+id/packages_section_title" android:id="@+id/packages_section_title"
style="?android:attr/listSeparatorTextViewStyle" style="?android:attr/listSeparatorTextViewStyle"

View File

@@ -0,0 +1,27 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2014 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.
-->
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/message"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="2dip"
android:minHeight="?android:attr/listPreferredItemHeight"
android:paddingStart="4dip"
android:paddingEnd="?android:attr/scrollbarSize"
android:paddingBottom="4dip"
android:ellipsize="marquee"
android:textAppearance="?android:attr/textAppearanceMedium" />

View File

@@ -14,18 +14,13 @@
limitations under the License. limitations under the License.
--> -->
<RelativeLayout <TextView
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent" android:id="@+id/label"
android:layout_height="wrap_content"> android:layout_width="wrap_content"
<!--Label for the item--> android:layout_height="wrap_content"
<TextView android:textAppearance="?android:attr/textAppearanceMedium"
android:id="@+id/label" android:singleLine="true"
android:layout_width="wrap_content" android:layout_alignParentStart="true"
android:layout_height="wrap_content" android:layout_marginBottom="2dip"
android:textAppearance="?android:attr/textAppearanceMedium" android:layout_marginTop="2dip" />
android:singleLine="true"
android:layout_alignParentStart="true"
android:layout_marginBottom="2dip"
android:layout_marginTop="2dip" />
</RelativeLayout>

View File

@@ -3876,12 +3876,11 @@
<string name="usage_type_no_coverage">Time without a signal</string> <string name="usage_type_no_coverage">Time without a signal</string>
<!-- Label for the total power capacity of the device's battery --> <!-- Label for the total power capacity of the device's battery -->
<string name="usage_type_total_battery_capacity">Total battery capacity</string> <string name="usage_type_total_battery_capacity">Total battery capacity</string>
<!-- Label for amount of power use that was computed --> <!-- [CHAR_LIMIT=NONE] Label for amount of power use that was computed -->
<string name="usage_type_computed_power">Computed power</string> <string name="usage_type_computed_power">Computed power use</string>
<!-- Label for minimum amount of actual power use --> <!-- [CHAR_LIMIT=NONE] Label for amount of power use that was actually observed (though
<string name="usage_type_min_actual_power">Min real power</string> the change in battery level) -->
<!-- Label for maximum amount of power use --> <string name="usage_type_actual_power">Observed power use</string>
<string name="usage_type_max_actual_power">Max real power</string>
<!-- Label for force stop action --> <!-- Label for force stop action -->
<string name="battery_action_stop">Force stop</string> <string name="battery_action_stop">Force stop</string>
<!-- Label for app details action --> <!-- Label for app details action -->
@@ -3908,6 +3907,9 @@
<!-- Suggestion to switch to airplane mode to save power --> <!-- Suggestion to switch to airplane mode to save power -->
<string name="battery_sugg_radio">Switch to airplane mode to save power in areas with no cell coverage</string> <string name="battery_sugg_radio">Switch to airplane mode to save power in areas with no cell coverage</string>
<!-- [CHAR_LIMIT=NONE] Description for power consumed by the flashlight -->
<string name="battery_desc_flashlight">Battery used by the flashlight</string>
<!-- Description for power consumed by display --> <!-- Description for power consumed by display -->
<string name="battery_desc_display">Battery used by the display and backlight</string> <string name="battery_desc_display">Battery used by the display and backlight</string>
<!-- Suggestion for reducing display power --> <!-- Suggestion for reducing display power -->
@@ -3938,7 +3940,12 @@
<string name="battery_desc_users">Battery used by user</string> <string name="battery_desc_users">Battery used by user</string>
<!-- [CHAR LIMIT=50] Description for unaccounted power use --> <!-- [CHAR LIMIT=50] Description for unaccounted power use -->
<string name="battery_desc_unaccounted">Unknown power use</string> <string name="battery_desc_unaccounted">Miscellaneous power use</string>
<!-- [CHAR LIMIT=NONE] Description for unaccounted power use -->
<string name="battery_msg_unaccounted">Battery use is an approximation of power
use and does not include every source of battery drain. Miscellaneous is the difference
between the computed approximate power use and the actual drain observed on the
battery.</string>
<!-- [CHAR LIMIT=50] Description for over-counted power use --> <!-- [CHAR LIMIT=50] Description for over-counted power use -->
<string name="battery_desc_overcounted">Over-counted power use</string> <string name="battery_desc_overcounted">Over-counted power use</string>
<!-- Representation of a mAh value. [CHAR LIMIT=NONE] --> <!-- Representation of a mAh value. [CHAR LIMIT=NONE] -->

View File

@@ -154,6 +154,7 @@ public class BatteryHistoryChart extends View {
BatteryStats mStats; BatteryStats mStats;
Intent mBatteryBroadcast; Intent mBatteryBroadcast;
long mStatsPeriod; long mStatsPeriod;
int mBatteryLevel;
String mMaxPercentLabelString; String mMaxPercentLabelString;
String mMinPercentLabelString; String mMinPercentLabelString;
String mDurationString; String mDurationString;
@@ -493,7 +494,7 @@ public class BatteryHistoryChart extends View {
mMinPercentLabelString = getContext().getResources().getString( mMinPercentLabelString = getContext().getResources().getString(
R.string.percentage, 0); R.string.percentage, 0);
int batteryLevel = com.android.settings.Utils.getBatteryLevel(mBatteryBroadcast); mBatteryLevel = com.android.settings.Utils.getBatteryLevel(mBatteryBroadcast);
long remainingTimeUs = 0; long remainingTimeUs = 0;
mDischarging = true; mDischarging = true;
if (mBatteryBroadcast.getIntExtra(BatteryManager.EXTRA_PLUGGED, 0) == 0) { if (mBatteryBroadcast.getIntExtra(BatteryManager.EXTRA_PLUGGED, 0) == 0) {
@@ -503,10 +504,10 @@ public class BatteryHistoryChart extends View {
String timeString = Formatter.formatShortElapsedTime(getContext(), String timeString = Formatter.formatShortElapsedTime(getContext(),
drainTime / 1000); drainTime / 1000);
mChargeLabelString = getContext().getResources().getString( mChargeLabelString = getContext().getResources().getString(
R.string.power_discharging_duration, batteryLevel, timeString); R.string.power_discharging_duration, mBatteryLevel, timeString);
} else { } else {
mChargeLabelString = getContext().getResources().getString( mChargeLabelString = getContext().getResources().getString(
R.string.power_discharging, batteryLevel); R.string.power_discharging, mBatteryLevel);
} }
} else { } else {
final long chargeTime = mStats.computeChargeTimeRemaining(elapsedRealtimeUs); final long chargeTime = mStats.computeChargeTimeRemaining(elapsedRealtimeUs);
@@ -531,10 +532,10 @@ public class BatteryHistoryChart extends View {
resId = R.string.power_charging_duration; resId = R.string.power_charging_duration;
} }
mChargeLabelString = getContext().getResources().getString( mChargeLabelString = getContext().getResources().getString(
resId, batteryLevel, timeString); resId, mBatteryLevel, timeString);
} else { } else {
mChargeLabelString = getContext().getResources().getString( mChargeLabelString = getContext().getResources().getString(
R.string.power_charging, batteryLevel, statusLabel); R.string.power_charging, mBatteryLevel, statusLabel);
} }
} }
mDrainString = ""; mDrainString = "";
@@ -772,14 +773,11 @@ public class BatteryHistoryChart extends View {
mDateLabels.clear(); mDateLabels.clear();
final long walltimeStart = mStartWallTime; final long walltimeStart = mStartWallTime;
final long walltimeChange = mEndWallTime-walltimeStart; final long walltimeChange = mEndWallTime > walltimeStart
? (mEndWallTime-walltimeStart) : 1;
long curWalltime = 0; long curWalltime = 0;
long lastRealtime = 0; long lastRealtime = 0;
if (walltimeChange == 0) {
return;
}
final int batLow = mBatLow; final int batLow = mBatLow;
final int batChange = mBatHigh-mBatLow; final int batChange = mBatHigh-mBatLow;
@@ -967,8 +965,26 @@ public class BatteryHistoryChart extends View {
} }
} }
// Figure out where the actual data ends on the screen. if (lastY < 0 || lastX < 0) {
x = mLevelLeft + (int)(((mEndDataWallTime-walltimeStart)*levelWidth)/walltimeChange); // Didn't get any data...
x = lastX = mLevelLeft;
y = lastY = mLevelTop + levelh - ((mBatteryLevel-batLow)*(levelh-1))/batChange;
Path path;
byte value = (byte)mBatteryLevel;
if (value <= mBatteryCriticalLevel) path = mBatCriticalPath;
else if (value <= mBatteryWarnLevel) path = mBatWarnPath;
else path = null; //mBatGoodPath;
if (path != null) {
path.moveTo(x, y);
lastLinePath = path;
}
mBatLevelPath.moveTo(x, y);
curLevelPath = mBatLevelPath;
x = w;
} else {
// Figure out where the actual data ends on the screen.
x = mLevelLeft + (int)(((mEndDataWallTime-walltimeStart)*levelWidth)/walltimeChange);
}
finishPaths(x, h, levelh, startX, lastY, curLevelPath, lastX, finishPaths(x, h, levelh, startX, lastY, curLevelPath, lastX,
lastCharging, lastScreenOn, lastGpsOn, lastWifiRunning, lastCharging, lastScreenOn, lastGpsOn, lastWifiRunning,

View File

@@ -22,6 +22,7 @@ import android.graphics.drawable.Drawable;
import android.os.BatteryStats; import android.os.BatteryStats;
import android.preference.Preference; import android.preference.Preference;
import android.view.View; import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView; import android.widget.ImageView;
import android.widget.TextView; import android.widget.TextView;
@@ -39,6 +40,7 @@ public class BatteryHistoryPreference extends Preference {
private boolean mHideLabels; private boolean mHideLabels;
private View mLabelHeader; private View mLabelHeader;
private BatteryHistoryChart mChart;
public BatteryHistoryPreference(Context context, BatteryStats stats, Intent batteryBroadcast) { public BatteryHistoryPreference(Context context, BatteryStats stats, Intent batteryBroadcast) {
super(context); super(context);
@@ -66,7 +68,21 @@ public class BatteryHistoryPreference extends Preference {
BatteryHistoryChart chart = (BatteryHistoryChart)view.findViewById( BatteryHistoryChart chart = (BatteryHistoryChart)view.findViewById(
R.id.battery_history_chart); R.id.battery_history_chart);
chart.setStats(mStats, mBatteryBroadcast); if (mChart == null) {
// First time: use and initialize this chart.
chart.setStats(mStats, mBatteryBroadcast);
mChart = chart;
} else {
// All future times: forget the newly inflated chart, re-use the
// already initialized chart from last time.
ViewGroup parent = (ViewGroup)chart.getParent();
int index = parent.indexOfChild(chart);
parent.removeViewAt(index);
if (mChart.getParent() != null) {
((ViewGroup)mChart.getParent()).removeView(mChart);
}
parent.addView(mChart, index);
}
mLabelHeader = view.findViewById(R.id.labelsHeader); mLabelHeader = view.findViewById(R.id.labelsHeader);
mLabelHeader.setVisibility(mHideLabels ? View.GONE : View.VISIBLE); mLabelHeader.setVisibility(mHideLabels ? View.GONE : View.VISIBLE);
} }

View File

@@ -72,6 +72,7 @@ public class PowerUsageDetail extends Fragment implements Button.OnClickListener
R.string.battery_desc_voice, R.string.battery_desc_voice,
R.string.battery_desc_wifi, R.string.battery_desc_wifi,
R.string.battery_desc_bluetooth, R.string.battery_desc_bluetooth,
R.string.battery_desc_flashlight,
R.string.battery_desc_display, R.string.battery_desc_display,
R.string.battery_desc_apps, R.string.battery_desc_apps,
R.string.battery_desc_users, R.string.battery_desc_users,
@@ -217,18 +218,28 @@ public class PowerUsageDetail extends Fragment implements Button.OnClickListener
}; };
} break; } break;
case UNACCOUNTED: case UNACCOUNTED:
case OVERCOUNTED:
{ {
types = new int[] { types = new int[] {
R.string.usage_type_total_battery_capacity, R.string.usage_type_total_battery_capacity,
R.string.usage_type_computed_power, R.string.usage_type_computed_power,
R.string.usage_type_min_actual_power, R.string.usage_type_actual_power,
R.string.usage_type_max_actual_power,
}; };
values = new double[] { values = new double[] {
helper.getPowerProfile().getBatteryCapacity(), helper.getPowerProfile().getBatteryCapacity(),
helper.getComputedPower(), helper.getComputedPower(),
helper.getMinDrainedPower(), helper.getMinDrainedPower(),
};
} break;
case OVERCOUNTED:
{
types = new int[] {
R.string.usage_type_total_battery_capacity,
R.string.usage_type_computed_power,
R.string.usage_type_actual_power,
};
values = new double[] {
helper.getPowerProfile().getBatteryCapacity(),
helper.getComputedPower(),
helper.getMaxDrainedPower(), helper.getMaxDrainedPower(),
}; };
} break; } break;
@@ -290,6 +301,7 @@ public class PowerUsageDetail extends Fragment implements Button.OnClickListener
private Button mReportButton; private Button mReportButton;
private ViewGroup mDetailsParent; private ViewGroup mDetailsParent;
private ViewGroup mControlsParent; private ViewGroup mControlsParent;
private ViewGroup mMessagesParent;
private long mStartTime; private long mStartTime;
private BatterySipper.DrainType mDrainType; private BatterySipper.DrainType mDrainType;
private Drawable mAppIcon; private Drawable mAppIcon;
@@ -390,10 +402,12 @@ public class PowerUsageDetail extends Fragment implements Button.OnClickListener
mDetailsParent = (ViewGroup)mRootView.findViewById(R.id.details); mDetailsParent = (ViewGroup)mRootView.findViewById(R.id.details);
mControlsParent = (ViewGroup)mRootView.findViewById(R.id.controls); mControlsParent = (ViewGroup)mRootView.findViewById(R.id.controls);
mMessagesParent = (ViewGroup)mRootView.findViewById(R.id.messages);
fillDetailsSection(); fillDetailsSection();
fillPackagesSection(mUid); fillPackagesSection(mUid);
fillControlsSection(mUid); fillControlsSection(mUid);
fillMessagesSection(mUid);
if (mUid >= Process.FIRST_APPLICATION_UID) { if (mUid >= Process.FIRST_APPLICATION_UID) {
mForceStopButton.setText(R.string.force_stop); mForceStopButton.setText(R.string.force_stop);
@@ -497,8 +511,7 @@ public class PowerUsageDetail extends Fragment implements Button.OnClickListener
break; break;
case R.string.usage_type_total_battery_capacity: case R.string.usage_type_total_battery_capacity:
case R.string.usage_type_computed_power: case R.string.usage_type_computed_power:
case R.string.usage_type_min_actual_power: case R.string.usage_type_actual_power:
case R.string.usage_type_max_actual_power:
value = getActivity().getString(R.string.mah, (long)(mValues[i])); value = getActivity().getString(R.string.mah, (long)(mValues[i]));
break; break;
case R.string.usage_type_gps: case R.string.usage_type_gps:
@@ -591,6 +604,28 @@ public class PowerUsageDetail extends Fragment implements Button.OnClickListener
actionButton.setTag(new Integer(action)); actionButton.setTag(new Integer(action));
} }
private void fillMessagesSection(int uid) {
boolean removeHeader = true;
switch (mDrainType) {
case UNACCOUNTED:
addMessage(R.string.battery_msg_unaccounted);
removeHeader = false;
break;
}
if (removeHeader) {
mMessagesParent.setVisibility(View.GONE);
}
}
private void addMessage(int message) {
final Resources res = getResources();
LayoutInflater inflater = getActivity().getLayoutInflater();
View item = inflater.inflate(R.layout.power_usage_message_item, null);
mMessagesParent.addView(item);
TextView messageView = (TextView) item.findViewById(R.id.message);
messageView.setText(res.getText(message));
}
private void removePackagesSection() { private void removePackagesSection() {
View view; View view;
if ((view = mRootView.findViewById(R.id.packages_section_title)) != null) { if ((view = mRootView.findViewById(R.id.packages_section_title)) != null) {
@@ -703,8 +738,7 @@ public class PowerUsageDetail extends Fragment implements Button.OnClickListener
//if (ai.icon != 0) { //if (ai.icon != 0) {
// icon = ai.loadIcon(pm); // icon = ai.loadIcon(pm);
//} //}
ViewGroup item = (ViewGroup) inflater.inflate(R.layout.power_usage_package_item, View item = inflater.inflate(R.layout.power_usage_package_item, null);
null);
packagesParent.addView(item); packagesParent.addView(item);
TextView labelView = (TextView) item.findViewById(R.id.label); TextView labelView = (TextView) item.findViewById(R.id.label);
labelView.setText(mPackages[i]); labelView.setText(mPackages[i]);

View File

@@ -272,6 +272,26 @@ public class PowerUsageSummary extends PreferenceFragment {
if (((int) (percentOfTotal + .5)) < 1) { if (((int) (percentOfTotal + .5)) < 1) {
continue; continue;
} }
if (sipper.drainType == BatterySipper.DrainType.OVERCOUNTED) {
// Don't show over-counted unless it is at least 2/3 the size of
// the largest real entry, and its percent of total is more significant
if (sipper.value < ((mStatsHelper.getMaxRealPower()*2)/3)) {
continue;
}
if (percentOfTotal < 10) {
continue;
}
}
if (sipper.drainType == BatterySipper.DrainType.UNACCOUNTED) {
// Don't show over-counted unless it is at least 1/2 the size of
// the largest real entry, and its percent of total is more significant
if (sipper.value < (mStatsHelper.getMaxRealPower()/2)) {
continue;
}
if (percentOfTotal < 5) {
continue;
}
}
final UserHandle userHandle = new UserHandle(UserHandle.getUserId(sipper.getUid())); final UserHandle userHandle = new UserHandle(UserHandle.getUserId(sipper.getUid()));
final BatteryEntry entry = new BatteryEntry(getActivity(), mHandler, mUm, sipper); final BatteryEntry entry = new BatteryEntry(getActivity(), mHandler, mUm, sipper);
final PowerGaugePreference pref = new PowerGaugePreference(getActivity(), final PowerGaugePreference pref = new PowerGaugePreference(getActivity(),