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