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