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.R.attr 20 import android.content.Context 21 import android.content.res.ColorStateList 22 import android.os.SystemClock 23 import android.text.TextUtils 24 import android.util.AttributeSet 25 import android.view.View 26 import android.widget.Button 27 import android.widget.LinearLayout 28 import android.widget.TextView 29 import androidx.core.view.ViewCompat 30 31 import com.android.deskclock.R 32 import com.android.deskclock.ThemeUtils 33 import com.android.deskclock.TimerTextController 34 import com.android.deskclock.Utils.ClickAccessibilityDelegate 35 import com.android.deskclock.data.Timer 36 37 /** 38 * This view is a visual representation of a [Timer]. 39 */ 40 class TimerItem @JvmOverloads constructor( 41 context: Context?, 42 attrs: AttributeSet? = null 43 ) : LinearLayout(context, attrs) { 44 /** Displays the remaining time or time since expiration. */ 45 private lateinit var mTimerText: TextView 46 47 /** Formats and displays the text in the timer. */ 48 private lateinit var mTimerTextController: TimerTextController 49 50 /** Displays timer progress as a color circle that changes from white to red. */ 51 private var mCircleView: TimerCircleView? = null 52 53 /** A button that either resets the timer or adds time to it, depending on its state. */ 54 private lateinit var mResetAddButton: Button 55 56 /** Displays the label associated with the timer. Tapping it presents an edit dialog. */ 57 private lateinit var mLabelView: TextView 58 59 /** The last state of the timer that was rendered; used to avoid expensive operations. */ 60 private var mLastState: Timer.State? = null 61 onFinishInflatenull62 override fun onFinishInflate() { 63 super.onFinishInflate() 64 mLabelView = findViewById<View>(R.id.timer_label) as TextView 65 mResetAddButton = findViewById<View>(R.id.reset_add) as Button 66 mCircleView = findViewById<View>(R.id.timer_time) as TimerCircleView 67 mTimerText = findViewById<View>(R.id.timer_time_text) as TextView 68 mTimerTextController = TimerTextController(mTimerText) 69 70 val c = mTimerText.context 71 val colorAccent = ThemeUtils.resolveColor(c, R.attr.colorAccent) 72 val textColorPrimary = ThemeUtils.resolveColor(c, attr.textColorPrimary) 73 mTimerText.setTextColor(ColorStateList( 74 arrayOf(intArrayOf(-attr.state_activated, -attr.state_pressed), 75 intArrayOf()), 76 intArrayOf(textColorPrimary, colorAccent))) 77 } 78 79 /** 80 * Updates this view to display the latest state of the `timer`. 81 */ updatenull82 fun update(timer: Timer) { 83 // Update the time. 84 mTimerTextController.setTimeString(timer.remainingTime) 85 86 // Update the label if it changed. 87 val label: String? = timer.label 88 if (!TextUtils.equals(label, mLabelView.text)) { 89 mLabelView.text = label 90 } 91 92 // Update visibility of things that may blink. 93 val blinkOff = SystemClock.elapsedRealtime() % 1000 < 500 94 if (mCircleView != null) { 95 val hideCircle = (timer.isExpired || timer.isMissed) && blinkOff 96 mCircleView!!.visibility = if (hideCircle) View.INVISIBLE else View.VISIBLE 97 98 if (!hideCircle) { 99 // Update the progress of the circle. 100 mCircleView!!.update(timer) 101 } 102 } 103 if (!timer.isPaused || !blinkOff || mTimerText.isPressed) { 104 mTimerText.alpha = 1f 105 } else { 106 mTimerText.alpha = 0f 107 } 108 109 // Update some potentially expensive areas of the user interface only on state changes. 110 if (timer.state != mLastState) { 111 mLastState = timer.state 112 val context = context 113 when (mLastState) { 114 Timer.State.RESET, Timer.State.PAUSED -> { 115 mResetAddButton.setText(R.string.timer_reset) 116 mResetAddButton.contentDescription = null 117 mTimerText.isClickable = true 118 mTimerText.isActivated = false 119 mTimerText.importantForAccessibility = View.IMPORTANT_FOR_ACCESSIBILITY_YES 120 ViewCompat.setAccessibilityDelegate(mTimerText, ClickAccessibilityDelegate( 121 context.getString(R.string.timer_start), true)) 122 } 123 Timer.State.RUNNING -> { 124 val addTimeDesc = context.getString(R.string.timer_plus_one) 125 mResetAddButton.setText(R.string.timer_add_minute) 126 mResetAddButton.contentDescription = addTimeDesc 127 mTimerText.isClickable = true 128 mTimerText.isActivated = false 129 mTimerText.importantForAccessibility = View.IMPORTANT_FOR_ACCESSIBILITY_YES 130 ViewCompat.setAccessibilityDelegate(mTimerText, ClickAccessibilityDelegate( 131 context.getString(R.string.timer_pause))) 132 } 133 Timer.State.EXPIRED, Timer.State.MISSED -> { 134 val addTimeDesc = context.getString(R.string.timer_plus_one) 135 mResetAddButton.setText(R.string.timer_add_minute) 136 mResetAddButton.contentDescription = addTimeDesc 137 mTimerText.isClickable = false 138 mTimerText.isActivated = true 139 mTimerText.importantForAccessibility = View.IMPORTANT_FOR_ACCESSIBILITY_NO 140 } 141 null -> { } 142 } 143 } 144 } 145 }