OSDN Git Service

Battery chart fixes.
authorDianne Hackborn <hackbod@google.com>
Thu, 10 Jul 2014 17:40:58 +0000 (10:40 -0700)
committerDianne Hackborn <hackbod@google.com>
Thu, 17 Jul 2014 19:09:54 +0000 (12:09 -0700)
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

res/layout/power_usage_details.xml
res/layout/power_usage_message_item.xml [new file with mode: 0644]
res/layout/power_usage_package_item.xml
res/values/strings.xml
src/com/android/settings/fuelgauge/BatteryHistoryChart.java
src/com/android/settings/fuelgauge/BatteryHistoryPreference.java
src/com/android/settings/fuelgauge/PowerUsageDetail.java
src/com/android/settings/fuelgauge/PowerUsageSummary.java

index a383a37..f711d9a 100644 (file)
 
         </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"
diff --git a/res/layout/power_usage_message_item.xml b/res/layout/power_usage_message_item.xml
new file mode 100644 (file)
index 0000000..6f9e619
--- /dev/null
@@ -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" />
index d939229..6c31792 100644 (file)
      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" />
index ec13ce1..c7f90c4 100644 (file)
     <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] -->
index 60e73f5..d88c516 100644 (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,
index fd7d87a..e7326b1 100644 (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);
     }
index d4624c4..fe4fd81 100644 (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]);
index 70e9176..a691d4d 100644 (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(),