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