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