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