1 /*
2  * Copyright (C) 2010 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.animation;
18 
19 import android.app.ActivityThread;
20 import android.app.Application;
21 import android.os.Build;
22 import android.os.Looper;
23 import android.util.AndroidRuntimeException;
24 import android.util.ArrayMap;
25 import android.util.Log;
26 import android.view.animation.Animation;
27 
28 import java.util.ArrayList;
29 import java.util.Collection;
30 import java.util.Comparator;
31 import java.util.HashMap;
32 import java.util.List;
33 
34 /**
35  * This class plays a set of {@link Animator} objects in the specified order. Animations
36  * can be set up to play together, in sequence, or after a specified delay.
37  *
38  * <p>There are two different approaches to adding animations to a <code>AnimatorSet</code>:
39  * either the {@link AnimatorSet#playTogether(Animator[]) playTogether()} or
40  * {@link AnimatorSet#playSequentially(Animator[]) playSequentially()} methods can be called to add
41  * a set of animations all at once, or the {@link AnimatorSet#play(Animator)} can be
42  * used in conjunction with methods in the {@link AnimatorSet.Builder Builder}
43  * class to add animations
44  * one by one.</p>
45  *
46  * <p>It is possible to set up a <code>AnimatorSet</code> with circular dependencies between
47  * its animations. For example, an animation a1 could be set up to start before animation a2, a2
48  * before a3, and a3 before a1. The results of this configuration are undefined, but will typically
49  * result in none of the affected animations being played. Because of this (and because
50  * circular dependencies do not make logical sense anyway), circular dependencies
51  * should be avoided, and the dependency flow of animations should only be in one direction.
52  *
53  * <div class="special reference">
54  * <h3>Developer Guides</h3>
55  * <p>For more information about animating with {@code AnimatorSet}, read the
56  * <a href="{@docRoot}guide/topics/graphics/prop-animation.html#choreography">Property
57  * Animation</a> developer guide.</p>
58  * </div>
59  */
60 public final class AnimatorSet extends Animator implements AnimationHandler.AnimationFrameCallback {
61 
62     private static final String TAG = "AnimatorSet";
63     /**
64      * Internal variables
65      * NOTE: This object implements the clone() method, making a deep copy of any referenced
66      * objects. As other non-trivial fields are added to this class, make sure to add logic
67      * to clone() to make deep copies of them.
68      */
69 
70     /**
71      * Tracks animations currently being played, so that we know what to
72      * cancel or end when cancel() or end() is called on this AnimatorSet
73      */
74     private ArrayList<Node> mPlayingSet = new ArrayList<Node>();
75 
76     /**
77      * Contains all nodes, mapped to their respective Animators. When new
78      * dependency information is added for an Animator, we want to add it
79      * to a single node representing that Animator, not create a new Node
80      * if one already exists.
81      */
82     private ArrayMap<Animator, Node> mNodeMap = new ArrayMap<Animator, Node>();
83 
84     /**
85      * Contains the start and end events of all the nodes. All these events are sorted in this list.
86      */
87     private ArrayList<AnimationEvent> mEvents = new ArrayList<>();
88 
89     /**
90      * Set of all nodes created for this AnimatorSet. This list is used upon
91      * starting the set, and the nodes are placed in sorted order into the
92      * sortedNodes collection.
93      */
94     private ArrayList<Node> mNodes = new ArrayList<Node>();
95 
96     /**
97      * Tracks whether any change has been made to the AnimatorSet, which is then used to
98      * determine whether the dependency graph should be re-constructed.
99      */
100     private boolean mDependencyDirty = false;
101 
102     /**
103      * Indicates whether an AnimatorSet has been start()'d, whether or
104      * not there is a nonzero startDelay.
105      */
106     private boolean mStarted = false;
107 
108     // The amount of time in ms to delay starting the animation after start() is called
109     private long mStartDelay = 0;
110 
111     // Animator used for a nonzero startDelay
112     private ValueAnimator mDelayAnim = ValueAnimator.ofFloat(0f, 1f).setDuration(0);
113 
114     // Root of the dependency tree of all the animators in the set. In this tree, parent-child
115     // relationship captures the order of animation (i.e. parent and child will play sequentially),
116     // and sibling relationship indicates "with" relationship, as sibling animators start at the
117     // same time.
118     private Node mRootNode = new Node(mDelayAnim);
119 
120     // How long the child animations should last in ms. The default value is negative, which
121     // simply means that there is no duration set on the AnimatorSet. When a real duration is
122     // set, it is passed along to the child animations.
123     private long mDuration = -1;
124 
125     // Records the interpolator for the set. Null value indicates that no interpolator
126     // was set on this AnimatorSet, so it should not be passed down to the children.
127     private TimeInterpolator mInterpolator = null;
128 
129     // The total duration of finishing all the Animators in the set.
130     private long mTotalDuration = 0;
131 
132     // In pre-N releases, calling end() before start() on an animator set is no-op. But that is not
133     // consistent with the behavior for other animator types. In order to keep the behavior
134     // consistent within Animation framework, when end() is called without start(), we will start
135     // the animator set and immediately end it for N and forward.
136     private final boolean mShouldIgnoreEndWithoutStart;
137 
138     // In pre-O releases, calling start() doesn't reset all the animators values to start values.
139     // As a result, the start of the animation is inconsistent with what setCurrentPlayTime(0) would
140     // look like on O. Also it is inconsistent with what reverse() does on O, as reverse would
141     // advance all the animations to the right beginning values for before starting to reverse.
142     // From O and forward, we will add an additional step of resetting the animation values (unless
143     // the animation was previously seeked and therefore doesn't start from the beginning).
144     private final boolean mShouldResetValuesAtStart;
145 
146     // In pre-O releases, end() may never explicitly called on a child animator. As a result, end()
147     // may not even be properly implemented in a lot of cases. After a few apps crashing on this,
148     // it became necessary to use an sdk target guard for calling end().
149     private final boolean mEndCanBeCalled;
150 
151     // The time, in milliseconds, when last frame of the animation came in. -1 when the animation is
152     // not running.
153     private long mLastFrameTime = -1;
154 
155     // The time, in milliseconds, when the first frame of the animation came in. This is the
156     // frame before we start counting down the start delay, if any.
157     // -1 when the animation is not running.
158     private long mFirstFrame = -1;
159 
160     // The time, in milliseconds, when the first frame of the animation came in.
161     // -1 when the animation is not running.
162     private int mLastEventId = -1;
163 
164     // Indicates whether the animation is reversing.
165     private boolean mReversing = false;
166 
167     // Indicates whether the animation should register frame callbacks. If false, the animation will
168     // passively wait for an AnimatorSet to pulse it.
169     private boolean mSelfPulse = true;
170 
171     // SeekState stores the last seeked play time as well as seek direction.
172     private SeekState mSeekState = new SeekState();
173 
174     // Indicates where children animators are all initialized with their start values captured.
175     private boolean mChildrenInitialized = false;
176 
177     /**
178      * Set on the next frame after pause() is called, used to calculate a new startTime
179      * or delayStartTime which allows the animator set to continue from the point at which
180      * it was paused. If negative, has not yet been set.
181      */
182     private long mPauseTime = -1;
183 
184     // This is to work around a bug in b/34736819. This needs to be removed once app team
185     // fixes their side.
186     private AnimatorListenerAdapter mDummyListener = new AnimatorListenerAdapter() {
187         @Override
188         public void onAnimationEnd(Animator animation) {
189             if (mNodeMap.get(animation) == null) {
190                 throw new AndroidRuntimeException("Error: animation ended is not in the node map");
191             }
192             mNodeMap.get(animation).mEnded = true;
193 
194         }
195     };
196 
AnimatorSet()197     public AnimatorSet() {
198         super();
199         mNodeMap.put(mDelayAnim, mRootNode);
200         mNodes.add(mRootNode);
201         boolean isPreO;
202         // Set the flag to ignore calling end() without start() for pre-N releases
203         Application app = ActivityThread.currentApplication();
204         if (app == null || app.getApplicationInfo() == null) {
205             mShouldIgnoreEndWithoutStart = true;
206             isPreO = true;
207         } else {
208             if (app.getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.N) {
209                 mShouldIgnoreEndWithoutStart = true;
210             } else {
211                 mShouldIgnoreEndWithoutStart = false;
212             }
213 
214             isPreO = app.getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.O;
215         }
216         mShouldResetValuesAtStart = !isPreO;
217         mEndCanBeCalled = !isPreO;
218     }
219 
220     /**
221      * Sets up this AnimatorSet to play all of the supplied animations at the same time.
222      * This is equivalent to calling {@link #play(Animator)} with the first animator in the
223      * set and then {@link Builder#with(Animator)} with each of the other animators. Note that
224      * an Animator with a {@link Animator#setStartDelay(long) startDelay} will not actually
225      * start until that delay elapses, which means that if the first animator in the list
226      * supplied to this constructor has a startDelay, none of the other animators will start
227      * until that first animator's startDelay has elapsed.
228      *
229      * @param items The animations that will be started simultaneously.
230      */
231     public void playTogether(Animator... items) {
232         if (items != null) {
233             Builder builder = play(items[0]);
234             for (int i = 1; i < items.length; ++i) {
235                 builder.with(items[i]);
236             }
237         }
238     }
239 
240     /**
241      * Sets up this AnimatorSet to play all of the supplied animations at the same time.
242      *
243      * @param items The animations that will be started simultaneously.
244      */
245     public void playTogether(Collection<Animator> items) {
246         if (items != null && items.size() > 0) {
247             Builder builder = null;
248             for (Animator anim : items) {
249                 if (builder == null) {
250                     builder = play(anim);
251                 } else {
252                     builder.with(anim);
253                 }
254             }
255         }
256     }
257 
258     /**
259      * Sets up this AnimatorSet to play each of the supplied animations when the
260      * previous animation ends.
261      *
262      * @param items The animations that will be started one after another.
263      */
264     public void playSequentially(Animator... items) {
265         if (items != null) {
266             if (items.length == 1) {
267                 play(items[0]);
268             } else {
269                 for (int i = 0; i < items.length - 1; ++i) {
270                     play(items[i]).before(items[i + 1]);
271                 }
272             }
273         }
274     }
275 
276     /**
277      * Sets up this AnimatorSet to play each of the supplied animations when the
278      * previous animation ends.
279      *
280      * @param items The animations that will be started one after another.
281      */
282     public void playSequentially(List<Animator> items) {
283         if (items != null && items.size() > 0) {
284             if (items.size() == 1) {
285                 play(items.get(0));
286             } else {
287                 for (int i = 0; i < items.size() - 1; ++i) {
288                     play(items.get(i)).before(items.get(i + 1));
289                 }
290             }
291         }
292     }
293 
294     /**
295      * Returns the current list of child Animator objects controlled by this
296      * AnimatorSet. This is a copy of the internal list; modifications to the returned list
297      * will not affect the AnimatorSet, although changes to the underlying Animator objects
298      * will affect those objects being managed by the AnimatorSet.
299      *
300      * @return ArrayList<Animator> The list of child animations of this AnimatorSet.
301      */
302     public ArrayList<Animator> getChildAnimations() {
303         ArrayList<Animator> childList = new ArrayList<Animator>();
304         int size = mNodes.size();
305         for (int i = 0; i < size; i++) {
306             Node node = mNodes.get(i);
307             if (node != mRootNode) {
308                 childList.add(node.mAnimation);
309             }
310         }
311         return childList;
312     }
313 
314     /**
315      * Sets the target object for all current {@link #getChildAnimations() child animations}
316      * of this AnimatorSet that take targets ({@link ObjectAnimator} and
317      * AnimatorSet).
318      *
319      * @param target The object being animated
320      */
321     @Override
322     public void setTarget(Object target) {
323         int size = mNodes.size();
324         for (int i = 0; i < size; i++) {
325             Node node = mNodes.get(i);
326             Animator animation = node.mAnimation;
327             if (animation instanceof AnimatorSet) {
328                 ((AnimatorSet)animation).setTarget(target);
329             } else if (animation instanceof ObjectAnimator) {
330                 ((ObjectAnimator)animation).setTarget(target);
331             }
332         }
333     }
334 
335     /**
336      * @hide
337      */
338     @Override
339     public int getChangingConfigurations() {
340         int conf = super.getChangingConfigurations();
341         final int nodeCount = mNodes.size();
342         for (int i = 0; i < nodeCount; i ++) {
343             conf |= mNodes.get(i).mAnimation.getChangingConfigurations();
344         }
345         return conf;
346     }
347 
348     /**
349      * Sets the TimeInterpolator for all current {@link #getChildAnimations() child animations}
350      * of this AnimatorSet. The default value is null, which means that no interpolator
351      * is set on this AnimatorSet. Setting the interpolator to any non-null value
352      * will cause that interpolator to be set on the child animations
353      * when the set is started.
354      *
355      * @param interpolator the interpolator to be used by each child animation of this AnimatorSet
356      */
357     @Override
358     public void setInterpolator(TimeInterpolator interpolator) {
359         mInterpolator = interpolator;
360     }
361 
362     @Override
363     public TimeInterpolator getInterpolator() {
364         return mInterpolator;
365     }
366 
367     /**
368      * This method creates a <code>Builder</code> object, which is used to
369      * set up playing constraints. This initial <code>play()</code> method
370      * tells the <code>Builder</code> the animation that is the dependency for
371      * the succeeding commands to the <code>Builder</code>. For example,
372      * calling <code>play(a1).with(a2)</code> sets up the AnimatorSet to play
373      * <code>a1</code> and <code>a2</code> at the same time,
374      * <code>play(a1).before(a2)</code> sets up the AnimatorSet to play
375      * <code>a1</code> first, followed by <code>a2</code>, and
376      * <code>play(a1).after(a2)</code> sets up the AnimatorSet to play
377      * <code>a2</code> first, followed by <code>a1</code>.
378      *
379      * <p>Note that <code>play()</code> is the only way to tell the
380      * <code>Builder</code> the animation upon which the dependency is created,
381      * so successive calls to the various functions in <code>Builder</code>
382      * will all refer to the initial parameter supplied in <code>play()</code>
383      * as the dependency of the other animations. For example, calling
384      * <code>play(a1).before(a2).before(a3)</code> will play both <code>a2</code>
385      * and <code>a3</code> when a1 ends; it does not set up a dependency between
386      * <code>a2</code> and <code>a3</code>.</p>
387      *
388      * @param anim The animation that is the dependency used in later calls to the
389      * methods in the returned <code>Builder</code> object. A null parameter will result
390      * in a null <code>Builder</code> return value.
391      * @return Builder The object that constructs the AnimatorSet based on the dependencies
392      * outlined in the calls to <code>play</code> and the other methods in the
393      * <code>Builder</code object.
394      */
395     public Builder play(Animator anim) {
396         if (anim != null) {
397             return new Builder(anim);
398         }
399         return null;
400     }
401 
402     /**
403      * {@inheritDoc}
404      *
405      * <p>Note that canceling a <code>AnimatorSet</code> also cancels all of the animations that it
406      * is responsible for.</p>
407      */
408     @SuppressWarnings("unchecked")
409     @Override
410     public void cancel() {
411         if (Looper.myLooper() == null) {
412             throw new AndroidRuntimeException("Animators may only be run on Looper threads");
413         }
414         if (isStarted()) {
415             ArrayList<AnimatorListener> tmpListeners = null;
416             if (mListeners != null) {
417                 tmpListeners = (ArrayList<AnimatorListener>) mListeners.clone();
418                 int size = tmpListeners.size();
419                 for (int i = 0; i < size; i++) {
420                     tmpListeners.get(i).onAnimationCancel(this);
421                 }
422             }
423             ArrayList<Node> playingSet = new ArrayList<>(mPlayingSet);
424             int setSize = playingSet.size();
425             for (int i = 0; i < setSize; i++) {
426                 playingSet.get(i).mAnimation.cancel();
427             }
428             mPlayingSet.clear();
429             endAnimation();
430         }
431     }
432 
433     // Force all the animations to end when the duration scale is 0.
434     private void forceToEnd() {
435         if (mEndCanBeCalled) {
436             end();
437             return;
438         }
439 
440         // Note: we don't want to combine this case with the end() method below because in
441         // the case of developer calling end(), we still need to make sure end() is explicitly
442         // called on the child animators to maintain the old behavior.
443         if (mReversing) {
444             handleAnimationEvents(mLastEventId, 0, getTotalDuration());
445         } else {
446             long zeroScalePlayTime = getTotalDuration();
447             if (zeroScalePlayTime == DURATION_INFINITE) {
448                 // Use a large number for the play time.
449                 zeroScalePlayTime = Integer.MAX_VALUE;
450             }
451             handleAnimationEvents(mLastEventId, mEvents.size() - 1, zeroScalePlayTime);
452         }
453         mPlayingSet.clear();
454         endAnimation();
455     }
456 
457     /**
458      * {@inheritDoc}
459      *
460      * <p>Note that ending a <code>AnimatorSet</code> also ends all of the animations that it is
461      * responsible for.</p>
462      */
463     @Override
464     public void end() {
465         if (Looper.myLooper() == null) {
466             throw new AndroidRuntimeException("Animators may only be run on Looper threads");
467         }
468         if (mShouldIgnoreEndWithoutStart && !isStarted()) {
469             return;
470         }
471         if (isStarted()) {
472             // Iterate the animations that haven't finished or haven't started, and end them.
473             if (mReversing) {
474                 // Between start() and first frame, mLastEventId would be unset (i.e. -1)
475                 mLastEventId = mLastEventId == -1 ? mEvents.size() : mLastEventId;
476                 while (mLastEventId > 0) {
477                     mLastEventId = mLastEventId - 1;
478                     AnimationEvent event = mEvents.get(mLastEventId);
479                     Animator anim = event.mNode.mAnimation;
480                     if (mNodeMap.get(anim).mEnded) {
481                         continue;
482                     }
483                     if (event.mEvent == AnimationEvent.ANIMATION_END) {
484                         anim.reverse();
485                     } else if (event.mEvent == AnimationEvent.ANIMATION_DELAY_ENDED
486                             && anim.isStarted()) {
487                         // Make sure anim hasn't finished before calling end() so that we don't end
488                         // already ended animations, which will cause start and end callbacks to be
489                         // triggered again.
490                         anim.end();
491                     }
492                 }
493             } else {
494                 while (mLastEventId < mEvents.size() - 1) {
495                     // Avoid potential reentrant loop caused by child animators manipulating
496                     // AnimatorSet's lifecycle (i.e. not a recommended approach).
497                     mLastEventId = mLastEventId + 1;
498                     AnimationEvent event = mEvents.get(mLastEventId);
499                     Animator anim = event.mNode.mAnimation;
500                     if (mNodeMap.get(anim).mEnded) {
501                         continue;
502                     }
503                     if (event.mEvent == AnimationEvent.ANIMATION_START) {
504                         anim.start();
505                     } else if (event.mEvent == AnimationEvent.ANIMATION_END && anim.isStarted()) {
506                         // Make sure anim hasn't finished before calling end() so that we don't end
507                         // already ended animations, which will cause start and end callbacks to be
508                         // triggered again.
509                         anim.end();
510                     }
511                 }
512             }
513             mPlayingSet.clear();
514         }
515         endAnimation();
516     }
517 
518     /**
519      * Returns true if any of the child animations of this AnimatorSet have been started and have
520      * not yet ended. Child animations will not be started until the AnimatorSet has gone past
521      * its initial delay set through {@link #setStartDelay(long)}.
522      *
523      * @return Whether this AnimatorSet has gone past the initial delay, and at least one child
524      *         animation has been started and not yet ended.
525      */
526     @Override
527     public boolean isRunning() {
528         if (mStartDelay == 0) {
529             return mStarted;
530         }
531         return mLastFrameTime > 0;
532     }
533 
534     @Override
535     public boolean isStarted() {
536         return mStarted;
537     }
538 
539     /**
540      * The amount of time, in milliseconds, to delay starting the animation after
541      * {@link #start()} is called.
542      *
543      * @return the number of milliseconds to delay running the animation
544      */
545     @Override
546     public long getStartDelay() {
547         return mStartDelay;
548     }
549 
550     /**
551      * The amount of time, in milliseconds, to delay starting the animation after
552      * {@link #start()} is called. Note that the start delay should always be non-negative. Any
553      * negative start delay will be clamped to 0 on N and above.
554      *
555      * @param startDelay The amount of the delay, in milliseconds
556      */
557     @Override
558     public void setStartDelay(long startDelay) {
559         // Clamp start delay to non-negative range.
560         if (startDelay < 0) {
561             Log.w(TAG, "Start delay should always be non-negative");
562             startDelay = 0;
563         }
564         long delta = startDelay - mStartDelay;
565         if (delta == 0) {
566             return;
567         }
568         mStartDelay = startDelay;
569         if (!mDependencyDirty) {
570             // Dependency graph already constructed, update all the nodes' start/end time
571             int size = mNodes.size();
572             for (int i = 0; i < size; i++) {
573                 Node node = mNodes.get(i);
574                 if (node == mRootNode) {
575                     node.mEndTime = mStartDelay;
576                 } else {
577                     node.mStartTime = node.mStartTime == DURATION_INFINITE ?
578                             DURATION_INFINITE : node.mStartTime + delta;
579                     node.mEndTime = node.mEndTime == DURATION_INFINITE ?
580                             DURATION_INFINITE : node.mEndTime + delta;
581                 }
582             }
583             // Update total duration, if necessary.
584             if (mTotalDuration != DURATION_INFINITE) {
585                 mTotalDuration += delta;
586             }
587         }
588     }
589 
590     /**
591      * Gets the length of each of the child animations of this AnimatorSet. This value may
592      * be less than 0, which indicates that no duration has been set on this AnimatorSet
593      * and each of the child animations will use their own duration.
594      *
595      * @return The length of the animation, in milliseconds, of each of the child
596      * animations of this AnimatorSet.
597      */
598     @Override
599     public long getDuration() {
600         return mDuration;
601     }
602 
603     /**
604      * Sets the length of each of the current child animations of this AnimatorSet. By default,
605      * each child animation will use its own duration. If the duration is set on the AnimatorSet,
606      * then each child animation inherits this duration.
607      *
608      * @param duration The length of the animation, in milliseconds, of each of the child
609      * animations of this AnimatorSet.
610      */
611     @Override
612     public AnimatorSet setDuration(long duration) {
613         if (duration < 0) {
614             throw new IllegalArgumentException("duration must be a value of zero or greater");
615         }
616         mDependencyDirty = true;
617         // Just record the value for now - it will be used later when the AnimatorSet starts
618         mDuration = duration;
619         return this;
620     }
621 
622     @Override
623     public void setupStartValues() {
624         int size = mNodes.size();
625         for (int i = 0; i < size; i++) {
626             Node node = mNodes.get(i);
627             if (node != mRootNode) {
628                 node.mAnimation.setupStartValues();
629             }
630         }
631     }
632 
633     @Override
634     public void setupEndValues() {
635         int size = mNodes.size();
636         for (int i = 0; i < size; i++) {
637             Node node = mNodes.get(i);
638             if (node != mRootNode) {
639                 node.mAnimation.setupEndValues();
640             }
641         }
642     }
643 
644     @Override
645     public void pause() {
646         if (Looper.myLooper() == null) {
647             throw new AndroidRuntimeException("Animators may only be run on Looper threads");
648         }
649         boolean previouslyPaused = mPaused;
650         super.pause();
651         if (!previouslyPaused && mPaused) {
652             mPauseTime = -1;
653         }
654     }
655 
656     @Override
657     public void resume() {
658         if (Looper.myLooper() == null) {
659             throw new AndroidRuntimeException("Animators may only be run on Looper threads");
660         }
661         boolean previouslyPaused = mPaused;
662         super.resume();
663         if (previouslyPaused && !mPaused) {
664             if (mPauseTime >= 0) {
665                 addAnimationCallback(0);
666             }
667         }
668     }
669 
670     /**
671      * {@inheritDoc}
672      *
673      * <p>Starting this <code>AnimatorSet</code> will, in turn, start the animations for which
674      * it is responsible. The details of when exactly those animations are started depends on
675      * the dependency relationships that have been set up between the animations.
676      *
677      * <b>Note:</b> Manipulating AnimatorSet's lifecycle in the child animators' listener callbacks
678      * will lead to undefined behaviors. Also, AnimatorSet will ignore any seeking in the child
679      * animators once {@link #start()} is called.
680      */
681     @SuppressWarnings("unchecked")
682     @Override
683     public void start() {
684         start(false, true);
685     }
686 
687     @Override
688     void startWithoutPulsing(boolean inReverse) {
689         start(inReverse, false);
690     }
691 
692     private void initAnimation() {
693         if (mInterpolator != null) {
694             for (int i = 0; i < mNodes.size(); i++) {
695                 Node node = mNodes.get(i);
696                 node.mAnimation.setInterpolator(mInterpolator);
697             }
698         }
699         updateAnimatorsDuration();
700         createDependencyGraph();
701     }
702 
703     private void start(boolean inReverse, boolean selfPulse) {
704         if (Looper.myLooper() == null) {
705             throw new AndroidRuntimeException("Animators may only be run on Looper threads");
706         }
707         mStarted = true;
708         mSelfPulse = selfPulse;
709         mPaused = false;
710         mPauseTime = -1;
711 
712         int size = mNodes.size();
713         for (int i = 0; i < size; i++) {
714             Node node = mNodes.get(i);
715             node.mEnded = false;
716             node.mAnimation.setAllowRunningAsynchronously(false);
717         }
718 
719         initAnimation();
720         if (inReverse && !canReverse()) {
721             throw new UnsupportedOperationException("Cannot reverse infinite AnimatorSet");
722         }
723 
724         mReversing = inReverse;
725 
726         // Now that all dependencies are set up, start the animations that should be started.
727         boolean isEmptySet = isEmptySet(this);
728         if (!isEmptySet) {
729             startAnimation();
730         }
731 
732         if (mListeners != null) {
733             ArrayList<AnimatorListener> tmpListeners =
734                     (ArrayList<AnimatorListener>) mListeners.clone();
735             int numListeners = tmpListeners.size();
736             for (int i = 0; i < numListeners; ++i) {
737                 tmpListeners.get(i).onAnimationStart(this, inReverse);
738             }
739         }
740         if (isEmptySet) {
741             // In the case of empty AnimatorSet, or 0 duration scale, we will trigger the
742             // onAnimationEnd() right away.
743             end();
744         }
745     }
746 
747     // Returns true if set is empty or contains nothing but animator sets with no start delay.
748     private static boolean isEmptySet(AnimatorSet set) {
749         if (set.getStartDelay() > 0) {
750             return false;
751         }
752         for (int i = 0; i < set.getChildAnimations().size(); i++) {
753             Animator anim = set.getChildAnimations().get(i);
754             if (!(anim instanceof AnimatorSet)) {
755                 // Contains non-AnimatorSet, not empty.
756                 return false;
757             } else {
758                 if (!isEmptySet((AnimatorSet) anim)) {
759                     return false;
760                 }
761             }
762         }
763         return true;
764     }
765 
766     private void updateAnimatorsDuration() {
767         if (mDuration >= 0) {
768             // If the duration was set on this AnimatorSet, pass it along to all child animations
769             int size = mNodes.size();
770             for (int i = 0; i < size; i++) {
771                 Node node = mNodes.get(i);
772                 // TODO: don't set the duration of the timing-only nodes created by AnimatorSet to
773                 // insert "play-after" delays
774                 node.mAnimation.setDuration(mDuration);
775             }
776         }
777         mDelayAnim.setDuration(mStartDelay);
778     }
779 
780     @Override
781     void skipToEndValue(boolean inReverse) {
782         if (!isInitialized()) {
783             throw new UnsupportedOperationException("Children must be initialized.");
784         }
785 
786         // This makes sure the animation events are sorted an up to date.
787         initAnimation();
788 
789         // Calling skip to the end in the sequence that they would be called in a forward/reverse
790         // run, such that the sequential animations modifying the same property would have
791         // the right value in the end.
792         if (inReverse) {
793             for (int i = mEvents.size() - 1; i >= 0; i--) {
794                 if (mEvents.get(i).mEvent == AnimationEvent.ANIMATION_DELAY_ENDED) {
795                     mEvents.get(i).mNode.mAnimation.skipToEndValue(true);
796                 }
797             }
798         } else {
799             for (int i = 0; i < mEvents.size(); i++) {
800                 if (mEvents.get(i).mEvent == AnimationEvent.ANIMATION_END) {
801                     mEvents.get(i).mNode.mAnimation.skipToEndValue(false);
802                 }
803             }
804         }
805     }
806 
807     /**
808      * Internal only.
809      *
810      * This method sets the animation values based on the play time. It also fast forward or
811      * backward all the child animations progress accordingly.
812      *
813      * This method is also responsible for calling
814      * {@link android.view.animation.Animation.AnimationListener#onAnimationRepeat(Animation)},
815      * as needed, based on the last play time and current play time.
816      */
817     @Override
818     void animateBasedOnPlayTime(long currentPlayTime, long lastPlayTime, boolean inReverse) {
819         if (currentPlayTime < 0 || lastPlayTime < 0) {
820             throw new UnsupportedOperationException("Error: Play time should never be negative.");
821         }
822         // TODO: take into account repeat counts and repeat callback when repeat is implemented.
823         // Clamp currentPlayTime and lastPlayTime
824 
825         // TODO: Make this more efficient
826 
827         // Convert the play times to the forward direction.
828         if (inReverse) {
829             if (getTotalDuration() == DURATION_INFINITE) {
830                 throw new UnsupportedOperationException("Cannot reverse AnimatorSet with infinite"
831                         + " duration");
832             }
833             long duration = getTotalDuration() - mStartDelay;
834             currentPlayTime = Math.min(currentPlayTime, duration);
835             currentPlayTime = duration - currentPlayTime;
836             lastPlayTime = duration - lastPlayTime;
837             inReverse = false;
838         }
839         // Skip all values to start, and iterate mEvents to get animations to the right fraction.
840         skipToStartValue(false);
841 
842         ArrayList<Node> unfinishedNodes = new ArrayList<>();
843         // Assumes forward playing from here on.
844         for (int i = 0; i < mEvents.size(); i++) {
845             AnimationEvent event = mEvents.get(i);
846             if (event.getTime() > currentPlayTime || event.getTime() == DURATION_INFINITE) {
847                 break;
848             }
849 
850             // This animation started prior to the current play time, and won't finish before the
851             // play time, add to the unfinished list.
852             if (event.mEvent == AnimationEvent.ANIMATION_DELAY_ENDED) {
853                 if (event.mNode.mEndTime == DURATION_INFINITE
854                         || event.mNode.mEndTime > currentPlayTime) {
855                     unfinishedNodes.add(event.mNode);
856                 }
857             }
858             // For animations that do finish before the play time, end them in the sequence that
859             // they would in a normal run.
860             if (event.mEvent == AnimationEvent.ANIMATION_END) {
861                 // Skip to the end of the animation.
862                 event.mNode.mAnimation.skipToEndValue(false);
863             }
864         }
865 
866         // Seek unfinished animation to the right time.
867         for (int i = 0; i < unfinishedNodes.size(); i++) {
868             Node node = unfinishedNodes.get(i);
869             long playTime = getPlayTimeForNode(currentPlayTime, node, inReverse);
870             if (!inReverse) {
871                 playTime -= node.mAnimation.getStartDelay();
872             }
873             node.mAnimation.animateBasedOnPlayTime(playTime, lastPlayTime, inReverse);
874         }
875     }
876 
877     @Override
878     boolean isInitialized() {
879         if (mChildrenInitialized) {
880             return true;
881         }
882 
883         boolean allInitialized = true;
884         for (int i = 0; i < mNodes.size(); i++) {
885             if (!mNodes.get(i).mAnimation.isInitialized()) {
886                 allInitialized = false;
887                 break;
888             }
889         }
890         mChildrenInitialized = allInitialized;
891         return mChildrenInitialized;
892     }
893 
894     private void skipToStartValue(boolean inReverse) {
895         skipToEndValue(!inReverse);
896     }
897 
898     /**
899      * Sets the position of the animation to the specified point in time. This time should
900      * be between 0 and the total duration of the animation, including any repetition. If
901      * the animation has not yet been started, then it will not advance forward after it is
902      * set to this time; it will simply set the time to this value and perform any appropriate
903      * actions based on that time. If the animation is already running, then setCurrentPlayTime()
904      * will set the current playing time to this value and continue playing from that point.
905      *
906      * @param playTime The time, in milliseconds, to which the animation is advanced or rewound.
907      *                 Unless the animation is reversing, the playtime is considered the time since
908      *                 the end of the start delay of the AnimatorSet in a forward playing direction.
909      *
910      */
911     public void setCurrentPlayTime(long playTime) {
912         if (mReversing && getTotalDuration() == DURATION_INFINITE) {
913             // Should never get here
914             throw new UnsupportedOperationException("Error: Cannot seek in reverse in an infinite"
915                     + " AnimatorSet");
916         }
917 
918         if ((getTotalDuration() != DURATION_INFINITE && playTime > getTotalDuration() - mStartDelay)
919                 || playTime < 0) {
920             throw new UnsupportedOperationException("Error: Play time should always be in between"
921                     + "0 and duration.");
922         }
923 
924         initAnimation();
925 
926         if (!isStarted()) {
927             if (mReversing) {
928                 throw new UnsupportedOperationException("Error: Something went wrong. mReversing"
929                         + " should not be set when AnimatorSet is not started.");
930             }
931             if (!mSeekState.isActive()) {
932                 findLatestEventIdForTime(0);
933                 // Set all the values to start values.
934                 initChildren();
935                 skipToStartValue(mReversing);
936                 mSeekState.setPlayTime(0, mReversing);
937             }
938             animateBasedOnPlayTime(playTime, 0, mReversing);
939             mSeekState.setPlayTime(playTime, mReversing);
940         } else {
941             // If the animation is running, just set the seek time and wait until the next frame
942             // (i.e. doAnimationFrame(...)) to advance the animation.
943             mSeekState.setPlayTime(playTime, mReversing);
944         }
945     }
946 
947     /**
948      * Returns the milliseconds elapsed since the start of the animation.
949      *
950      * <p>For ongoing animations, this method returns the current progress of the animation in
951      * terms of play time. For an animation that has not yet been started: if the animation has been
952      * seeked to a certain time via {@link #setCurrentPlayTime(long)}, the seeked play time will
953      * be returned; otherwise, this method will return 0.
954      *
955      * @return the current position in time of the animation in milliseconds
956      */
957     public long getCurrentPlayTime() {
958         if (mSeekState.isActive()) {
959             return mSeekState.getPlayTime();
960         }
961         if (mLastFrameTime == -1) {
962             // Not yet started or during start delay
963             return 0;
964         }
965         float durationScale = ValueAnimator.getDurationScale();
966         durationScale = durationScale == 0 ? 1 : durationScale;
967         if (mReversing) {
968             return (long) ((mLastFrameTime - mFirstFrame) / durationScale);
969         } else {
970             return (long) ((mLastFrameTime - mFirstFrame - mStartDelay) / durationScale);
971         }
972     }
973 
974     private void initChildren() {
975         if (!isInitialized()) {
976             mChildrenInitialized = true;
977             // Forcefully initialize all children based on their end time, so that if the start
978             // value of a child is dependent on a previous animation, the animation will be
979             // initialized after the the previous animations have been advanced to the end.
980             skipToEndValue(false);
981         }
982     }
983 
984     /**
985      * @param frameTime The frame start time, in the {@link SystemClock#uptimeMillis()} time
986      *                  base.
987      * @return
988      * @hide
989      */
990     @Override
991     public boolean doAnimationFrame(long frameTime) {
992         float durationScale = ValueAnimator.getDurationScale();
993         if (durationScale == 0f) {
994             // Duration scale is 0, end the animation right away.
995             forceToEnd();
996             return true;
997         }
998 
999         // After the first frame comes in, we need to wait for start delay to pass before updating
1000         // any animation values.
1001         if (mFirstFrame < 0) {
1002             mFirstFrame = frameTime;
1003         }
1004 
1005         // Handle pause/resume
1006         if (mPaused) {
1007             // Note: Child animations don't receive pause events. Since it's never a contract that
1008             // the child animators will be paused when set is paused, this is unlikely to be an
1009             // issue.
1010             mPauseTime = frameTime;
1011             removeAnimationCallback();
1012             return false;
1013         } else if (mPauseTime > 0) {
1014                 // Offset by the duration that the animation was paused
1015             mFirstFrame += (frameTime - mPauseTime);
1016             mPauseTime = -1;
1017         }
1018 
1019         // Continue at seeked position
1020         if (mSeekState.isActive()) {
1021             mSeekState.updateSeekDirection(mReversing);
1022             if (mReversing) {
1023                 mFirstFrame = (long) (frameTime - mSeekState.getPlayTime() * durationScale);
1024             } else {
1025                 mFirstFrame = (long) (frameTime - (mSeekState.getPlayTime() + mStartDelay)
1026                         * durationScale);
1027             }
1028             mSeekState.reset();
1029         }
1030 
1031         if (!mReversing && frameTime < mFirstFrame + mStartDelay * durationScale) {
1032             // Still during start delay in a forward playing case.
1033             return false;
1034         }
1035 
1036         // From here on, we always use unscaled play time. Note this unscaled playtime includes
1037         // the start delay.
1038         long unscaledPlayTime = (long) ((frameTime - mFirstFrame) / durationScale);
1039         mLastFrameTime = frameTime;
1040 
1041         // 1. Pulse the animators that will start or end in this frame
1042         // 2. Pulse the animators that will finish in a later frame
1043         int latestId = findLatestEventIdForTime(unscaledPlayTime);
1044         int startId = mLastEventId;
1045 
1046         handleAnimationEvents(startId, latestId, unscaledPlayTime);
1047 
1048         mLastEventId = latestId;
1049 
1050         // Pump a frame to the on-going animators
1051         for (int i = 0; i < mPlayingSet.size(); i++) {
1052             Node node = mPlayingSet.get(i);
1053             if (!node.mEnded) {
1054                 pulseFrame(node, getPlayTimeForNode(unscaledPlayTime, node));
1055             }
1056         }
1057 
1058         // Remove all the finished anims
1059         for (int i = mPlayingSet.size() - 1; i >= 0; i--) {
1060             if (mPlayingSet.get(i).mEnded) {
1061                 mPlayingSet.remove(i);
1062             }
1063         }
1064 
1065         boolean finished = false;
1066         if (mReversing) {
1067             if (mPlayingSet.size() == 1 && mPlayingSet.get(0) == mRootNode) {
1068                 // The only animation that is running is the delay animation.
1069                 finished = true;
1070             } else if (mPlayingSet.isEmpty() && mLastEventId < 3) {
1071                 // The only remaining animation is the delay animation
1072                 finished = true;
1073             }
1074         } else {
1075             finished = mPlayingSet.isEmpty() && mLastEventId == mEvents.size() - 1;
1076         }
1077 
1078         if (finished) {
1079             endAnimation();
1080             return true;
1081         }
1082         return false;
1083     }
1084 
1085     /**
1086      * @hide
1087      */
1088     @Override
1089     public void commitAnimationFrame(long frameTime) {
1090         // No op.
1091     }
1092 
1093     @Override
1094     boolean pulseAnimationFrame(long frameTime) {
1095         return doAnimationFrame(frameTime);
1096     }
1097 
1098     /**
1099      * When playing forward, we call start() at the animation's scheduled start time, and make sure
1100      * to pump a frame at the animation's scheduled end time.
1101      *
1102      * When playing in reverse, we should reverse the animation when we hit animation's end event,
1103      * and expect the animation to end at the its delay ended event, rather than start event.
1104      */
1105     private void handleAnimationEvents(int startId, int latestId, long playTime) {
1106         if (mReversing) {
1107             startId = startId == -1 ? mEvents.size() : startId;
1108             for (int i = startId - 1; i >= latestId; i--) {
1109                 AnimationEvent event = mEvents.get(i);
1110                 Node node = event.mNode;
1111                 if (event.mEvent == AnimationEvent.ANIMATION_END) {
1112                     if (node.mAnimation.isStarted()) {
1113                         // If the animation has already been started before its due time (i.e.
1114                         // the child animator is being manipulated outside of the AnimatorSet), we
1115                         // need to cancel the animation to reset the internal state (e.g. frame
1116                         // time tracking) and remove the self pulsing callbacks
1117                         node.mAnimation.cancel();
1118                     }
1119                     node.mEnded = false;
1120                     mPlayingSet.add(event.mNode);
1121                     node.mAnimation.startWithoutPulsing(true);
1122                     pulseFrame(node, 0);
1123                 } else if (event.mEvent == AnimationEvent.ANIMATION_DELAY_ENDED && !node.mEnded) {
1124                     // end event:
1125                     pulseFrame(node, getPlayTimeForNode(playTime, node));
1126                 }
1127             }
1128         } else {
1129             for (int i = startId + 1; i <= latestId; i++) {
1130                 AnimationEvent event = mEvents.get(i);
1131                 Node node = event.mNode;
1132                 if (event.mEvent == AnimationEvent.ANIMATION_START) {
1133                     mPlayingSet.add(event.mNode);
1134                     if (node.mAnimation.isStarted()) {
1135                         // If the animation has already been started before its due time (i.e.
1136                         // the child animator is being manipulated outside of the AnimatorSet), we
1137                         // need to cancel the animation to reset the internal state (e.g. frame
1138                         // time tracking) and remove the self pulsing callbacks
1139                         node.mAnimation.cancel();
1140                     }
1141                     node.mEnded = false;
1142                     node.mAnimation.startWithoutPulsing(false);
1143                     pulseFrame(node, 0);
1144                 } else if (event.mEvent == AnimationEvent.ANIMATION_END && !node.mEnded) {
1145                     // start event:
1146                     pulseFrame(node, getPlayTimeForNode(playTime, node));
1147                 }
1148             }
1149         }
1150     }
1151 
1152     /**
1153      * This method pulses frames into child animations. It scales the input animation play time
1154      * with the duration scale and pass that to the child animation via pulseAnimationFrame(long).
1155      *
1156      * @param node child animator node
1157      * @param animPlayTime unscaled play time (including start delay) for the child animator
1158      */
1159     private void pulseFrame(Node node, long animPlayTime) {
1160         if (!node.mEnded) {
1161             float durationScale = ValueAnimator.getDurationScale();
1162             durationScale = durationScale == 0  ? 1 : durationScale;
1163             node.mEnded = node.mAnimation.pulseAnimationFrame(
1164                     (long) (animPlayTime * durationScale));
1165         }
1166     }
1167 
1168     private long getPlayTimeForNode(long overallPlayTime, Node node) {
1169         return getPlayTimeForNode(overallPlayTime, node, mReversing);
1170     }
1171 
1172     private long getPlayTimeForNode(long overallPlayTime, Node node, boolean inReverse) {
1173         if (inReverse) {
1174             overallPlayTime = getTotalDuration() - overallPlayTime;
1175             return node.mEndTime - overallPlayTime;
1176         } else {
1177             return overallPlayTime - node.mStartTime;
1178         }
1179     }
1180 
1181     private void startAnimation() {
1182         addDummyListener();
1183 
1184         // Register animation callback
1185         addAnimationCallback(0);
1186 
1187         if (mSeekState.getPlayTimeNormalized() == 0 && mReversing) {
1188             // Maintain old behavior, if seeked to 0 then call reverse, we'll treat the case
1189             // the same as no seeking at all.
1190             mSeekState.reset();
1191         }
1192         // Set the child animators to the right end:
1193         if (mShouldResetValuesAtStart) {
1194             if (isInitialized()) {
1195                 skipToEndValue(!mReversing);
1196             } else if (mReversing) {
1197                 // Reversing but haven't initialized all the children yet.
1198                 initChildren();
1199                 skipToEndValue(!mReversing);
1200             } else {
1201                 // If not all children are initialized and play direction is forward
1202                 for (int i = mEvents.size() - 1; i >= 0; i--) {
1203                     if (mEvents.get(i).mEvent == AnimationEvent.ANIMATION_DELAY_ENDED) {
1204                         Animator anim = mEvents.get(i).mNode.mAnimation;
1205                         // Only reset the animations that have been initialized to start value,
1206                         // so that if they are defined without a start value, they will get the
1207                         // values set at the right time (i.e. the next animation run)
1208                         if (anim.isInitialized()) {
1209                             anim.skipToEndValue(true);
1210                         }
1211                     }
1212                 }
1213             }
1214         }
1215 
1216         if (mReversing || mStartDelay == 0 || mSeekState.isActive()) {
1217             long playTime;
1218             // If no delay, we need to call start on the first animations to be consistent with old
1219             // behavior.
1220             if (mSeekState.isActive()) {
1221                 mSeekState.updateSeekDirection(mReversing);
1222                 playTime = mSeekState.getPlayTime();
1223             } else {
1224                 playTime = 0;
1225             }
1226             int toId = findLatestEventIdForTime(playTime);
1227             handleAnimationEvents(-1, toId, playTime);
1228             for (int i = mPlayingSet.size() - 1; i >= 0; i--) {
1229                 if (mPlayingSet.get(i).mEnded) {
1230                     mPlayingSet.remove(i);
1231                 }
1232             }
1233             mLastEventId = toId;
1234         }
1235     }
1236 
1237     // This is to work around the issue in b/34736819, as the old behavior in AnimatorSet had
1238     // masked a real bug in play movies. TODO: remove this and below once the root cause is fixed.
1239     private void addDummyListener() {
1240         for (int i = 1; i < mNodes.size(); i++) {
1241             mNodes.get(i).mAnimation.addListener(mDummyListener);
1242         }
1243     }
1244 
1245     private void removeDummyListener() {
1246         for (int i = 1; i < mNodes.size(); i++) {
1247             mNodes.get(i).mAnimation.removeListener(mDummyListener);
1248         }
1249     }
1250 
1251     private int findLatestEventIdForTime(long currentPlayTime) {
1252         int size = mEvents.size();
1253         int latestId = mLastEventId;
1254         // Call start on the first animations now to be consistent with the old behavior
1255         if (mReversing) {
1256             currentPlayTime = getTotalDuration() - currentPlayTime;
1257             mLastEventId = mLastEventId == -1 ? size : mLastEventId;
1258             for (int j = mLastEventId - 1; j >= 0; j--) {
1259                 AnimationEvent event = mEvents.get(j);
1260                 if (event.getTime() >= currentPlayTime) {
1261                     latestId = j;
1262                 }
1263             }
1264         } else {
1265             for (int i = mLastEventId + 1; i < size; i++) {
1266                 AnimationEvent event = mEvents.get(i);
1267                 // TODO: need a function that accounts for infinite duration to compare time
1268                 if (event.getTime() != DURATION_INFINITE && event.getTime() <= currentPlayTime) {
1269                     latestId = i;
1270                 }
1271             }
1272         }
1273         return latestId;
1274     }
1275 
1276     private void endAnimation() {
1277         mStarted = false;
1278         mLastFrameTime = -1;
1279         mFirstFrame = -1;
1280         mLastEventId = -1;
1281         mPaused = false;
1282         mPauseTime = -1;
1283         mSeekState.reset();
1284         mPlayingSet.clear();
1285 
1286         // No longer receive callbacks
1287         removeAnimationCallback();
1288         // Call end listener
1289         if (mListeners != null) {
1290             ArrayList<AnimatorListener> tmpListeners =
1291                     (ArrayList<AnimatorListener>) mListeners.clone();
1292             int numListeners = tmpListeners.size();
1293             for (int i = 0; i < numListeners; ++i) {
1294                 tmpListeners.get(i).onAnimationEnd(this, mReversing);
1295             }
1296         }
1297         removeDummyListener();
1298         mSelfPulse = true;
1299         mReversing = false;
1300     }
1301 
1302     private void removeAnimationCallback() {
1303         if (!mSelfPulse) {
1304             return;
1305         }
1306         AnimationHandler handler = AnimationHandler.getInstance();
1307         handler.removeCallback(this);
1308     }
1309 
1310     private void addAnimationCallback(long delay) {
1311         if (!mSelfPulse) {
1312             return;
1313         }
1314         AnimationHandler handler = AnimationHandler.getInstance();
1315         handler.addAnimationFrameCallback(this, delay);
1316     }
1317 
1318     @Override
1319     public AnimatorSet clone() {
1320         final AnimatorSet anim = (AnimatorSet) super.clone();
1321         /*
1322          * The basic clone() operation copies all items. This doesn't work very well for
1323          * AnimatorSet, because it will copy references that need to be recreated and state
1324          * that may not apply. What we need to do now is put the clone in an uninitialized
1325          * state, with fresh, empty data structures. Then we will build up the nodes list
1326          * manually, as we clone each Node (and its animation). The clone will then be sorted,
1327          * and will populate any appropriate lists, when it is started.
1328          */
1329         final int nodeCount = mNodes.size();
1330         anim.mStarted = false;
1331         anim.mLastFrameTime = -1;
1332         anim.mFirstFrame = -1;
1333         anim.mLastEventId = -1;
1334         anim.mPaused = false;
1335         anim.mPauseTime = -1;
1336         anim.mSeekState = new SeekState();
1337         anim.mSelfPulse = true;
1338         anim.mPlayingSet = new ArrayList<Node>();
1339         anim.mNodeMap = new ArrayMap<Animator, Node>();
1340         anim.mNodes = new ArrayList<Node>(nodeCount);
1341         anim.mEvents = new ArrayList<AnimationEvent>();
1342         anim.mDummyListener = new AnimatorListenerAdapter() {
1343             @Override
1344             public void onAnimationEnd(Animator animation) {
1345                 if (anim.mNodeMap.get(animation) == null) {
1346                     throw new AndroidRuntimeException("Error: animation ended is not in the node"
1347                             + " map");
1348                 }
1349                 anim.mNodeMap.get(animation).mEnded = true;
1350 
1351             }
1352         };
1353         anim.mReversing = false;
1354         anim.mDependencyDirty = true;
1355 
1356         // Walk through the old nodes list, cloning each node and adding it to the new nodemap.
1357         // One problem is that the old node dependencies point to nodes in the old AnimatorSet.
1358         // We need to track the old/new nodes in order to reconstruct the dependencies in the clone.
1359 
1360         HashMap<Node, Node> clonesMap = new HashMap<>(nodeCount);
1361         for (int n = 0; n < nodeCount; n++) {
1362             final Node node = mNodes.get(n);
1363             Node nodeClone = node.clone();
1364             // Remove the old internal listener from the cloned child
1365             nodeClone.mAnimation.removeListener(mDummyListener);
1366             clonesMap.put(node, nodeClone);
1367             anim.mNodes.add(nodeClone);
1368             anim.mNodeMap.put(nodeClone.mAnimation, nodeClone);
1369         }
1370 
1371         anim.mRootNode = clonesMap.get(mRootNode);
1372         anim.mDelayAnim = (ValueAnimator) anim.mRootNode.mAnimation;
1373 
1374         // Now that we've cloned all of the nodes, we're ready to walk through their
1375         // dependencies, mapping the old dependencies to the new nodes
1376         for (int i = 0; i < nodeCount; i++) {
1377             Node node = mNodes.get(i);
1378             // Update dependencies for node's clone
1379             Node nodeClone = clonesMap.get(node);
1380             nodeClone.mLatestParent = node.mLatestParent == null
1381                     ? null : clonesMap.get(node.mLatestParent);
1382             int size = node.mChildNodes == null ? 0 : node.mChildNodes.size();
1383             for (int j = 0; j < size; j++) {
1384                 nodeClone.mChildNodes.set(j, clonesMap.get(node.mChildNodes.get(j)));
1385             }
1386             size = node.mSiblings == null ? 0 : node.mSiblings.size();
1387             for (int j = 0; j < size; j++) {
1388                 nodeClone.mSiblings.set(j, clonesMap.get(node.mSiblings.get(j)));
1389             }
1390             size = node.mParents == null ? 0 : node.mParents.size();
1391             for (int j = 0; j < size; j++) {
1392                 nodeClone.mParents.set(j, clonesMap.get(node.mParents.get(j)));
1393             }
1394         }
1395         return anim;
1396     }
1397 
1398 
1399     /**
1400      * AnimatorSet is only reversible when the set contains no sequential animation, and no child
1401      * animators have a start delay.
1402      * @hide
1403      */
1404     @Override
1405     public boolean canReverse() {
1406         return getTotalDuration() != DURATION_INFINITE;
1407     }
1408 
1409     /**
1410      * Plays the AnimatorSet in reverse. If the animation has been seeked to a specific play time
1411      * using {@link #setCurrentPlayTime(long)}, it will play backwards from the point seeked when
1412      * reverse was called. Otherwise, then it will start from the end and play backwards. This
1413      * behavior is only set for the current animation; future playing of the animation will use the
1414      * default behavior of playing forward.
1415      * <p>
1416      * Note: reverse is not supported for infinite AnimatorSet.
1417      */
1418     @Override
1419     public void reverse() {
1420         start(true, true);
1421     }
1422 
1423     @Override
1424     public String toString() {
1425         String returnVal = "AnimatorSet@" + Integer.toHexString(hashCode()) + "{";
1426         int size = mNodes.size();
1427         for (int i = 0; i < size; i++) {
1428             Node node = mNodes.get(i);
1429             returnVal += "\n    " + node.mAnimation.toString();
1430         }
1431         return returnVal + "\n}";
1432     }
1433 
1434     private void printChildCount() {
1435         // Print out the child count through a level traverse.
1436         ArrayList<Node> list = new ArrayList<>(mNodes.size());
1437         list.add(mRootNode);
1438         Log.d(TAG, "Current tree: ");
1439         int index = 0;
1440         while (index < list.size()) {
1441             int listSize = list.size();
1442             StringBuilder builder = new StringBuilder();
1443             for (; index < listSize; index++) {
1444                 Node node = list.get(index);
1445                 int num = 0;
1446                 if (node.mChildNodes != null) {
1447                     for (int i = 0; i < node.mChildNodes.size(); i++) {
1448                         Node child = node.mChildNodes.get(i);
1449                         if (child.mLatestParent == node) {
1450                             num++;
1451                             list.add(child);
1452                         }
1453                     }
1454                 }
1455                 builder.append(" ");
1456                 builder.append(num);
1457             }
1458             Log.d(TAG, builder.toString());
1459         }
1460     }
1461 
1462     private void createDependencyGraph() {
1463         if (!mDependencyDirty) {
1464             // Check whether any duration of the child animations has changed
1465             boolean durationChanged = false;
1466             for (int i = 0; i < mNodes.size(); i++) {
1467                 Animator anim = mNodes.get(i).mAnimation;
1468                 if (mNodes.get(i).mTotalDuration != anim.getTotalDuration()) {
1469                     durationChanged = true;
1470                     break;
1471                 }
1472             }
1473             if (!durationChanged) {
1474                 return;
1475             }
1476         }
1477 
1478         mDependencyDirty = false;
1479         // Traverse all the siblings and make sure they have all the parents
1480         int size = mNodes.size();
1481         for (int i = 0; i < size; i++) {
1482             mNodes.get(i).mParentsAdded = false;
1483         }
1484         for (int i = 0; i < size; i++) {
1485             Node node = mNodes.get(i);
1486             if (node.mParentsAdded) {
1487                 continue;
1488             }
1489 
1490             node.mParentsAdded = true;
1491             if (node.mSiblings == null) {
1492                 continue;
1493             }
1494 
1495             // Find all the siblings
1496             findSiblings(node, node.mSiblings);
1497             node.mSiblings.remove(node);
1498 
1499             // Get parents from all siblings
1500             int siblingSize = node.mSiblings.size();
1501             for (int j = 0; j < siblingSize; j++) {
1502                 node.addParents(node.mSiblings.get(j).mParents);
1503             }
1504 
1505             // Now make sure all siblings share the same set of parents
1506             for (int j = 0; j < siblingSize; j++) {
1507                 Node sibling = node.mSiblings.get(j);
1508                 sibling.addParents(node.mParents);
1509                 sibling.mParentsAdded = true;
1510             }
1511         }
1512 
1513         for (int i = 0; i < size; i++) {
1514             Node node = mNodes.get(i);
1515             if (node != mRootNode && node.mParents == null) {
1516                 node.addParent(mRootNode);
1517             }
1518         }
1519 
1520         // Do a DFS on the tree
1521         ArrayList<Node> visited = new ArrayList<Node>(mNodes.size());
1522         // Assign start/end time
1523         mRootNode.mStartTime = 0;
1524         mRootNode.mEndTime = mDelayAnim.getDuration();
1525         updatePlayTime(mRootNode, visited);
1526 
1527         sortAnimationEvents();
1528         mTotalDuration = mEvents.get(mEvents.size() - 1).getTime();
1529     }
1530 
1531     private void sortAnimationEvents() {
1532         // Sort the list of events in ascending order of their time
1533         // Create the list including the delay animation.
1534         mEvents.clear();
1535         for (int i = 1; i < mNodes.size(); i++) {
1536             Node node = mNodes.get(i);
1537             mEvents.add(new AnimationEvent(node, AnimationEvent.ANIMATION_START));
1538             mEvents.add(new AnimationEvent(node, AnimationEvent.ANIMATION_DELAY_ENDED));
1539             mEvents.add(new AnimationEvent(node, AnimationEvent.ANIMATION_END));
1540         }
1541         mEvents.sort(new Comparator<AnimationEvent>() {
1542             @Override
1543             public int compare(AnimationEvent e1, AnimationEvent e2) {
1544                 long t1 = e1.getTime();
1545                 long t2 = e2.getTime();
1546                 if (t1 == t2) {
1547                     // For events that happen at the same time, we need them to be in the sequence
1548                     // (end, start, start delay ended)
1549                     if (e2.mEvent + e1.mEvent == AnimationEvent.ANIMATION_START
1550                             + AnimationEvent.ANIMATION_DELAY_ENDED) {
1551                         // Ensure start delay happens after start
1552                         return e1.mEvent - e2.mEvent;
1553                     } else {
1554                         return e2.mEvent - e1.mEvent;
1555                     }
1556                 }
1557                 if (t2 == DURATION_INFINITE) {
1558                     return -1;
1559                 }
1560                 if (t1 == DURATION_INFINITE) {
1561                     return 1;
1562                 }
1563                 // When neither event happens at INFINITE time:
1564                 return (int) (t1 - t2);
1565             }
1566         });
1567 
1568         int eventSize = mEvents.size();
1569         // For the same animation, start event has to happen before end.
1570         for (int i = 0; i < eventSize;) {
1571             AnimationEvent event = mEvents.get(i);
1572             if (event.mEvent == AnimationEvent.ANIMATION_END) {
1573                 boolean needToSwapStart;
1574                 if (event.mNode.mStartTime == event.mNode.mEndTime) {
1575                     needToSwapStart = true;
1576                 } else if (event.mNode.mEndTime == event.mNode.mStartTime
1577                         + event.mNode.mAnimation.getStartDelay()) {
1578                     // Swapping start delay
1579                     needToSwapStart = false;
1580                 } else {
1581                     i++;
1582                     continue;
1583                 }
1584 
1585                 int startEventId = eventSize;
1586                 int startDelayEndId = eventSize;
1587                 for (int j = i + 1; j < eventSize; j++) {
1588                     if (startEventId < eventSize && startDelayEndId < eventSize) {
1589                         break;
1590                     }
1591                     if (mEvents.get(j).mNode == event.mNode) {
1592                         if (mEvents.get(j).mEvent == AnimationEvent.ANIMATION_START) {
1593                             // Found start event
1594                             startEventId = j;
1595                         } else if (mEvents.get(j).mEvent == AnimationEvent.ANIMATION_DELAY_ENDED) {
1596                             startDelayEndId = j;
1597                         }
1598                     }
1599 
1600                 }
1601                 if (needToSwapStart && startEventId == mEvents.size()) {
1602                     throw new UnsupportedOperationException("Something went wrong, no start is"
1603                             + "found after stop for an animation that has the same start and end"
1604                             + "time.");
1605 
1606                 }
1607                 if (startDelayEndId == mEvents.size()) {
1608                     throw new UnsupportedOperationException("Something went wrong, no start"
1609                             + "delay end is found after stop for an animation");
1610 
1611                 }
1612 
1613                 // We need to make sure start is inserted before start delay ended event,
1614                 // because otherwise inserting start delay ended events first would change
1615                 // the start event index.
1616                 if (needToSwapStart) {
1617                     AnimationEvent startEvent = mEvents.remove(startEventId);
1618                     mEvents.add(i, startEvent);
1619                     i++;
1620                 }
1621 
1622                 AnimationEvent startDelayEndEvent = mEvents.remove(startDelayEndId);
1623                 mEvents.add(i, startDelayEndEvent);
1624                 i += 2;
1625             } else {
1626                 i++;
1627             }
1628         }
1629 
1630         if (!mEvents.isEmpty() && mEvents.get(0).mEvent != AnimationEvent.ANIMATION_START) {
1631             throw new UnsupportedOperationException(
1632                     "Sorting went bad, the start event should always be at index 0");
1633         }
1634 
1635         // Add AnimatorSet's start delay node to the beginning
1636         mEvents.add(0, new AnimationEvent(mRootNode, AnimationEvent.ANIMATION_START));
1637         mEvents.add(1, new AnimationEvent(mRootNode, AnimationEvent.ANIMATION_DELAY_ENDED));
1638         mEvents.add(2, new AnimationEvent(mRootNode, AnimationEvent.ANIMATION_END));
1639 
1640         if (mEvents.get(mEvents.size() - 1).mEvent == AnimationEvent.ANIMATION_START
1641                 || mEvents.get(mEvents.size() - 1).mEvent == AnimationEvent.ANIMATION_DELAY_ENDED) {
1642             throw new UnsupportedOperationException(
1643                     "Something went wrong, the last event is not an end event");
1644         }
1645     }
1646 
1647     /**
1648      * Based on parent's start/end time, calculate children's start/end time. If cycle exists in
1649      * the graph, all the nodes on the cycle will be marked to start at {@link #DURATION_INFINITE},
1650      * meaning they will ever play.
1651      */
1652     private void updatePlayTime(Node parent,  ArrayList<Node> visited) {
1653         if (parent.mChildNodes == null) {
1654             if (parent == mRootNode) {
1655                 // All the animators are in a cycle
1656                 for (int i = 0; i < mNodes.size(); i++) {
1657                     Node node = mNodes.get(i);
1658                     if (node != mRootNode) {
1659                         node.mStartTime = DURATION_INFINITE;
1660                         node.mEndTime = DURATION_INFINITE;
1661                     }
1662                 }
1663             }
1664             return;
1665         }
1666 
1667         visited.add(parent);
1668         int childrenSize = parent.mChildNodes.size();
1669         for (int i = 0; i < childrenSize; i++) {
1670             Node child = parent.mChildNodes.get(i);
1671             child.mTotalDuration = child.mAnimation.getTotalDuration();  // Update cached duration.
1672 
1673             int index = visited.indexOf(child);
1674             if (index >= 0) {
1675                 // Child has been visited, cycle found. Mark all the nodes in the cycle.
1676                 for (int j = index; j < visited.size(); j++) {
1677                     visited.get(j).mLatestParent = null;
1678                     visited.get(j).mStartTime = DURATION_INFINITE;
1679                     visited.get(j).mEndTime = DURATION_INFINITE;
1680                 }
1681                 child.mStartTime = DURATION_INFINITE;
1682                 child.mEndTime = DURATION_INFINITE;
1683                 child.mLatestParent = null;
1684                 Log.w(TAG, "Cycle found in AnimatorSet: " + this);
1685                 continue;
1686             }
1687 
1688             if (child.mStartTime != DURATION_INFINITE) {
1689                 if (parent.mEndTime == DURATION_INFINITE) {
1690                     child.mLatestParent = parent;
1691                     child.mStartTime = DURATION_INFINITE;
1692                     child.mEndTime = DURATION_INFINITE;
1693                 } else {
1694                     if (parent.mEndTime >= child.mStartTime) {
1695                         child.mLatestParent = parent;
1696                         child.mStartTime = parent.mEndTime;
1697                     }
1698 
1699                     child.mEndTime = child.mTotalDuration == DURATION_INFINITE
1700                             ? DURATION_INFINITE : child.mStartTime + child.mTotalDuration;
1701                 }
1702             }
1703             updatePlayTime(child, visited);
1704         }
1705         visited.remove(parent);
1706     }
1707 
1708     // Recursively find all the siblings
1709     private void findSiblings(Node node, ArrayList<Node> siblings) {
1710         if (!siblings.contains(node)) {
1711             siblings.add(node);
1712             if (node.mSiblings == null) {
1713                 return;
1714             }
1715             for (int i = 0; i < node.mSiblings.size(); i++) {
1716                 findSiblings(node.mSiblings.get(i), siblings);
1717             }
1718         }
1719     }
1720 
1721     /**
1722      * @hide
1723      * TODO: For animatorSet defined in XML, we can use a flag to indicate what the play order
1724      * if defined (i.e. sequential or together), then we can use the flag instead of calculating
1725      * dynamically. Note that when AnimatorSet is empty this method returns true.
1726      * @return whether all the animators in the set are supposed to play together
1727      */
1728     public boolean shouldPlayTogether() {
1729         updateAnimatorsDuration();
1730         createDependencyGraph();
1731         // All the child nodes are set out to play right after the delay animation
1732         return mRootNode.mChildNodes == null || mRootNode.mChildNodes.size() == mNodes.size() - 1;
1733     }
1734 
1735     @Override
1736     public long getTotalDuration() {
1737         updateAnimatorsDuration();
1738         createDependencyGraph();
1739         return mTotalDuration;
1740     }
1741 
1742     private Node getNodeForAnimation(Animator anim) {
1743         Node node = mNodeMap.get(anim);
1744         if (node == null) {
1745             node = new Node(anim);
1746             mNodeMap.put(anim, node);
1747             mNodes.add(node);
1748         }
1749         return node;
1750     }
1751 
1752     /**
1753      * A Node is an embodiment of both the Animator that it wraps as well as
1754      * any dependencies that are associated with that Animation. This includes
1755      * both dependencies upon other nodes (in the dependencies list) as
1756      * well as dependencies of other nodes upon this (in the nodeDependents list).
1757      */
1758     private static class Node implements Cloneable {
1759         Animator mAnimation;
1760 
1761         /**
1762          * Child nodes are the nodes associated with animations that will be played immediately
1763          * after current node.
1764          */
1765         ArrayList<Node> mChildNodes = null;
1766 
1767         /**
1768          * Flag indicating whether the animation in this node is finished. This flag
1769          * is used by AnimatorSet to check, as each animation ends, whether all child animations
1770          * are mEnded and it's time to send out an end event for the entire AnimatorSet.
1771          */
1772         boolean mEnded = false;
1773 
1774         /**
1775          * Nodes with animations that are defined to play simultaneously with the animation
1776          * associated with this current node.
1777          */
1778         ArrayList<Node> mSiblings;
1779 
1780         /**
1781          * Parent nodes are the nodes with animations preceding current node's animation. Parent
1782          * nodes here are derived from user defined animation sequence.
1783          */
1784         ArrayList<Node> mParents;
1785 
1786         /**
1787          * Latest parent is the parent node associated with a animation that finishes after all
1788          * the other parents' animations.
1789          */
1790         Node mLatestParent = null;
1791 
1792         boolean mParentsAdded = false;
1793         long mStartTime = 0;
1794         long mEndTime = 0;
1795         long mTotalDuration = 0;
1796 
1797         /**
1798          * Constructs the Node with the animation that it encapsulates. A Node has no
1799          * dependencies by default; dependencies are added via the addDependency()
1800          * method.
1801          *
1802          * @param animation The animation that the Node encapsulates.
1803          */
1804         public Node(Animator animation) {
1805             this.mAnimation = animation;
1806         }
1807 
1808         @Override
1809         public Node clone() {
1810             try {
1811                 Node node = (Node) super.clone();
1812                 node.mAnimation = mAnimation.clone();
1813                 if (mChildNodes != null) {
1814                     node.mChildNodes = new ArrayList<>(mChildNodes);
1815                 }
1816                 if (mSiblings != null) {
1817                     node.mSiblings = new ArrayList<>(mSiblings);
1818                 }
1819                 if (mParents != null) {
1820                     node.mParents = new ArrayList<>(mParents);
1821                 }
1822                 node.mEnded = false;
1823                 return node;
1824             } catch (CloneNotSupportedException e) {
1825                throw new AssertionError();
1826             }
1827         }
1828 
1829         void addChild(Node node) {
1830             if (mChildNodes == null) {
1831                 mChildNodes = new ArrayList<>();
1832             }
1833             if (!mChildNodes.contains(node)) {
1834                 mChildNodes.add(node);
1835                 node.addParent(this);
1836             }
1837         }
1838 
1839         public void addSibling(Node node) {
1840             if (mSiblings == null) {
1841                 mSiblings = new ArrayList<Node>();
1842             }
1843             if (!mSiblings.contains(node)) {
1844                 mSiblings.add(node);
1845                 node.addSibling(this);
1846             }
1847         }
1848 
1849         public void addParent(Node node) {
1850             if (mParents == null) {
1851                 mParents =  new ArrayList<Node>();
1852             }
1853             if (!mParents.contains(node)) {
1854                 mParents.add(node);
1855                 node.addChild(this);
1856             }
1857         }
1858 
1859         public void addParents(ArrayList<Node> parents) {
1860             if (parents == null) {
1861                 return;
1862             }
1863             int size = parents.size();
1864             for (int i = 0; i < size; i++) {
1865                 addParent(parents.get(i));
1866             }
1867         }
1868     }
1869 
1870     /**
1871      * This class is a wrapper around a node and an event for the animation corresponding to the
1872      * node. The 3 types of events represent the start of an animation, the end of a start delay of
1873      * an animation, and the end of an animation. When playing forward (i.e. in the non-reverse
1874      * direction), start event marks when start() should be called, and end event corresponds to
1875      * when the animation should finish. When playing in reverse, start delay will not be a part
1876      * of the animation. Therefore, reverse() is called at the end event, and animation should end
1877      * at the delay ended event.
1878      */
1879     private static class AnimationEvent {
1880         static final int ANIMATION_START = 0;
1881         static final int ANIMATION_DELAY_ENDED = 1;
1882         static final int ANIMATION_END = 2;
1883         final Node mNode;
1884         final int mEvent;
1885 
1886         AnimationEvent(Node node, int event) {
1887             mNode = node;
1888             mEvent = event;
1889         }
1890 
1891         long getTime() {
1892             if (mEvent == ANIMATION_START) {
1893                 return mNode.mStartTime;
1894             } else if (mEvent == ANIMATION_DELAY_ENDED) {
1895                 return mNode.mStartTime == DURATION_INFINITE
1896                         ? DURATION_INFINITE : mNode.mStartTime + mNode.mAnimation.getStartDelay();
1897             } else {
1898                 return mNode.mEndTime;
1899             }
1900         }
1901 
1902         public String toString() {
1903             String eventStr = mEvent == ANIMATION_START ? "start" : (
1904                     mEvent == ANIMATION_DELAY_ENDED ? "delay ended" : "end");
1905             return eventStr + " " + mNode.mAnimation.toString();
1906         }
1907     }
1908 
1909     private class SeekState {
1910         private long mPlayTime = -1;
1911         private boolean mSeekingInReverse = false;
1912         void reset() {
1913             mPlayTime = -1;
1914             mSeekingInReverse = false;
1915         }
1916 
1917         void setPlayTime(long playTime, boolean inReverse) {
1918             // TODO: This can be simplified.
1919 
1920             // Clamp the play time
1921             if (getTotalDuration() != DURATION_INFINITE) {
1922                 mPlayTime = Math.min(playTime, getTotalDuration() - mStartDelay);
1923             }
1924             mPlayTime = Math.max(0, mPlayTime);
1925             mSeekingInReverse = inReverse;
1926         }
1927 
1928         void updateSeekDirection(boolean inReverse) {
1929             // Change seek direction without changing the overall fraction
1930             if (inReverse && getTotalDuration() == DURATION_INFINITE) {
1931                 throw new UnsupportedOperationException("Error: Cannot reverse infinite animator"
1932                         + " set");
1933             }
1934             if (mPlayTime >= 0) {
1935                 if (inReverse != mSeekingInReverse) {
1936                     mPlayTime = getTotalDuration() - mStartDelay - mPlayTime;
1937                     mSeekingInReverse = inReverse;
1938                 }
1939             }
1940         }
1941 
1942         long getPlayTime() {
1943             return mPlayTime;
1944         }
1945 
1946         /**
1947          * Returns the playtime assuming the animation is forward playing
1948          */
1949         long getPlayTimeNormalized() {
1950             if (mReversing) {
1951                 return getTotalDuration() - mStartDelay - mPlayTime;
1952             }
1953             return mPlayTime;
1954         }
1955 
1956         boolean isActive() {
1957             return mPlayTime != -1;
1958         }
1959     }
1960 
1961     /**
1962      * The <code>Builder</code> object is a utility class to facilitate adding animations to a
1963      * <code>AnimatorSet</code> along with the relationships between the various animations. The
1964      * intention of the <code>Builder</code> methods, along with the {@link
1965      * AnimatorSet#play(Animator) play()} method of <code>AnimatorSet</code> is to make it possible
1966      * to express the dependency relationships of animations in a natural way. Developers can also
1967      * use the {@link AnimatorSet#playTogether(Animator[]) playTogether()} and {@link
1968      * AnimatorSet#playSequentially(Animator[]) playSequentially()} methods if these suit the need,
1969      * but it might be easier in some situations to express the AnimatorSet of animations in pairs.
1970      * <p/>
1971      * <p>The <code>Builder</code> object cannot be constructed directly, but is rather constructed
1972      * internally via a call to {@link AnimatorSet#play(Animator)}.</p>
1973      * <p/>
1974      * <p>For example, this sets up a AnimatorSet to play anim1 and anim2 at the same time, anim3 to
1975      * play when anim2 finishes, and anim4 to play when anim3 finishes:</p>
1976      * <pre>
1977      *     AnimatorSet s = new AnimatorSet();
1978      *     s.play(anim1).with(anim2);
1979      *     s.play(anim2).before(anim3);
1980      *     s.play(anim4).after(anim3);
1981      * </pre>
1982      * <p/>
1983      * <p>Note in the example that both {@link Builder#before(Animator)} and {@link
1984      * Builder#after(Animator)} are used. These are just different ways of expressing the same
1985      * relationship and are provided to make it easier to say things in a way that is more natural,
1986      * depending on the situation.</p>
1987      * <p/>
1988      * <p>It is possible to make several calls into the same <code>Builder</code> object to express
1989      * multiple relationships. However, note that it is only the animation passed into the initial
1990      * {@link AnimatorSet#play(Animator)} method that is the dependency in any of the successive
1991      * calls to the <code>Builder</code> object. For example, the following code starts both anim2
1992      * and anim3 when anim1 ends; there is no direct dependency relationship between anim2 and
1993      * anim3:
1994      * <pre>
1995      *   AnimatorSet s = new AnimatorSet();
1996      *   s.play(anim1).before(anim2).before(anim3);
1997      * </pre>
1998      * If the desired result is to play anim1 then anim2 then anim3, this code expresses the
1999      * relationship correctly:</p>
2000      * <pre>
2001      *   AnimatorSet s = new AnimatorSet();
2002      *   s.play(anim1).before(anim2);
2003      *   s.play(anim2).before(anim3);
2004      * </pre>
2005      * <p/>
2006      * <p>Note that it is possible to express relationships that cannot be resolved and will not
2007      * result in sensible results. For example, <code>play(anim1).after(anim1)</code> makes no
2008      * sense. In general, circular dependencies like this one (or more indirect ones where a depends
2009      * on b, which depends on c, which depends on a) should be avoided. Only create AnimatorSets
2010      * that can boil down to a simple, one-way relationship of animations starting with, before, and
2011      * after other, different, animations.</p>
2012      */
2013     public class Builder {
2014 
2015         /**
2016          * This tracks the current node being processed. It is supplied to the play() method
2017          * of AnimatorSet and passed into the constructor of Builder.
2018          */
2019         private Node mCurrentNode;
2020 
2021         /**
2022          * package-private constructor. Builders are only constructed by AnimatorSet, when the
2023          * play() method is called.
2024          *
2025          * @param anim The animation that is the dependency for the other animations passed into
2026          * the other methods of this Builder object.
2027          */
2028         Builder(Animator anim) {
2029             mDependencyDirty = true;
2030             mCurrentNode = getNodeForAnimation(anim);
2031         }
2032 
2033         /**
2034          * Sets up the given animation to play at the same time as the animation supplied in the
2035          * {@link AnimatorSet#play(Animator)} call that created this <code>Builder</code> object.
2036          *
2037          * @param anim The animation that will play when the animation supplied to the
2038          * {@link AnimatorSet#play(Animator)} method starts.
2039          */
2040         public Builder with(Animator anim) {
2041             Node node = getNodeForAnimation(anim);
2042             mCurrentNode.addSibling(node);
2043             return this;
2044         }
2045 
2046         /**
2047          * Sets up the given animation to play when the animation supplied in the
2048          * {@link AnimatorSet#play(Animator)} call that created this <code>Builder</code> object
2049          * ends.
2050          *
2051          * @param anim The animation that will play when the animation supplied to the
2052          * {@link AnimatorSet#play(Animator)} method ends.
2053          */
2054         public Builder before(Animator anim) {
2055             Node node = getNodeForAnimation(anim);
2056             mCurrentNode.addChild(node);
2057             return this;
2058         }
2059 
2060         /**
2061          * Sets up the given animation to play when the animation supplied in the
2062          * {@link AnimatorSet#play(Animator)} call that created this <code>Builder</code> object
2063          * to start when the animation supplied in this method call ends.
2064          *
2065          * @param anim The animation whose end will cause the animation supplied to the
2066          * {@link AnimatorSet#play(Animator)} method to play.
2067          */
2068         public Builder after(Animator anim) {
2069             Node node = getNodeForAnimation(anim);
2070             mCurrentNode.addParent(node);
2071             return this;
2072         }
2073 
2074         /**
2075          * Sets up the animation supplied in the
2076          * {@link AnimatorSet#play(Animator)} call that created this <code>Builder</code> object
2077          * to play when the given amount of time elapses.
2078          *
2079          * @param delay The number of milliseconds that should elapse before the
2080          * animation starts.
2081          */
2082         public Builder after(long delay) {
2083             // setup dummy ValueAnimator just to run the clock
2084             ValueAnimator anim = ValueAnimator.ofFloat(0f, 1f);
2085             anim.setDuration(delay);
2086             after(anim);
2087             return this;
2088         }
2089 
2090     }
2091 
2092 }
2093