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