1 /*
2  * Copyright (C) 2022 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.internal.dynamicanimation.animation;
18 
19 import android.animation.AnimationHandler;
20 import android.animation.ValueAnimator;
21 import android.annotation.FloatRange;
22 import android.annotation.MainThread;
23 import android.annotation.NonNull;
24 import android.annotation.SuppressLint;
25 import android.os.Looper;
26 import android.util.AndroidRuntimeException;
27 import android.util.FloatProperty;
28 import android.view.View;
29 
30 import java.util.ArrayList;
31 
32 /**
33  * This class is the base class of physics-based animations. It manages the animation's
34  * lifecycle such as {@link #start()} and {@link #cancel()}. This base class also handles the common
35  * setup for all the subclass animations. For example, DynamicAnimation supports adding
36  * {@link OnAnimationEndListener} and {@link OnAnimationUpdateListener} so that the important
37  * animation events can be observed through the callbacks. The start conditions for any subclass of
38  * DynamicAnimation can be set using {@link #setStartValue(float)} and
39  * {@link #setStartVelocity(float)}.
40  *
41  * @param <T> subclass of DynamicAnimation
42  */
43 public abstract class DynamicAnimation<T extends DynamicAnimation<T>>
44         implements AnimationHandler.AnimationFrameCallback {
45 
46     /**
47      * ViewProperty holds the access of a property of a {@link View}. When an animation is
48      * created with a {@link ViewProperty} instance, the corresponding property value of the view
49      * will be updated through this ViewProperty instance.
50      */
51     public abstract static class ViewProperty extends FloatProperty<View> {
ViewProperty(String name)52         private ViewProperty(String name) {
53             super(name);
54         }
55     }
56 
57     /**
58      * View's translationX property.
59      */
60     public static final ViewProperty TRANSLATION_X = new ViewProperty("translationX") {
61         @Override
62         public void setValue(View view, float value) {
63             view.setTranslationX(value);
64         }
65 
66         @Override
67         public Float get(View view) {
68             return view.getTranslationX();
69         }
70     };
71 
72     /**
73      * View's translationY property.
74      */
75     public static final ViewProperty TRANSLATION_Y = new ViewProperty("translationY") {
76         @Override
77         public void setValue(View view, float value) {
78             view.setTranslationY(value);
79         }
80 
81         @Override
82         public Float get(View view) {
83             return view.getTranslationY();
84         }
85     };
86 
87     /**
88      * View's translationZ property.
89      */
90     public static final ViewProperty TRANSLATION_Z = new ViewProperty("translationZ") {
91         @Override
92         public void setValue(View view, float value) {
93             view.setTranslationZ(value);
94         }
95 
96         @Override
97         public Float get(View view) {
98             return view.getTranslationZ();
99         }
100     };
101 
102     /**
103      * View's scaleX property.
104      */
105     public static final ViewProperty SCALE_X = new ViewProperty("scaleX") {
106         @Override
107         public void setValue(View view, float value) {
108             view.setScaleX(value);
109         }
110 
111         @Override
112         public Float get(View view) {
113             return view.getScaleX();
114         }
115     };
116 
117     /**
118      * View's scaleY property.
119      */
120     public static final ViewProperty SCALE_Y = new ViewProperty("scaleY") {
121         @Override
122         public void setValue(View view, float value) {
123             view.setScaleY(value);
124         }
125 
126         @Override
127         public Float get(View view) {
128             return view.getScaleY();
129         }
130     };
131 
132     /**
133      * View's rotation property.
134      */
135     public static final ViewProperty ROTATION = new ViewProperty("rotation") {
136         @Override
137         public void setValue(View view, float value) {
138             view.setRotation(value);
139         }
140 
141         @Override
142         public Float get(View view) {
143             return view.getRotation();
144         }
145     };
146 
147     /**
148      * View's rotationX property.
149      */
150     public static final ViewProperty ROTATION_X = new ViewProperty("rotationX") {
151         @Override
152         public void setValue(View view, float value) {
153             view.setRotationX(value);
154         }
155 
156         @Override
157         public Float get(View view) {
158             return view.getRotationX();
159         }
160     };
161 
162     /**
163      * View's rotationY property.
164      */
165     public static final ViewProperty ROTATION_Y = new ViewProperty("rotationY") {
166         @Override
167         public void setValue(View view, float value) {
168             view.setRotationY(value);
169         }
170 
171         @Override
172         public Float get(View view) {
173             return view.getRotationY();
174         }
175     };
176 
177     /**
178      * View's x property.
179      */
180     public static final ViewProperty X = new ViewProperty("x") {
181         @Override
182         public void setValue(View view, float value) {
183             view.setX(value);
184         }
185 
186         @Override
187         public Float get(View view) {
188             return view.getX();
189         }
190     };
191 
192     /**
193      * View's y property.
194      */
195     public static final ViewProperty Y = new ViewProperty("y") {
196         @Override
197         public void setValue(View view, float value) {
198             view.setY(value);
199         }
200 
201         @Override
202         public Float get(View view) {
203             return view.getY();
204         }
205     };
206 
207     /**
208      * View's z property.
209      */
210     public static final ViewProperty Z = new ViewProperty("z") {
211         @Override
212         public void setValue(View view, float value) {
213             view.setZ(value);
214         }
215 
216         @Override
217         public Float get(View view) {
218             return view.getZ();
219         }
220     };
221 
222     /**
223      * View's alpha property.
224      */
225     public static final ViewProperty ALPHA = new ViewProperty("alpha") {
226         @Override
227         public void setValue(View view, float value) {
228             view.setAlpha(value);
229         }
230 
231         @Override
232         public Float get(View view) {
233             return view.getAlpha();
234         }
235     };
236 
237     // Properties below are not RenderThread compatible
238     /**
239      * View's scrollX property.
240      */
241     public static final ViewProperty SCROLL_X = new ViewProperty("scrollX") {
242         @Override
243         public void setValue(View view, float value) {
244             view.setScrollX((int) value);
245         }
246 
247         @Override
248         public Float get(View view) {
249             return (float) view.getScrollX();
250         }
251     };
252 
253     /**
254      * View's scrollY property.
255      */
256     public static final ViewProperty SCROLL_Y = new ViewProperty("scrollY") {
257         @Override
258         public void setValue(View view, float value) {
259             view.setScrollY((int) value);
260         }
261 
262         @Override
263         public Float get(View view) {
264             return (float) view.getScrollY();
265         }
266     };
267 
268     /**
269      * The minimum visible change in pixels that can be visible to users.
270      */
271     @SuppressLint("MinMaxConstant")
272     public static final float MIN_VISIBLE_CHANGE_PIXELS = 1f;
273     /**
274      * The minimum visible change in degrees that can be visible to users.
275      */
276     @SuppressLint("MinMaxConstant")
277     public static final float MIN_VISIBLE_CHANGE_ROTATION_DEGREES = 1f / 10f;
278     /**
279      * The minimum visible change in alpha that can be visible to users.
280      */
281     @SuppressLint("MinMaxConstant")
282     public static final float MIN_VISIBLE_CHANGE_ALPHA = 1f / 256f;
283     /**
284      * The minimum visible change in scale that can be visible to users.
285      */
286     @SuppressLint("MinMaxConstant")
287     public static final float MIN_VISIBLE_CHANGE_SCALE = 1f / 500f;
288 
289     // Use the max value of float to indicate an unset state.
290     private static final float UNSET = Float.MAX_VALUE;
291 
292     // Multiplier to the min visible change value for value threshold
293     private static final float THRESHOLD_MULTIPLIER = 0.75f;
294 
295     // Internal tracking for velocity.
296     float mVelocity = 0;
297 
298     // Internal tracking for value.
299     float mValue = UNSET;
300 
301     // Tracks whether start value is set. If not, the animation will obtain the value at the time
302     // of starting through the getter and use that as the starting value of the animation.
303     boolean mStartValueIsSet = false;
304 
305     // Target to be animated.
306     final Object mTarget;
307 
308     // View property id.
309     final FloatProperty mProperty;
310 
311     // Package private tracking of animation lifecycle state. Visible to subclass animations.
312     boolean mRunning = false;
313 
314     // Min and max values that defines the range of the animation values.
315     float mMaxValue = Float.MAX_VALUE;
316     float mMinValue = -mMaxValue;
317 
318     // Last frame time. Always gets reset to -1  at the end of the animation.
319     private long mLastFrameTime = 0;
320 
321     private float mMinVisibleChange;
322 
323     // List of end listeners
324     private final ArrayList<OnAnimationEndListener> mEndListeners = new ArrayList<>();
325 
326     // List of update listeners
327     private final ArrayList<OnAnimationUpdateListener> mUpdateListeners = new ArrayList<>();
328 
329     // Animation handler used to schedule updates for this animation.
330     private AnimationHandler mAnimationHandler;
331 
332     // Internal state for value/velocity pair.
333     static class MassState {
334         float mValue;
335         float mVelocity;
336     }
337 
338     /**
339      * Creates a dynamic animation with the given FloatValueHolder instance.
340      *
341      * @param floatValueHolder the FloatValueHolder instance to be animated.
342      */
DynamicAnimation(final FloatValueHolder floatValueHolder)343     DynamicAnimation(final FloatValueHolder floatValueHolder) {
344         mTarget = null;
345         mProperty = new FloatProperty("FloatValueHolder") {
346             @Override
347             public Float get(Object object) {
348                 return floatValueHolder.getValue();
349             }
350 
351             @Override
352             public void setValue(Object object, float value) {
353                 floatValueHolder.setValue(value);
354             }
355         };
356         mMinVisibleChange = MIN_VISIBLE_CHANGE_PIXELS;
357     }
358 
359     /**
360      * Creates a dynamic animation to animate the given property for the given {@link View}
361      *
362      * @param object the Object whose property is to be animated
363      * @param property the property to be animated
364      */
365 
DynamicAnimation(K object, FloatProperty<K> property)366     <K> DynamicAnimation(K object, FloatProperty<K> property) {
367         mTarget = object;
368         mProperty = property;
369         if (mProperty == ROTATION || mProperty == ROTATION_X
370                 || mProperty == ROTATION_Y) {
371             mMinVisibleChange = MIN_VISIBLE_CHANGE_ROTATION_DEGREES;
372         } else if (mProperty == ALPHA) {
373             mMinVisibleChange = MIN_VISIBLE_CHANGE_ALPHA;
374         } else if (mProperty == SCALE_X || mProperty == SCALE_Y) {
375             mMinVisibleChange = MIN_VISIBLE_CHANGE_SCALE;
376         } else {
377             mMinVisibleChange = MIN_VISIBLE_CHANGE_PIXELS;
378         }
379     }
380 
381     /**
382      * Sets the start value of the animation. If start value is not set, the animation will get
383      * the current value for the view's property, and use that as the start value.
384      *
385      * @param startValue start value for the animation
386      * @return the Animation whose start value is being set
387      */
388     @SuppressWarnings("unchecked")
setStartValue(float startValue)389     public T setStartValue(float startValue) {
390         mValue = startValue;
391         mStartValueIsSet = true;
392         return (T) this;
393     }
394 
395     /**
396      * Start velocity of the animation. Default velocity is 0. Unit: change in property per
397      * second (e.g. pixels per second, scale/alpha value change per second).
398      *
399      * <p>Note when using a fixed value as the start velocity (as opposed to getting the velocity
400      * through touch events), it is recommended to define such a value in dp/second and convert it
401      * to pixel/second based on the density of the screen to achieve a consistent look across
402      * different screens.
403      *
404      * <p>To convert from dp/second to pixel/second:
405      * <pre class="prettyprint">
406      * float pixelPerSecond = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dpPerSecond,
407      *         getResources().getDisplayMetrics());
408      * </pre>
409      *
410      * @param startVelocity start velocity of the animation
411      * @return the Animation whose start velocity is being set
412      */
413     @SuppressWarnings("unchecked")
setStartVelocity(float startVelocity)414     public T setStartVelocity(float startVelocity) {
415         mVelocity = startVelocity;
416         return (T) this;
417     }
418 
419     /**
420      * Sets the max value of the animation. Animations will not animate beyond their max value.
421      * Whether or not animation will come to an end when max value is reached is dependent on the
422      * child animation's implementation.
423      *
424      * @param max maximum value of the property to be animated
425      * @return the Animation whose max value is being set
426      */
427     @SuppressWarnings("unchecked")
setMaxValue(float max)428     public T setMaxValue(float max) {
429         // This max value should be checked and handled in the subclass animations, instead of
430         // assuming the end of the animations when the max/min value is hit in the base class.
431         // The reason is that hitting max/min value may just be a transient state, such as during
432         // the spring oscillation.
433         mMaxValue = max;
434         return (T) this;
435     }
436 
437     /**
438      * Sets the min value of the animation. Animations will not animate beyond their min value.
439      * Whether or not animation will come to an end when min value is reached is dependent on the
440      * child animation's implementation.
441      *
442      * @param min minimum value of the property to be animated
443      * @return the Animation whose min value is being set
444      */
445     @SuppressWarnings("unchecked")
setMinValue(float min)446     public T setMinValue(float min) {
447         mMinValue = min;
448         return (T) this;
449     }
450 
451     /**
452      * Adds an end listener to the animation for receiving onAnimationEnd callbacks. If the listener
453      * is {@code null} or has already been added to the list of listeners for the animation, no op.
454      *
455      * @param listener the listener to be added
456      * @return the animation to which the listener is added
457      */
458     @SuppressWarnings("unchecked")
addEndListener(OnAnimationEndListener listener)459     public T addEndListener(OnAnimationEndListener listener) {
460         if (!mEndListeners.contains(listener)) {
461             mEndListeners.add(listener);
462         }
463         return (T) this;
464     }
465 
466     /**
467      * Removes the end listener from the animation, so as to stop receiving animation end callbacks.
468      *
469      * @param listener the listener to be removed
470      */
removeEndListener(OnAnimationEndListener listener)471     public void removeEndListener(OnAnimationEndListener listener) {
472         removeEntry(mEndListeners, listener);
473     }
474 
475     /**
476      * Adds an update listener to the animation for receiving per-frame animation update callbacks.
477      * If the listener is {@code null} or has already been added to the list of listeners for the
478      * animation, no op.
479      *
480      * <p>Note that update listener should only be added before the start of the animation.
481      *
482      * @param listener the listener to be added
483      * @return the animation to which the listener is added
484      * @throws UnsupportedOperationException if the update listener is added after the animation has
485      *                                       started
486      */
487     @SuppressWarnings("unchecked")
addUpdateListener(OnAnimationUpdateListener listener)488     public T addUpdateListener(OnAnimationUpdateListener listener) {
489         if (isRunning()) {
490             // Require update listener to be added before the animation, such as when we start
491             // the animation, we know whether the animation is RenderThread compatible.
492             throw new UnsupportedOperationException("Error: Update listeners must be added before"
493                     + "the animation.");
494         }
495         if (!mUpdateListeners.contains(listener)) {
496             mUpdateListeners.add(listener);
497         }
498         return (T) this;
499     }
500 
501     /**
502      * Removes the update listener from the animation, so as to stop receiving animation update
503      * callbacks.
504      *
505      * @param listener the listener to be removed
506      */
removeUpdateListener(OnAnimationUpdateListener listener)507     public void removeUpdateListener(OnAnimationUpdateListener listener) {
508         removeEntry(mUpdateListeners, listener);
509     }
510 
511 
512     /**
513      * This method sets the minimal change of animation value that is visible to users, which helps
514      * determine a reasonable threshold for the animation's termination condition. It is critical
515      * to set the minimal visible change for custom properties (i.e. non-<code>ViewProperty</code>s)
516      * unless the custom property is in pixels.
517      *
518      * <p>For custom properties, this minimum visible change defaults to change in pixel
519      * (i.e. {@link #MIN_VISIBLE_CHANGE_PIXELS}. It is recommended to adjust this value that is
520      * reasonable for the property to be animated. A general rule of thumb to calculate such a value
521      * is: minimum visible change = range of custom property value / corresponding pixel range. For
522      * example, if the property to be animated is a progress (from 0 to 100) that corresponds to a
523      * 200-pixel change. Then the min visible change should be 100 / 200. (i.e. 0.5).
524      *
525      * <p>It's not necessary to call this method when animating {@link ViewProperty}s, as the
526      * minimum visible change will be derived from the property. For example, if the property to be
527      * animated is in pixels (i.e. {@link #TRANSLATION_X}, {@link #TRANSLATION_Y},
528      * {@link #TRANSLATION_Z}, @{@link #SCROLL_X} or {@link #SCROLL_Y}), the default minimum visible
529      * change is 1 (pixel). For {@link #ROTATION}, {@link #ROTATION_X} or {@link #ROTATION_Y}, the
530      * animation will use {@link #MIN_VISIBLE_CHANGE_ROTATION_DEGREES} as the min visible change,
531      * which is 1/10. Similarly, the minimum visible change for alpha (
532      * i.e. {@link #MIN_VISIBLE_CHANGE_ALPHA} is defined as 1 / 256.
533      *
534      * @param minimumVisibleChange minimum change in property value that is visible to users
535      * @return the animation whose min visible change is being set
536      * @throws IllegalArgumentException if the given threshold is not positive
537      */
538     @SuppressWarnings("unchecked")
setMinimumVisibleChange(@loatRangefrom = 0.0, fromInclusive = false) float minimumVisibleChange)539     public T setMinimumVisibleChange(@FloatRange(from = 0.0, fromInclusive = false)
540             float minimumVisibleChange) {
541         if (minimumVisibleChange <= 0) {
542             throw new IllegalArgumentException("Minimum visible change must be positive.");
543         }
544         mMinVisibleChange = minimumVisibleChange;
545         setValueThreshold(minimumVisibleChange * THRESHOLD_MULTIPLIER);
546         return (T) this;
547     }
548 
549     /**
550      * Returns the minimum change in the animation property that could be visibly different to
551      * users.
552      *
553      * @return minimum change in property value that is visible to users
554      */
getMinimumVisibleChange()555     public float getMinimumVisibleChange() {
556         return mMinVisibleChange;
557     }
558 
559     /**
560      * Remove {@code null} entries from the list.
561      */
removeNullEntries(ArrayList<T> list)562     private static <T> void removeNullEntries(ArrayList<T> list) {
563         // Clean up null entries
564         for (int i = list.size() - 1; i >= 0; i--) {
565             if (list.get(i) == null) {
566                 list.remove(i);
567             }
568         }
569     }
570 
571     /**
572      * Remove an entry from the list by marking it {@code null} and clean up later.
573      */
removeEntry(ArrayList<T> list, T entry)574     private static <T> void removeEntry(ArrayList<T> list, T entry) {
575         int id = list.indexOf(entry);
576         if (id >= 0) {
577             list.set(id, null);
578         }
579     }
580 
581     /****************Animation Lifecycle Management***************/
582 
583     /**
584      * Starts an animation. If the animation has already been started, no op. Note that calling
585      * {@link #start()} will not immediately set the property value to start value of the animation.
586      * The property values will be changed at each animation pulse, which happens before the draw
587      * pass. As a result, the changes will be reflected in the next frame, the same as if the values
588      * were set immediately. This method should only be called on main thread.
589      *
590      * Unless a AnimationHandler is provided via setAnimationHandler, a default AnimationHandler
591      * is created on the same thread as the first call to start/cancel an animation. All the
592      * subsequent animation lifecycle manipulations need to be on that same thread, until the
593      * AnimationHandler is reset (using [setAnimationHandler]).
594      *
595      * @throws AndroidRuntimeException if this method is not called on the same thread as the
596      * animation handler
597      */
598     @MainThread
start()599     public void start() {
600         if (!isCurrentThread()) {
601             throw new AndroidRuntimeException("Animations may only be started on the same thread "
602                     + "as the animation handler");
603         }
604         if (!mRunning) {
605             startAnimationInternal();
606         }
607     }
608 
isCurrentThread()609     boolean isCurrentThread() {
610         return Thread.currentThread() == Looper.myLooper().getThread();
611     }
612 
613     /**
614      * Cancels the on-going animation. If the animation hasn't started, no op.
615      *
616      * Unless a AnimationHandler is provided via setAnimationHandler, a default AnimationHandler
617      * is created on the same thread as the first call to start/cancel an animation. All the
618      * subsequent animation lifecycle manipulations need to be on that same thread, until the
619      * AnimationHandler is reset (using [setAnimationHandler]).
620      *
621      * @throws AndroidRuntimeException if this method is not called on the same thread as the
622      * animation handler
623      */
624     @MainThread
cancel()625     public void cancel() {
626         if (!isCurrentThread()) {
627             throw new AndroidRuntimeException("Animations may only be canceled from the same "
628                     + "thread as the animation handler");
629         }
630         if (mRunning) {
631             endAnimationInternal(true);
632         }
633     }
634 
635     /**
636      * Returns whether the animation is currently running.
637      *
638      * @return {@code true} if the animation is currently running, {@code false} otherwise
639      */
isRunning()640     public boolean isRunning() {
641         return mRunning;
642     }
643 
644     /************************** Private APIs below ********************************/
645 
646     // This gets called when the animation is started, to finish the setup of the animation
647     // before the animation pulsing starts.
startAnimationInternal()648     private void startAnimationInternal() {
649         if (!mRunning) {
650             mRunning = true;
651             if (!mStartValueIsSet) {
652                 mValue = getPropertyValue();
653             }
654             // Sanity check:
655             if (mValue > mMaxValue || mValue < mMinValue) {
656                 throw new IllegalArgumentException("Starting value need to be in between min"
657                         + " value and max value");
658             }
659             getAnimationHandler().addAnimationFrameCallback(this, 0);
660         }
661     }
662 
663     /**
664      * This gets call on each frame of the animation. Animation value and velocity are updated
665      * in this method based on the new frame time. The property value of the view being animated
666      * is then updated. The animation's ending conditions are also checked in this method. Once
667      * the animation reaches equilibrium, the animation will come to its end, and end listeners
668      * will be notified, if any.
669      */
670     @Override
doAnimationFrame(long frameTime)671     public boolean doAnimationFrame(long frameTime) {
672         if (mLastFrameTime == 0) {
673             // First frame.
674             mLastFrameTime = frameTime;
675             setPropertyValue(mValue);
676             return false;
677         }
678         long deltaT = frameTime - mLastFrameTime;
679         mLastFrameTime = frameTime;
680         float durationScale = ValueAnimator.getDurationScale();
681         deltaT = durationScale == 0.0f ? Integer.MAX_VALUE : (long) (deltaT / durationScale);
682         boolean finished = updateValueAndVelocity(deltaT);
683         // Clamp value & velocity.
684         mValue = Math.min(mValue, mMaxValue);
685         mValue = Math.max(mValue, mMinValue);
686 
687         setPropertyValue(mValue);
688 
689         if (finished) {
690             endAnimationInternal(false);
691         }
692         return finished;
693     }
694 
695     @Override
commitAnimationFrame(long frameTime)696     public void commitAnimationFrame(long frameTime) {
697         doAnimationFrame(frameTime);
698     }
699 
700     /**
701      * Updates the animation state (i.e. value and velocity). This method is package private, so
702      * subclasses can override this method to calculate the new value and velocity in their custom
703      * way.
704      *
705      * @param deltaT time elapsed in millisecond since last frame
706      * @return whether the animation has finished
707      */
updateValueAndVelocity(long deltaT)708     abstract boolean updateValueAndVelocity(long deltaT);
709 
710     /**
711      * Internal method to reset the animation states when animation is finished/canceled.
712      */
endAnimationInternal(boolean canceled)713     private void endAnimationInternal(boolean canceled) {
714         mRunning = false;
715         getAnimationHandler().removeCallback(this);
716         mLastFrameTime = 0;
717         mStartValueIsSet = false;
718         for (int i = 0; i < mEndListeners.size(); i++) {
719             if (mEndListeners.get(i) != null) {
720                 mEndListeners.get(i).onAnimationEnd(this, canceled, mValue, mVelocity);
721             }
722         }
723         removeNullEntries(mEndListeners);
724     }
725 
726     /**
727      * Updates the property value through the corresponding setter.
728      */
729     @SuppressWarnings("unchecked")
setPropertyValue(float value)730     void setPropertyValue(float value) {
731         mProperty.setValue(mTarget, value);
732         for (int i = 0; i < mUpdateListeners.size(); i++) {
733             if (mUpdateListeners.get(i) != null) {
734                 mUpdateListeners.get(i).onAnimationUpdate(this, mValue, mVelocity);
735             }
736         }
737         removeNullEntries(mUpdateListeners);
738     }
739 
740     /**
741      * Returns the default threshold.
742      */
getValueThreshold()743     float getValueThreshold() {
744         return mMinVisibleChange * THRESHOLD_MULTIPLIER;
745     }
746 
747     /**
748      * Obtain the property value through the corresponding getter.
749      */
750     @SuppressWarnings("unchecked")
getPropertyValue()751     private float getPropertyValue() {
752         return (Float) mProperty.get(mTarget);
753     }
754 
755     /**
756      * Returns the {@link AnimationHandler} used to schedule updates for this animator.
757      *
758      * @return the {@link AnimationHandler} for this animator.
759      */
760     @NonNull
getAnimationHandler()761     public AnimationHandler getAnimationHandler() {
762         return mAnimationHandler != null ? mAnimationHandler : AnimationHandler.getInstance();
763     }
764 
765     /****************Sub class animations**************/
766     /**
767      * Returns the acceleration at the given value with the given velocity.
768      **/
getAcceleration(float value, float velocity)769     abstract float getAcceleration(float value, float velocity);
770 
771     /**
772      * Returns whether the animation has reached equilibrium.
773      */
isAtEquilibrium(float value, float velocity)774     abstract boolean isAtEquilibrium(float value, float velocity);
775 
776     /**
777      * Updates the default value threshold for the animation based on the property to be animated.
778      */
setValueThreshold(float threshold)779     abstract void setValueThreshold(float threshold);
780 
781     /**
782      * An animation listener that receives end notifications from an animation.
783      */
784     public interface OnAnimationEndListener {
785         /**
786          * Notifies the end of an animation. Note that this callback will be invoked not only when
787          * an animation reach equilibrium, but also when the animation is canceled.
788          *
789          * @param animation animation that has ended or was canceled
790          * @param canceled whether the animation has been canceled
791          * @param value the final value when the animation stopped
792          * @param velocity the final velocity when the animation stopped
793          */
onAnimationEnd(DynamicAnimation animation, boolean canceled, float value, float velocity)794         void onAnimationEnd(DynamicAnimation animation, boolean canceled, float value,
795                 float velocity);
796     }
797 
798     /**
799      * Implementors of this interface can add themselves as update listeners
800      * to an <code>DynamicAnimation</code> instance to receive callbacks on every animation
801      * frame, after the current frame's values have been calculated for that
802      * <code>DynamicAnimation</code>.
803      */
804     public interface OnAnimationUpdateListener {
805 
806         /**
807          * Notifies the occurrence of another frame of the animation.
808          *
809          * @param animation animation that the update listener is added to
810          * @param value the current value of the animation
811          * @param velocity the current velocity of the animation
812          */
onAnimationUpdate(DynamicAnimation animation, float value, float velocity)813         void onAnimationUpdate(DynamicAnimation animation, float value, float velocity);
814     }
815 }
816