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