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