Animation.java revision f013e1afd1e68af5e3b868c26a653bbfb39538f8
1/*
2 * Copyright (C) 2006 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
17package android.view.animation;
18
19import android.content.Context;
20import android.content.res.TypedArray;
21import android.util.AttributeSet;
22import android.util.TypedValue;
23
24/**
25 * Abstraction for an Animation that can be applied to Views, Surfaces, or
26 * other objects. See the {@link android.view.animation animation package
27 * description file}.
28 */
29public abstract class Animation {
30    /**
31     * Repeat the animation indefinitely.
32     */
33    public static final int INFINITE = -1;
34
35    /**
36     * When the animation reaches the end and the repeat count is INFINTE_REPEAT
37     * or a positive value, the animation restarts from the beginning.
38     */
39    public static final int RESTART = 1;
40
41    /**
42     * When the animation reaches the end and the repeat count is INFINTE_REPEAT
43     * or a positive value, the animation plays backward (and then forward again).
44     */
45    public static final int REVERSE = 2;
46
47    /**
48     * Can be used as the start time to indicate the start time should be the current
49     * time when {@link #getTransformation(long, Transformation)} is invoked for the
50     * first animation frame. This can is useful for short animations.
51     */
52    public static final int START_ON_FIRST_FRAME = -1;
53
54    /**
55     * The specified dimension is an absolute number of pixels.
56     */
57    public static final int ABSOLUTE = 0;
58
59    /**
60     * The specified dimension holds a float and should be multiplied by the
61     * height or width of the object being animated.
62     */
63    public static final int RELATIVE_TO_SELF = 1;
64
65    /**
66     * The specified dimension holds a float and should be multiplied by the
67     * height or width of the parent of the object being animated.
68     */
69    public static final int RELATIVE_TO_PARENT = 2;
70
71    /**
72     * Requests that the content being animated be kept in its current Z
73     * order.
74     */
75    public static final int ZORDER_NORMAL = 0;
76
77    /**
78     * Requests that the content being animated be forced on top of all other
79     * content for the duration of the animation.
80     */
81    public static final int ZORDER_TOP = 1;
82
83    /**
84     * Requests that the content being animated be forced under all other
85     * content for the duration of the animation.
86     */
87    public static final int ZORDER_BOTTOM = -1;
88
89    /**
90     * Set by {@link #getTransformation(long, Transformation)} when the animation ends.
91     */
92    boolean mEnded = false;
93
94    /**
95     * Set by {@link #getTransformation(long, Transformation)} when the animation starts.
96     */
97    boolean mStarted = false;
98
99    /**
100     * Set by {@link #getTransformation(long, Transformation)} when the animation repeats
101     * in REVERSE mode.
102     */
103    boolean mCycleFlip = false;
104
105    /**
106     * This value must be set to true by {@link #initialize(int, int, int, int)}. It
107     * indicates the animation was successfully initialized and can be played.
108     */
109    boolean mInitialized = false;
110
111    /**
112     * Indicates whether the animation transformation should be applied before the
113     * animation starts.
114     */
115    boolean mFillBefore = true;
116
117    /**
118     * Indicates whether the animation transformation should be applied after the
119     * animation ends.
120     */
121    boolean mFillAfter = false;
122
123    /**
124     * The time in milliseconds at which the animation must start;
125     */
126    long mStartTime = -1;
127
128    /**
129     * The delay in milliseconds after which the animation must start. When the
130     * start offset is > 0, the start time of the animation is startTime + startOffset.
131     */
132    long mStartOffset;
133
134    /**
135     * The duration of one animation cycle in milliseconds.
136     */
137    long mDuration;
138
139    /**
140     * The number of times the animation must repeat. By default, an animation repeats
141     * indefinitely.
142     */
143    int mRepeatCount = 0;
144
145    /**
146     * Indicates how many times the animation was repeated.
147     */
148    int mRepeated = 0;
149
150    /**
151     * The behavior of the animation when it repeats. The repeat mode is either
152     * {@link #RESTART} or {@link #REVERSE}.
153     *
154     */
155    int mRepeatMode = RESTART;
156
157    /**
158     * The interpolator used by the animation to smooth the movement.
159     */
160    Interpolator mInterpolator;
161
162    /**
163     * The animation listener to be notified when the animation starts, ends or repeats.
164     */
165    AnimationListener mListener;
166
167    /**
168     * Desired Z order mode during animation.
169     */
170    private int mZAdjustment;
171
172    // Indicates what was the last value returned by getTransformation()
173    private boolean mMore = true;
174
175    /**
176     * Creates a new animation with a duration of 0ms, the default interpolator, with
177     * fillBefore set to true and fillAfter set to false
178     */
179    public Animation() {
180        ensureInterpolator();
181    }
182
183    /**
184     * Creates a new animation whose parameters come from the specified context and
185     * attributes set.
186     *
187     * @param context the application environment
188     * @param attrs the set of attributes holding the animation parameters
189     */
190    public Animation(Context context, AttributeSet attrs) {
191        TypedArray a = context.obtainStyledAttributes(attrs, com.android.internal.R.styleable.Animation);
192
193        setDuration((long) a.getInt(com.android.internal.R.styleable.Animation_duration, 0));
194        setStartOffset((long) a.getInt(com.android.internal.R.styleable.Animation_startOffset, 0));
195
196        setFillBefore(a.getBoolean(com.android.internal.R.styleable.Animation_fillBefore, mFillBefore));
197        setFillAfter(a.getBoolean(com.android.internal.R.styleable.Animation_fillAfter, mFillAfter));
198
199        final int resID = a.getResourceId(com.android.internal.R.styleable.Animation_interpolator, 0);
200        if (resID > 0) {
201            setInterpolator(context, resID);
202        }
203
204        setRepeatCount(a.getInt(com.android.internal.R.styleable.Animation_repeatCount, mRepeatCount));
205        setRepeatMode(a.getInt(com.android.internal.R.styleable.Animation_repeatMode, RESTART));
206
207        setZAdjustment(a.getInt(com.android.internal.R.styleable.Animation_zAdjustment, ZORDER_NORMAL));
208
209        ensureInterpolator();
210
211        a.recycle();
212    }
213
214    /**
215     * Reset the initialization state of this animation.
216     *
217     * @see #initialize(int, int, int, int)
218     */
219    public void reset() {
220        mInitialized = false;
221        mCycleFlip = false;
222        mRepeated = 0;
223        mMore = true;
224    }
225
226    /**
227     * Whether or not the animation has been initialized.
228     *
229     * @return Has this animation been initialized.
230     * @see #initialize(int, int, int, int)
231     */
232    public boolean isInitialized() {
233        return mInitialized;
234    }
235
236    /**
237     * Initialize this animation with the dimensions of the object being
238     * animated as well as the objects parents. (This is to support animation
239     * sizes being specifed relative to these dimensions.)
240     *
241     * <p>Objects that interpret a Animations should call this method when
242     * the sizes of the object being animated and its parent are known, and
243     * before calling {@link #getTransformation}.
244     *
245     *
246     * @param width Width of the object being animated
247     * @param height Height of the object being animated
248     * @param parentWidth Width of the animated object's parent
249     * @param parentHeight Height of the animated object's parent
250     */
251    public void initialize(int width, int height, int parentWidth, int parentHeight) {
252        mInitialized = true;
253        mCycleFlip = false;
254        mRepeated = 0;
255        mMore = true;
256    }
257
258    /**
259     * Sets the acceleration curve for this animation. The interpolator is loaded as
260     * a resource from the specified context.
261     *
262     * @param context The application environment
263     * @param resID The resource identifier of the interpolator to load
264     * @attr ref android.R.styleable#Animation_interpolator
265     */
266    public void setInterpolator(Context context, int resID) {
267        setInterpolator(AnimationUtils.loadInterpolator(context, resID));
268    }
269
270    /**
271     * Sets the acceleration curve for this animation. Defaults to a linear
272     * interpolation.
273     *
274     * @param i The interpolator which defines the acceleration curve
275     * @attr ref android.R.styleable#Animation_interpolator
276     */
277    public void setInterpolator(Interpolator i) {
278        mInterpolator = i;
279    }
280
281    /**
282     * When this animation should start relative to the start time. This is most
283     * useful when composing complex animations using an {@link AnimationSet }
284     * where some of the animations components start at different times.
285     *
286     * @param startOffset When this Animation should start, in milliseconds from
287     *                    the start time of the root AnimationSet.
288     * @attr ref android.R.styleable#Animation_startOffset
289     */
290    public void setStartOffset(long startOffset) {
291        mStartOffset = startOffset;
292    }
293
294    /**
295     * How long this animation should last.
296     *
297     * @param durationMillis Duration in milliseconds
298     * @attr ref android.R.styleable#Animation_duration
299     */
300    public void setDuration(long durationMillis) {
301        mDuration = durationMillis;
302    }
303
304    /**
305     * Ensure that the duration that this animation will run is not longer
306     * than <var>durationMillis</var>.  In addition to adjusting the duration
307     * itself, this ensures that the repeat count also will not make it run
308     * longer than the given time.
309     *
310     * @param durationMillis The maximum duration the animation is allowed
311     * to run.
312     */
313    public void restrictDuration(long durationMillis) {
314        if (mStartOffset > durationMillis) {
315            mStartOffset = durationMillis;
316            mDuration = 0;
317            mRepeatCount = 0;
318            return;
319        }
320
321        long dur = mDuration + mStartOffset;
322        if (dur > durationMillis) {
323            mDuration = dur = durationMillis-mStartOffset;
324        }
325        if (mRepeatCount < 0 || mRepeatCount > durationMillis
326                || (dur*mRepeatCount) > durationMillis) {
327            mRepeatCount = (int)(durationMillis/dur);
328        }
329    }
330
331    /**
332     * How much to scale the duration by.
333     *
334     * @param scale The amount to scale the duration.
335     */
336    public void scaleCurrentDuration(float scale) {
337        mDuration = (long) (mDuration * scale);
338    }
339
340    /**
341     * When this animation should start. When the start time is set to
342     * {@link #START_ON_FIRST_FRAME}, the animation will start the first time
343     * {@link #getTransformation(long, Transformation)} is invoked. The time passed
344     * to this method should be obtained by calling
345     * {@link AnimationUtils#currentAnimationTimeMillis()} instead of
346     * {@link System#currentTimeMillis()}.
347     *
348     * @param startTimeMillis the start time in milliseconds
349     */
350    public void setStartTime(long startTimeMillis) {
351        mStartTime = startTimeMillis;
352        mStarted = mEnded = false;
353        mCycleFlip = false;
354        mRepeated = 0;
355        mMore = true;
356    }
357
358    /**
359     * Convenience method to start the animation the first time
360     * {@link #getTransformation(long, Transformation)} is invoked.
361     */
362    public void start() {
363        setStartTime(-1);
364    }
365
366    /**
367     * Convenience method to start the animation at the current time in
368     * milliseconds.
369     */
370    public void startNow() {
371        setStartTime(AnimationUtils.currentAnimationTimeMillis());
372    }
373
374    /**
375     * Defines what this animation should do when it reaches the end. This
376     * setting is applied only when the repeat count is either greater than
377     * 0 or {@link #INFINITE}. Defaults to {@link #RESTART}.
378     *
379     * @param repeatMode {@link #RESTART} or {@link #REVERSE}
380     * @attr ref android.R.styleable#Animation_repeatMode
381     */
382    public void setRepeatMode(int repeatMode) {
383        mRepeatMode = repeatMode;
384    }
385
386    /**
387     * Sets how many times the animation should be repeated. If the repeat
388     * count is 0, the animation is never repeated. If the repeat count is
389     * greater than 0 or {@link #INFINITE}, the repeat mode will be taken
390     * into account. The repeat count if 0 by default.
391     *
392     * @param repeatCount the number of times the animation should be repeated
393     * @attr ref android.R.styleable#Animation_repeatCount
394     */
395    public void setRepeatCount(int repeatCount) {
396        if (repeatCount < 0) {
397            repeatCount = INFINITE;
398        }
399        mRepeatCount = repeatCount;
400    }
401
402    /**
403     * If fillBefore is true, this animation will apply its transformation
404     * before the start time of the animation. Defaults to false if not set.
405     * Note that this applies when using an {@link
406     * android.view.animation.AnimationSet AnimationSet} to chain
407     * animations. The transformation is not applied before the AnimationSet
408     * itself starts.
409     *
410     * @param fillBefore true if the animation should apply its transformation before it starts
411     * @attr ref android.R.styleable#Animation_fillBefore
412     */
413    public void setFillBefore(boolean fillBefore) {
414        mFillBefore = fillBefore;
415    }
416
417    /**
418     * If fillAfter is true, the transformation that this animation performed
419     * will persist when it is finished. Defaults to false if not set.
420     * Note that this applies when using an {@link
421     * android.view.animation.AnimationSet AnimationSet} to chain
422     * animations. The transformation is not applied before the AnimationSet
423     * itself starts.
424     *
425     * @param fillAfter true if the animation should apply its transformation after it ends
426     * @attr ref android.R.styleable#Animation_fillAfter
427     */
428    public void setFillAfter(boolean fillAfter) {
429        mFillAfter = fillAfter;
430    }
431
432    /**
433     * Set the Z ordering mode to use while running the animation.
434     *
435     * @param zAdjustment The desired mode, one of {@link #ZORDER_NORMAL},
436     * {@link #ZORDER_TOP}, or {@link #ZORDER_BOTTOM}.
437     * @attr ref android.R.styleable#Animation_zAdjustment
438     */
439    public void setZAdjustment(int zAdjustment) {
440        mZAdjustment = zAdjustment;
441    }
442
443    /**
444     * Gets the acceleration curve type for this animation.
445     *
446     * @return the {@link Interpolator} associated to this animation
447     * @attr ref android.R.styleable#Animation_interpolator
448     */
449    public Interpolator getInterpolator() {
450        return mInterpolator;
451    }
452
453    /**
454     * When this animation should start. If the animation has not startet yet,
455     * this method might return {@link #START_ON_FIRST_FRAME}.
456     *
457     * @return the time in milliseconds when the animation should start or
458     *         {@link #START_ON_FIRST_FRAME}
459     */
460    public long getStartTime() {
461        return mStartTime;
462    }
463
464    /**
465     * How long this animation should last
466     *
467     * @return the duration in milliseconds of the animation
468     * @attr ref android.R.styleable#Animation_duration
469     */
470    public long getDuration() {
471        return mDuration;
472    }
473
474    /**
475     * When this animation should start, relative to StartTime
476     *
477     * @return the start offset in milliseconds
478     * @attr ref android.R.styleable#Animation_startOffset
479     */
480    public long getStartOffset() {
481        return mStartOffset;
482    }
483
484    /**
485     * Defines what this animation should do when it reaches the end.
486     *
487     * @return either one of {@link #REVERSE} or {@link #RESTART}
488     * @attr ref android.R.styleable#Animation_repeatMode
489     */
490    public int getRepeatMode() {
491        return mRepeatMode;
492    }
493
494    /**
495     * Defines how many times the animation should repeat. The default value
496     * is 0.
497     *
498     * @return the number of times the animation should repeat, or {@link #INFINITE}
499     * @attr ref android.R.styleable#Animation_repeatCount
500     */
501    public int getRepeatCount() {
502        return mRepeatCount;
503    }
504
505    /**
506     * If fillBefore is true, this animation will apply its transformation
507     * before the start time of the animation.
508     *
509     * @return true if the animation applies its transformation before it starts
510     * @attr ref android.R.styleable#Animation_fillBefore
511     */
512    public boolean getFillBefore() {
513        return mFillBefore;
514    }
515
516    /**
517     * If fillAfter is true, this animation will apply its transformation
518     * after the end time of the animation.
519     *
520     * @return true if the animation applies its transformation after it ends
521     * @attr ref android.R.styleable#Animation_fillAfter
522     */
523    public boolean getFillAfter() {
524        return mFillAfter;
525    }
526
527    /**
528     * Returns the Z ordering mode to use while running the animation as
529     * previously set by {@link #setZAdjustment}.
530     *
531     * @return Returns one of {@link #ZORDER_NORMAL},
532     * {@link #ZORDER_TOP}, or {@link #ZORDER_BOTTOM}.
533     * @attr ref android.R.styleable#Animation_zAdjustment
534     */
535    public int getZAdjustment() {
536        return mZAdjustment;
537    }
538
539    /**
540     * <p>Indicates whether or not this animation will affect the transformation
541     * matrix. For instance, a fade animation will not affect the matrix whereas
542     * a scale animation will.</p>
543     *
544     * @return true if this animation will change the transformation matrix
545     */
546    public boolean willChangeTransformationMatrix() {
547        // assume we will change the matrix
548        return true;
549    }
550
551    /**
552     * <p>Indicates whether or not this animation will affect the bounds of the
553     * animated view. For instance, a fade animation will not affect the bounds
554     * whereas a 200% scale animation will.</p>
555     *
556     * @return true if this animation will change the view's bounds
557     */
558    public boolean willChangeBounds() {
559        // assume we will change the bounds
560        return true;
561    }
562
563    /**
564     * <p>Binds an animation listener to this animation. The animation listener
565     * is notified of animation events such as the end of the animation or the
566     * repetition of the animation.</p>
567     *
568     * @param listener the animation listener to be notified
569     */
570    public void setAnimationListener(AnimationListener listener) {
571        mListener = listener;
572    }
573
574    /**
575     * Gurantees that this animation has an interpolator. Will use
576     * a AccelerateDecelerateInterpolator is nothing else was specified.
577     */
578    protected void ensureInterpolator() {
579        if (mInterpolator == null) {
580            mInterpolator = new AccelerateDecelerateInterpolator();
581        }
582    }
583
584    /**
585     * Compute a hint at how long the entire animation may last, in milliseconds.
586     * Animations can be written to cause themselves to run for a different
587     * duration than what is computed here, but generally this should be
588     * accurate.
589     */
590    public long computeDurationHint() {
591        return (getStartOffset() + getDuration()) * (getRepeatCount() + 1);
592    }
593
594    /**
595     * Gets the transformation to apply at a specified point in time. Implementations of this
596     * method should always replace the specified Transformation or document they are doing
597     * otherwise.
598     *
599     * @param currentTime Where we are in the animation. This is wall clock time.
600     * @param outTransformation A tranformation object that is provided by the
601     *        caller and will be filled in by the animation.
602     * @return True if the animation is still running
603     */
604    public boolean getTransformation(long currentTime, Transformation outTransformation) {
605        if (mStartTime == -1) {
606            mStartTime = currentTime;
607        }
608
609        final long startOffset = getStartOffset();
610        float normalizedTime = ((float) (currentTime - (mStartTime + startOffset))) /
611                    (float) mDuration;
612
613        boolean expired = normalizedTime >= 1.0f;
614        // Pin time to 0.0 to 1.0 range
615        normalizedTime = Math.max(Math.min(normalizedTime, 1.0f), 0.0f);
616        mMore = !expired;
617
618        if ((normalizedTime >= 0.0f || mFillBefore) && (normalizedTime <= 1.0f || mFillAfter)) {
619            if (!mStarted) {
620                if (mListener != null) {
621                    mListener.onAnimationStart(this);
622                }
623                mStarted = true;
624            }
625
626            if (mCycleFlip) {
627                normalizedTime = 1.0f - normalizedTime;
628            }
629
630            final float interpolatedTime = mInterpolator.getInterpolation(normalizedTime);
631            applyTransformation(interpolatedTime, outTransformation);
632        }
633
634        if (expired) {
635            if (mRepeatCount == mRepeated) {
636                if (!mEnded) {
637                    if (mListener != null) {
638                        mListener.onAnimationEnd(this);
639                    }
640                    mEnded = true;
641                }
642            } else {
643                if (mRepeatCount > 0) {
644                    mRepeated++;
645                }
646
647                if (mRepeatMode == REVERSE) {
648                    mCycleFlip = !mCycleFlip;
649                }
650
651                mStartTime = -1;
652                mMore = true;
653
654                if (mListener != null) {
655                    mListener.onAnimationRepeat(this);
656                }
657            }
658        }
659
660        return mMore;
661    }
662
663    /**
664     * <p>Indicates whether this animation has started or not.</p>
665     *
666     * @return true if the animation has started, false otherwise
667     */
668    public boolean hasStarted() {
669        return mStarted;
670    }
671
672    /**
673     * <p>Indicates whether this animation has ended or not.</p>
674     *
675     * @return true if the animation has ended, false otherwise
676     */
677    public boolean hasEnded() {
678        return mEnded;
679    }
680
681    /**
682     * Helper for getTransformation. Subclasses should implement this to apply
683     * their transforms given an interpolation value.  Implementations of this
684     * method should always replace the specified Transformation or document
685     * they are doing otherwise.
686     *
687     * @param interpolatedTime The value of the normalized time (0.0 to 1.0)
688     *        after it has been run through the interpolation function.
689     * @param t The Transofrmation object to fill in with the current
690     *        transforms.
691     */
692    protected void applyTransformation(float interpolatedTime, Transformation t) {
693    }
694
695    /**
696     * Convert the information in the description of a size to an actual
697     * dimension
698     *
699     * @param type One of Animation.ABSOLUTE, Animation.RELATIVE_TO_SELF, or
700     *             Animation.RELATIVE_TO_PARENT.
701     * @param value The dimension associated with the type parameter
702     * @param size The size of the object being animated
703     * @param parentSize The size of the parent of the object being animated
704     * @return The dimension to use for the animation
705     */
706    protected float resolveSize(int type, float value, int size, int parentSize) {
707        switch (type) {
708            case ABSOLUTE:
709                return value;
710            case RELATIVE_TO_SELF:
711                return size * value;
712            case RELATIVE_TO_PARENT:
713                return parentSize * value;
714            default:
715                return value;
716        }
717    }
718
719    /**
720     * Utility class to parse a string description of a size.
721     */
722    protected static class Description {
723        /**
724         * One of Animation.ABSOLUTE, Animation.RELATIVE_TO_SELF, or
725         * Animation.RELATIVE_TO_PARENT.
726         */
727        public int type;
728
729        /**
730         * The absolute or relative dimension for this Description.
731         */
732        public float value;
733
734        /**
735         * Size descriptions can appear inthree forms:
736         * <ol>
737         * <li>An absolute size. This is represented by a number.</li>
738         * <li>A size relative to the size of the object being animated. This
739         * is represented by a number followed by "%".</li> *
740         * <li>A size relative to the size of the parent of object being
741         * animated. This is represented by a number followed by "%p".</li>
742         * </ol>
743         * @param value The typed value to parse
744         * @return The parsed version of the description
745         */
746        static Description parseValue(TypedValue value) {
747            Description d = new Description();
748            if (value == null) {
749                d.type = ABSOLUTE;
750                d.value = 0;
751            } else {
752                if (value.type == TypedValue.TYPE_FRACTION) {
753                    d.type = (value.data & TypedValue.COMPLEX_UNIT_MASK) ==
754                            TypedValue.COMPLEX_UNIT_FRACTION_PARENT ?
755                                    RELATIVE_TO_PARENT : RELATIVE_TO_SELF;
756                    d.value = TypedValue.complexToFloat(value.data);
757                    return d;
758                } else if (value.type == TypedValue.TYPE_FLOAT) {
759                    d.type = ABSOLUTE;
760                    d.value = value.getFloat();
761                    return d;
762                } else if (value.type >= TypedValue.TYPE_FIRST_INT &&
763                        value.type <= TypedValue.TYPE_LAST_INT) {
764                    d.type = ABSOLUTE;
765                    d.value = value.data;
766                    return d;
767                }
768            }
769
770            d.type = ABSOLUTE;
771            d.value = 0.0f;
772
773            return d;
774        }
775    }
776
777    /**
778     * <p>An animation listener receives notifications from an animation.
779     * Notifications indicate animation related events, such as the end or the
780     * repetition of the animation.</p>
781     */
782    public static interface AnimationListener {
783        /**
784         * <p>Notifies the start of the animation.</p>
785         *
786         * @param animation The started animation.
787         */
788        void onAnimationStart(Animation animation);
789
790        /**
791         * <p>Notifies the end of the animation. This callback is invoked
792         * only for animation with repeat mode set to NO_REPEAT.</p>
793         *
794         * @param animation The animation which reached its end.
795         */
796        void onAnimationEnd(Animation animation);
797
798        /**
799         * <p>Notifies the repetition of the animation. This callback is invoked
800         * only for animation with repeat mode set to RESTART or REVERSE.</p>
801         *
802         * @param animation The animation which was repeated.
803         */
804        void onAnimationRepeat(Animation animation);
805    }
806}
807