1 /* 2 * Copyright (C) 2015 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.deskclock.timer; 18 19 import android.content.Context; 20 import android.content.res.Resources; 21 import android.graphics.Canvas; 22 import android.graphics.Color; 23 import android.graphics.Paint; 24 import android.graphics.RectF; 25 import android.util.AttributeSet; 26 import android.view.View; 27 28 import com.android.deskclock.R; 29 import com.android.deskclock.ThemeUtils; 30 import com.android.deskclock.Utils; 31 import com.android.deskclock.data.Timer; 32 33 /** 34 * Custom view that draws timer progress as a circle. 35 */ 36 public final class TimerCircleView extends View { 37 38 /** The size of the dot indicating the progress through the timer. */ 39 private final float mDotRadius; 40 41 /** An amount to subtract from the true radius to account for drawing thicknesses. */ 42 private final float mRadiusOffset; 43 44 /** The color indicating the remaining portion of the timer. */ 45 private final int mRemainderColor; 46 47 /** The color indicating the completed portion of the timer. */ 48 private final int mCompletedColor; 49 50 /** The size of the stroke that paints the timer circle. */ 51 private final float mStrokeSize; 52 53 private final Paint mPaint = new Paint(); 54 private final Paint mFill = new Paint(); 55 private final RectF mArcRect = new RectF(); 56 57 private Timer mTimer; 58 59 @SuppressWarnings("unused") TimerCircleView(Context context)60 public TimerCircleView(Context context) { 61 this(context, null); 62 } 63 TimerCircleView(Context context, AttributeSet attrs)64 public TimerCircleView(Context context, AttributeSet attrs) { 65 super(context, attrs); 66 67 final Resources resources = context.getResources(); 68 final float dotDiameter = resources.getDimension(R.dimen.circletimer_dot_size); 69 70 mDotRadius = dotDiameter / 2f; 71 mStrokeSize = resources.getDimension(R.dimen.circletimer_circle_size); 72 mRadiusOffset = Utils.calculateRadiusOffset(mStrokeSize, dotDiameter, 0); 73 74 mRemainderColor = Color.WHITE; 75 mCompletedColor = ThemeUtils.resolveColor(context, R.attr.colorAccent); 76 77 mPaint.setAntiAlias(true); 78 mPaint.setStyle(Paint.Style.STROKE); 79 80 mFill.setAntiAlias(true); 81 mFill.setColor(mCompletedColor); 82 mFill.setStyle(Paint.Style.FILL); 83 } 84 update(Timer timer)85 void update(Timer timer) { 86 if (mTimer != timer) { 87 mTimer = timer; 88 postInvalidateOnAnimation(); 89 } 90 } 91 92 @Override onDraw(Canvas canvas)93 public void onDraw(Canvas canvas) { 94 if (mTimer == null) { 95 return; 96 } 97 98 // Compute the size and location of the circle to be drawn. 99 final int xCenter = getWidth() / 2; 100 final int yCenter = getHeight() / 2; 101 final float radius = Math.min(xCenter, yCenter) - mRadiusOffset; 102 103 // Reset old painting state. 104 mPaint.setColor(mRemainderColor); 105 mPaint.setStrokeWidth(mStrokeSize); 106 107 // If the timer is reset, draw a simple white circle. 108 final float redPercent; 109 if (mTimer.isReset()) { 110 // Draw a complete white circle; no red arc required. 111 canvas.drawCircle(xCenter, yCenter, radius, mPaint); 112 113 // Red percent is 0 since no timer progress has been made. 114 redPercent = 0; 115 } else if (mTimer.isExpired()) { 116 mPaint.setColor(mCompletedColor); 117 118 // Draw a complete white circle; no red arc required. 119 canvas.drawCircle(xCenter, yCenter, radius, mPaint); 120 121 // Red percent is 1 since the timer has expired. 122 redPercent = 1; 123 } else { 124 // Draw a combination of red and white arcs to create a circle. 125 mArcRect.top = yCenter - radius; 126 mArcRect.bottom = yCenter + radius; 127 mArcRect.left = xCenter - radius; 128 mArcRect.right = xCenter + radius; 129 redPercent = Math.min(1, (float) mTimer.getElapsedTime() / (float) mTimer.getTotalLength()); 130 final float whitePercent = 1 - redPercent; 131 132 // Draw a white arc to indicate the amount of timer that remains. 133 canvas.drawArc(mArcRect, 270, whitePercent * 360, false, mPaint); 134 135 // Draw a red arc to indicate the amount of timer completed. 136 mPaint.setColor(mCompletedColor); 137 canvas.drawArc(mArcRect, 270, -redPercent * 360 , false, mPaint); 138 } 139 140 // Draw a red dot to indicate current progress through the timer. 141 final float dotAngleDegrees = 270 - redPercent * 360; 142 final double dotAngleRadians = Math.toRadians(dotAngleDegrees); 143 final float dotX = xCenter + (float) (radius * Math.cos(dotAngleRadians)); 144 final float dotY = yCenter + (float) (radius * Math.sin(dotAngleRadians)); 145 canvas.drawCircle(dotX, dotY, mDotRadius, mFill); 146 147 if (mTimer.isRunning()) { 148 postInvalidateOnAnimation(); 149 } 150 } 151 } 152