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
</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"
--- /dev/null
+<?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" />
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" />
<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 -->
<!-- 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 -->
<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] -->
BatteryStats mStats;
Intent mBatteryBroadcast;
long mStatsPeriod;
+ int mBatteryLevel;
String mMaxPercentLabelString;
String mMinPercentLabelString;
String mDurationString;
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) {
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);
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 = "";
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;
}
}
- // 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,
import android.os.BatteryStats;
import android.preference.Preference;
import android.view.View;
+import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
private boolean mHideLabels;
private View mLabelHeader;
+ private BatteryHistoryChart mChart;
public BatteryHistoryPreference(Context context, BatteryStats stats, Intent batteryBroadcast) {
super(context);
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);
}
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,
};
} 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;
private Button mReportButton;
private ViewGroup mDetailsParent;
private ViewGroup mControlsParent;
+ private ViewGroup mMessagesParent;
private long mStartTime;
private BatterySipper.DrainType mDrainType;
private Drawable mAppIcon;
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);
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:
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) {
//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]);
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(),