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