• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
18 
19 import android.animation.Animator
20 import android.animation.AnimatorListenerAdapter
21 import android.animation.AnimatorSet
22 import android.animation.ObjectAnimator
23 import android.animation.PropertyValuesHolder
24 import android.view.View
25 import androidx.collection.ArrayMap
26 import androidx.recyclerview.widget.RecyclerView.State
27 import androidx.recyclerview.widget.RecyclerView.ViewHolder
28 import androidx.recyclerview.widget.RecyclerView.ItemAnimator
29 import androidx.recyclerview.widget.SimpleItemAnimator
30 
31 class ItemAnimator : SimpleItemAnimator() {
32     private val mAddAnimatorsList: MutableList<Animator> = ArrayList()
33     private val mRemoveAnimatorsList: MutableList<Animator> = ArrayList()
34     private val mChangeAnimatorsList: MutableList<Animator> = ArrayList()
35     private val mMoveAnimatorsList: MutableList<Animator> = ArrayList()
36 
37     private val mAnimators: MutableMap<ViewHolder, Animator> = ArrayMap()
38 
animateRemovenull39     override fun animateRemove(holder: ViewHolder): Boolean {
40         endAnimation(holder)
41 
42         val prevAlpha: Float = holder.itemView.getAlpha()
43 
44         val removeAnimator: Animator? = ObjectAnimator.ofFloat(holder.itemView, View.ALPHA, 0f)
45         removeAnimator!!.duration = getRemoveDuration()
46         removeAnimator.addListener(object : AnimatorListenerAdapter() {
47             override fun onAnimationStart(animator: Animator) {
48                 dispatchRemoveStarting(holder)
49             }
50 
51             override fun onAnimationEnd(animator: Animator) {
52                 animator.removeAllListeners()
53                 mAnimators.remove(holder)
54                 holder.itemView.setAlpha(prevAlpha)
55                 dispatchRemoveFinished(holder)
56             }
57         })
58         mRemoveAnimatorsList.add(removeAnimator)
59         mAnimators[holder] = removeAnimator
60         return true
61     }
62 
animateAddnull63     override fun animateAdd(holder: ViewHolder): Boolean {
64         endAnimation(holder)
65 
66         val prevAlpha: Float = holder.itemView.getAlpha()
67         holder.itemView.setAlpha(0f)
68 
69         val addAnimator: Animator = ObjectAnimator.ofFloat(holder.itemView, View.ALPHA, 1f)
70                 .setDuration(getAddDuration())
71         addAnimator.addListener(object : AnimatorListenerAdapter() {
72             override fun onAnimationStart(animator: Animator) {
73                 dispatchAddStarting(holder)
74             }
75 
76             override fun onAnimationEnd(animator: Animator) {
77                 animator.removeAllListeners()
78                 mAnimators.remove(holder)
79                 holder.itemView.setAlpha(prevAlpha)
80                 dispatchAddFinished(holder)
81             }
82         })
83         mAddAnimatorsList.add(addAnimator)
84         mAnimators[holder] = addAnimator
85         return true
86     }
87 
animateMovenull88     override fun animateMove(
89         holder: ViewHolder,
90         fromX: Int,
91         fromY: Int,
92         toX: Int,
93         toY: Int
94     ): Boolean {
95         endAnimation(holder)
96 
97         val deltaX = toX - fromX
98         val deltaY = toY - fromY
99         val moveDuration: Long = getMoveDuration()
100 
101         if (deltaX == 0 && deltaY == 0) {
102             dispatchMoveFinished(holder)
103             return false
104         }
105 
106         val view: View = holder.itemView
107         val prevTranslationX = view.translationX
108         val prevTranslationY = view.translationY
109         view.translationX = -deltaX.toFloat()
110         view.translationY = -deltaY.toFloat()
111 
112         val moveAnimator: ObjectAnimator?
113         if (deltaX != 0 && deltaY != 0) {
114             val moveX = PropertyValuesHolder.ofFloat(View.TRANSLATION_X, 0f)
115             val moveY = PropertyValuesHolder.ofFloat(View.TRANSLATION_Y, 0f)
116             moveAnimator = ObjectAnimator.ofPropertyValuesHolder(holder.itemView, moveX, moveY)
117         } else if (deltaX != 0) {
118             val moveX = PropertyValuesHolder.ofFloat(View.TRANSLATION_X, 0f)
119             moveAnimator = ObjectAnimator.ofPropertyValuesHolder(holder.itemView, moveX)
120         } else {
121             val moveY = PropertyValuesHolder.ofFloat(View.TRANSLATION_Y, 0f)
122             moveAnimator = ObjectAnimator.ofPropertyValuesHolder(holder.itemView, moveY)
123         }
124 
125         moveAnimator?.duration = moveDuration
126         moveAnimator.interpolator = AnimatorUtils.INTERPOLATOR_FAST_OUT_SLOW_IN
127         moveAnimator.addListener(object : AnimatorListenerAdapter() {
128             override fun onAnimationStart(animator: Animator?) {
129                 dispatchMoveStarting(holder)
130             }
131 
132             override fun onAnimationEnd(animator: Animator?) {
133                 animator?.removeAllListeners()
134                 mAnimators.remove(holder)
135                 view.translationX = prevTranslationX
136                 view.translationY = prevTranslationY
137                 dispatchMoveFinished(holder)
138             }
139         })
140         mMoveAnimatorsList.add(moveAnimator)
141         mAnimators[holder] = moveAnimator
142 
143         return true
144     }
145 
animateChangenull146     override fun animateChange(
147         oldHolder: ViewHolder,
148         newHolder: ViewHolder,
149         preInfo: ItemHolderInfo,
150         postInfo: ItemHolderInfo
151     ): Boolean {
152         endAnimation(oldHolder)
153         endAnimation(newHolder)
154 
155         val changeDuration: Long = getChangeDuration()
156         val payloads = if (preInfo is PayloadItemHolderInfo) preInfo.payloads else null
157 
158         if (oldHolder === newHolder) {
159             val animator = (newHolder as OnAnimateChangeListener)
160                     .onAnimateChange(payloads, preInfo.left, preInfo.top, preInfo.right,
161                             preInfo.bottom, changeDuration)
162             if (animator == null) {
163                 dispatchChangeFinished(newHolder, false)
164                 return false
165             }
166             animator.addListener(object : AnimatorListenerAdapter() {
167                 override fun onAnimationStart(animator: Animator) {
168                     dispatchChangeStarting(newHolder, false)
169                 }
170 
171                 override fun onAnimationEnd(animator: Animator) {
172                     animator.removeAllListeners()
173                     mAnimators.remove(newHolder)
174                     dispatchChangeFinished(newHolder, false)
175                 }
176             })
177             mChangeAnimatorsList.add(animator)
178             mAnimators[newHolder] = animator
179             return true
180         } else if (oldHolder !is OnAnimateChangeListener ||
181                 newHolder !is OnAnimateChangeListener) {
182             // Both holders must implement OnAnimateChangeListener in order to animate.
183             dispatchChangeFinished(oldHolder, true)
184             dispatchChangeFinished(newHolder, true)
185             return false
186         }
187 
188         val oldChangeAnimator = (oldHolder as OnAnimateChangeListener)
189                 .onAnimateChange(oldHolder, newHolder, changeDuration)
190         if (oldChangeAnimator != null) {
191             oldChangeAnimator.addListener(object : AnimatorListenerAdapter() {
192                 override fun onAnimationStart(animator: Animator) {
193                     dispatchChangeStarting(oldHolder, true)
194                 }
195 
196                 override fun onAnimationEnd(animator: Animator) {
197                     animator.removeAllListeners()
198                     mAnimators.remove(oldHolder)
199                     dispatchChangeFinished(oldHolder, true)
200                 }
201             })
202             mAnimators[oldHolder] = oldChangeAnimator
203             mChangeAnimatorsList.add(oldChangeAnimator)
204         } else {
205             dispatchChangeFinished(oldHolder, true)
206         }
207 
208         val newChangeAnimator = (newHolder as OnAnimateChangeListener)
209                 .onAnimateChange(oldHolder, newHolder, changeDuration)
210         if (newChangeAnimator != null) {
211             newChangeAnimator.addListener(object : AnimatorListenerAdapter() {
212                 override fun onAnimationStart(animator: Animator) {
213                     dispatchChangeStarting(newHolder, false)
214                 }
215 
216                 override fun onAnimationEnd(animator: Animator) {
217                     animator.removeAllListeners()
218                     mAnimators.remove(newHolder)
219                     dispatchChangeFinished(newHolder, false)
220                 }
221             })
222             mAnimators[newHolder] = newChangeAnimator
223             mChangeAnimatorsList.add(newChangeAnimator)
224         } else {
225             dispatchChangeFinished(newHolder, false)
226         }
227 
228         return true
229     }
230 
animateChangenull231     override fun animateChange(
232         oldHolder: ViewHolder,
233         newHolder: ViewHolder,
234         fromLeft: Int,
235         fromTop: Int,
236         toLeft: Int,
237         toTop: Int
238     ): Boolean {
239         /* Unused */
240         throw IllegalStateException("This method should not be used")
241     }
242 
runPendingAnimationsnull243     override fun runPendingAnimations() {
244         val removeAnimatorSet = AnimatorSet()
245         removeAnimatorSet.playTogether(mRemoveAnimatorsList)
246         mRemoveAnimatorsList.clear()
247 
248         val addAnimatorSet = AnimatorSet()
249         addAnimatorSet.playTogether(mAddAnimatorsList)
250         mAddAnimatorsList.clear()
251 
252         val changeAnimatorSet = AnimatorSet()
253         changeAnimatorSet.playTogether(mChangeAnimatorsList)
254         mChangeAnimatorsList.clear()
255 
256         val moveAnimatorSet = AnimatorSet()
257         moveAnimatorSet.playTogether(mMoveAnimatorsList)
258         mMoveAnimatorsList.clear()
259 
260         val pendingAnimatorSet = AnimatorSet()
261         pendingAnimatorSet.addListener(object : AnimatorListenerAdapter() {
262             override fun onAnimationEnd(animator: Animator) {
263                 animator.removeAllListeners()
264                 dispatchFinishedWhenDone()
265             }
266         })
267         // Required order: removes, then changes & moves simultaneously, then additions. There are
268         // redundant edges because changes or moves may be empty, causing the removes to incorrectly
269         // play immediately.
270         pendingAnimatorSet.play(removeAnimatorSet).before(changeAnimatorSet)
271         pendingAnimatorSet.play(removeAnimatorSet).before(moveAnimatorSet)
272         pendingAnimatorSet.play(changeAnimatorSet).with(moveAnimatorSet)
273         pendingAnimatorSet.play(addAnimatorSet).after(changeAnimatorSet)
274         pendingAnimatorSet.play(addAnimatorSet).after(moveAnimatorSet)
275         pendingAnimatorSet.start()
276     }
277 
endAnimationnull278     override fun endAnimation(holder: ViewHolder) {
279         val animator = mAnimators[holder]
280 
281         mAnimators.remove(holder)
282         mAddAnimatorsList.remove(animator)
283         mRemoveAnimatorsList.remove(animator)
284         mChangeAnimatorsList.remove(animator)
285         mMoveAnimatorsList.remove(animator)
286 
287         animator?.end()
288         dispatchFinishedWhenDone()
289     }
290 
endAnimationsnull291     override fun endAnimations() {
292         val animatorList: MutableList<Animator?> = ArrayList(mAnimators.values)
293         for (animator in animatorList) {
294             animator?.end()
295         }
296         dispatchFinishedWhenDone()
297     }
298 
isRunningnull299     override fun isRunning(): Boolean = mAnimators.isNotEmpty()
300 
301     private fun dispatchFinishedWhenDone() {
302         if (!isRunning()) {
303             dispatchAnimationsFinished()
304         }
305     }
306 
recordPreLayoutInformationnull307     override fun recordPreLayoutInformation(
308         state: State,
309         viewHolder: ViewHolder,
310         @AdapterChanges changeFlags: Int,
311         payloads: MutableList<Any>
312     ): ItemAnimator.ItemHolderInfo {
313         val itemHolderInfo: ItemHolderInfo =
314                 super.recordPreLayoutInformation(state, viewHolder, changeFlags, payloads)
315         if (itemHolderInfo is PayloadItemHolderInfo) {
316             itemHolderInfo.payloads = payloads
317         }
318         return itemHolderInfo
319     }
320 
obtainHolderInfonull321     override fun obtainHolderInfo(): ItemAnimator.ItemHolderInfo {
322         return PayloadItemHolderInfo()
323     }
324 
canReuseUpdatedViewHoldernull325     override fun canReuseUpdatedViewHolder(
326         viewHolder: ViewHolder,
327         payloads: MutableList<Any?>
328     ): Boolean {
329         val defaultReusePolicy: Boolean = super.canReuseUpdatedViewHolder(viewHolder, payloads)
330         // Whenever we have a payload, this is an in-place animation.
331         return payloads.isNotEmpty() || defaultReusePolicy
332     }
333 
334     private class PayloadItemHolderInfo : ItemHolderInfo() {
335         private val mPayloads: MutableList<Any> = ArrayList()
336 
337         var payloads: MutableList<Any>
338             get() = mPayloads
339             set(payloads) {
340                 mPayloads.clear()
341                 mPayloads.addAll(payloads)
342             }
343     }
344 
345     interface OnAnimateChangeListener {
onAnimateChangenull346         fun onAnimateChange(oldHolder: ViewHolder, newHolder: ViewHolder, duration: Long): Animator?
347 
348         fun onAnimateChange(
349             payloads: List<Any>?,
350             fromLeft: Int,
351             fromTop: Int,
352             fromRight: Int,
353             fromBottom: Int,
354             duration: Long
355         ): Animator?
356     }
357 }