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