Animation.java revision 3001a035439d8134a7d70d796376d1dfbff3cdcd
1/*
2 * Copyright (C) 2006 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package android.view.animation;
18
19import android.content.Context;
20import android.content.res.TypedArray;
21import android.util.AttributeSet;
22import android.util.TypedValue;
23import 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 */
30public 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    private boolean mMore = true;
179    private boolean mOneMoreTime = true;
180
181    RectF mPreviousRegion = new RectF();
182    RectF mRegion = new RectF();
183    Transformation mTransformation = new Transformation();
184    Transformation mPreviousTransformation = new Transformation();
185
186    /**
187     * Creates a new animation with a duration of 0ms, the default interpolator, with
188     * fillBefore set to true and fillAfter set to false
189     */
190    public Animation() {
191        ensureInterpolator();
192    }
193
194    /**
195     * Creates a new animation whose parameters come from the specified context and
196     * attributes set.
197     *
198     * @param context the application environment
199     * @param attrs the set of attributes holding the animation parameters
200     */
201    public Animation(Context context, AttributeSet attrs) {
202        TypedArray a = context.obtainStyledAttributes(attrs, com.android.internal.R.styleable.Animation);
203
204        setDuration((long) a.getInt(com.android.internal.R.styleable.Animation_duration, 0));
205        setStartOffset((long) a.getInt(com.android.internal.R.styleable.Animation_startOffset, 0));
206
207        setFillEnabled(a.getBoolean(com.android.internal.R.styleable.Animation_fillEnabled, mFillEnabled));
208        setFillBefore(a.getBoolean(com.android.internal.R.styleable.Animation_fillBefore, mFillBefore));
209        setFillAfter(a.getBoolean(com.android.internal.R.styleable.Animation_fillAfter, mFillAfter));
210
211        final int resID = a.getResourceId(com.android.internal.R.styleable.Animation_interpolator, 0);
212        if (resID > 0) {
213            setInterpolator(context, resID);
214        }
215
216        setRepeatCount(a.getInt(com.android.internal.R.styleable.Animation_repeatCount, mRepeatCount));
217        setRepeatMode(a.getInt(com.android.internal.R.styleable.Animation_repeatMode, RESTART));
218
219        setZAdjustment(a.getInt(com.android.internal.R.styleable.Animation_zAdjustment, ZORDER_NORMAL));
220
221        ensureInterpolator();
222
223        a.recycle();
224    }
225
226    @Override
227    protected Animation clone() throws CloneNotSupportedException {
228        final Animation animation = (Animation) super.clone();
229        animation.mPreviousRegion = new RectF();
230        animation.mRegion = new RectF();
231        animation.mTransformation = new Transformation();
232        animation.mPreviousTransformation = new Transformation();
233        return animation;
234    }
235
236    /**
237     * Reset the initialization state of this animation.
238     *
239     * @see #initialize(int, int, int, int)
240     */
241    public void reset() {
242        mPreviousRegion.setEmpty();
243        mPreviousTransformation.clear();
244        mInitialized = false;
245        mCycleFlip = false;
246        mRepeated = 0;
247        mMore = true;
248        mOneMoreTime = true;
249    }
250
251    /**
252     * Whether or not the animation has been initialized.
253     *
254     * @return Has this animation been initialized.
255     * @see #initialize(int, int, int, int)
256     */
257    public boolean isInitialized() {
258        return mInitialized;
259    }
260
261    /**
262     * Initialize this animation with the dimensions of the object being
263     * animated as well as the objects parents. (This is to support animation
264     * sizes being specifed relative to these dimensions.)
265     *
266     * <p>Objects that interpret a Animations should call this method when
267     * the sizes of the object being animated and its parent are known, and
268     * before calling {@link #getTransformation}.
269     *
270     *
271     * @param width Width of the object being animated
272     * @param height Height of the object being animated
273     * @param parentWidth Width of the animated object's parent
274     * @param parentHeight Height of the animated object's parent
275     */
276    public void initialize(int width, int height, int parentWidth, int parentHeight) {
277        reset();
278        mInitialized = true;
279    }
280
281    /**
282     * Sets the acceleration curve for this animation. The interpolator is loaded as
283     * a resource from the specified context.
284     *
285     * @param context The application environment
286     * @param resID The resource identifier of the interpolator to load
287     * @attr ref android.R.styleable#Animation_interpolator
288     */
289    public void setInterpolator(Context context, int resID) {
290        setInterpolator(AnimationUtils.loadInterpolator(context, resID));
291    }
292
293    /**
294     * Sets the acceleration curve for this animation. Defaults to a linear
295     * interpolation.
296     *
297     * @param i The interpolator which defines the acceleration curve
298     * @attr ref android.R.styleable#Animation_interpolator
299     */
300    public void setInterpolator(Interpolator i) {
301        mInterpolator = i;
302    }
303
304    /**
305     * When this animation should start relative to the start time. This is most
306     * useful when composing complex animations using an {@link AnimationSet }
307     * where some of the animations components start at different times.
308     *
309     * @param startOffset When this Animation should start, in milliseconds from
310     *                    the start time of the root AnimationSet.
311     * @attr ref android.R.styleable#Animation_startOffset
312     */
313    public void setStartOffset(long startOffset) {
314        mStartOffset = startOffset;
315    }
316
317    /**
318     * How long this animation should last. The duration cannot be negative.
319     *
320     * @param durationMillis Duration in milliseconds
321     *
322     * @throw java.lang.IllegalArgumentException if the duration is < 0
323     *
324     * @attr ref android.R.styleable#Animation_duration
325     */
326    public void setDuration(long durationMillis) {
327        if (durationMillis < 0) {
328            throw new IllegalArgumentException("Animation duration cannot be negative");
329        }
330        mDuration = durationMillis;
331    }
332
333    /**
334     * Ensure that the duration that this animation will run is not longer
335     * than <var>durationMillis</var>.  In addition to adjusting the duration
336     * itself, this ensures that the repeat count also will not make it run
337     * longer than the given time.
338     *
339     * @param durationMillis The maximum duration the animation is allowed
340     * to run.
341     */
342    public void restrictDuration(long durationMillis) {
343        if (mStartOffset > durationMillis) {
344            mStartOffset = durationMillis;
345            mDuration = 0;
346            mRepeatCount = 0;
347            return;
348        }
349
350        long dur = mDuration + mStartOffset;
351        if (dur > durationMillis) {
352            mDuration = dur = durationMillis-mStartOffset;
353        }
354        if (mRepeatCount < 0 || mRepeatCount > durationMillis
355                || (dur*mRepeatCount) > durationMillis) {
356            mRepeatCount = (int)(durationMillis/dur);
357        }
358    }
359
360    /**
361     * How much to scale the duration by.
362     *
363     * @param scale The amount to scale the duration.
364     */
365    public void scaleCurrentDuration(float scale) {
366        mDuration = (long) (mDuration * scale);
367    }
368
369    /**
370     * When this animation should start. When the start time is set to
371     * {@link #START_ON_FIRST_FRAME}, the animation will start the first time
372     * {@link #getTransformation(long, Transformation)} is invoked. The time passed
373     * to this method should be obtained by calling
374     * {@link AnimationUtils#currentAnimationTimeMillis()} instead of
375     * {@link System#currentTimeMillis()}.
376     *
377     * @param startTimeMillis the start time in milliseconds
378     */
379    public void setStartTime(long startTimeMillis) {
380        mStartTime = startTimeMillis;
381        mStarted = mEnded = false;
382        mCycleFlip = false;
383        mRepeated = 0;
384        mMore = true;
385    }
386
387    /**
388     * Convenience method to start the animation the first time
389     * {@link #getTransformation(long, Transformation)} is invoked.
390     */
391    public void start() {
392        setStartTime(-1);
393    }
394
395    /**
396     * Convenience method to start the animation at the current time in
397     * milliseconds.
398     */
399    public void startNow() {
400        setStartTime(AnimationUtils.currentAnimationTimeMillis());
401    }
402
403    /**
404     * Defines what this animation should do when it reaches the end. This
405     * setting is applied only when the repeat count is either greater than
406     * 0 or {@link #INFINITE}. Defaults to {@link #RESTART}.
407     *
408     * @param repeatMode {@link #RESTART} or {@link #REVERSE}
409     * @attr ref android.R.styleable#Animation_repeatMode
410     */
411    public void setRepeatMode(int repeatMode) {
412        mRepeatMode = repeatMode;
413    }
414
415    /**
416     * Sets how many times the animation should be repeated. If the repeat
417     * count is 0, the animation is never repeated. If the repeat count is
418     * greater than 0 or {@link #INFINITE}, the repeat mode will be taken
419     * into account. The repeat count if 0 by default.
420     *
421     * @param repeatCount the number of times the animation should be repeated
422     * @attr ref android.R.styleable#Animation_repeatCount
423     */
424    public void setRepeatCount(int repeatCount) {
425        if (repeatCount < 0) {
426            repeatCount = INFINITE;
427        }
428        mRepeatCount = repeatCount;
429    }
430
431    /**
432     * If fillEnabled is true, this animation will apply fillBefore and fillAfter.
433     *
434     * @return true if the animation will take fillBefore and fillAfter into account
435     * @attr ref android.R.styleable#Animation_fillEnabled
436     */
437    public boolean isFillEnabled() {
438        return mFillEnabled;
439    }
440
441    /**
442     * If fillEnabled is true, the animation will apply the value of fillBefore and
443     * fillAfter. Otherwise, fillBefore and fillAfter are ignored and the animation
444     * transformation is always applied.
445     *
446     * @param fillEnabled true if the animation should take fillBefore and fillAfter into account
447     * @attr ref android.R.styleable#Animation_fillEnabled
448     *
449     * @see #setFillBefore(boolean)
450     * @see #setFillAfter(boolean)
451     */
452    public void setFillEnabled(boolean fillEnabled) {
453        mFillEnabled = fillEnabled;
454    }
455
456    /**
457     * If fillBefore is true, this animation will apply its transformation
458     * before the start time of the animation. Defaults to true if not set.
459     * Note that this applies when using an {@link
460     * android.view.animation.AnimationSet AnimationSet} to chain
461     * animations. The transformation is not applied before the AnimationSet
462     * itself starts.
463     *
464     * @param fillBefore true if the animation should apply its transformation before it starts
465     * @attr ref android.R.styleable#Animation_fillBefore
466     *
467     * @see #setFillEnabled(boolean)
468     */
469    public void setFillBefore(boolean fillBefore) {
470        mFillBefore = fillBefore;
471    }
472
473    /**
474     * If fillAfter is true, the transformation that this animation performed
475     * will persist when it is finished. Defaults to false if not set.
476     * Note that this applies when using an {@link
477     * android.view.animation.AnimationSet AnimationSet} to chain
478     * animations. The transformation is not applied before the AnimationSet
479     * itself starts.
480     *
481     * @param fillAfter true if the animation should apply its transformation after it ends
482     * @attr ref android.R.styleable#Animation_fillAfter
483     *
484     * @see #setFillEnabled(boolean)
485     */
486    public void setFillAfter(boolean fillAfter) {
487        mFillAfter = fillAfter;
488    }
489
490    /**
491     * Set the Z ordering mode to use while running the animation.
492     *
493     * @param zAdjustment The desired mode, one of {@link #ZORDER_NORMAL},
494     * {@link #ZORDER_TOP}, or {@link #ZORDER_BOTTOM}.
495     * @attr ref android.R.styleable#Animation_zAdjustment
496     */
497    public void setZAdjustment(int zAdjustment) {
498        mZAdjustment = zAdjustment;
499    }
500
501    /**
502     * Gets the acceleration curve type for this animation.
503     *
504     * @return the {@link Interpolator} associated to this animation
505     * @attr ref android.R.styleable#Animation_interpolator
506     */
507    public Interpolator getInterpolator() {
508        return mInterpolator;
509    }
510
511    /**
512     * When this animation should start. If the animation has not startet yet,
513     * this method might return {@link #START_ON_FIRST_FRAME}.
514     *
515     * @return the time in milliseconds when the animation should start or
516     *         {@link #START_ON_FIRST_FRAME}
517     */
518    public long getStartTime() {
519        return mStartTime;
520    }
521
522    /**
523     * How long this animation should last
524     *
525     * @return the duration in milliseconds of the animation
526     * @attr ref android.R.styleable#Animation_duration
527     */
528    public long getDuration() {
529        return mDuration;
530    }
531
532    /**
533     * When this animation should start, relative to StartTime
534     *
535     * @return the start offset in milliseconds
536     * @attr ref android.R.styleable#Animation_startOffset
537     */
538    public long getStartOffset() {
539        return mStartOffset;
540    }
541
542    /**
543     * Defines what this animation should do when it reaches the end.
544     *
545     * @return either one of {@link #REVERSE} or {@link #RESTART}
546     * @attr ref android.R.styleable#Animation_repeatMode
547     */
548    public int getRepeatMode() {
549        return mRepeatMode;
550    }
551
552    /**
553     * Defines how many times the animation should repeat. The default value
554     * is 0.
555     *
556     * @return the number of times the animation should repeat, or {@link #INFINITE}
557     * @attr ref android.R.styleable#Animation_repeatCount
558     */
559    public int getRepeatCount() {
560        return mRepeatCount;
561    }
562
563    /**
564     * If fillBefore is true, this animation will apply its transformation
565     * before the start time of the animation.
566     *
567     * @return true if the animation applies its transformation before it starts
568     * @attr ref android.R.styleable#Animation_fillBefore
569     */
570    public boolean getFillBefore() {
571        return mFillBefore;
572    }
573
574    /**
575     * If fillAfter is true, this animation will apply its transformation
576     * after the end time of the animation.
577     *
578     * @return true if the animation applies its transformation after it ends
579     * @attr ref android.R.styleable#Animation_fillAfter
580     */
581    public boolean getFillAfter() {
582        return mFillAfter;
583    }
584
585    /**
586     * Returns the Z ordering mode to use while running the animation as
587     * previously set by {@link #setZAdjustment}.
588     *
589     * @return Returns one of {@link #ZORDER_NORMAL},
590     * {@link #ZORDER_TOP}, or {@link #ZORDER_BOTTOM}.
591     * @attr ref android.R.styleable#Animation_zAdjustment
592     */
593    public int getZAdjustment() {
594        return mZAdjustment;
595    }
596
597    /**
598     * <p>Indicates whether or not this animation will affect the transformation
599     * matrix. For instance, a fade animation will not affect the matrix whereas
600     * a scale animation will.</p>
601     *
602     * @return true if this animation will change the transformation matrix
603     */
604    public boolean willChangeTransformationMatrix() {
605        // assume we will change the matrix
606        return true;
607    }
608
609    /**
610     * <p>Indicates whether or not this animation will affect the bounds of the
611     * animated view. For instance, a fade animation will not affect the bounds
612     * whereas a 200% scale animation will.</p>
613     *
614     * @return true if this animation will change the view's bounds
615     */
616    public boolean willChangeBounds() {
617        // assume we will change the bounds
618        return true;
619    }
620
621    /**
622     * <p>Binds an animation listener to this animation. The animation listener
623     * is notified of animation events such as the end of the animation or the
624     * repetition of the animation.</p>
625     *
626     * @param listener the animation listener to be notified
627     */
628    public void setAnimationListener(AnimationListener listener) {
629        mListener = listener;
630    }
631
632    /**
633     * Gurantees that this animation has an interpolator. Will use
634     * a AccelerateDecelerateInterpolator is nothing else was specified.
635     */
636    protected void ensureInterpolator() {
637        if (mInterpolator == null) {
638            mInterpolator = new AccelerateDecelerateInterpolator();
639        }
640    }
641
642    /**
643     * Compute a hint at how long the entire animation may last, in milliseconds.
644     * Animations can be written to cause themselves to run for a different
645     * duration than what is computed here, but generally this should be
646     * accurate.
647     */
648    public long computeDurationHint() {
649        return (getStartOffset() + getDuration()) * (getRepeatCount() + 1);
650    }
651
652    /**
653     * Gets the transformation to apply at a specified point in time. Implementations of this
654     * method should always replace the specified Transformation or document they are doing
655     * otherwise.
656     *
657     * @param currentTime Where we are in the animation. This is wall clock time.
658     * @param outTransformation A tranformation object that is provided by the
659     *        caller and will be filled in by the animation.
660     * @return True if the animation is still running
661     */
662    public boolean getTransformation(long currentTime, Transformation outTransformation) {
663        if (mStartTime == -1) {
664            mStartTime = currentTime;
665        }
666
667        final long startOffset = getStartOffset();
668        final long duration = mDuration;
669        float normalizedTime;
670        if (duration != 0) {
671            normalizedTime = ((float) (currentTime - (mStartTime + startOffset))) /
672                    (float) duration;
673        } else {
674            // time is a step-change with a zero duration
675            normalizedTime = currentTime < mStartTime ? 0.0f : 1.0f;
676        }
677
678        final boolean expired = normalizedTime >= 1.0f;
679        mMore = !expired;
680
681        if (!mFillEnabled) normalizedTime = Math.max(Math.min(normalizedTime, 1.0f), 0.0f);
682
683        if ((normalizedTime >= 0.0f || mFillBefore) && (normalizedTime <= 1.0f || mFillAfter)) {
684            if (!mStarted) {
685                if (mListener != null) {
686                    mListener.onAnimationStart(this);
687                }
688                mStarted = true;
689            }
690
691            if (mFillEnabled) normalizedTime = Math.max(Math.min(normalizedTime, 1.0f), 0.0f);
692
693            if (mCycleFlip) {
694                normalizedTime = 1.0f - normalizedTime;
695            }
696
697            final float interpolatedTime = mInterpolator.getInterpolation(normalizedTime);
698            applyTransformation(interpolatedTime, outTransformation);
699        }
700
701        if (expired) {
702            if (mRepeatCount == mRepeated) {
703                if (!mEnded) {
704                    if (mListener != null) {
705                        mListener.onAnimationEnd(this);
706                    }
707                    mEnded = true;
708                }
709            } else {
710                if (mRepeatCount > 0) {
711                    mRepeated++;
712                }
713
714                if (mRepeatMode == REVERSE) {
715                    mCycleFlip = !mCycleFlip;
716                }
717
718                mStartTime = -1;
719                mMore = true;
720
721                if (mListener != null) {
722                    mListener.onAnimationRepeat(this);
723                }
724            }
725        }
726
727        if (!mMore && mOneMoreTime) {
728            mOneMoreTime = false;
729            return true;
730        }
731
732        return mMore;
733    }
734
735    /**
736     * <p>Indicates whether this animation has started or not.</p>
737     *
738     * @return true if the animation has started, false otherwise
739     */
740    public boolean hasStarted() {
741        return mStarted;
742    }
743
744    /**
745     * <p>Indicates whether this animation has ended or not.</p>
746     *
747     * @return true if the animation has ended, false otherwise
748     */
749    public boolean hasEnded() {
750        return mEnded;
751    }
752
753    /**
754     * Helper for getTransformation. Subclasses should implement this to apply
755     * their transforms given an interpolation value.  Implementations of this
756     * method should always replace the specified Transformation or document
757     * they are doing otherwise.
758     *
759     * @param interpolatedTime The value of the normalized time (0.0 to 1.0)
760     *        after it has been run through the interpolation function.
761     * @param t The Transofrmation object to fill in with the current
762     *        transforms.
763     */
764    protected void applyTransformation(float interpolatedTime, Transformation t) {
765    }
766
767    /**
768     * Convert the information in the description of a size to an actual
769     * dimension
770     *
771     * @param type One of Animation.ABSOLUTE, Animation.RELATIVE_TO_SELF, or
772     *             Animation.RELATIVE_TO_PARENT.
773     * @param value The dimension associated with the type parameter
774     * @param size The size of the object being animated
775     * @param parentSize The size of the parent of the object being animated
776     * @return The dimension to use for the animation
777     */
778    protected float resolveSize(int type, float value, int size, int parentSize) {
779        switch (type) {
780            case ABSOLUTE:
781                return value;
782            case RELATIVE_TO_SELF:
783                return size * value;
784            case RELATIVE_TO_PARENT:
785                return parentSize * value;
786            default:
787                return value;
788        }
789    }
790
791    /**
792     * @param left
793     * @param top
794     * @param right
795     * @param bottom
796     * @param invalidate
797     * @param transformation
798     *
799     * @hide
800     */
801    public void getInvalidateRegion(int left, int top, int right, int bottom,
802            RectF invalidate, Transformation transformation) {
803
804        final RectF tempRegion = mRegion;
805        final RectF previousRegion = mPreviousRegion;
806
807        invalidate.set(left, top, right, bottom);
808        transformation.getMatrix().mapRect(invalidate);
809        tempRegion.set(invalidate);
810        invalidate.union(previousRegion);
811
812        previousRegion.set(tempRegion);
813
814        final Transformation tempTransformation = mTransformation;
815        final Transformation previousTransformation = mPreviousTransformation;
816
817        tempTransformation.set(transformation);
818        transformation.set(previousTransformation);
819        previousTransformation.set(tempTransformation);
820    }
821
822    /**
823     * @param left
824     * @param top
825     * @param right
826     * @param bottom
827     *
828     * @hide
829     */
830    public void initializeInvalidateRegion(int left, int top, int right, int bottom) {
831        final RectF region = mPreviousRegion;
832        region.set(left, top, right, bottom);
833        if (mFillBefore) {
834            final Transformation previousTransformation = mPreviousTransformation;
835            applyTransformation(0.0f, previousTransformation);
836        }
837    }
838
839    /**
840     * Utility class to parse a string description of a size.
841     */
842    protected static class Description {
843        /**
844         * One of Animation.ABSOLUTE, Animation.RELATIVE_TO_SELF, or
845         * Animation.RELATIVE_TO_PARENT.
846         */
847        public int type;
848
849        /**
850         * The absolute or relative dimension for this Description.
851         */
852        public float value;
853
854        /**
855         * Size descriptions can appear inthree forms:
856         * <ol>
857         * <li>An absolute size. This is represented by a number.</li>
858         * <li>A size relative to the size of the object being animated. This
859         * is represented by a number followed by "%".</li> *
860         * <li>A size relative to the size of the parent of object being
861         * animated. This is represented by a number followed by "%p".</li>
862         * </ol>
863         * @param value The typed value to parse
864         * @return The parsed version of the description
865         */
866        static Description parseValue(TypedValue value) {
867            Description d = new Description();
868            if (value == null) {
869                d.type = ABSOLUTE;
870                d.value = 0;
871            } else {
872                if (value.type == TypedValue.TYPE_FRACTION) {
873                    d.type = (value.data & TypedValue.COMPLEX_UNIT_MASK) ==
874                            TypedValue.COMPLEX_UNIT_FRACTION_PARENT ?
875                                    RELATIVE_TO_PARENT : RELATIVE_TO_SELF;
876                    d.value = TypedValue.complexToFloat(value.data);
877                    return d;
878                } else if (value.type == TypedValue.TYPE_FLOAT) {
879                    d.type = ABSOLUTE;
880                    d.value = value.getFloat();
881                    return d;
882                } else if (value.type >= TypedValue.TYPE_FIRST_INT &&
883                        value.type <= TypedValue.TYPE_LAST_INT) {
884                    d.type = ABSOLUTE;
885                    d.value = value.data;
886                    return d;
887                }
888            }
889
890            d.type = ABSOLUTE;
891            d.value = 0.0f;
892
893            return d;
894        }
895    }
896
897    /**
898     * <p>An animation listener receives notifications from an animation.
899     * Notifications indicate animation related events, such as the end or the
900     * repetition of the animation.</p>
901     */
902    public static interface AnimationListener {
903        /**
904         * <p>Notifies the start of the animation.</p>
905         *
906         * @param animation The started animation.
907         */
908        void onAnimationStart(Animation animation);
909
910        /**
911         * <p>Notifies the end of the animation. This callback is not invoked
912         * for animations with repeat count set to INFINITE.</p>
913         *
914         * @param animation The animation which reached its end.
915         */
916        void onAnimationEnd(Animation animation);
917
918        /**
919         * <p>Notifies the repetition of the animation.</p>
920         *
921         * @param animation The animation which was repeated.
922         */
923        void onAnimationRepeat(Animation animation);
924    }
925}
926