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