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