private void drawCircle(Canvas canvas, BatteryTracker tracker, float textX, RectF drawRect) {
      boolean unknownStatus = tracker.status == BatteryManager.BATTERY_STATUS_UNKNOWN;
      int animOffset = tracker.shouldIndicateCharging() ? mAnimOffset : 0;
      int level = tracker.level;
      Paint paint;

      if (unknownStatus) {
        paint = mBackPaint;
        level = 100; // Draw all the circle;
      } else {
        paint = mFrontPaint;
        paint.setColor(getColorForLevel(level));
        if (tracker.status == BatteryManager.BATTERY_STATUS_FULL) {
          level = 100;
        }
      }

      // draw thin gray ring first
      canvas.drawArc(drawRect, 270, 360, false, mBackPaint);
      // draw colored arc representing charge level
      canvas.drawArc(drawRect, 270 + animOffset, 3.6f * level, false, paint);
      // if chosen by options, draw percentage text in the middle
      // always skip percentage when 100, so layout doesnt break
      if (unknownStatus) {
        mTextPaint.setColor(paint.getColor());
        canvas.drawText("?", textX, mTextY, mTextPaint);

      } else if (tracker.shouldIndicateCharging() && (!mShowPercent || level == 100)) {
        // draw the bolt
        final float bl = (int) (drawRect.left + drawRect.width() / 3.2f);
        final float bt = (int) (drawRect.top + drawRect.height() / 4f);
        final float br = (int) (drawRect.right - drawRect.width() / 5.2f);
        final float bb = (int) (drawRect.bottom - drawRect.height() / 8f);
        if (mBoltFrame.left != bl
            || mBoltFrame.top != bt
            || mBoltFrame.right != br
            || mBoltFrame.bottom != bb) {
          mBoltFrame.set(bl, bt, br, bb);
          mBoltPath.reset();
          mBoltPath.moveTo(
              mBoltFrame.left + mBoltPoints[0] * mBoltFrame.width(),
              mBoltFrame.top + mBoltPoints[1] * mBoltFrame.height());
          for (int i = 2; i < mBoltPoints.length; i += 2) {
            mBoltPath.lineTo(
                mBoltFrame.left + mBoltPoints[i] * mBoltFrame.width(),
                mBoltFrame.top + mBoltPoints[i + 1] * mBoltFrame.height());
          }
          mBoltPath.lineTo(
              mBoltFrame.left + mBoltPoints[0] * mBoltFrame.width(),
              mBoltFrame.top + mBoltPoints[1] * mBoltFrame.height());
        }
        canvas.drawPath(mBoltPath, mBoltPaint);

      } else if (level < 100 && mShowPercent) {
        mTextPaint.setColor(paint.getColor());
        canvas.drawText(Integer.toString(level), textX, mTextY, mTextPaint);
      }
    }
    private void updateChargeAnimation(BatteryTracker tracker) {
      if (!tracker.shouldIndicateCharging()) {
        resetChargeAnimation();
        return;
      }

      mIsAnimating = true;

      if (mAnimOffset > 100) {
        mAnimOffset = 0;
      } else {
        mAnimOffset += 5;
      }

      mHandler.removeCallbacks(mInvalidate);
      mHandler.postDelayed(mInvalidate, 100);
    }
    @Override
    public void onDraw(Canvas c, BatteryTracker tracker) {
      if (mDisposed) return;

      int level = tracker.level;
      boolean plugged = tracker.plugged;
      boolean unknownStatus = tracker.status == BatteryManager.BATTERY_STATUS_UNKNOWN;

      if (mOldLevel != level || mOldPlugged != plugged) {
        mOldLevel = level;
        mOldPlugged = plugged;

        postInvalidate();
        requestLayout();
        return;
      }

      if (unknownStatus) {
        c.drawText("?", mTextX, mTextY, mBackPaint);
        Resources res = mContext.getResources();
        mFrontPaint.setColor(res.getColor(R.color.batterymeter_frame_color));
        drawWithoutLevel(c, "?");
        return;
      }

      mFrontPaint.setColor(getColorForLevel(level));

      // Is plugged? Then use the animation status
      if (tracker.shouldIndicateCharging() && level != 100) {
        updateChargeAnimation(tracker);
        drawWithLevel(c, tracker, mAnimOffset, getLevel(level));
      } else {
        resetChargeAnimation();
        if (DRAW_LEVEL) {
          drawWithLevel(c, tracker, level, getLevel(level));
        } else {
          drawWithoutLevel(c, getLevel(level));
        }
      }
    }
    /**
     * updates the animation counter cares for timed callbacks to continue animation cycles uses
     * mInvalidate for delayed invalidate() callbacks
     */
    private void updateChargeAnim(BatteryTracker tracker) {
      if (!tracker.shouldIndicateCharging()
          || tracker.status == BatteryManager.BATTERY_STATUS_FULL) {
        if (mIsAnimating) {
          mIsAnimating = false;
          mAnimOffset = 0;
          mHandler.removeCallbacks(mInvalidate);
        }
        return;
      }

      mIsAnimating = true;

      if (mAnimOffset > 360) {
        mAnimOffset = 0;
      } else {
        mAnimOffset += 3;
      }

      mHandler.removeCallbacks(mInvalidate);
      mHandler.postDelayed(mInvalidate, 50);
    }
    @Override
    public void onDraw(Canvas c, BatteryTracker tracker) {
      if (mDisposed) return;

      final int level = tracker.level;

      if (level == BatteryTracker.UNKNOWN_LEVEL) return;

      float drawFrac = (float) level / 100f;
      final int pt = getPaddingTop() + (mHorizontal ? (int) (mHeight * 0.20f) : 0);
      final int pl = getPaddingLeft();
      final int pr = getPaddingRight();
      final int pb = getPaddingBottom();
      int height = mHeight - pt - pb;
      int width = mWidth - pl - pr;

      mButtonHeight = (int) ((mHorizontal ? width : height) * 0.12f);

      mFrame.set(0, 0, width, height);
      mFrame.offset(pl, pt);

      if (mHorizontal) {
        mButtonFrame.set(
            /*cover frame border of intersecting area*/
            width - (mButtonHeight + 5) - mFrame.left,
            mFrame.top + height * 0.25f,
            mFrame.right,
            mFrame.bottom - height * 0.25f);

        mButtonFrame.top += SUBPIXEL;
        mButtonFrame.bottom -= SUBPIXEL;
        mButtonFrame.right -= SUBPIXEL;
      } else {
        mButtonFrame.set(
            mFrame.left + width * 0.25f,
            mFrame.top,
            mFrame.right - width * 0.25f,
            mFrame.top + mButtonHeight + 5 /*cover frame border of intersecting area*/);

        mButtonFrame.top += SUBPIXEL;
        mButtonFrame.left += SUBPIXEL;
        mButtonFrame.right -= SUBPIXEL;
      }

      if (mHorizontal) {
        mFrame.right -= mButtonHeight;
      } else {
        mFrame.top += mButtonHeight;
      }
      mFrame.left += SUBPIXEL;
      mFrame.top += SUBPIXEL;
      mFrame.right -= SUBPIXEL;
      mFrame.bottom -= SUBPIXEL;

      // first, draw the battery shape
      c.drawRect(mFrame, mFramePaint);

      // fill 'er up
      final int color = tracker.plugged ? mChargeColor : getColorForLevel(level);
      mBatteryPaint.setColor(color);

      if (level >= FULL) {
        drawFrac = 1f;
      } else if (level <= EMPTY) {
        drawFrac = 0f;
      }

      c.drawRect(mButtonFrame, drawFrac == 1f ? mBatteryPaint : mFramePaint);

      mClipFrame.set(mFrame);
      if (mHorizontal) {
        mClipFrame.right -= (mFrame.width() * (1f - drawFrac));
      } else {
        mClipFrame.top += (mFrame.height() * (1f - drawFrac));
      }

      c.save(Canvas.CLIP_SAVE_FLAG);
      c.clipRect(mClipFrame);
      c.drawRect(mFrame, mBatteryPaint);
      c.restore();

      if (tracker.shouldIndicateCharging()) {
        // draw the bolt
        final float bl = (int) (mFrame.left + mFrame.width() / (mHorizontal ? 9f : 4.5f));
        final float bt = (int) (mFrame.top + mFrame.height() / (mHorizontal ? 4.5f : 6f));
        final float br = (int) (mFrame.right - mFrame.width() / (mHorizontal ? 6f : 7f));
        final float bb = (int) (mFrame.bottom - mFrame.height() / (mHorizontal ? 7f : 10f));
        if (mBoltFrame.left != bl
            || mBoltFrame.top != bt
            || mBoltFrame.right != br
            || mBoltFrame.bottom != bb) {
          mBoltFrame.set(bl, bt, br, bb);
          mBoltPath.reset();
          mBoltPath.moveTo(
              mBoltFrame.left + mBoltPoints[0] * mBoltFrame.width(),
              mBoltFrame.top + mBoltPoints[1] * mBoltFrame.height());
          for (int i = 2; i < mBoltPoints.length; i += 2) {
            mBoltPath.lineTo(
                mBoltFrame.left + mBoltPoints[i] * mBoltFrame.width(),
                mBoltFrame.top + mBoltPoints[i + 1] * mBoltFrame.height());
          }
          mBoltPath.lineTo(
              mBoltFrame.left + mBoltPoints[0] * mBoltFrame.width(),
              mBoltFrame.top + mBoltPoints[1] * mBoltFrame.height());
        }
        c.drawPath(mBoltPath, mBoltPaint);
      } else if (level <= EMPTY) {
        final float x = mHorizontal ? pt + (height * 0.5f) : mWidth * 0.5f;
        final float y =
            mHorizontal
                ? (mWidth - mWarningTextHeight) * -0.48f
                : (mHeight + mWarningTextHeight) * 0.48f;
        if (mHorizontal) {
          c.save();
          c.rotate(90);
        }
        c.drawText(mWarningString, x, y, mWarningTextPaint);
        if (mHorizontal) {
          c.restore();
        }
      } else if (mShowPercent && !(tracker.level == 100 && !SHOW_100_PERCENT)) {
        final float full = mHorizontal ? 0.60f : 0.45f;
        final float nofull = mHorizontal ? 0.75f : 0.6f;
        final float single = mHorizontal ? 0.86f : 0.75f;
        mTextPaint.setTextSize(
            height * (SINGLE_DIGIT_PERCENT ? single : (tracker.level == 100 ? full : nofull)));
        mTextHeight = -mTextPaint.getFontMetrics().ascent;

        final String str = String.valueOf(SINGLE_DIGIT_PERCENT ? (level / 10) : level);
        final float x = mWidth * 0.5f;
        final float y = pt + (height + mTextHeight) * 0.47f;

        c.drawText(str, x, y, mTextPaint);
      }
    }