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