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 boolean mHavePhoneSignal; 177 178 public BatteryHistoryChart(Context context, AttributeSet attrs) { 179 super(context, attrs); 180 181 mBatteryBackgroundPaint.setARGB(255, 128, 128, 128); 182 mBatteryBackgroundPaint.setStyle(Paint.Style.FILL); 183 mBatteryGoodPaint.setARGB(128, 0, 255, 0); 184 mBatteryGoodPaint.setStyle(Paint.Style.STROKE); 185 mBatteryWarnPaint.setARGB(128, 255, 255, 0); 186 mBatteryWarnPaint.setStyle(Paint.Style.STROKE); 187 mBatteryCriticalPaint.setARGB(192, 255, 0, 0); 188 mBatteryCriticalPaint.setStyle(Paint.Style.STROKE); 189 mChargingPaint.setARGB(255, 0, 128, 0); 190 mChargingPaint.setStyle(Paint.Style.STROKE); 191 mScreenOnPaint.setStyle(Paint.Style.STROKE); 192 mGpsOnPaint.setStyle(Paint.Style.STROKE); 193 mWifiRunningPaint.setStyle(Paint.Style.STROKE); 194 mWakeLockPaint.setStyle(Paint.Style.STROKE); 195 mPhoneSignalChart.setColors(new int[] { 196 0x00000000, 0xffa00000, 0xffa0a000, 0xff808020, 197 0xff808040, 0xff808060, 0xff008000 198 }); 199 200 mTextPaint.density = getResources().getDisplayMetrics().density; 201 mTextPaint.setCompatibilityScaling( 202 getResources().getCompatibilityInfo().applicationScale); 203 204 TypedArray a = 205 context.obtainStyledAttributes( 206 attrs, R.styleable.BatteryHistoryChart, 0, 0); 207 208 ColorStateList textColor = null; 209 int textSize = 15; 210 int typefaceIndex = -1; 211 int styleIndex = -1; 212 213 TypedArray appearance = null; 214 int ap = a.getResourceId(R.styleable.BatteryHistoryChart_android_textAppearance, -1); 215 if (ap != -1) { 216 appearance = context.obtainStyledAttributes(ap, 217 com.android.internal.R.styleable. 218 TextAppearance); 219 } 220 if (appearance != null) { 221 int n = appearance.getIndexCount(); 222 for (int i = 0; i < n; i++) { 223 int attr = appearance.getIndex(i); 224 225 switch (attr) { 226 case com.android.internal.R.styleable.TextAppearance_textColor: 227 textColor = appearance.getColorStateList(attr); 228 break; 229 230 case com.android.internal.R.styleable.TextAppearance_textSize: 231 textSize = appearance.getDimensionPixelSize(attr, textSize); 232 break; 233 234 case com.android.internal.R.styleable.TextAppearance_typeface: 235 typefaceIndex = appearance.getInt(attr, -1); 236 break; 237 238 case com.android.internal.R.styleable.TextAppearance_textStyle: 239 styleIndex = appearance.getInt(attr, -1); 240 break; 241 } 242 } 243 244 appearance.recycle(); 245 } 246 247 int shadowcolor = 0; 248 float dx=0, dy=0, r=0; 249 250 int n = a.getIndexCount(); 251 for (int i = 0; i < n; i++) { 252 int attr = a.getIndex(i); 253 254 switch (attr) { 255 case R.styleable.BatteryHistoryChart_android_shadowColor: 256 shadowcolor = a.getInt(attr, 0); 257 break; 258 259 case R.styleable.BatteryHistoryChart_android_shadowDx: 260 dx = a.getFloat(attr, 0); 261 break; 262 263 case R.styleable.BatteryHistoryChart_android_shadowDy: 264 dy = a.getFloat(attr, 0); 265 break; 266 267 case R.styleable.BatteryHistoryChart_android_shadowRadius: 268 r = a.getFloat(attr, 0); 269 break; 270 271 case R.styleable.BatteryHistoryChart_android_textColor: 272 textColor = a.getColorStateList(attr); 273 break; 274 275 case R.styleable.BatteryHistoryChart_android_textSize: 276 textSize = a.getDimensionPixelSize(attr, textSize); 277 break; 278 279 case R.styleable.BatteryHistoryChart_android_typeface: 280 typefaceIndex = a.getInt(attr, typefaceIndex); 281 break; 282 283 case R.styleable.BatteryHistoryChart_android_textStyle: 284 styleIndex = a.getInt(attr, styleIndex); 285 break; 286 } 287 } 288 289 a.recycle(); 290 291 mTextPaint.setColor(textColor.getDefaultColor()); 292 mTextPaint.setTextSize(textSize); 293 294 Typeface tf = null; 295 switch (typefaceIndex) { 296 case SANS: 297 tf = Typeface.SANS_SERIF; 298 break; 299 300 case SERIF: 301 tf = Typeface.SERIF; 302 break; 303 304 case MONOSPACE: 305 tf = Typeface.MONOSPACE; 306 break; 307 } 308 309 setTypeface(tf, styleIndex); 310 311 if (shadowcolor != 0) { 312 mTextPaint.setShadowLayer(r, dx, dy, shadowcolor); 313 } 314 } 315 316 public void setTypeface(Typeface tf, int style) { 317 if (style > 0) { 318 if (tf == null) { 319 tf = Typeface.defaultFromStyle(style); 320 } else { 321 tf = Typeface.create(tf, style); 322 } 323 324 mTextPaint.setTypeface(tf); 325 // now compute what (if any) algorithmic styling is needed 326 int typefaceStyle = tf != null ? tf.getStyle() : 0; 327 int need = style & ~typefaceStyle; 328 mTextPaint.setFakeBoldText((need & Typeface.BOLD) != 0); 329 mTextPaint.setTextSkewX((need & Typeface.ITALIC) != 0 ? -0.25f : 0); 330 } else { 331 mTextPaint.setFakeBoldText(false); 332 mTextPaint.setTextSkewX(0); 333 mTextPaint.setTypeface(tf); 334 } 335 } 336 337 void setStats(BatteryStats stats) { 338 mStats = stats; 339 340 long uSecTime = mStats.computeBatteryRealtime(SystemClock.elapsedRealtime() * 1000, 341 BatteryStats.STATS_SINCE_CHARGED); 342 mStatsPeriod = uSecTime; 343 String durationString = Utils.formatElapsedTime(getContext(), mStatsPeriod / 1000, true); 344 mDurationString = getContext().getString(R.string.battery_stats_on_battery, 345 durationString); 346 mChargingLabel = getContext().getString(R.string.battery_stats_charging_label); 347 mScreenOnLabel = getContext().getString(R.string.battery_stats_screen_on_label); 348 mGpsOnLabel = getContext().getString(R.string.battery_stats_gps_on_label); 349 mWifiRunningLabel = getContext().getString(R.string.battery_stats_wifi_running_label); 350 mWakeLockLabel = getContext().getString(R.string.battery_stats_wake_lock_label); 351 mPhoneSignalLabel = getContext().getString(R.string.battery_stats_phone_signal_label); 352 353 int pos = 0; 354 int lastInteresting = 0; 355 byte lastLevel = -1; 356 mBatLow = 0; 357 mBatHigh = 100; 358 int aggrStates = 0; 359 boolean first = true; 360 if (stats.startIteratingHistoryLocked()) { 361 final HistoryItem rec = new HistoryItem(); 362 while (stats.getNextHistoryLocked(rec)) { 363 pos++; 364 if (rec.cmd == HistoryItem.CMD_UPDATE) { 365 if (first) { 366 first = false; 367 mHistStart = rec.time; 368 } 369 if (rec.batteryLevel != lastLevel || pos == 1) { 370 lastLevel = rec.batteryLevel; 371 } 372 lastInteresting = pos; 373 mHistEnd = rec.time; 374 aggrStates |= rec.states; 375 } 376 } 377 } 378 mNumHist = lastInteresting; 379 mHaveGps = (aggrStates&HistoryItem.STATE_GPS_ON_FLAG) != 0; 380 mHaveWifi = (aggrStates&HistoryItem.STATE_WIFI_RUNNING_FLAG) != 0; 381 if (!com.android.settings.Utils.isWifiOnly(getContext())) { 382 mHavePhoneSignal = true; 383 } 384 if (mHistEnd <= mHistStart) mHistEnd = mHistStart+1; 385 mTotalDurationString = Utils.formatElapsedTime(getContext(), mHistEnd - mHistStart, true); 386 } 387 388 @Override 389 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 390 super.onMeasure(widthMeasureSpec, heightMeasureSpec); 391 mDurationStringWidth = (int)mTextPaint.measureText(mDurationString); 392 mTotalDurationStringWidth = (int)mTextPaint.measureText(mTotalDurationString); 393 mTextAscent = (int)mTextPaint.ascent(); 394 mTextDescent = (int)mTextPaint.descent(); 395 } 396 397 void finishPaths(int w, int h, int levelh, int startX, int y, Path curLevelPath, 398 int lastX, boolean lastCharging, boolean lastScreenOn, boolean lastGpsOn, 399 boolean lastWifiRunning, boolean lastWakeLock, Path lastPath) { 400 if (curLevelPath != null) { 401 if (lastX >= 0 && lastX < w) { 402 if (lastPath != null) { 403 lastPath.lineTo(w, y); 404 } 405 curLevelPath.lineTo(w, y); 406 } 407 curLevelPath.lineTo(w, mLevelTop+levelh); 408 curLevelPath.lineTo(startX, mLevelTop+levelh); 409 curLevelPath.close(); 410 } 411 412 if (lastCharging) { 413 mChargingPath.lineTo(w, h-mChargingOffset); 414 } 415 if (lastScreenOn) { 416 mScreenOnPath.lineTo(w, h-mScreenOnOffset); 417 } 418 if (lastGpsOn) { 419 mGpsOnPath.lineTo(w, h-mGpsOnOffset); 420 } 421 if (lastWifiRunning) { 422 mWifiRunningPath.lineTo(w, h-mWifiRunningOffset); 423 } 424 if (lastWakeLock) { 425 mWakeLockPath.lineTo(w, h-mWakeLockOffset); 426 } 427 if (mHavePhoneSignal) { 428 mPhoneSignalChart.finish(w); 429 } 430 } 431 432 @Override 433 protected void onSizeChanged(int w, int h, int oldw, int oldh) { 434 super.onSizeChanged(w, h, oldw, oldh); 435 436 int textHeight = mTextDescent - mTextAscent; 437 mThinLineWidth = (int)TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 438 2, getResources().getDisplayMetrics()); 439 if (h > (textHeight*6)) { 440 mLargeMode = true; 441 if (h > (textHeight*15)) { 442 // Plenty of room for the chart. 443 mLineWidth = textHeight/2; 444 } else { 445 // Compress lines to make more room for chart. 446 mLineWidth = textHeight/3; 447 } 448 mLevelTop = textHeight + mLineWidth; 449 mScreenOnPaint.setARGB(255, 32, 64, 255); 450 mGpsOnPaint.setARGB(255, 32, 64, 255); 451 mWifiRunningPaint.setARGB(255, 32, 64, 255); 452 mWakeLockPaint.setARGB(255, 32, 64, 255); 453 } else { 454 mLargeMode = false; 455 mLineWidth = mThinLineWidth; 456 mLevelTop = 0; 457 mScreenOnPaint.setARGB(255, 0, 0, 255); 458 mGpsOnPaint.setARGB(255, 0, 0, 255); 459 mWifiRunningPaint.setARGB(255, 0, 0, 255); 460 mWakeLockPaint.setARGB(255, 0, 0, 255); 461 } 462 if (mLineWidth <= 0) mLineWidth = 1; 463 mTextPaint.setStrokeWidth(mThinLineWidth); 464 mBatteryGoodPaint.setStrokeWidth(mThinLineWidth); 465 mBatteryWarnPaint.setStrokeWidth(mThinLineWidth); 466 mBatteryCriticalPaint.setStrokeWidth(mThinLineWidth); 467 mChargingPaint.setStrokeWidth(mLineWidth); 468 mScreenOnPaint.setStrokeWidth(mLineWidth); 469 mGpsOnPaint.setStrokeWidth(mLineWidth); 470 mWifiRunningPaint.setStrokeWidth(mLineWidth); 471 mWakeLockPaint.setStrokeWidth(mLineWidth); 472 473 if (mLargeMode) { 474 int barOffset = textHeight + mLineWidth; 475 mChargingOffset = mLineWidth; 476 mScreenOnOffset = mChargingOffset + barOffset; 477 mWakeLockOffset = mScreenOnOffset + barOffset; 478 mWifiRunningOffset = mWakeLockOffset + barOffset; 479 mGpsOnOffset = mWifiRunningOffset + (mHaveWifi ? barOffset : 0); 480 mPhoneSignalOffset = mGpsOnOffset + (mHaveGps ? barOffset : 0); 481 mLevelOffset = mPhoneSignalOffset + (mHavePhoneSignal ? barOffset : 0) 482 + ((mLineWidth*3)/2); 483 if (mHavePhoneSignal) { 484 mPhoneSignalChart.init(w); 485 } 486 } else { 487 mScreenOnOffset = mGpsOnOffset = mWifiRunningOffset 488 = mWakeLockOffset = mLineWidth; 489 mChargingOffset = mLineWidth*2; 490 mPhoneSignalOffset = 0; 491 mLevelOffset = mLineWidth*3; 492 if (mHavePhoneSignal) { 493 mPhoneSignalChart.init(0); 494 } 495 } 496 497 mBatLevelPath.reset(); 498 mBatGoodPath.reset(); 499 mBatWarnPath.reset(); 500 mBatCriticalPath.reset(); 501 mScreenOnPath.reset(); 502 mGpsOnPath.reset(); 503 mWifiRunningPath.reset(); 504 mWakeLockPath.reset(); 505 mChargingPath.reset(); 506 507 final long timeStart = mHistStart; 508 final long timeChange = mHistEnd-mHistStart; 509 510 final int batLow = mBatLow; 511 final int batChange = mBatHigh-mBatLow; 512 513 final int levelh = h - mLevelOffset - mLevelTop; 514 mLevelBottom = mLevelTop + levelh; 515 516 int x = 0, y = 0, startX = 0, lastX = -1, lastY = -1; 517 int i = 0; 518 Path curLevelPath = null; 519 Path lastLinePath = null; 520 boolean lastCharging = false, lastScreenOn = false, lastGpsOn = false; 521 boolean lastWifiRunning = false, lastWakeLock = false; 522 final int N = mNumHist; 523 if (mStats.startIteratingHistoryLocked()) { 524 final HistoryItem rec = new HistoryItem(); 525 while (mStats.getNextHistoryLocked(rec) && i < N) { 526 if (rec.cmd == BatteryStats.HistoryItem.CMD_UPDATE) { 527 x = (int)(((rec.time-timeStart)*w)/timeChange); 528 y = mLevelTop + levelh - ((rec.batteryLevel-batLow)*(levelh-1))/batChange; 529 530 if (lastX != x) { 531 // We have moved by at least a pixel. 532 if (lastY != y) { 533 // Don't plot changes within a pixel. 534 Path path; 535 byte value = rec.batteryLevel; 536 if (value <= BATTERY_CRITICAL) path = mBatCriticalPath; 537 else if (value <= BATTERY_WARN) path = mBatWarnPath; 538 else path = mBatGoodPath; 539 540 if (path != lastLinePath) { 541 if (lastLinePath != null) { 542 lastLinePath.lineTo(x, y); 543 } 544 path.moveTo(x, y); 545 lastLinePath = path; 546 } else { 547 path.lineTo(x, y); 548 } 549 550 if (curLevelPath == null) { 551 curLevelPath = mBatLevelPath; 552 curLevelPath.moveTo(x, y); 553 startX = x; 554 } else { 555 curLevelPath.lineTo(x, y); 556 } 557 lastX = x; 558 lastY = y; 559 } 560 } 561 562 final boolean charging = 563 (rec.states&HistoryItem.STATE_BATTERY_PLUGGED_FLAG) != 0; 564 if (charging != lastCharging) { 565 if (charging) { 566 mChargingPath.moveTo(x, h-mChargingOffset); 567 } else { 568 mChargingPath.lineTo(x, h-mChargingOffset); 569 } 570 lastCharging = charging; 571 } 572 573 final boolean screenOn = 574 (rec.states&HistoryItem.STATE_SCREEN_ON_FLAG) != 0; 575 if (screenOn != lastScreenOn) { 576 if (screenOn) { 577 mScreenOnPath.moveTo(x, h-mScreenOnOffset); 578 } else { 579 mScreenOnPath.lineTo(x, h-mScreenOnOffset); 580 } 581 lastScreenOn = screenOn; 582 } 583 584 final boolean gpsOn = 585 (rec.states&HistoryItem.STATE_GPS_ON_FLAG) != 0; 586 if (gpsOn != lastGpsOn) { 587 if (gpsOn) { 588 mGpsOnPath.moveTo(x, h-mGpsOnOffset); 589 } else { 590 mGpsOnPath.lineTo(x, h-mGpsOnOffset); 591 } 592 lastGpsOn = gpsOn; 593 } 594 595 final boolean wifiRunning = 596 (rec.states&HistoryItem.STATE_WIFI_RUNNING_FLAG) != 0; 597 if (wifiRunning != lastWifiRunning) { 598 if (wifiRunning) { 599 mWifiRunningPath.moveTo(x, h-mWifiRunningOffset); 600 } else { 601 mWifiRunningPath.lineTo(x, h-mWifiRunningOffset); 602 } 603 lastWifiRunning = wifiRunning; 604 } 605 606 final boolean wakeLock = 607 (rec.states&HistoryItem.STATE_WAKE_LOCK_FLAG) != 0; 608 if (wakeLock != lastWakeLock) { 609 if (wakeLock) { 610 mWakeLockPath.moveTo(x, h-mWakeLockOffset); 611 } else { 612 mWakeLockPath.lineTo(x, h-mWakeLockOffset); 613 } 614 lastWakeLock = wakeLock; 615 } 616 617 if (mLargeMode && mHavePhoneSignal) { 618 int bin; 619 if (((rec.states&HistoryItem.STATE_PHONE_STATE_MASK) 620 >> HistoryItem.STATE_PHONE_STATE_SHIFT) 621 == ServiceState.STATE_POWER_OFF) { 622 bin = 0; 623 } else if ((rec.states&HistoryItem.STATE_PHONE_SCANNING_FLAG) != 0) { 624 bin = 1; 625 } else { 626 bin = (rec.states&HistoryItem.STATE_SIGNAL_STRENGTH_MASK) 627 >> HistoryItem.STATE_SIGNAL_STRENGTH_SHIFT; 628 bin += 2; 629 } 630 mPhoneSignalChart.addTick(x, bin); 631 } 632 633 } else if (rec.cmd != BatteryStats.HistoryItem.CMD_OVERFLOW) { 634 if (curLevelPath != null) { 635 finishPaths(x+1, h, levelh, startX, lastY, curLevelPath, lastX, 636 lastCharging, lastScreenOn, lastGpsOn, lastWifiRunning, 637 lastWakeLock, lastLinePath); 638 lastX = lastY = -1; 639 curLevelPath = null; 640 lastLinePath = null; 641 lastCharging = lastScreenOn = lastGpsOn = lastWakeLock = false; 642 } 643 } 644 645 i++; 646 } 647 } 648 649 finishPaths(w, h, levelh, startX, lastY, curLevelPath, lastX, 650 lastCharging, lastScreenOn, lastGpsOn, lastWifiRunning, 651 lastWakeLock, lastLinePath); 652 } 653 654 @Override 655 protected void onDraw(Canvas canvas) { 656 super.onDraw(canvas); 657 658 final int width = getWidth(); 659 final int height = getHeight(); 660 final boolean layoutRtl = isLayoutRtl(); 661 final int textStartX = layoutRtl ? width : 0; 662 mTextPaint.setTextAlign(layoutRtl ? Paint.Align.RIGHT : Paint.Align.LEFT); 663 664 canvas.drawPath(mBatLevelPath, mBatteryBackgroundPaint); 665 if (mLargeMode) { 666 int durationHalfWidth = mTotalDurationStringWidth / 2; 667 if (layoutRtl) durationHalfWidth = -durationHalfWidth; 668 canvas.drawText(mDurationString, textStartX, -mTextAscent + (mLineWidth / 2), 669 mTextPaint); 670 canvas.drawText(mTotalDurationString, (width / 2) - durationHalfWidth, 671 mLevelBottom - mTextAscent + mThinLineWidth, mTextPaint); 672 } else { 673 int durationHalfWidth = mDurationStringWidth / 2; 674 if (layoutRtl) durationHalfWidth = -durationHalfWidth; 675 canvas.drawText(mDurationString, (width / 2) - durationHalfWidth, 676 (height / 2) - ((mTextDescent - mTextAscent) / 2) - mTextAscent, mTextPaint); 677 } 678 if (!mBatGoodPath.isEmpty()) { 679 canvas.drawPath(mBatGoodPath, mBatteryGoodPaint); 680 } 681 if (!mBatWarnPath.isEmpty()) { 682 canvas.drawPath(mBatWarnPath, mBatteryWarnPaint); 683 } 684 if (!mBatCriticalPath.isEmpty()) { 685 canvas.drawPath(mBatCriticalPath, mBatteryCriticalPaint); 686 } 687 if (mHavePhoneSignal) { 688 int top = height-mPhoneSignalOffset - (mLineWidth/2); 689 mPhoneSignalChart.draw(canvas, top, mLineWidth); 690 } 691 if (!mScreenOnPath.isEmpty()) { 692 canvas.drawPath(mScreenOnPath, mScreenOnPaint); 693 } 694 if (!mChargingPath.isEmpty()) { 695 canvas.drawPath(mChargingPath, mChargingPaint); 696 } 697 if (mHaveGps) { 698 if (!mGpsOnPath.isEmpty()) { 699 canvas.drawPath(mGpsOnPath, mGpsOnPaint); 700 } 701 } 702 if (mHaveWifi) { 703 if (!mWifiRunningPath.isEmpty()) { 704 canvas.drawPath(mWifiRunningPath, mWifiRunningPaint); 705 } 706 } 707 if (!mWakeLockPath.isEmpty()) { 708 canvas.drawPath(mWakeLockPath, mWakeLockPaint); 709 } 710 711 if (mLargeMode) { 712 if (mHavePhoneSignal) { 713 canvas.drawText(mPhoneSignalLabel, textStartX, 714 height - mPhoneSignalOffset - mTextDescent, mTextPaint); 715 } 716 if (mHaveGps) { 717 canvas.drawText(mGpsOnLabel, textStartX, 718 height - mGpsOnOffset - mTextDescent, mTextPaint); 719 } 720 if (mHaveWifi) { 721 canvas.drawText(mWifiRunningLabel, textStartX, 722 height - mWifiRunningOffset - mTextDescent, mTextPaint); 723 } 724 canvas.drawText(mWakeLockLabel, textStartX, 725 height - mWakeLockOffset - mTextDescent, mTextPaint); 726 canvas.drawText(mChargingLabel, textStartX, 727 height - mChargingOffset - mTextDescent, mTextPaint); 728 canvas.drawText(mScreenOnLabel, textStartX, 729 height - mScreenOnOffset - mTextDescent, mTextPaint); 730 canvas.drawLine(0, mLevelBottom+(mThinLineWidth/2), width, 731 mLevelBottom+(mThinLineWidth/2), mTextPaint); 732 canvas.drawLine(0, mLevelTop, 0, 733 mLevelBottom+(mThinLineWidth/2), mTextPaint); 734 for (int i=0; i<10; i++) { 735 int y = mLevelTop + ((mLevelBottom-mLevelTop)*i)/10; 736 canvas.drawLine(0, y, mThinLineWidth*2, y, mTextPaint); 737 } 738 } 739 } 740 } 741