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