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