BatteryHistoryChart.java revision cb818619c669d4257a4df969dd03eff479ba84d0
1/*
2 * Copyright (C) 2010 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.settings.fuelgauge;
18
19import com.android.settings.R;
20
21import android.content.Context;
22import android.content.res.ColorStateList;
23import android.content.res.TypedArray;
24import android.graphics.Canvas;
25import android.graphics.Paint;
26import android.graphics.Path;
27import android.graphics.Typeface;
28import android.os.BatteryStats;
29import android.os.SystemClock;
30import android.os.BatteryStats.HistoryItem;
31import android.telephony.ServiceState;
32import android.text.TextPaint;
33import android.util.AttributeSet;
34import android.util.TypedValue;
35import android.view.View;
36
37public class BatteryHistoryChart extends View {
38    static final int SANS = 1;
39    static final int SERIF = 2;
40    static final int MONOSPACE = 3;
41
42    static final int BATTERY_WARN = 29;
43    static final int BATTERY_CRITICAL = 14;
44
45    // First value if for phone off; sirst value is "scanning"; following values
46    // are battery stats signal strength buckets.
47    static final int NUM_PHONE_SIGNALS = 7;
48
49    final Paint mBatteryBackgroundPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
50    final Paint mBatteryGoodPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
51    final Paint mBatteryWarnPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
52    final Paint mBatteryCriticalPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
53    final Paint mChargingPaint = new Paint();
54    final Paint mScreenOnPaint = new Paint();
55    final Paint mGpsOnPaint = new Paint();
56    final Paint mWifiRunningPaint = new Paint();
57    final Paint mWakeLockPaint = new Paint();
58    final Paint[] mPhoneSignalPaints = new Paint[NUM_PHONE_SIGNALS];
59    final int[] mPhoneSignalColors = new int[] {
60            0x00000000, 0xffa00000, 0xffa0a000, 0xff808020,
61            0xff808040, 0xff808060, 0xff008000
62    };
63    final TextPaint mTextPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG);
64
65    final Path mBatLevelPath = new Path();
66    final Path mBatGoodPath = new Path();
67    final Path mBatWarnPath = new Path();
68    final Path mBatCriticalPath = new Path();
69    final Path mChargingPath = new Path();
70    final Path mScreenOnPath = new Path();
71    final Path mGpsOnPath = new Path();
72    final Path mWifiRunningPath = new Path();
73    final Path mWakeLockPath = new Path();
74
75    int mFontSize;
76
77    BatteryStats mStats;
78    long mStatsPeriod;
79    String mDurationString;
80    String mTotalDurationString;
81    String mChargingLabel;
82    String mScreenOnLabel;
83    String mGpsOnLabel;
84    String mWifiRunningLabel;
85    String mWakeLockLabel;
86    String mPhoneSignalLabel;
87
88    int mTextAscent;
89    int mTextDescent;
90    int mDurationStringWidth;
91    int mTotalDurationStringWidth;
92
93    boolean mLargeMode;
94
95    int mLineWidth;
96    int mThinLineWidth;
97    int mChargingOffset;
98    int mScreenOnOffset;
99    int mGpsOnOffset;
100    int mWifiRunningOffset;
101    int mWakeLockOffset;
102    int mPhoneSignalOffset;
103    int mLevelOffset;
104    int mLevelTop;
105    int mLevelBottom;
106    static final int PHONE_SIGNAL_X_MASK = 0x0000ffff;
107    static final int PHONE_SIGNAL_BIN_MASK = 0xffff0000;
108    static final int PHONE_SIGNAL_BIN_SHIFT = 16;
109    int mNumPhoneSignalTicks;
110    int[] mPhoneSignalTicks;
111
112    int mNumHist;
113    BatteryStats.HistoryItem mHistFirst;
114    long mHistStart;
115    long mHistEnd;
116    int mBatLow;
117    int mBatHigh;
118    boolean mHaveWifi;
119    boolean mHaveGps;
120
121    public BatteryHistoryChart(Context context, AttributeSet attrs) {
122        super(context, attrs);
123
124        mBatteryBackgroundPaint.setARGB(255, 128, 128, 128);
125        mBatteryBackgroundPaint.setStyle(Paint.Style.FILL);
126        mBatteryGoodPaint.setARGB(128, 0, 255, 0);
127        mBatteryGoodPaint.setStyle(Paint.Style.STROKE);
128        mBatteryWarnPaint.setARGB(128, 255, 255, 0);
129        mBatteryWarnPaint.setStyle(Paint.Style.STROKE);
130        mBatteryCriticalPaint.setARGB(192, 255, 0, 0);
131        mBatteryCriticalPaint.setStyle(Paint.Style.STROKE);
132        mChargingPaint.setARGB(255, 0, 128, 0);
133        mChargingPaint.setStyle(Paint.Style.STROKE);
134        mScreenOnPaint.setARGB(255, 0, 0, 255);
135        mScreenOnPaint.setStyle(Paint.Style.STROKE);
136        mGpsOnPaint.setARGB(255, 0, 0, 255);
137        mGpsOnPaint.setStyle(Paint.Style.STROKE);
138        mWifiRunningPaint.setARGB(255, 0, 0, 255);
139        mWifiRunningPaint.setStyle(Paint.Style.STROKE);
140        mWakeLockPaint.setARGB(255, 0, 0, 255);
141        mWakeLockPaint.setStyle(Paint.Style.STROKE);
142        for (int i=0; i<NUM_PHONE_SIGNALS; i++) {
143            mPhoneSignalPaints[i] = new Paint();
144            mPhoneSignalPaints[i].setColor(mPhoneSignalColors[i]);
145            mPhoneSignalPaints[i].setStyle(Paint.Style.FILL);
146        }
147
148        mTextPaint.density = getResources().getDisplayMetrics().density;
149        mTextPaint.setCompatibilityScaling(
150                getResources().getCompatibilityInfo().applicationScale);
151
152        TypedArray a =
153            context.obtainStyledAttributes(
154                attrs, R.styleable.BatteryHistoryChart, 0, 0);
155
156        ColorStateList textColor = null;
157        int textSize = 15;
158        int typefaceIndex = -1;
159        int styleIndex = -1;
160
161        TypedArray appearance = null;
162        int ap = a.getResourceId(R.styleable.BatteryHistoryChart_android_textAppearance, -1);
163        if (ap != -1) {
164            appearance = context.obtainStyledAttributes(ap,
165                                com.android.internal.R.styleable.
166                                TextAppearance);
167        }
168        if (appearance != null) {
169            int n = appearance.getIndexCount();
170            for (int i = 0; i < n; i++) {
171                int attr = appearance.getIndex(i);
172
173                switch (attr) {
174                case com.android.internal.R.styleable.TextAppearance_textColor:
175                    textColor = appearance.getColorStateList(attr);
176                    break;
177
178                case com.android.internal.R.styleable.TextAppearance_textSize:
179                    textSize = appearance.getDimensionPixelSize(attr, textSize);
180                    break;
181
182                case com.android.internal.R.styleable.TextAppearance_typeface:
183                    typefaceIndex = appearance.getInt(attr, -1);
184                    break;
185
186                case com.android.internal.R.styleable.TextAppearance_textStyle:
187                    styleIndex = appearance.getInt(attr, -1);
188                    break;
189                }
190            }
191
192            appearance.recycle();
193        }
194
195        int shadowcolor = 0;
196        float dx=0, dy=0, r=0;
197
198        int n = a.getIndexCount();
199        for (int i = 0; i < n; i++) {
200            int attr = a.getIndex(i);
201
202            switch (attr) {
203                case R.styleable.BatteryHistoryChart_android_shadowColor:
204                    shadowcolor = a.getInt(attr, 0);
205                    break;
206
207                case R.styleable.BatteryHistoryChart_android_shadowDx:
208                    dx = a.getFloat(attr, 0);
209                    break;
210
211                case R.styleable.BatteryHistoryChart_android_shadowDy:
212                    dy = a.getFloat(attr, 0);
213                    break;
214
215                case R.styleable.BatteryHistoryChart_android_shadowRadius:
216                    r = a.getFloat(attr, 0);
217                    break;
218
219                case R.styleable.BatteryHistoryChart_android_textColor:
220                    textColor = a.getColorStateList(attr);
221                    break;
222
223                case R.styleable.BatteryHistoryChart_android_textSize:
224                    textSize = a.getDimensionPixelSize(attr, textSize);
225                    break;
226
227                case R.styleable.BatteryHistoryChart_android_typeface:
228                    typefaceIndex = a.getInt(attr, typefaceIndex);
229                    break;
230
231                case R.styleable.BatteryHistoryChart_android_textStyle:
232                    styleIndex = a.getInt(attr, styleIndex);
233                    break;
234            }
235        }
236
237        mTextPaint.setColor(textColor.getDefaultColor());
238        mTextPaint.setTextSize(textSize);
239
240        Typeface tf = null;
241        switch (typefaceIndex) {
242            case SANS:
243                tf = Typeface.SANS_SERIF;
244                break;
245
246            case SERIF:
247                tf = Typeface.SERIF;
248                break;
249
250            case MONOSPACE:
251                tf = Typeface.MONOSPACE;
252                break;
253        }
254
255        setTypeface(tf, styleIndex);
256
257        if (shadowcolor != 0) {
258            mTextPaint.setShadowLayer(r, dx, dy, shadowcolor);
259        }
260    }
261
262    public void setTypeface(Typeface tf, int style) {
263        if (style > 0) {
264            if (tf == null) {
265                tf = Typeface.defaultFromStyle(style);
266            } else {
267                tf = Typeface.create(tf, style);
268            }
269
270            mTextPaint.setTypeface(tf);
271            // now compute what (if any) algorithmic styling is needed
272            int typefaceStyle = tf != null ? tf.getStyle() : 0;
273            int need = style & ~typefaceStyle;
274            mTextPaint.setFakeBoldText((need & Typeface.BOLD) != 0);
275            mTextPaint.setTextSkewX((need & Typeface.ITALIC) != 0 ? -0.25f : 0);
276        } else {
277            mTextPaint.setFakeBoldText(false);
278            mTextPaint.setTextSkewX(0);
279            mTextPaint.setTypeface(tf);
280        }
281    }
282
283    void setStats(BatteryStats stats) {
284        mStats = stats;
285
286        long uSecTime = mStats.computeBatteryRealtime(SystemClock.elapsedRealtime() * 1000,
287                BatteryStats.STATS_SINCE_CHARGED);
288        mStatsPeriod = uSecTime;
289        String durationString = Utils.formatElapsedTime(getContext(), mStatsPeriod / 1000);
290        mDurationString = getContext().getString(R.string.battery_stats_on_battery,
291                durationString);
292        mChargingLabel = getContext().getString(R.string.battery_stats_charging_label);
293        mScreenOnLabel = getContext().getString(R.string.battery_stats_screen_on_label);
294        mGpsOnLabel = getContext().getString(R.string.battery_stats_gps_on_label);
295        mWifiRunningLabel = getContext().getString(R.string.battery_stats_wifi_running_label);
296        mWakeLockLabel = getContext().getString(R.string.battery_stats_wake_lock_label);
297        mPhoneSignalLabel = getContext().getString(R.string.battery_stats_phone_signal_label);
298
299        BatteryStats.HistoryItem rec = stats.getHistory();
300        mHistFirst = null;
301        int pos = 0;
302        int lastInteresting = 0;
303        byte lastLevel = -1;
304        mBatLow = 0;
305        mBatHigh = 100;
306        int aggrStates = 0;
307        while (rec != null) {
308            pos++;
309            if (rec.cmd == HistoryItem.CMD_UPDATE) {
310                if (mHistFirst == null) {
311                    mHistFirst = rec;
312                    mHistStart = rec.time;
313                }
314                if (rec.batteryLevel != lastLevel || pos == 1) {
315                    lastLevel = rec.batteryLevel;
316                    lastInteresting = pos;
317                    mHistEnd = rec.time;
318                }
319                aggrStates |= rec.states;
320            }
321            rec = rec.next;
322        }
323        mNumHist = lastInteresting;
324        mHaveGps = (aggrStates&HistoryItem.STATE_GPS_ON_FLAG) != 0;
325        mHaveWifi = (aggrStates&HistoryItem.STATE_WIFI_RUNNING_FLAG) != 0;
326
327        if (mHistEnd <= mHistStart) mHistEnd = mHistStart+1;
328        mTotalDurationString = Utils.formatElapsedTime(getContext(), mHistEnd - mHistStart);
329    }
330
331    @Override
332    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
333        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
334        mDurationStringWidth = (int)mTextPaint.measureText(mDurationString);
335        mTotalDurationStringWidth = (int)mTextPaint.measureText(mTotalDurationString);
336        mTextAscent = (int)mTextPaint.ascent();
337        mTextDescent = (int)mTextPaint.descent();
338    }
339
340    void addPhoneSignalTick(int x, int bin) {
341        mPhoneSignalTicks[mNumPhoneSignalTicks]
342                = x | bin << PHONE_SIGNAL_BIN_SHIFT;
343        mNumPhoneSignalTicks++;
344    }
345
346    void finishPaths(int w, int h, int levelh, int startX, int y, Path curLevelPath,
347            int lastX, boolean lastCharging, boolean lastScreenOn, boolean lastGpsOn,
348            boolean lastWifiRunning, boolean lastWakeLock, int lastPhoneSignal, Path lastPath) {
349        if (curLevelPath != null) {
350            if (lastX >= 0 && lastX < w) {
351                if (lastPath != null) {
352                    lastPath.lineTo(w, y);
353                }
354                curLevelPath.lineTo(w, y);
355            }
356            curLevelPath.lineTo(w, mLevelTop+levelh);
357            curLevelPath.lineTo(startX, mLevelTop+levelh);
358            curLevelPath.close();
359        }
360
361        if (lastCharging) {
362            mChargingPath.lineTo(w, h-mChargingOffset);
363        }
364        if (lastScreenOn) {
365            mScreenOnPath.lineTo(w, h-mScreenOnOffset);
366        }
367        if (lastGpsOn) {
368            mGpsOnPath.lineTo(w, h-mGpsOnOffset);
369        }
370        if (lastWifiRunning) {
371            mWifiRunningPath.lineTo(w, h-mWifiRunningOffset);
372        }
373        if (lastWakeLock) {
374            mWakeLockPath.lineTo(w, h-mWakeLockOffset);
375        }
376        if (lastPhoneSignal != 0) {
377            addPhoneSignalTick(w, 0);
378        }
379    }
380
381    @Override
382    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
383        super.onSizeChanged(w, h, oldw, oldh);
384
385        int textHeight = mTextDescent - mTextAscent;
386        mThinLineWidth = (int)TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
387                2, getResources().getDisplayMetrics());
388        if (h > (textHeight*6)) {
389            mLargeMode = true;
390            mLineWidth = textHeight/2;
391            mLevelTop = textHeight + mLineWidth;
392        } else {
393            mLargeMode = false;
394            mLineWidth = mThinLineWidth;
395            mLevelTop = 0;
396        }
397        if (mLineWidth <= 0) mLineWidth = 1;
398        mTextPaint.setStrokeWidth(mThinLineWidth);
399        mBatteryGoodPaint.setStrokeWidth(mThinLineWidth);
400        mBatteryWarnPaint.setStrokeWidth(mThinLineWidth);
401        mBatteryCriticalPaint.setStrokeWidth(mThinLineWidth);
402        mChargingPaint.setStrokeWidth(mLineWidth);
403        mScreenOnPaint.setStrokeWidth(mLineWidth);
404        mGpsOnPaint.setStrokeWidth(mLineWidth);
405        mWifiRunningPaint.setStrokeWidth(mLineWidth);
406        mWakeLockPaint.setStrokeWidth(mLineWidth);
407
408        if (mLargeMode) {
409            int barOffset = textHeight + mLineWidth;
410            mChargingOffset = mLineWidth;
411            mScreenOnOffset = mChargingOffset + barOffset;
412            mWakeLockOffset = mScreenOnOffset + barOffset;
413            mWifiRunningOffset = mWakeLockOffset + barOffset;
414            mGpsOnOffset = mWifiRunningOffset + (mHaveWifi ? barOffset : 0);
415            mPhoneSignalOffset = mGpsOnOffset + (mHaveGps ? barOffset : 0);
416            mLevelOffset = mPhoneSignalOffset + barOffset + mLineWidth;
417            mPhoneSignalTicks = new int[w+2];
418        } else {
419            mScreenOnOffset = mGpsOnOffset = mWifiRunningOffset
420                    = mWakeLockOffset = mLineWidth;
421            mChargingOffset = mLineWidth*2;
422            mPhoneSignalOffset = 0;
423            mLevelOffset = mLineWidth*3;
424            mPhoneSignalTicks = null;
425        }
426
427        mBatLevelPath.reset();
428        mBatGoodPath.reset();
429        mBatWarnPath.reset();
430        mBatCriticalPath.reset();
431        mScreenOnPath.reset();
432        mGpsOnPath.reset();
433        mWifiRunningPath.reset();
434        mWakeLockPath.reset();
435        mChargingPath.reset();
436
437        final long timeStart = mHistStart;
438        final long timeChange = mHistEnd-mHistStart;
439
440        final int batLow = mBatLow;
441        final int batChange = mBatHigh-mBatLow;
442
443        final int levelh = h - mLevelOffset - mLevelTop;
444        mLevelBottom = mLevelTop + levelh;
445
446        BatteryStats.HistoryItem rec = mHistFirst;
447        int x = 0, y = 0, startX = 0, lastX = -1, lastY = -1;
448        int i = 0;
449        Path curLevelPath = null;
450        Path lastLinePath = null;
451        boolean lastCharging = false, lastScreenOn = false, lastGpsOn = false;
452        boolean lastWifiRunning = false, lastWakeLock = false;
453        int lastPhoneSignalBin = 0;
454        final int N = mNumHist;
455        while (rec != null && i < N) {
456            if (rec.cmd == BatteryStats.HistoryItem.CMD_UPDATE) {
457                x = (int)(((rec.time-timeStart)*w)/timeChange);
458                y = mLevelTop + levelh - ((rec.batteryLevel-batLow)*(levelh-1))/batChange;
459
460                if (lastX != x) {
461                    // We have moved by at least a pixel.
462                    if (lastY != y) {
463                        // Don't plot changes within a pixel.
464                        Path path;
465                        byte value = rec.batteryLevel;
466                        if (value <= BATTERY_CRITICAL) path = mBatCriticalPath;
467                        else if (value <= BATTERY_WARN) path = mBatWarnPath;
468                        else path = mBatGoodPath;
469
470                        if (path != lastLinePath) {
471                            if (lastLinePath != null) {
472                                lastLinePath.lineTo(x, y);
473                            }
474                            path.moveTo(x, y);
475                            lastLinePath = path;
476                        } else {
477                            path.lineTo(x, y);
478                        }
479
480                        if (curLevelPath == null) {
481                            curLevelPath = mBatLevelPath;
482                            curLevelPath.moveTo(x, y);
483                            startX = x;
484                        } else {
485                            curLevelPath.lineTo(x, y);
486                        }
487                        lastX = x;
488                        lastY = y;
489                    }
490
491                    final boolean charging =
492                        (rec.states&HistoryItem.STATE_BATTERY_PLUGGED_FLAG) != 0;
493                    if (charging != lastCharging) {
494                        if (charging) {
495                            mChargingPath.moveTo(x, h-mChargingOffset);
496                        } else {
497                            mChargingPath.lineTo(x, h-mChargingOffset);
498                        }
499                        lastCharging = charging;
500                    }
501
502                    final boolean screenOn =
503                        (rec.states&HistoryItem.STATE_SCREEN_ON_FLAG) != 0;
504                    if (screenOn != lastScreenOn) {
505                        if (screenOn) {
506                            mScreenOnPath.moveTo(x, h-mScreenOnOffset);
507                        } else {
508                            mScreenOnPath.lineTo(x, h-mScreenOnOffset);
509                        }
510                        lastScreenOn = screenOn;
511                    }
512
513                    final boolean gpsOn =
514                        (rec.states&HistoryItem.STATE_GPS_ON_FLAG) != 0;
515                    if (gpsOn != lastGpsOn) {
516                        if (gpsOn) {
517                            mGpsOnPath.moveTo(x, h-mGpsOnOffset);
518                        } else {
519                            mGpsOnPath.lineTo(x, h-mGpsOnOffset);
520                        }
521                        lastGpsOn = gpsOn;
522                    }
523
524                    final boolean wifiRunning =
525                        (rec.states&HistoryItem.STATE_WIFI_RUNNING_FLAG) != 0;
526                    if (wifiRunning != lastWifiRunning) {
527                        if (wifiRunning) {
528                            mWifiRunningPath.moveTo(x, h-mWifiRunningOffset);
529                        } else {
530                            mWifiRunningPath.lineTo(x, h-mWifiRunningOffset);
531                        }
532                        lastWifiRunning = wifiRunning;
533                    }
534
535                    final boolean wakeLock =
536                        (rec.states&HistoryItem.STATE_WAKE_LOCK_FLAG) != 0;
537                    if (wakeLock != lastWakeLock) {
538                        if (wakeLock) {
539                            mWakeLockPath.moveTo(x, h-mWakeLockOffset);
540                        } else {
541                            mWakeLockPath.lineTo(x, h-mWakeLockOffset);
542                        }
543                        lastWakeLock = wakeLock;
544                    }
545
546                    if (mLargeMode) {
547                        int bin;
548                        if (((rec.states&HistoryItem.STATE_PHONE_STATE_MASK)
549                                >> HistoryItem.STATE_PHONE_STATE_SHIFT)
550                                == ServiceState.STATE_POWER_OFF) {
551                            bin = 0;
552                        } else if ((rec.states&HistoryItem.STATE_PHONE_SCANNING_FLAG) != 0) {
553                            bin = 1;
554                        } else {
555                            bin = (rec.states&HistoryItem.STATE_SIGNAL_STRENGTH_MASK)
556                                    >> HistoryItem.STATE_SIGNAL_STRENGTH_SHIFT;
557                            bin += 2;
558                        }
559                        if (bin != lastPhoneSignalBin) {
560                            addPhoneSignalTick(x, bin);
561                            lastPhoneSignalBin = bin;
562                        }
563                    }
564                }
565
566            } else if (rec.cmd != BatteryStats.HistoryItem.CMD_OVERFLOW) {
567                if (curLevelPath != null) {
568                    finishPaths(x+1, h, levelh, startX, lastY, curLevelPath, lastX,
569                            lastCharging, lastScreenOn, lastGpsOn, lastWifiRunning,
570                            lastWakeLock, lastPhoneSignalBin, lastLinePath);
571                    lastX = lastY = -1;
572                    curLevelPath = null;
573                    lastLinePath = null;
574                    lastCharging = lastScreenOn = lastGpsOn = lastWakeLock = false;
575                    lastPhoneSignalBin = 0;
576                }
577            }
578
579            rec = rec.next;
580            i++;
581        }
582
583        finishPaths(w, h, levelh, startX, lastY, curLevelPath, lastX,
584                lastCharging, lastScreenOn, lastGpsOn, lastWifiRunning,
585                lastWakeLock, lastPhoneSignalBin, lastLinePath);
586    }
587
588    @Override
589    protected void onDraw(Canvas canvas) {
590        super.onDraw(canvas);
591
592        final int width = getWidth();
593        final int height = getHeight();
594
595        canvas.drawPath(mBatLevelPath, mBatteryBackgroundPaint);
596        if (mLargeMode) {
597            canvas.drawText(mDurationString, 0, -mTextAscent + (mLineWidth/2),
598                    mTextPaint);
599            canvas.drawText(mTotalDurationString, (width/2) - (mTotalDurationStringWidth/2),
600                    mLevelBottom - mTextAscent + mThinLineWidth, mTextPaint);
601        } else {
602            canvas.drawText(mDurationString, (width/2) - (mDurationStringWidth/2),
603                    (height/2) - ((mTextDescent-mTextAscent)/2) - mTextAscent, mTextPaint);
604        }
605        if (!mBatGoodPath.isEmpty()) {
606            canvas.drawPath(mBatGoodPath, mBatteryGoodPaint);
607        }
608        if (!mBatWarnPath.isEmpty()) {
609            canvas.drawPath(mBatWarnPath, mBatteryWarnPaint);
610        }
611        if (!mBatCriticalPath.isEmpty()) {
612            canvas.drawPath(mBatCriticalPath, mBatteryCriticalPaint);
613        }
614        int lastBin=0, lastX=0;
615        int top = height-mPhoneSignalOffset - (mLineWidth/2);
616        int bottom = top + mLineWidth;
617        for (int i=0; i<mNumPhoneSignalTicks; i++) {
618            int tick = mPhoneSignalTicks[i];
619            int x = tick&PHONE_SIGNAL_X_MASK;
620            int bin = (tick&PHONE_SIGNAL_BIN_MASK) >> PHONE_SIGNAL_BIN_SHIFT;
621            if (lastBin != 0) {
622                canvas.drawRect(lastX, top, x, bottom, mPhoneSignalPaints[lastBin]);
623            }
624            lastBin = bin;
625            lastX = x;
626        }
627        if (!mScreenOnPath.isEmpty()) {
628            canvas.drawPath(mScreenOnPath, mScreenOnPaint);
629        }
630        if (!mChargingPath.isEmpty()) {
631            canvas.drawPath(mChargingPath, mChargingPaint);
632        }
633        if (mHaveGps) {
634            if (!mGpsOnPath.isEmpty()) {
635                canvas.drawPath(mGpsOnPath, mGpsOnPaint);
636            }
637        }
638        if (mHaveWifi) {
639            if (!mWifiRunningPath.isEmpty()) {
640                canvas.drawPath(mWifiRunningPath, mWifiRunningPaint);
641            }
642        }
643        if (!mWakeLockPath.isEmpty()) {
644            canvas.drawPath(mWakeLockPath, mWakeLockPaint);
645        }
646
647        if (mLargeMode) {
648            canvas.drawText(mPhoneSignalLabel, 0,
649                    height - mPhoneSignalOffset - mTextDescent, mTextPaint);
650            if (mHaveGps) {
651                canvas.drawText(mGpsOnLabel, 0,
652                        height - mGpsOnOffset - mTextDescent, mTextPaint);
653            }
654            if (mHaveWifi) {
655                canvas.drawText(mWifiRunningLabel, 0,
656                        height - mWifiRunningOffset - mTextDescent, mTextPaint);
657            }
658            canvas.drawText(mWakeLockLabel, 0,
659                    height - mWakeLockOffset - mTextDescent, mTextPaint);
660            canvas.drawText(mChargingLabel, 0,
661                    height - mChargingOffset - mTextDescent, mTextPaint);
662            canvas.drawText(mScreenOnLabel, 0,
663                    height - mScreenOnOffset - mTextDescent, mTextPaint);
664            canvas.drawLine(0, mLevelBottom+(mThinLineWidth/2), width,
665                    mLevelBottom+(mThinLineWidth/2), mTextPaint);
666            canvas.drawLine(0, mLevelTop, 0,
667                    mLevelBottom+(mThinLineWidth/2), mTextPaint);
668            for (int i=0; i<10; i++) {
669                int y = mLevelTop + ((mLevelBottom-mLevelTop)*i)/10;
670                canvas.drawLine(0, y, mThinLineWidth*2, y, mTextPaint);
671            }
672        }
673    }
674}
675