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