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 mAnimationEndingListener = 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 
840         ArrayList<Node> unfinishedNodes = new ArrayList<>();
841         // Assumes forward playing from here on.
842         for (int i = 0; i < mEvents.size(); i++) {
843             AnimationEvent event = mEvents.get(i);
844             if (event.getTime() > currentPlayTime || event.getTime() == DURATION_INFINITE) {
845                 break;
846             }
847 
848             // This animation started prior to the current play time, and won't finish before the
849             // play time, add to the unfinished list.
850             if (event.mEvent == AnimationEvent.ANIMATION_DELAY_ENDED) {
851                 if (event.mNode.mEndTime == DURATION_INFINITE
852                         || event.mNode.mEndTime > currentPlayTime) {
853                     unfinishedNodes.add(event.mNode);
854                 }
855             }
856             // For animations that do finish before the play time, end them in the sequence that
857             // they would in a normal run.
858             if (event.mEvent == AnimationEvent.ANIMATION_END) {
859                 // Skip to the end of the animation.
860                 event.mNode.mAnimation.skipToEndValue(false);
861             }
862         }
863 
864         // Seek unfinished animation to the right time.
865         for (int i = 0; i < unfinishedNodes.size(); i++) {
866             Node node = unfinishedNodes.get(i);
867             long playTime = getPlayTimeForNode(currentPlayTime, node, inReverse);
868             if (!inReverse) {
869                 playTime -= node.mAnimation.getStartDelay();
870             }
871             node.mAnimation.animateBasedOnPlayTime(playTime, lastPlayTime, inReverse);
872         }
873 
874         // Seek not yet started animations.
875         for (int i = 0; i < mEvents.size(); i++) {
876             AnimationEvent event = mEvents.get(i);
877             if (event.getTime() > currentPlayTime
878                     && event.mEvent == AnimationEvent.ANIMATION_DELAY_ENDED) {
879                 event.mNode.mAnimation.skipToEndValue(true);
880             }
881         }
882 
883     }
884 
885     @Override
886     boolean isInitialized() {
887         if (mChildrenInitialized) {
888             return true;
889         }
890 
891         boolean allInitialized = true;
892         for (int i = 0; i < mNodes.size(); i++) {
893             if (!mNodes.get(i).mAnimation.isInitialized()) {
894                 allInitialized = false;
895                 break;
896             }
897         }
898         mChildrenInitialized = allInitialized;
899         return mChildrenInitialized;
900     }
901 
902     private void skipToStartValue(boolean inReverse) {
903         skipToEndValue(!inReverse);
904     }
905 
906     /**
907      * Sets the position of the animation to the specified point in time. This time should
908      * be between 0 and the total duration of the animation, including any repetition. If
909      * the animation has not yet been started, then it will not advance forward after it is
910      * set to this time; it will simply set the time to this value and perform any appropriate
911      * actions based on that time. If the animation is already running, then setCurrentPlayTime()
912      * will set the current playing time to this value and continue playing from that point.
913      *
914      * @param playTime The time, in milliseconds, to which the animation is advanced or rewound.
915      *                 Unless the animation is reversing, the playtime is considered the time since
916      *                 the end of the start delay of the AnimatorSet in a forward playing direction.
917      *
918      */
919     public void setCurrentPlayTime(long playTime) {
920         if (mReversing && getTotalDuration() == DURATION_INFINITE) {
921             // Should never get here
922             throw new UnsupportedOperationException("Error: Cannot seek in reverse in an infinite"
923                     + " AnimatorSet");
924         }
925 
926         if ((getTotalDuration() != DURATION_INFINITE && playTime > getTotalDuration() - mStartDelay)
927                 || playTime < 0) {
928             throw new UnsupportedOperationException("Error: Play time should always be in between"
929                     + "0 and duration.");
930         }
931 
932         initAnimation();
933 
934         if (!isStarted() || isPaused()) {
935             if (mReversing) {
936                 throw new UnsupportedOperationException("Error: Something went wrong. mReversing"
937                         + " should not be set when AnimatorSet is not started.");
938             }
939             if (!mSeekState.isActive()) {
940                 findLatestEventIdForTime(0);
941                 // Set all the values to start values.
942                 initChildren();
943                 mSeekState.setPlayTime(0, mReversing);
944             }
945             animateBasedOnPlayTime(playTime, 0, mReversing);
946             mSeekState.setPlayTime(playTime, mReversing);
947         } else {
948             // If the animation is running, just set the seek time and wait until the next frame
949             // (i.e. doAnimationFrame(...)) to advance the animation.
950             mSeekState.setPlayTime(playTime, mReversing);
951         }
952     }
953 
954     /**
955      * Returns the milliseconds elapsed since the start of the animation.
956      *
957      * <p>For ongoing animations, this method returns the current progress of the animation in
958      * terms of play time. For an animation that has not yet been started: if the animation has been
959      * seeked to a certain time via {@link #setCurrentPlayTime(long)}, the seeked play time will
960      * be returned; otherwise, this method will return 0.
961      *
962      * @return the current position in time of the animation in milliseconds
963      */
964     public long getCurrentPlayTime() {
965         if (mSeekState.isActive()) {
966             return mSeekState.getPlayTime();
967         }
968         if (mLastFrameTime == -1) {
969             // Not yet started or during start delay
970             return 0;
971         }
972         float durationScale = ValueAnimator.getDurationScale();
973         durationScale = durationScale == 0 ? 1 : durationScale;
974         if (mReversing) {
975             return (long) ((mLastFrameTime - mFirstFrame) / durationScale);
976         } else {
977             return (long) ((mLastFrameTime - mFirstFrame - mStartDelay) / durationScale);
978         }
979     }
980 
981     private void initChildren() {
982         if (!isInitialized()) {
983             mChildrenInitialized = true;
984             // Forcefully initialize all children based on their end time, so that if the start
985             // value of a child is dependent on a previous animation, the animation will be
986             // initialized after the the previous animations have been advanced to the end.
987             skipToEndValue(false);
988         }
989     }
990 
991     /**
992      * @param frameTime The frame start time, in the {@link SystemClock#uptimeMillis()} time
993      *                  base.
994      * @return
995      * @hide
996      */
997     @Override
998     public boolean doAnimationFrame(long frameTime) {
999         float durationScale = ValueAnimator.getDurationScale();
1000         if (durationScale == 0f) {
1001             // Duration scale is 0, end the animation right away.
1002             forceToEnd();
1003             return true;
1004         }
1005 
1006         // After the first frame comes in, we need to wait for start delay to pass before updating
1007         // any animation values.
1008         if (mFirstFrame < 0) {
1009             mFirstFrame = frameTime;
1010         }
1011 
1012         // Handle pause/resume
1013         if (mPaused) {
1014             // Note: Child animations don't receive pause events. Since it's never a contract that
1015             // the child animators will be paused when set is paused, this is unlikely to be an
1016             // issue.
1017             mPauseTime = frameTime;
1018             removeAnimationCallback();
1019             return false;
1020         } else if (mPauseTime > 0) {
1021                 // Offset by the duration that the animation was paused
1022             mFirstFrame += (frameTime - mPauseTime);
1023             mPauseTime = -1;
1024         }
1025 
1026         // Continue at seeked position
1027         if (mSeekState.isActive()) {
1028             mSeekState.updateSeekDirection(mReversing);
1029             if (mReversing) {
1030                 mFirstFrame = (long) (frameTime - mSeekState.getPlayTime() * durationScale);
1031             } else {
1032                 mFirstFrame = (long) (frameTime - (mSeekState.getPlayTime() + mStartDelay)
1033                         * durationScale);
1034             }
1035             mSeekState.reset();
1036         }
1037 
1038         if (!mReversing && frameTime < mFirstFrame + mStartDelay * durationScale) {
1039             // Still during start delay in a forward playing case.
1040             return false;
1041         }
1042 
1043         // From here on, we always use unscaled play time. Note this unscaled playtime includes
1044         // the start delay.
1045         long unscaledPlayTime = (long) ((frameTime - mFirstFrame) / durationScale);
1046         mLastFrameTime = frameTime;
1047 
1048         // 1. Pulse the animators that will start or end in this frame
1049         // 2. Pulse the animators that will finish in a later frame
1050         int latestId = findLatestEventIdForTime(unscaledPlayTime);
1051         int startId = mLastEventId;
1052 
1053         handleAnimationEvents(startId, latestId, unscaledPlayTime);
1054 
1055         mLastEventId = latestId;
1056 
1057         // Pump a frame to the on-going animators
1058         for (int i = 0; i < mPlayingSet.size(); i++) {
1059             Node node = mPlayingSet.get(i);
1060             if (!node.mEnded) {
1061                 pulseFrame(node, getPlayTimeForNode(unscaledPlayTime, node));
1062             }
1063         }
1064 
1065         // Remove all the finished anims
1066         for (int i = mPlayingSet.size() - 1; i >= 0; i--) {
1067             if (mPlayingSet.get(i).mEnded) {
1068                 mPlayingSet.remove(i);
1069             }
1070         }
1071 
1072         boolean finished = false;
1073         if (mReversing) {
1074             if (mPlayingSet.size() == 1 && mPlayingSet.get(0) == mRootNode) {
1075                 // The only animation that is running is the delay animation.
1076                 finished = true;
1077             } else if (mPlayingSet.isEmpty() && mLastEventId < 3) {
1078                 // The only remaining animation is the delay animation
1079                 finished = true;
1080             }
1081         } else {
1082             finished = mPlayingSet.isEmpty() && mLastEventId == mEvents.size() - 1;
1083         }
1084 
1085         if (finished) {
1086             endAnimation();
1087             return true;
1088         }
1089         return false;
1090     }
1091 
1092     /**
1093      * @hide
1094      */
1095     @Override
1096     public void commitAnimationFrame(long frameTime) {
1097         // No op.
1098     }
1099 
1100     @Override
1101     boolean pulseAnimationFrame(long frameTime) {
1102         return doAnimationFrame(frameTime);
1103     }
1104 
1105     /**
1106      * When playing forward, we call start() at the animation's scheduled start time, and make sure
1107      * to pump a frame at the animation's scheduled end time.
1108      *
1109      * When playing in reverse, we should reverse the animation when we hit animation's end event,
1110      * and expect the animation to end at the its delay ended event, rather than start event.
1111      */
1112     private void handleAnimationEvents(int startId, int latestId, long playTime) {
1113         if (mReversing) {
1114             startId = startId == -1 ? mEvents.size() : startId;
1115             for (int i = startId - 1; i >= latestId; i--) {
1116                 AnimationEvent event = mEvents.get(i);
1117                 Node node = event.mNode;
1118                 if (event.mEvent == AnimationEvent.ANIMATION_END) {
1119                     if (node.mAnimation.isStarted()) {
1120                         // If the animation has already been started before its due time (i.e.
1121                         // the child animator is being manipulated outside of the AnimatorSet), we
1122                         // need to cancel the animation to reset the internal state (e.g. frame
1123                         // time tracking) and remove the self pulsing callbacks
1124                         node.mAnimation.cancel();
1125                     }
1126                     node.mEnded = false;
1127                     mPlayingSet.add(event.mNode);
1128                     node.mAnimation.startWithoutPulsing(true);
1129                     pulseFrame(node, 0);
1130                 } else if (event.mEvent == AnimationEvent.ANIMATION_DELAY_ENDED && !node.mEnded) {
1131                     // end event:
1132                     pulseFrame(node, getPlayTimeForNode(playTime, node));
1133                 }
1134             }
1135         } else {
1136             for (int i = startId + 1; i <= latestId; i++) {
1137                 AnimationEvent event = mEvents.get(i);
1138                 Node node = event.mNode;
1139                 if (event.mEvent == AnimationEvent.ANIMATION_START) {
1140                     mPlayingSet.add(event.mNode);
1141                     if (node.mAnimation.isStarted()) {
1142                         // If the animation has already been started before its due time (i.e.
1143                         // the child animator is being manipulated outside of the AnimatorSet), we
1144                         // need to cancel the animation to reset the internal state (e.g. frame
1145                         // time tracking) and remove the self pulsing callbacks
1146                         node.mAnimation.cancel();
1147                     }
1148                     node.mEnded = false;
1149                     node.mAnimation.startWithoutPulsing(false);
1150                     pulseFrame(node, 0);
1151                 } else if (event.mEvent == AnimationEvent.ANIMATION_END && !node.mEnded) {
1152                     // start event:
1153                     pulseFrame(node, getPlayTimeForNode(playTime, node));
1154                 }
1155             }
1156         }
1157     }
1158 
1159     /**
1160      * This method pulses frames into child animations. It scales the input animation play time
1161      * with the duration scale and pass that to the child animation via pulseAnimationFrame(long).
1162      *
1163      * @param node child animator node
1164      * @param animPlayTime unscaled play time (including start delay) for the child animator
1165      */
1166     private void pulseFrame(Node node, long animPlayTime) {
1167         if (!node.mEnded) {
1168             float durationScale = ValueAnimator.getDurationScale();
1169             durationScale = durationScale == 0  ? 1 : durationScale;
1170             node.mEnded = node.mAnimation.pulseAnimationFrame(
1171                     (long) (animPlayTime * durationScale));
1172         }
1173     }
1174 
1175     private long getPlayTimeForNode(long overallPlayTime, Node node) {
1176         return getPlayTimeForNode(overallPlayTime, node, mReversing);
1177     }
1178 
1179     private long getPlayTimeForNode(long overallPlayTime, Node node, boolean inReverse) {
1180         if (inReverse) {
1181             overallPlayTime = getTotalDuration() - overallPlayTime;
1182             return node.mEndTime - overallPlayTime;
1183         } else {
1184             return overallPlayTime - node.mStartTime;
1185         }
1186     }
1187 
1188     private void startAnimation() {
1189         addAnimationEndingListener();
1190 
1191         // Register animation callback
1192         addAnimationCallback(0);
1193 
1194         if (mSeekState.getPlayTimeNormalized() == 0 && mReversing) {
1195             // Maintain old behavior, if seeked to 0 then call reverse, we'll treat the case
1196             // the same as no seeking at all.
1197             mSeekState.reset();
1198         }
1199         // Set the child animators to the right end:
1200         if (mShouldResetValuesAtStart) {
1201             if (isInitialized()) {
1202                 skipToEndValue(!mReversing);
1203             } else if (mReversing) {
1204                 // Reversing but haven't initialized all the children yet.
1205                 initChildren();
1206                 skipToEndValue(!mReversing);
1207             } else {
1208                 // If not all children are initialized and play direction is forward
1209                 for (int i = mEvents.size() - 1; i >= 0; i--) {
1210                     if (mEvents.get(i).mEvent == AnimationEvent.ANIMATION_DELAY_ENDED) {
1211                         Animator anim = mEvents.get(i).mNode.mAnimation;
1212                         // Only reset the animations that have been initialized to start value,
1213                         // so that if they are defined without a start value, they will get the
1214                         // values set at the right time (i.e. the next animation run)
1215                         if (anim.isInitialized()) {
1216                             anim.skipToEndValue(true);
1217                         }
1218                     }
1219                 }
1220             }
1221         }
1222 
1223         if (mReversing || mStartDelay == 0 || mSeekState.isActive()) {
1224             long playTime;
1225             // If no delay, we need to call start on the first animations to be consistent with old
1226             // behavior.
1227             if (mSeekState.isActive()) {
1228                 mSeekState.updateSeekDirection(mReversing);
1229                 playTime = mSeekState.getPlayTime();
1230             } else {
1231                 playTime = 0;
1232             }
1233             int toId = findLatestEventIdForTime(playTime);
1234             handleAnimationEvents(-1, toId, playTime);
1235             for (int i = mPlayingSet.size() - 1; i >= 0; i--) {
1236                 if (mPlayingSet.get(i).mEnded) {
1237                     mPlayingSet.remove(i);
1238                 }
1239             }
1240             mLastEventId = toId;
1241         }
1242     }
1243 
1244     // This is to work around the issue in b/34736819, as the old behavior in AnimatorSet had
1245     // masked a real bug in play movies. TODO: remove this and below once the root cause is fixed.
1246     private void addAnimationEndingListener() {
1247         for (int i = 1; i < mNodes.size(); i++) {
1248             mNodes.get(i).mAnimation.addListener(mAnimationEndingListener);
1249         }
1250     }
1251 
1252     private void removeAnimationEndingListener() {
1253         for (int i = 1; i < mNodes.size(); i++) {
1254             mNodes.get(i).mAnimation.removeListener(mAnimationEndingListener);
1255         }
1256     }
1257 
1258     private int findLatestEventIdForTime(long currentPlayTime) {
1259         int size = mEvents.size();
1260         int latestId = mLastEventId;
1261         // Call start on the first animations now to be consistent with the old behavior
1262         if (mReversing) {
1263             currentPlayTime = getTotalDuration() - currentPlayTime;
1264             mLastEventId = mLastEventId == -1 ? size : mLastEventId;
1265             for (int j = mLastEventId - 1; j >= 0; j--) {
1266                 AnimationEvent event = mEvents.get(j);
1267                 if (event.getTime() >= currentPlayTime) {
1268                     latestId = j;
1269                 }
1270             }
1271         } else {
1272             for (int i = mLastEventId + 1; i < size; i++) {
1273                 AnimationEvent event = mEvents.get(i);
1274                 // TODO: need a function that accounts for infinite duration to compare time
1275                 if (event.getTime() != DURATION_INFINITE && event.getTime() <= currentPlayTime) {
1276                     latestId = i;
1277                 }
1278             }
1279         }
1280         return latestId;
1281     }
1282 
1283     private void endAnimation() {
1284         mStarted = false;
1285         mLastFrameTime = -1;
1286         mFirstFrame = -1;
1287         mLastEventId = -1;
1288         mPaused = false;
1289         mPauseTime = -1;
1290         mSeekState.reset();
1291         mPlayingSet.clear();
1292 
1293         // No longer receive callbacks
1294         removeAnimationCallback();
1295         // Call end listener
1296         if (mListeners != null) {
1297             ArrayList<AnimatorListener> tmpListeners =
1298                     (ArrayList<AnimatorListener>) mListeners.clone();
1299             int numListeners = tmpListeners.size();
1300             for (int i = 0; i < numListeners; ++i) {
1301                 tmpListeners.get(i).onAnimationEnd(this, mReversing);
1302             }
1303         }
1304         removeAnimationEndingListener();
1305         mSelfPulse = true;
1306         mReversing = false;
1307     }
1308 
1309     private void removeAnimationCallback() {
1310         if (!mSelfPulse) {
1311             return;
1312         }
1313         AnimationHandler handler = AnimationHandler.getInstance();
1314         handler.removeCallback(this);
1315     }
1316 
1317     private void addAnimationCallback(long delay) {
1318         if (!mSelfPulse) {
1319             return;
1320         }
1321         AnimationHandler handler = AnimationHandler.getInstance();
1322         handler.addAnimationFrameCallback(this, delay);
1323     }
1324 
1325     @Override
1326     public AnimatorSet clone() {
1327         final AnimatorSet anim = (AnimatorSet) super.clone();
1328         /*
1329          * The basic clone() operation copies all items. This doesn't work very well for
1330          * AnimatorSet, because it will copy references that need to be recreated and state
1331          * that may not apply. What we need to do now is put the clone in an uninitialized
1332          * state, with fresh, empty data structures. Then we will build up the nodes list
1333          * manually, as we clone each Node (and its animation). The clone will then be sorted,
1334          * and will populate any appropriate lists, when it is started.
1335          */
1336         final int nodeCount = mNodes.size();
1337         anim.mStarted = false;
1338         anim.mLastFrameTime = -1;
1339         anim.mFirstFrame = -1;
1340         anim.mLastEventId = -1;
1341         anim.mPaused = false;
1342         anim.mPauseTime = -1;
1343         anim.mSeekState = new SeekState();
1344         anim.mSelfPulse = true;
1345         anim.mPlayingSet = new ArrayList<Node>();
1346         anim.mNodeMap = new ArrayMap<Animator, Node>();
1347         anim.mNodes = new ArrayList<Node>(nodeCount);
1348         anim.mEvents = new ArrayList<AnimationEvent>();
1349         anim.mAnimationEndingListener = new AnimatorListenerAdapter() {
1350             @Override
1351             public void onAnimationEnd(Animator animation) {
1352                 if (anim.mNodeMap.get(animation) == null) {
1353                     throw new AndroidRuntimeException("Error: animation ended is not in the node"
1354                             + " map");
1355                 }
1356                 anim.mNodeMap.get(animation).mEnded = true;
1357 
1358             }
1359         };
1360         anim.mReversing = false;
1361         anim.mDependencyDirty = true;
1362 
1363         // Walk through the old nodes list, cloning each node and adding it to the new nodemap.
1364         // One problem is that the old node dependencies point to nodes in the old AnimatorSet.
1365         // We need to track the old/new nodes in order to reconstruct the dependencies in the clone.
1366 
1367         HashMap<Node, Node> clonesMap = new HashMap<>(nodeCount);
1368         for (int n = 0; n < nodeCount; n++) {
1369             final Node node = mNodes.get(n);
1370             Node nodeClone = node.clone();
1371             // Remove the old internal listener from the cloned child
1372             nodeClone.mAnimation.removeListener(mAnimationEndingListener);
1373             clonesMap.put(node, nodeClone);
1374             anim.mNodes.add(nodeClone);
1375             anim.mNodeMap.put(nodeClone.mAnimation, nodeClone);
1376         }
1377 
1378         anim.mRootNode = clonesMap.get(mRootNode);
1379         anim.mDelayAnim = (ValueAnimator) anim.mRootNode.mAnimation;
1380 
1381         // Now that we've cloned all of the nodes, we're ready to walk through their
1382         // dependencies, mapping the old dependencies to the new nodes
1383         for (int i = 0; i < nodeCount; i++) {
1384             Node node = mNodes.get(i);
1385             // Update dependencies for node's clone
1386             Node nodeClone = clonesMap.get(node);
1387             nodeClone.mLatestParent = node.mLatestParent == null
1388                     ? null : clonesMap.get(node.mLatestParent);
1389             int size = node.mChildNodes == null ? 0 : node.mChildNodes.size();
1390             for (int j = 0; j < size; j++) {
1391                 nodeClone.mChildNodes.set(j, clonesMap.get(node.mChildNodes.get(j)));
1392             }
1393             size = node.mSiblings == null ? 0 : node.mSiblings.size();
1394             for (int j = 0; j < size; j++) {
1395                 nodeClone.mSiblings.set(j, clonesMap.get(node.mSiblings.get(j)));
1396             }
1397             size = node.mParents == null ? 0 : node.mParents.size();
1398             for (int j = 0; j < size; j++) {
1399                 nodeClone.mParents.set(j, clonesMap.get(node.mParents.get(j)));
1400             }
1401         }
1402         return anim;
1403     }
1404 
1405 
1406     /**
1407      * AnimatorSet is only reversible when the set contains no sequential animation, and no child
1408      * animators have a start delay.
1409      * @hide
1410      */
1411     @Override
1412     public boolean canReverse() {
1413         return getTotalDuration() != DURATION_INFINITE;
1414     }
1415 
1416     /**
1417      * Plays the AnimatorSet in reverse. If the animation has been seeked to a specific play time
1418      * using {@link #setCurrentPlayTime(long)}, it will play backwards from the point seeked when
1419      * reverse was called. Otherwise, then it will start from the end and play backwards. This
1420      * behavior is only set for the current animation; future playing of the animation will use the
1421      * default behavior of playing forward.
1422      * <p>
1423      * Note: reverse is not supported for infinite AnimatorSet.
1424      */
1425     @Override
1426     public void reverse() {
1427         start(true, true);
1428     }
1429 
1430     @Override
1431     public String toString() {
1432         String returnVal = "AnimatorSet@" + Integer.toHexString(hashCode()) + "{";
1433         int size = mNodes.size();
1434         for (int i = 0; i < size; i++) {
1435             Node node = mNodes.get(i);
1436             returnVal += "\n    " + node.mAnimation.toString();
1437         }
1438         return returnVal + "\n}";
1439     }
1440 
1441     private void printChildCount() {
1442         // Print out the child count through a level traverse.
1443         ArrayList<Node> list = new ArrayList<>(mNodes.size());
1444         list.add(mRootNode);
1445         Log.d(TAG, "Current tree: ");
1446         int index = 0;
1447         while (index < list.size()) {
1448             int listSize = list.size();
1449             StringBuilder builder = new StringBuilder();
1450             for (; index < listSize; index++) {
1451                 Node node = list.get(index);
1452                 int num = 0;
1453                 if (node.mChildNodes != null) {
1454                     for (int i = 0; i < node.mChildNodes.size(); i++) {
1455                         Node child = node.mChildNodes.get(i);
1456                         if (child.mLatestParent == node) {
1457                             num++;
1458                             list.add(child);
1459                         }
1460                     }
1461                 }
1462                 builder.append(" ");
1463                 builder.append(num);
1464             }
1465             Log.d(TAG, builder.toString());
1466         }
1467     }
1468 
1469     private void createDependencyGraph() {
1470         if (!mDependencyDirty) {
1471             // Check whether any duration of the child animations has changed
1472             boolean durationChanged = false;
1473             for (int i = 0; i < mNodes.size(); i++) {
1474                 Animator anim = mNodes.get(i).mAnimation;
1475                 if (mNodes.get(i).mTotalDuration != anim.getTotalDuration()) {
1476                     durationChanged = true;
1477                     break;
1478                 }
1479             }
1480             if (!durationChanged) {
1481                 return;
1482             }
1483         }
1484 
1485         mDependencyDirty = false;
1486         // Traverse all the siblings and make sure they have all the parents
1487         int size = mNodes.size();
1488         for (int i = 0; i < size; i++) {
1489             mNodes.get(i).mParentsAdded = false;
1490         }
1491         for (int i = 0; i < size; i++) {
1492             Node node = mNodes.get(i);
1493             if (node.mParentsAdded) {
1494                 continue;
1495             }
1496 
1497             node.mParentsAdded = true;
1498             if (node.mSiblings == null) {
1499                 continue;
1500             }
1501 
1502             // Find all the siblings
1503             findSiblings(node, node.mSiblings);
1504             node.mSiblings.remove(node);
1505 
1506             // Get parents from all siblings
1507             int siblingSize = node.mSiblings.size();
1508             for (int j = 0; j < siblingSize; j++) {
1509                 node.addParents(node.mSiblings.get(j).mParents);
1510             }
1511 
1512             // Now make sure all siblings share the same set of parents
1513             for (int j = 0; j < siblingSize; j++) {
1514                 Node sibling = node.mSiblings.get(j);
1515                 sibling.addParents(node.mParents);
1516                 sibling.mParentsAdded = true;
1517             }
1518         }
1519 
1520         for (int i = 0; i < size; i++) {
1521             Node node = mNodes.get(i);
1522             if (node != mRootNode && node.mParents == null) {
1523                 node.addParent(mRootNode);
1524             }
1525         }
1526 
1527         // Do a DFS on the tree
1528         ArrayList<Node> visited = new ArrayList<Node>(mNodes.size());
1529         // Assign start/end time
1530         mRootNode.mStartTime = 0;
1531         mRootNode.mEndTime = mDelayAnim.getDuration();
1532         updatePlayTime(mRootNode, visited);
1533 
1534         sortAnimationEvents();
1535         mTotalDuration = mEvents.get(mEvents.size() - 1).getTime();
1536     }
1537 
1538     private void sortAnimationEvents() {
1539         // Sort the list of events in ascending order of their time
1540         // Create the list including the delay animation.
1541         mEvents.clear();
1542         for (int i = 1; i < mNodes.size(); i++) {
1543             Node node = mNodes.get(i);
1544             mEvents.add(new AnimationEvent(node, AnimationEvent.ANIMATION_START));
1545             mEvents.add(new AnimationEvent(node, AnimationEvent.ANIMATION_DELAY_ENDED));
1546             mEvents.add(new AnimationEvent(node, AnimationEvent.ANIMATION_END));
1547         }
1548         mEvents.sort(new Comparator<AnimationEvent>() {
1549             @Override
1550             public int compare(AnimationEvent e1, AnimationEvent e2) {
1551                 long t1 = e1.getTime();
1552                 long t2 = e2.getTime();
1553                 if (t1 == t2) {
1554                     // For events that happen at the same time, we need them to be in the sequence
1555                     // (end, start, start delay ended)
1556                     if (e2.mEvent + e1.mEvent == AnimationEvent.ANIMATION_START
1557                             + AnimationEvent.ANIMATION_DELAY_ENDED) {
1558                         // Ensure start delay happens after start
1559                         return e1.mEvent - e2.mEvent;
1560                     } else {
1561                         return e2.mEvent - e1.mEvent;
1562                     }
1563                 }
1564                 if (t2 == DURATION_INFINITE) {
1565                     return -1;
1566                 }
1567                 if (t1 == DURATION_INFINITE) {
1568                     return 1;
1569                 }
1570                 // When neither event happens at INFINITE time:
1571                 return (int) (t1 - t2);
1572             }
1573         });
1574 
1575         int eventSize = mEvents.size();
1576         // For the same animation, start event has to happen before end.
1577         for (int i = 0; i < eventSize;) {
1578             AnimationEvent event = mEvents.get(i);
1579             if (event.mEvent == AnimationEvent.ANIMATION_END) {
1580                 boolean needToSwapStart;
1581                 if (event.mNode.mStartTime == event.mNode.mEndTime) {
1582                     needToSwapStart = true;
1583                 } else if (event.mNode.mEndTime == event.mNode.mStartTime
1584                         + event.mNode.mAnimation.getStartDelay()) {
1585                     // Swapping start delay
1586                     needToSwapStart = false;
1587                 } else {
1588                     i++;
1589                     continue;
1590                 }
1591 
1592                 int startEventId = eventSize;
1593                 int startDelayEndId = eventSize;
1594                 for (int j = i + 1; j < eventSize; j++) {
1595                     if (startEventId < eventSize && startDelayEndId < eventSize) {
1596                         break;
1597                     }
1598                     if (mEvents.get(j).mNode == event.mNode) {
1599                         if (mEvents.get(j).mEvent == AnimationEvent.ANIMATION_START) {
1600                             // Found start event
1601                             startEventId = j;
1602                         } else if (mEvents.get(j).mEvent == AnimationEvent.ANIMATION_DELAY_ENDED) {
1603                             startDelayEndId = j;
1604                         }
1605                     }
1606 
1607                 }
1608                 if (needToSwapStart && startEventId == mEvents.size()) {
1609                     throw new UnsupportedOperationException("Something went wrong, no start is"
1610                             + "found after stop for an animation that has the same start and end"
1611                             + "time.");
1612 
1613                 }
1614                 if (startDelayEndId == mEvents.size()) {
1615                     throw new UnsupportedOperationException("Something went wrong, no start"
1616                             + "delay end is found after stop for an animation");
1617 
1618                 }
1619 
1620                 // We need to make sure start is inserted before start delay ended event,
1621                 // because otherwise inserting start delay ended events first would change
1622                 // the start event index.
1623                 if (needToSwapStart) {
1624                     AnimationEvent startEvent = mEvents.remove(startEventId);
1625                     mEvents.add(i, startEvent);
1626                     i++;
1627                 }
1628 
1629                 AnimationEvent startDelayEndEvent = mEvents.remove(startDelayEndId);
1630                 mEvents.add(i, startDelayEndEvent);
1631                 i += 2;
1632             } else {
1633                 i++;
1634             }
1635         }
1636 
1637         if (!mEvents.isEmpty() && mEvents.get(0).mEvent != AnimationEvent.ANIMATION_START) {
1638             throw new UnsupportedOperationException(
1639                     "Sorting went bad, the start event should always be at index 0");
1640         }
1641 
1642         // Add AnimatorSet's start delay node to the beginning
1643         mEvents.add(0, new AnimationEvent(mRootNode, AnimationEvent.ANIMATION_START));
1644         mEvents.add(1, new AnimationEvent(mRootNode, AnimationEvent.ANIMATION_DELAY_ENDED));
1645         mEvents.add(2, new AnimationEvent(mRootNode, AnimationEvent.ANIMATION_END));
1646 
1647         if (mEvents.get(mEvents.size() - 1).mEvent == AnimationEvent.ANIMATION_START
1648                 || mEvents.get(mEvents.size() - 1).mEvent == AnimationEvent.ANIMATION_DELAY_ENDED) {
1649             throw new UnsupportedOperationException(
1650                     "Something went wrong, the last event is not an end event");
1651         }
1652     }
1653 
1654     /**
1655      * Based on parent's start/end time, calculate children's start/end time. If cycle exists in
1656      * the graph, all the nodes on the cycle will be marked to start at {@link #DURATION_INFINITE},
1657      * meaning they will ever play.
1658      */
1659     private void updatePlayTime(Node parent,  ArrayList<Node> visited) {
1660         if (parent.mChildNodes == null) {
1661             if (parent == mRootNode) {
1662                 // All the animators are in a cycle
1663                 for (int i = 0; i < mNodes.size(); i++) {
1664                     Node node = mNodes.get(i);
1665                     if (node != mRootNode) {
1666                         node.mStartTime = DURATION_INFINITE;
1667                         node.mEndTime = DURATION_INFINITE;
1668                     }
1669                 }
1670             }
1671             return;
1672         }
1673 
1674         visited.add(parent);
1675         int childrenSize = parent.mChildNodes.size();
1676         for (int i = 0; i < childrenSize; i++) {
1677             Node child = parent.mChildNodes.get(i);
1678             child.mTotalDuration = child.mAnimation.getTotalDuration();  // Update cached duration.
1679 
1680             int index = visited.indexOf(child);
1681             if (index >= 0) {
1682                 // Child has been visited, cycle found. Mark all the nodes in the cycle.
1683                 for (int j = index; j < visited.size(); j++) {
1684                     visited.get(j).mLatestParent = null;
1685                     visited.get(j).mStartTime = DURATION_INFINITE;
1686                     visited.get(j).mEndTime = DURATION_INFINITE;
1687                 }
1688                 child.mStartTime = DURATION_INFINITE;
1689                 child.mEndTime = DURATION_INFINITE;
1690                 child.mLatestParent = null;
1691                 Log.w(TAG, "Cycle found in AnimatorSet: " + this);
1692                 continue;
1693             }
1694 
1695             if (child.mStartTime != DURATION_INFINITE) {
1696                 if (parent.mEndTime == DURATION_INFINITE) {
1697                     child.mLatestParent = parent;
1698                     child.mStartTime = DURATION_INFINITE;
1699                     child.mEndTime = DURATION_INFINITE;
1700                 } else {
1701                     if (parent.mEndTime >= child.mStartTime) {
1702                         child.mLatestParent = parent;
1703                         child.mStartTime = parent.mEndTime;
1704                     }
1705 
1706                     child.mEndTime = child.mTotalDuration == DURATION_INFINITE
1707                             ? DURATION_INFINITE : child.mStartTime + child.mTotalDuration;
1708                 }
1709             }
1710             updatePlayTime(child, visited);
1711         }
1712         visited.remove(parent);
1713     }
1714 
1715     // Recursively find all the siblings
1716     private void findSiblings(Node node, ArrayList<Node> siblings) {
1717         if (!siblings.contains(node)) {
1718             siblings.add(node);
1719             if (node.mSiblings == null) {
1720                 return;
1721             }
1722             for (int i = 0; i < node.mSiblings.size(); i++) {
1723                 findSiblings(node.mSiblings.get(i), siblings);
1724             }
1725         }
1726     }
1727 
1728     /**
1729      * @hide
1730      * TODO: For animatorSet defined in XML, we can use a flag to indicate what the play order
1731      * if defined (i.e. sequential or together), then we can use the flag instead of calculating
1732      * dynamically. Note that when AnimatorSet is empty this method returns true.
1733      * @return whether all the animators in the set are supposed to play together
1734      */
1735     public boolean shouldPlayTogether() {
1736         updateAnimatorsDuration();
1737         createDependencyGraph();
1738         // All the child nodes are set out to play right after the delay animation
1739         return mRootNode.mChildNodes == null || mRootNode.mChildNodes.size() == mNodes.size() - 1;
1740     }
1741 
1742     @Override
1743     public long getTotalDuration() {
1744         updateAnimatorsDuration();
1745         createDependencyGraph();
1746         return mTotalDuration;
1747     }
1748 
1749     private Node getNodeForAnimation(Animator anim) {
1750         Node node = mNodeMap.get(anim);
1751         if (node == null) {
1752             node = new Node(anim);
1753             mNodeMap.put(anim, node);
1754             mNodes.add(node);
1755         }
1756         return node;
1757     }
1758 
1759     /**
1760      * A Node is an embodiment of both the Animator that it wraps as well as
1761      * any dependencies that are associated with that Animation. This includes
1762      * both dependencies upon other nodes (in the dependencies list) as
1763      * well as dependencies of other nodes upon this (in the nodeDependents list).
1764      */
1765     private static class Node implements Cloneable {
1766         Animator mAnimation;
1767 
1768         /**
1769          * Child nodes are the nodes associated with animations that will be played immediately
1770          * after current node.
1771          */
1772         ArrayList<Node> mChildNodes = null;
1773 
1774         /**
1775          * Flag indicating whether the animation in this node is finished. This flag
1776          * is used by AnimatorSet to check, as each animation ends, whether all child animations
1777          * are mEnded and it's time to send out an end event for the entire AnimatorSet.
1778          */
1779         boolean mEnded = false;
1780 
1781         /**
1782          * Nodes with animations that are defined to play simultaneously with the animation
1783          * associated with this current node.
1784          */
1785         ArrayList<Node> mSiblings;
1786 
1787         /**
1788          * Parent nodes are the nodes with animations preceding current node's animation. Parent
1789          * nodes here are derived from user defined animation sequence.
1790          */
1791         ArrayList<Node> mParents;
1792 
1793         /**
1794          * Latest parent is the parent node associated with a animation that finishes after all
1795          * the other parents' animations.
1796          */
1797         Node mLatestParent = null;
1798 
1799         boolean mParentsAdded = false;
1800         long mStartTime = 0;
1801         long mEndTime = 0;
1802         long mTotalDuration = 0;
1803 
1804         /**
1805          * Constructs the Node with the animation that it encapsulates. A Node has no
1806          * dependencies by default; dependencies are added via the addDependency()
1807          * method.
1808          *
1809          * @param animation The animation that the Node encapsulates.
1810          */
1811         public Node(Animator animation) {
1812             this.mAnimation = animation;
1813         }
1814 
1815         @Override
1816         public Node clone() {
1817             try {
1818                 Node node = (Node) super.clone();
1819                 node.mAnimation = mAnimation.clone();
1820                 if (mChildNodes != null) {
1821                     node.mChildNodes = new ArrayList<>(mChildNodes);
1822                 }
1823                 if (mSiblings != null) {
1824                     node.mSiblings = new ArrayList<>(mSiblings);
1825                 }
1826                 if (mParents != null) {
1827                     node.mParents = new ArrayList<>(mParents);
1828                 }
1829                 node.mEnded = false;
1830                 return node;
1831             } catch (CloneNotSupportedException e) {
1832                throw new AssertionError();
1833             }
1834         }
1835 
1836         void addChild(Node node) {
1837             if (mChildNodes == null) {
1838                 mChildNodes = new ArrayList<>();
1839             }
1840             if (!mChildNodes.contains(node)) {
1841                 mChildNodes.add(node);
1842                 node.addParent(this);
1843             }
1844         }
1845 
1846         public void addSibling(Node node) {
1847             if (mSiblings == null) {
1848                 mSiblings = new ArrayList<Node>();
1849             }
1850             if (!mSiblings.contains(node)) {
1851                 mSiblings.add(node);
1852                 node.addSibling(this);
1853             }
1854         }
1855 
1856         public void addParent(Node node) {
1857             if (mParents == null) {
1858                 mParents =  new ArrayList<Node>();
1859             }
1860             if (!mParents.contains(node)) {
1861                 mParents.add(node);
1862                 node.addChild(this);
1863             }
1864         }
1865 
1866         public void addParents(ArrayList<Node> parents) {
1867             if (parents == null) {
1868                 return;
1869             }
1870             int size = parents.size();
1871             for (int i = 0; i < size; i++) {
1872                 addParent(parents.get(i));
1873             }
1874         }
1875     }
1876 
1877     /**
1878      * This class is a wrapper around a node and an event for the animation corresponding to the
1879      * node. The 3 types of events represent the start of an animation, the end of a start delay of
1880      * an animation, and the end of an animation. When playing forward (i.e. in the non-reverse
1881      * direction), start event marks when start() should be called, and end event corresponds to
1882      * when the animation should finish. When playing in reverse, start delay will not be a part
1883      * of the animation. Therefore, reverse() is called at the end event, and animation should end
1884      * at the delay ended event.
1885      */
1886     private static class AnimationEvent {
1887         static final int ANIMATION_START = 0;
1888         static final int ANIMATION_DELAY_ENDED = 1;
1889         static final int ANIMATION_END = 2;
1890         final Node mNode;
1891         final int mEvent;
1892 
1893         AnimationEvent(Node node, int event) {
1894             mNode = node;
1895             mEvent = event;
1896         }
1897 
1898         long getTime() {
1899             if (mEvent == ANIMATION_START) {
1900                 return mNode.mStartTime;
1901             } else if (mEvent == ANIMATION_DELAY_ENDED) {
1902                 return mNode.mStartTime == DURATION_INFINITE
1903                         ? DURATION_INFINITE : mNode.mStartTime + mNode.mAnimation.getStartDelay();
1904             } else {
1905                 return mNode.mEndTime;
1906             }
1907         }
1908 
1909         public String toString() {
1910             String eventStr = mEvent == ANIMATION_START ? "start" : (
1911                     mEvent == ANIMATION_DELAY_ENDED ? "delay ended" : "end");
1912             return eventStr + " " + mNode.mAnimation.toString();
1913         }
1914     }
1915 
1916     private class SeekState {
1917         private long mPlayTime = -1;
1918         private boolean mSeekingInReverse = false;
1919         void reset() {
1920             mPlayTime = -1;
1921             mSeekingInReverse = false;
1922         }
1923 
1924         void setPlayTime(long playTime, boolean inReverse) {
1925             // TODO: This can be simplified.
1926 
1927             // Clamp the play time
1928             if (getTotalDuration() != DURATION_INFINITE) {
1929                 mPlayTime = Math.min(playTime, getTotalDuration() - mStartDelay);
1930             }
1931             mPlayTime = Math.max(0, mPlayTime);
1932             mSeekingInReverse = inReverse;
1933         }
1934 
1935         void updateSeekDirection(boolean inReverse) {
1936             // Change seek direction without changing the overall fraction
1937             if (inReverse && getTotalDuration() == DURATION_INFINITE) {
1938                 throw new UnsupportedOperationException("Error: Cannot reverse infinite animator"
1939                         + " set");
1940             }
1941             if (mPlayTime >= 0) {
1942                 if (inReverse != mSeekingInReverse) {
1943                     mPlayTime = getTotalDuration() - mStartDelay - mPlayTime;
1944                     mSeekingInReverse = inReverse;
1945                 }
1946             }
1947         }
1948 
1949         long getPlayTime() {
1950             return mPlayTime;
1951         }
1952 
1953         /**
1954          * Returns the playtime assuming the animation is forward playing
1955          */
1956         long getPlayTimeNormalized() {
1957             if (mReversing) {
1958                 return getTotalDuration() - mStartDelay - mPlayTime;
1959             }
1960             return mPlayTime;
1961         }
1962 
1963         boolean isActive() {
1964             return mPlayTime != -1;
1965         }
1966     }
1967 
1968     /**
1969      * The <code>Builder</code> object is a utility class to facilitate adding animations to a
1970      * <code>AnimatorSet</code> along with the relationships between the various animations. The
1971      * intention of the <code>Builder</code> methods, along with the {@link
1972      * AnimatorSet#play(Animator) play()} method of <code>AnimatorSet</code> is to make it possible
1973      * to express the dependency relationships of animations in a natural way. Developers can also
1974      * use the {@link AnimatorSet#playTogether(Animator[]) playTogether()} and {@link
1975      * AnimatorSet#playSequentially(Animator[]) playSequentially()} methods if these suit the need,
1976      * but it might be easier in some situations to express the AnimatorSet of animations in pairs.
1977      * <p/>
1978      * <p>The <code>Builder</code> object cannot be constructed directly, but is rather constructed
1979      * internally via a call to {@link AnimatorSet#play(Animator)}.</p>
1980      * <p/>
1981      * <p>For example, this sets up a AnimatorSet to play anim1 and anim2 at the same time, anim3 to
1982      * play when anim2 finishes, and anim4 to play when anim3 finishes:</p>
1983      * <pre>
1984      *     AnimatorSet s = new AnimatorSet();
1985      *     s.play(anim1).with(anim2);
1986      *     s.play(anim2).before(anim3);
1987      *     s.play(anim4).after(anim3);
1988      * </pre>
1989      * <p/>
1990      * <p>Note in the example that both {@link Builder#before(Animator)} and {@link
1991      * Builder#after(Animator)} are used. These are just different ways of expressing the same
1992      * relationship and are provided to make it easier to say things in a way that is more natural,
1993      * depending on the situation.</p>
1994      * <p/>
1995      * <p>It is possible to make several calls into the same <code>Builder</code> object to express
1996      * multiple relationships. However, note that it is only the animation passed into the initial
1997      * {@link AnimatorSet#play(Animator)} method that is the dependency in any of the successive
1998      * calls to the <code>Builder</code> object. For example, the following code starts both anim2
1999      * and anim3 when anim1 ends; there is no direct dependency relationship between anim2 and
2000      * anim3:
2001      * <pre>
2002      *   AnimatorSet s = new AnimatorSet();
2003      *   s.play(anim1).before(anim2).before(anim3);
2004      * </pre>
2005      * If the desired result is to play anim1 then anim2 then anim3, this code expresses the
2006      * relationship correctly:</p>
2007      * <pre>
2008      *   AnimatorSet s = new AnimatorSet();
2009      *   s.play(anim1).before(anim2);
2010      *   s.play(anim2).before(anim3);
2011      * </pre>
2012      * <p/>
2013      * <p>Note that it is possible to express relationships that cannot be resolved and will not
2014      * result in sensible results. For example, <code>play(anim1).after(anim1)</code> makes no
2015      * sense. In general, circular dependencies like this one (or more indirect ones where a depends
2016      * on b, which depends on c, which depends on a) should be avoided. Only create AnimatorSets
2017      * that can boil down to a simple, one-way relationship of animations starting with, before, and
2018      * after other, different, animations.</p>
2019      */
2020     public class Builder {
2021 
2022         /**
2023          * This tracks the current node being processed. It is supplied to the play() method
2024          * of AnimatorSet and passed into the constructor of Builder.
2025          */
2026         private Node mCurrentNode;
2027 
2028         /**
2029          * package-private constructor. Builders are only constructed by AnimatorSet, when the
2030          * play() method is called.
2031          *
2032          * @param anim The animation that is the dependency for the other animations passed into
2033          * the other methods of this Builder object.
2034          */
2035         Builder(Animator anim) {
2036             mDependencyDirty = true;
2037             mCurrentNode = getNodeForAnimation(anim);
2038         }
2039 
2040         /**
2041          * Sets up the given animation to play at the same time as the animation supplied in the
2042          * {@link AnimatorSet#play(Animator)} call that created this <code>Builder</code> object.
2043          *
2044          * @param anim The animation that will play when the animation supplied to the
2045          * {@link AnimatorSet#play(Animator)} method starts.
2046          */
2047         public Builder with(Animator anim) {
2048             Node node = getNodeForAnimation(anim);
2049             mCurrentNode.addSibling(node);
2050             return this;
2051         }
2052 
2053         /**
2054          * Sets up the given animation to play when the animation supplied in the
2055          * {@link AnimatorSet#play(Animator)} call that created this <code>Builder</code> object
2056          * ends.
2057          *
2058          * @param anim The animation that will play when the animation supplied to the
2059          * {@link AnimatorSet#play(Animator)} method ends.
2060          */
2061         public Builder before(Animator anim) {
2062             Node node = getNodeForAnimation(anim);
2063             mCurrentNode.addChild(node);
2064             return this;
2065         }
2066 
2067         /**
2068          * Sets up the given animation to play when the animation supplied in the
2069          * {@link AnimatorSet#play(Animator)} call that created this <code>Builder</code> object
2070          * to start when the animation supplied in this method call ends.
2071          *
2072          * @param anim The animation whose end will cause the animation supplied to the
2073          * {@link AnimatorSet#play(Animator)} method to play.
2074          */
2075         public Builder after(Animator anim) {
2076             Node node = getNodeForAnimation(anim);
2077             mCurrentNode.addParent(node);
2078             return this;
2079         }
2080 
2081         /**
2082          * Sets up the animation supplied in the
2083          * {@link AnimatorSet#play(Animator)} call that created this <code>Builder</code> object
2084          * to play when the given amount of time elapses.
2085          *
2086          * @param delay The number of milliseconds that should elapse before the
2087          * animation starts.
2088          */
2089         public Builder after(long delay) {
2090             // setup a ValueAnimator just to run the clock
2091             ValueAnimator anim = ValueAnimator.ofFloat(0f, 1f);
2092             anim.setDuration(delay);
2093             after(anim);
2094             return this;
2095         }
2096 
2097     }
2098 
2099 }
2100