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