1 /* 2 * Copyright (C) 2020 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.graphics.Canvas 21 import android.graphics.Color 22 import android.graphics.Paint 23 import android.graphics.RectF 24 import android.util.AttributeSet 25 import android.view.View 26 27 import com.android.deskclock.R 28 import com.android.deskclock.ThemeUtils 29 import com.android.deskclock.Utils 30 import com.android.deskclock.data.Timer 31 32 import kotlin.math.cos 33 import kotlin.math.min 34 import kotlin.math.sin 35 36 /** 37 * Custom view that draws timer progress as a circle. 38 */ 39 class TimerCircleView @JvmOverloads constructor( 40 context: Context, 41 attrs: AttributeSet? = null 42 ) : View(context, attrs) { 43 /** The size of the dot indicating the progress through the timer. */ 44 private val mDotRadius: Float 45 46 /** An amount to subtract from the true radius to account for drawing thicknesses. */ 47 private val mRadiusOffset: Float 48 49 /** The color indicating the remaining portion of the timer. */ 50 private val mRemainderColor: Int 51 52 /** The color indicating the completed portion of the timer. */ 53 private val mCompletedColor: Int 54 55 /** The size of the stroke that paints the timer circle. */ 56 private val mStrokeSize: Float 57 58 private val mPaint = Paint() 59 private val mFill = Paint() 60 private val mArcRect = RectF() 61 62 private var mTimer: Timer? = null 63 64 init { 65 val resources = context.resources 66 val dotDiameter = resources.getDimension(R.dimen.circletimer_dot_size) 67 68 mDotRadius = dotDiameter / 2f 69 mStrokeSize = resources.getDimension(R.dimen.circletimer_circle_size) 70 mRadiusOffset = Utils.calculateRadiusOffset(mStrokeSize, dotDiameter, 0f) 71 72 mRemainderColor = Color.WHITE 73 mCompletedColor = ThemeUtils.resolveColor(context, R.attr.colorAccent) 74 75 mPaint.isAntiAlias = true 76 mPaint.style = Paint.Style.STROKE 77 78 mFill.isAntiAlias = true 79 mFill.color = mCompletedColor 80 mFill.style = Paint.Style.FILL 81 } 82 updatenull83 fun update(timer: Timer) { 84 if (mTimer !== timer) { 85 mTimer = timer 86 postInvalidateOnAnimation() 87 } 88 } 89 onDrawnull90 public override fun onDraw(canvas: Canvas) { 91 if (mTimer == null) { 92 return 93 } 94 95 // Compute the size and location of the circle to be drawn. 96 val xCenter = width / 2 97 val yCenter = height / 2 98 val radius = min(xCenter, yCenter) - mRadiusOffset 99 100 // Reset old painting state. 101 mPaint.color = mRemainderColor 102 mPaint.strokeWidth = mStrokeSize 103 104 // If the timer is reset, draw a simple white circle. 105 val redPercent: Float 106 when { 107 mTimer!!.isReset -> { 108 // Draw a complete white circle; no red arc required. 109 canvas.drawCircle(xCenter.toFloat(), yCenter.toFloat(), radius, mPaint) 110 111 // Red percent is 0 since no timer progress has been made. 112 redPercent = 0f 113 } 114 mTimer!!.isExpired -> { 115 mPaint.color = mCompletedColor 116 117 // Draw a complete white circle; no red arc required. 118 canvas.drawCircle(xCenter.toFloat(), yCenter.toFloat(), radius, mPaint) 119 120 // Red percent is 1 since the timer has expired. 121 redPercent = 1f 122 } 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 = min(1f, 130 mTimer!!.elapsedTime.toFloat() / mTimer!!.totalLength.toFloat()) 131 val whitePercent = 1 - redPercent 132 133 // Draw a white arc to indicate the amount of timer that remains. 134 canvas.drawArc(mArcRect, 270f, whitePercent * 360, false, mPaint) 135 136 // Draw a red arc to indicate the amount of timer completed. 137 mPaint.color = mCompletedColor 138 canvas.drawArc(mArcRect, 270f, -redPercent * 360, false, mPaint) 139 } 140 } 141 142 // Draw a red dot to indicate current progress through the timer. 143 val dotAngleDegrees = 270 - redPercent * 360 144 val dotAngleRadians = Math.toRadians(dotAngleDegrees.toDouble()) 145 val dotX = xCenter + (radius * cos(dotAngleRadians)).toFloat() 146 val dotY = yCenter + (radius * sin(dotAngleRadians)).toFloat() 147 canvas.drawCircle(dotX, dotY, mDotRadius, mFill) 148 149 if (mTimer!!.isRunning) { 150 postInvalidateOnAnimation() 151 } 152 } 153 }