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