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
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
android:id="@+id/packages_section_title"
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.
-->
<RelativeLayout
<TextView
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<!--Label for the item-->
<TextView
android:id="@+id/label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceMedium"
android:singleLine="true"
android:layout_alignParentStart="true"
android:layout_marginBottom="2dip"
android:layout_marginTop="2dip" />
</RelativeLayout>
android:id="@+id/label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceMedium"
android:singleLine="true"
android:layout_alignParentStart="true"
android:layout_marginBottom="2dip"
android:layout_marginTop="2dip" />

View File

@@ -3876,12 +3876,11 @@
<string name="usage_type_no_coverage">Time without a signal</string>
<!-- Label for the total power capacity of the device's battery -->
<string name="usage_type_total_battery_capacity">Total battery capacity</string>
<!-- Label for amount of power use that was computed -->
<string name="usage_type_computed_power">Computed power</string>
<!-- Label for minimum amount of actual power use -->
<string name="usage_type_min_actual_power">Min real power</string>
<!-- Label for maximum amount of power use -->
<string name="usage_type_max_actual_power">Max real power</string>
<!-- [CHAR_LIMIT=NONE] Label for amount of power use that was computed -->
<string name="usage_type_computed_power">Computed power use</string>
<!-- [CHAR_LIMIT=NONE] Label for amount of power use that was actually observed (though
the change in battery level) -->
<string name="usage_type_actual_power">Observed power use</string>
<!-- Label for force stop action -->
<string name="battery_action_stop">Force stop</string>
<!-- Label for app details action -->
@@ -3908,6 +3907,9 @@
<!-- 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>
<!-- [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 -->
<string name="battery_desc_display">Battery used by the display and backlight</string>
<!-- Suggestion for reducing display power -->
@@ -3938,7 +3940,12 @@
<string name="battery_desc_users">Battery used by user</string>
<!-- [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 -->
<string name="battery_desc_overcounted">Over-counted power use</string>
<!-- Representation of a mAh value. [CHAR LIMIT=NONE] -->

View File

@@ -154,6 +154,7 @@ public class BatteryHistoryChart extends View {
BatteryStats mStats;
Intent mBatteryBroadcast;
long mStatsPeriod;
int mBatteryLevel;
String mMaxPercentLabelString;
String mMinPercentLabelString;
String mDurationString;
@@ -493,7 +494,7 @@ public class BatteryHistoryChart extends View {
mMinPercentLabelString = getContext().getResources().getString(
R.string.percentage, 0);
int batteryLevel = com.android.settings.Utils.getBatteryLevel(mBatteryBroadcast);
mBatteryLevel = com.android.settings.Utils.getBatteryLevel(mBatteryBroadcast);
long remainingTimeUs = 0;
mDischarging = true;
if (mBatteryBroadcast.getIntExtra(BatteryManager.EXTRA_PLUGGED, 0) == 0) {
@@ -503,10 +504,10 @@ public class BatteryHistoryChart extends View {
String timeString = Formatter.formatShortElapsedTime(getContext(),
drainTime / 1000);
mChargeLabelString = getContext().getResources().getString(
R.string.power_discharging_duration, batteryLevel, timeString);
R.string.power_discharging_duration, mBatteryLevel, timeString);
} else {
mChargeLabelString = getContext().getResources().getString(
R.string.power_discharging, batteryLevel);
R.string.power_discharging, mBatteryLevel);
}
} else {
final long chargeTime = mStats.computeChargeTimeRemaining(elapsedRealtimeUs);
@@ -531,10 +532,10 @@ public class BatteryHistoryChart extends View {
resId = R.string.power_charging_duration;
}
mChargeLabelString = getContext().getResources().getString(
resId, batteryLevel, timeString);
resId, mBatteryLevel, timeString);
} else {
mChargeLabelString = getContext().getResources().getString(
R.string.power_charging, batteryLevel, statusLabel);
R.string.power_charging, mBatteryLevel, statusLabel);
}
}
mDrainString = "";
@@ -772,14 +773,11 @@ public class BatteryHistoryChart extends View {
mDateLabels.clear();
final long walltimeStart = mStartWallTime;
final long walltimeChange = mEndWallTime-walltimeStart;
final long walltimeChange = mEndWallTime > walltimeStart
? (mEndWallTime-walltimeStart) : 1;
long curWalltime = 0;
long lastRealtime = 0;
if (walltimeChange == 0) {
return;
}
final int batLow = 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.
x = mLevelLeft + (int)(((mEndDataWallTime-walltimeStart)*levelWidth)/walltimeChange);
if (lastY < 0 || lastX < 0) {
// 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,
lastCharging, lastScreenOn, lastGpsOn, lastWifiRunning,

View File

@@ -22,6 +22,7 @@ import android.graphics.drawable.Drawable;
import android.os.BatteryStats;
import android.preference.Preference;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
@@ -39,6 +40,7 @@ public class BatteryHistoryPreference extends Preference {
private boolean mHideLabels;
private View mLabelHeader;
private BatteryHistoryChart mChart;
public BatteryHistoryPreference(Context context, BatteryStats stats, Intent batteryBroadcast) {
super(context);
@@ -66,7 +68,21 @@ public class BatteryHistoryPreference extends Preference {
BatteryHistoryChart chart = (BatteryHistoryChart)view.findViewById(
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.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_wifi,
R.string.battery_desc_bluetooth,
R.string.battery_desc_flashlight,
R.string.battery_desc_display,
R.string.battery_desc_apps,
R.string.battery_desc_users,
@@ -217,18 +218,28 @@ public class PowerUsageDetail extends Fragment implements Button.OnClickListener
};
} break;
case UNACCOUNTED:
case OVERCOUNTED:
{
types = new int[] {
R.string.usage_type_total_battery_capacity,
R.string.usage_type_computed_power,
R.string.usage_type_min_actual_power,
R.string.usage_type_max_actual_power,
R.string.usage_type_actual_power,
};
values = new double[] {
helper.getPowerProfile().getBatteryCapacity(),
helper.getComputedPower(),
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(),
};
} break;
@@ -290,6 +301,7 @@ public class PowerUsageDetail extends Fragment implements Button.OnClickListener
private Button mReportButton;
private ViewGroup mDetailsParent;
private ViewGroup mControlsParent;
private ViewGroup mMessagesParent;
private long mStartTime;
private BatterySipper.DrainType mDrainType;
private Drawable mAppIcon;
@@ -390,10 +402,12 @@ public class PowerUsageDetail extends Fragment implements Button.OnClickListener
mDetailsParent = (ViewGroup)mRootView.findViewById(R.id.details);
mControlsParent = (ViewGroup)mRootView.findViewById(R.id.controls);
mMessagesParent = (ViewGroup)mRootView.findViewById(R.id.messages);
fillDetailsSection();
fillPackagesSection(mUid);
fillControlsSection(mUid);
fillMessagesSection(mUid);
if (mUid >= Process.FIRST_APPLICATION_UID) {
mForceStopButton.setText(R.string.force_stop);
@@ -497,8 +511,7 @@ public class PowerUsageDetail extends Fragment implements Button.OnClickListener
break;
case R.string.usage_type_total_battery_capacity:
case R.string.usage_type_computed_power:
case R.string.usage_type_min_actual_power:
case R.string.usage_type_max_actual_power:
case R.string.usage_type_actual_power:
value = getActivity().getString(R.string.mah, (long)(mValues[i]));
break;
case R.string.usage_type_gps:
@@ -591,6 +604,28 @@ public class PowerUsageDetail extends Fragment implements Button.OnClickListener
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() {
View view;
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) {
// icon = ai.loadIcon(pm);
//}
ViewGroup item = (ViewGroup) inflater.inflate(R.layout.power_usage_package_item,
null);
View item = inflater.inflate(R.layout.power_usage_package_item, null);
packagesParent.addView(item);
TextView labelView = (TextView) item.findViewById(R.id.label);
labelView.setText(mPackages[i]);

View File

@@ -272,6 +272,26 @@ public class PowerUsageSummary extends PreferenceFragment {
if (((int) (percentOfTotal + .5)) < 1) {
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 BatteryEntry entry = new BatteryEntry(getActivity(), mHandler, mUm, sipper);
final PowerGaugePreference pref = new PowerGaugePreference(getActivity(),