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