• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
<lambda>null2  * 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.alarms.dataadapter
18 
19 import android.animation.Animator
20 import android.animation.AnimatorListenerAdapter
21 import android.animation.AnimatorSet
22 import android.animation.ObjectAnimator
23 import android.content.Context
24 import android.graphics.Rect
25 import android.view.LayoutInflater
26 import android.view.View
27 import android.view.ViewGroup
28 import android.widget.TextView
29 import androidx.recyclerview.widget.RecyclerView.ViewHolder
30 
31 import com.android.deskclock.AnimatorUtils
32 import com.android.deskclock.ItemAdapter.ItemViewHolder
33 import com.android.deskclock.R
34 import com.android.deskclock.data.DataModel
35 import com.android.deskclock.events.Events
36 import com.android.deskclock.provider.Alarm
37 
38 import java.util.Calendar
39 
40 /**
41  * A ViewHolder containing views for an alarm item in collapsed stated.
42  */
43 class CollapsedAlarmViewHolder private constructor(itemView: View) : AlarmItemViewHolder(itemView) {
44     private val alarmLabel: TextView = itemView.findViewById(R.id.label) as TextView
45     val daysOfWeek: TextView = itemView.findViewById(R.id.days_of_week) as TextView
46     private val upcomingInstanceLabel: TextView =
47             itemView.findViewById(R.id.upcoming_instance_label) as TextView
48     private val hairLine: View = itemView.findViewById(R.id.hairline)
49 
50     init {
51         // Expand handler
52         itemView.setOnClickListener { _ ->
53             Events.sendAlarmEvent(R.string.action_expand_implied, R.string.label_deskclock)
54             itemHolder?.expand()
55         }
56         alarmLabel.setOnClickListener { _ ->
57             Events.sendAlarmEvent(R.string.action_expand_implied, R.string.label_deskclock)
58             itemHolder?.expand()
59         }
60         arrow.setOnClickListener { _ ->
61             Events.sendAlarmEvent(R.string.action_expand, R.string.label_deskclock)
62             itemHolder?.expand()
63         }
64         // Edit time handler
65         clock.setOnClickListener { _ ->
66             itemHolder!!.alarmTimeClickHandler.onClockClicked(itemHolder!!.item)
67             Events.sendAlarmEvent(R.string.action_expand_implied, R.string.label_deskclock)
68             itemHolder?.expand()
69         }
70 
71         itemView.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO)
72     }
73 
74     override fun onBindItemView(itemHolder: AlarmItemHolder) {
75         super.onBindItemView(itemHolder)
76         val alarm = itemHolder.item
77         val alarmInstance = itemHolder.alarmInstance
78         val context: Context = itemView.getContext()
79         bindRepeatText(context, alarm)
80         bindReadOnlyLabel(context, alarm)
81         bindUpcomingInstance(context, alarm)
82         bindPreemptiveDismissButton(context, alarm, alarmInstance)
83     }
84 
85     private fun bindReadOnlyLabel(context: Context, alarm: Alarm) {
86         if (!alarm.label.isNullOrEmpty()) {
87             alarmLabel.text = alarm.label
88             alarmLabel.visibility = View.VISIBLE
89             alarmLabel.setContentDescription(context.getString(R.string.label_description)
90                     .toString() + " " + alarm.label)
91         } else {
92             alarmLabel.visibility = View.GONE
93         }
94     }
95 
96     private fun bindRepeatText(context: Context, alarm: Alarm) {
97         if (alarm.daysOfWeek.isRepeating) {
98             val weekdayOrder = DataModel.dataModel.weekdayOrder
99             val daysOfWeekText = alarm.daysOfWeek.toString(context, weekdayOrder)
100             daysOfWeek.text = daysOfWeekText
101 
102             val string = alarm.daysOfWeek.toAccessibilityString(context, weekdayOrder)
103             daysOfWeek.setContentDescription(string)
104 
105             daysOfWeek.visibility = View.VISIBLE
106         } else {
107             daysOfWeek.visibility = View.GONE
108         }
109     }
110 
111     private fun bindUpcomingInstance(context: Context, alarm: Alarm) {
112         if (alarm.daysOfWeek.isRepeating) {
113             upcomingInstanceLabel.visibility = View.GONE
114         } else {
115             upcomingInstanceLabel.visibility = View.VISIBLE
116             val labelText: String = if (Alarm.isTomorrow(alarm, Calendar.getInstance())) {
117                 context.getString(R.string.alarm_tomorrow)
118             } else {
119                 context.getString(R.string.alarm_today)
120             }
121             upcomingInstanceLabel.text = labelText
122         }
123     }
124 
125     override fun onAnimateChange(
126         payloads: List<Any>?,
127         fromLeft: Int,
128         fromTop: Int,
129         fromRight: Int,
130         fromBottom: Int,
131         duration: Long
132     ): Animator? {
133         /* There are no possible partial animations for collapsed view holders. */
134         return null
135     }
136 
137     override fun onAnimateChange(
138         oldHolder: ViewHolder,
139         newHolder: ViewHolder,
140         duration: Long
141     ): Animator? {
142         if (oldHolder !is AlarmItemViewHolder ||
143                 newHolder !is AlarmItemViewHolder) {
144             return null
145         }
146 
147         val isCollapsing = this == newHolder
148         setChangingViewsAlpha(if (isCollapsing) 0f else 1f)
149 
150         val changeAnimatorSet: Animator = if (isCollapsing) {
151             createCollapsingAnimator(oldHolder, duration)
152         } else {
153             createExpandingAnimator(newHolder, duration)
154         }
155         changeAnimatorSet.addListener(object : AnimatorListenerAdapter() {
156             override fun onAnimationEnd(animator: Animator?) {
157                 clock.visibility = View.VISIBLE
158                 onOff.visibility = View.VISIBLE
159                 arrow.visibility = View.VISIBLE
160                 arrow.setTranslationY(0f)
161                 setChangingViewsAlpha(1f)
162                 arrow.jumpDrawablesToCurrentState()
163             }
164         })
165         return changeAnimatorSet
166     }
167 
168     private fun createExpandingAnimator(newHolder: AlarmItemViewHolder, duration: Long): Animator {
169         clock.visibility = View.INVISIBLE
170         onOff.visibility = View.INVISIBLE
171         arrow.visibility = View.INVISIBLE
172 
173         val alphaAnimatorSet = AnimatorSet()
174         alphaAnimatorSet.playTogether(
175                 ObjectAnimator.ofFloat(alarmLabel, View.ALPHA, 0f),
176                 ObjectAnimator.ofFloat(daysOfWeek, View.ALPHA, 0f),
177                 ObjectAnimator.ofFloat(upcomingInstanceLabel, View.ALPHA, 0f),
178                 ObjectAnimator.ofFloat(preemptiveDismissButton, View.ALPHA, 0f),
179                 ObjectAnimator.ofFloat(hairLine, View.ALPHA, 0f))
180         alphaAnimatorSet.setDuration((duration * ANIM_SHORT_DURATION_MULTIPLIER).toLong())
181 
182         val oldView: View = itemView
183         val newView: View = newHolder.itemView
184         val boundsAnimator: Animator = AnimatorUtils.getBoundsAnimator(oldView, oldView, newView)
185                 .setDuration(duration)
186         boundsAnimator.interpolator = AnimatorUtils.INTERPOLATOR_FAST_OUT_SLOW_IN
187 
188         val animatorSet = AnimatorSet()
189         animatorSet.playTogether(alphaAnimatorSet, boundsAnimator)
190         return animatorSet
191     }
192 
193     private fun createCollapsingAnimator(oldHolder: AlarmItemViewHolder, duration: Long): Animator {
194         val alphaAnimatorSet = AnimatorSet()
195         alphaAnimatorSet.playTogether(
196                 ObjectAnimator.ofFloat(alarmLabel, View.ALPHA, 1f),
197                 ObjectAnimator.ofFloat(daysOfWeek, View.ALPHA, 1f),
198                 ObjectAnimator.ofFloat(upcomingInstanceLabel, View.ALPHA, 1f),
199                 ObjectAnimator.ofFloat(preemptiveDismissButton, View.ALPHA, 1f),
200                 ObjectAnimator.ofFloat(hairLine, View.ALPHA, 1f))
201         val standardDelay = (duration * ANIM_STANDARD_DELAY_MULTIPLIER).toLong()
202         alphaAnimatorSet.setDuration(standardDelay)
203         alphaAnimatorSet.setStartDelay(duration - standardDelay)
204 
205         val oldView: View = oldHolder.itemView
206         val newView: View = itemView
207         val boundsAnimator: Animator = AnimatorUtils.getBoundsAnimator(newView, oldView, newView)
208                 .setDuration(duration)
209         boundsAnimator.interpolator = AnimatorUtils.INTERPOLATOR_FAST_OUT_SLOW_IN
210 
211         val oldArrow: View = oldHolder.arrow
212         val oldArrowRect = Rect(0, 0, oldArrow.getWidth(), oldArrow.getHeight())
213         val newArrowRect = Rect(0, 0, arrow.getWidth(), arrow.getHeight())
214         (newView as ViewGroup).offsetDescendantRectToMyCoords(arrow, newArrowRect)
215         (oldView as ViewGroup).offsetDescendantRectToMyCoords(oldArrow, oldArrowRect)
216         val arrowTranslationY: Float = (oldArrowRect.bottom - newArrowRect.bottom).toFloat()
217         arrow.setTranslationY(arrowTranslationY)
218         arrow.visibility = View.VISIBLE
219         clock.visibility = View.VISIBLE
220         onOff.visibility = View.VISIBLE
221 
222         val arrowAnimation: Animator = ObjectAnimator.ofFloat(arrow, View.TRANSLATION_Y, 0f)
223                 .setDuration(duration)
224         arrowAnimation.interpolator = AnimatorUtils.INTERPOLATOR_FAST_OUT_SLOW_IN
225 
226         val animatorSet = AnimatorSet()
227         animatorSet.playTogether(alphaAnimatorSet, boundsAnimator, arrowAnimation)
228         animatorSet.addListener(object : AnimatorListenerAdapter() {
229             override fun onAnimationStart(animator: Animator?) {
230                 AnimatorUtils.startDrawableAnimation(arrow)
231             }
232         })
233         return animatorSet
234     }
235 
236     private fun setChangingViewsAlpha(alpha: Float) {
237         alarmLabel.alpha = alpha
238         daysOfWeek.alpha = alpha
239         upcomingInstanceLabel.alpha = alpha
240         hairLine.alpha = alpha
241         preemptiveDismissButton.alpha = alpha
242     }
243 
244     class Factory(private val layoutInflater: LayoutInflater) : ItemViewHolder.Factory {
245         override fun createViewHolder(parent: ViewGroup, viewType: Int): ItemViewHolder<*> {
246             return CollapsedAlarmViewHolder(layoutInflater.inflate(
247                     viewType, parent, false /* attachToRoot */))
248         }
249     }
250 
251     companion object {
252         @JvmField
253         val VIEW_TYPE: Int = R.layout.alarm_time_collapsed
254     }
255 }