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