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