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