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