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) {
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                if (event.getTime() <= currentPlayTime) {
1268                    latestId = i;
1269                }
1270            }
1271        }
1272        return latestId;
1273    }
1274
1275    private void endAnimation() {
1276        mStarted = false;
1277        mLastFrameTime = -1;
1278        mFirstFrame = -1;
1279        mLastEventId = -1;
1280        mPaused = false;
1281        mPauseTime = -1;
1282        mSeekState.reset();
1283        mPlayingSet.clear();
1284
1285        // No longer receive callbacks
1286        removeAnimationCallback();
1287        // Call end listener
1288        if (mListeners != null) {
1289            ArrayList<AnimatorListener> tmpListeners =
1290                    (ArrayList<AnimatorListener>) mListeners.clone();
1291            int numListeners = tmpListeners.size();
1292            for (int i = 0; i < numListeners; ++i) {
1293                tmpListeners.get(i).onAnimationEnd(this, mReversing);
1294            }
1295        }
1296        removeDummyListener();
1297        mSelfPulse = true;
1298        mReversing = false;
1299    }
1300
1301    private void removeAnimationCallback() {
1302        if (!mSelfPulse) {
1303            return;
1304        }
1305        AnimationHandler handler = AnimationHandler.getInstance();
1306        handler.removeCallback(this);
1307    }
1308
1309    private void addAnimationCallback(long delay) {
1310        if (!mSelfPulse) {
1311            return;
1312        }
1313        AnimationHandler handler = AnimationHandler.getInstance();
1314        handler.addAnimationFrameCallback(this, delay);
1315    }
1316
1317    @Override
1318    public AnimatorSet clone() {
1319        final AnimatorSet anim = (AnimatorSet) super.clone();
1320        /*
1321         * The basic clone() operation copies all items. This doesn't work very well for
1322         * AnimatorSet, because it will copy references that need to be recreated and state
1323         * that may not apply. What we need to do now is put the clone in an uninitialized
1324         * state, with fresh, empty data structures. Then we will build up the nodes list
1325         * manually, as we clone each Node (and its animation). The clone will then be sorted,
1326         * and will populate any appropriate lists, when it is started.
1327         */
1328        final int nodeCount = mNodes.size();
1329        anim.mStarted = false;
1330        anim.mLastFrameTime = -1;
1331        anim.mFirstFrame = -1;
1332        anim.mLastEventId = -1;
1333        anim.mPaused = false;
1334        anim.mPauseTime = -1;
1335        anim.mSeekState = new SeekState();
1336        anim.mSelfPulse = true;
1337        anim.mPlayingSet = new ArrayList<Node>();
1338        anim.mNodeMap = new ArrayMap<Animator, Node>();
1339        anim.mNodes = new ArrayList<Node>(nodeCount);
1340        anim.mEvents = new ArrayList<AnimationEvent>();
1341        anim.mDummyListener = new AnimatorListenerAdapter() {
1342            @Override
1343            public void onAnimationEnd(Animator animation) {
1344                if (anim.mNodeMap.get(animation) == null) {
1345                    throw new AndroidRuntimeException("Error: animation ended is not in the node"
1346                            + " map");
1347                }
1348                anim.mNodeMap.get(animation).mEnded = true;
1349
1350            }
1351        };
1352        anim.mReversing = false;
1353        anim.mDependencyDirty = true;
1354
1355        // Walk through the old nodes list, cloning each node and adding it to the new nodemap.
1356        // One problem is that the old node dependencies point to nodes in the old AnimatorSet.
1357        // We need to track the old/new nodes in order to reconstruct the dependencies in the clone.
1358
1359        HashMap<Node, Node> clonesMap = new HashMap<>(nodeCount);
1360        for (int n = 0; n < nodeCount; n++) {
1361            final Node node = mNodes.get(n);
1362            Node nodeClone = node.clone();
1363            // Remove the old internal listener from the cloned child
1364            nodeClone.mAnimation.removeListener(mDummyListener);
1365            clonesMap.put(node, nodeClone);
1366            anim.mNodes.add(nodeClone);
1367            anim.mNodeMap.put(nodeClone.mAnimation, nodeClone);
1368        }
1369
1370        anim.mRootNode = clonesMap.get(mRootNode);
1371        anim.mDelayAnim = (ValueAnimator) anim.mRootNode.mAnimation;
1372
1373        // Now that we've cloned all of the nodes, we're ready to walk through their
1374        // dependencies, mapping the old dependencies to the new nodes
1375        for (int i = 0; i < nodeCount; i++) {
1376            Node node = mNodes.get(i);
1377            // Update dependencies for node's clone
1378            Node nodeClone = clonesMap.get(node);
1379            nodeClone.mLatestParent = node.mLatestParent == null
1380                    ? null : clonesMap.get(node.mLatestParent);
1381            int size = node.mChildNodes == null ? 0 : node.mChildNodes.size();
1382            for (int j = 0; j < size; j++) {
1383                nodeClone.mChildNodes.set(j, clonesMap.get(node.mChildNodes.get(j)));
1384            }
1385            size = node.mSiblings == null ? 0 : node.mSiblings.size();
1386            for (int j = 0; j < size; j++) {
1387                nodeClone.mSiblings.set(j, clonesMap.get(node.mSiblings.get(j)));
1388            }
1389            size = node.mParents == null ? 0 : node.mParents.size();
1390            for (int j = 0; j < size; j++) {
1391                nodeClone.mParents.set(j, clonesMap.get(node.mParents.get(j)));
1392            }
1393        }
1394        return anim;
1395    }
1396
1397
1398    /**
1399     * AnimatorSet is only reversible when the set contains no sequential animation, and no child
1400     * animators have a start delay.
1401     * @hide
1402     */
1403    @Override
1404    public boolean canReverse() {
1405        return getTotalDuration() != DURATION_INFINITE;
1406    }
1407
1408    /**
1409     * Plays the AnimatorSet in reverse. If the animation has been seeked to a specific play time
1410     * using {@link #setCurrentPlayTime(long)}, it will play backwards from the point seeked when
1411     * reverse was called. Otherwise, then it will start from the end and play backwards. This
1412     * behavior is only set for the current animation; future playing of the animation will use the
1413     * default behavior of playing forward.
1414     * <p>
1415     * Note: reverse is not supported for infinite AnimatorSet.
1416     */
1417    @Override
1418    public void reverse() {
1419        start(true, true);
1420    }
1421
1422    @Override
1423    public String toString() {
1424        String returnVal = "AnimatorSet@" + Integer.toHexString(hashCode()) + "{";
1425        int size = mNodes.size();
1426        for (int i = 0; i < size; i++) {
1427            Node node = mNodes.get(i);
1428            returnVal += "\n    " + node.mAnimation.toString();
1429        }
1430        return returnVal + "\n}";
1431    }
1432
1433    private void printChildCount() {
1434        // Print out the child count through a level traverse.
1435        ArrayList<Node> list = new ArrayList<>(mNodes.size());
1436        list.add(mRootNode);
1437        Log.d(TAG, "Current tree: ");
1438        int index = 0;
1439        while (index < list.size()) {
1440            int listSize = list.size();
1441            StringBuilder builder = new StringBuilder();
1442            for (; index < listSize; index++) {
1443                Node node = list.get(index);
1444                int num = 0;
1445                if (node.mChildNodes != null) {
1446                    for (int i = 0; i < node.mChildNodes.size(); i++) {
1447                        Node child = node.mChildNodes.get(i);
1448                        if (child.mLatestParent == node) {
1449                            num++;
1450                            list.add(child);
1451                        }
1452                    }
1453                }
1454                builder.append(" ");
1455                builder.append(num);
1456            }
1457            Log.d(TAG, builder.toString());
1458        }
1459    }
1460
1461    private void createDependencyGraph() {
1462        if (!mDependencyDirty) {
1463            // Check whether any duration of the child animations has changed
1464            boolean durationChanged = false;
1465            for (int i = 0; i < mNodes.size(); i++) {
1466                Animator anim = mNodes.get(i).mAnimation;
1467                if (mNodes.get(i).mTotalDuration != anim.getTotalDuration()) {
1468                    durationChanged = true;
1469                    break;
1470                }
1471            }
1472            if (!durationChanged) {
1473                return;
1474            }
1475        }
1476
1477        mDependencyDirty = false;
1478        // Traverse all the siblings and make sure they have all the parents
1479        int size = mNodes.size();
1480        for (int i = 0; i < size; i++) {
1481            mNodes.get(i).mParentsAdded = false;
1482        }
1483        for (int i = 0; i < size; i++) {
1484            Node node = mNodes.get(i);
1485            if (node.mParentsAdded) {
1486                continue;
1487            }
1488
1489            node.mParentsAdded = true;
1490            if (node.mSiblings == null) {
1491                continue;
1492            }
1493
1494            // Find all the siblings
1495            findSiblings(node, node.mSiblings);
1496            node.mSiblings.remove(node);
1497
1498            // Get parents from all siblings
1499            int siblingSize = node.mSiblings.size();
1500            for (int j = 0; j < siblingSize; j++) {
1501                node.addParents(node.mSiblings.get(j).mParents);
1502            }
1503
1504            // Now make sure all siblings share the same set of parents
1505            for (int j = 0; j < siblingSize; j++) {
1506                Node sibling = node.mSiblings.get(j);
1507                sibling.addParents(node.mParents);
1508                sibling.mParentsAdded = true;
1509            }
1510        }
1511
1512        for (int i = 0; i < size; i++) {
1513            Node node = mNodes.get(i);
1514            if (node != mRootNode && node.mParents == null) {
1515                node.addParent(mRootNode);
1516            }
1517        }
1518
1519        // Do a DFS on the tree
1520        ArrayList<Node> visited = new ArrayList<Node>(mNodes.size());
1521        // Assign start/end time
1522        mRootNode.mStartTime = 0;
1523        mRootNode.mEndTime = mDelayAnim.getDuration();
1524        updatePlayTime(mRootNode, visited);
1525
1526        sortAnimationEvents();
1527        mTotalDuration = mEvents.get(mEvents.size() - 1).getTime();
1528    }
1529
1530    private void sortAnimationEvents() {
1531        // Sort the list of events in ascending order of their time
1532        // Create the list including the delay animation.
1533        mEvents.clear();
1534        for (int i = 1; i < mNodes.size(); i++) {
1535            Node node = mNodes.get(i);
1536            mEvents.add(new AnimationEvent(node, AnimationEvent.ANIMATION_START));
1537            mEvents.add(new AnimationEvent(node, AnimationEvent.ANIMATION_DELAY_ENDED));
1538            mEvents.add(new AnimationEvent(node, AnimationEvent.ANIMATION_END));
1539        }
1540        mEvents.sort(new Comparator<AnimationEvent>() {
1541            @Override
1542            public int compare(AnimationEvent e1, AnimationEvent e2) {
1543                long t1 = e1.getTime();
1544                long t2 = e2.getTime();
1545                if (t1 == t2) {
1546                    // For events that happen at the same time, we need them to be in the sequence
1547                    // (end, start, start delay ended)
1548                    if (e2.mEvent + e1.mEvent == AnimationEvent.ANIMATION_START
1549                            + AnimationEvent.ANIMATION_DELAY_ENDED) {
1550                        // Ensure start delay happens after start
1551                        return e1.mEvent - e2.mEvent;
1552                    } else {
1553                        return e2.mEvent - e1.mEvent;
1554                    }
1555                }
1556                if (t2 == DURATION_INFINITE) {
1557                    return -1;
1558                }
1559                if (t1 == DURATION_INFINITE) {
1560                    return 1;
1561                }
1562                // When neither event happens at INFINITE time:
1563                return (int) (t1 - t2);
1564            }
1565        });
1566
1567        int eventSize = mEvents.size();
1568        // For the same animation, start event has to happen before end.
1569        for (int i = 0; i < eventSize;) {
1570            AnimationEvent event = mEvents.get(i);
1571            if (event.mEvent == AnimationEvent.ANIMATION_END) {
1572                boolean needToSwapStart;
1573                if (event.mNode.mStartTime == event.mNode.mEndTime) {
1574                    needToSwapStart = true;
1575                } else if (event.mNode.mEndTime == event.mNode.mStartTime
1576                        + event.mNode.mAnimation.getStartDelay()) {
1577                    // Swapping start delay
1578                    needToSwapStart = false;
1579                } else {
1580                    i++;
1581                    continue;
1582                }
1583
1584                int startEventId = eventSize;
1585                int startDelayEndId = eventSize;
1586                for (int j = i + 1; j < eventSize; j++) {
1587                    if (startEventId < eventSize && startDelayEndId < eventSize) {
1588                        break;
1589                    }
1590                    if (mEvents.get(j).mNode == event.mNode) {
1591                        if (mEvents.get(j).mEvent == AnimationEvent.ANIMATION_START) {
1592                            // Found start event
1593                            startEventId = j;
1594                        } else if (mEvents.get(j).mEvent == AnimationEvent.ANIMATION_DELAY_ENDED) {
1595                            startDelayEndId = j;
1596                        }
1597                    }
1598
1599                }
1600                if (needToSwapStart && startEventId == mEvents.size()) {
1601                    throw new UnsupportedOperationException("Something went wrong, no start is"
1602                            + "found after stop for an animation that has the same start and end"
1603                            + "time.");
1604
1605                }
1606                if (startDelayEndId == mEvents.size()) {
1607                    throw new UnsupportedOperationException("Something went wrong, no start"
1608                            + "delay end is found after stop for an animation");
1609
1610                }
1611
1612                // We need to make sure start is inserted before start delay ended event,
1613                // because otherwise inserting start delay ended events first would change
1614                // the start event index.
1615                if (needToSwapStart) {
1616                    AnimationEvent startEvent = mEvents.remove(startEventId);
1617                    mEvents.add(i, startEvent);
1618                    i++;
1619                }
1620
1621                AnimationEvent startDelayEndEvent = mEvents.remove(startDelayEndId);
1622                mEvents.add(i, startDelayEndEvent);
1623                i += 2;
1624            } else {
1625                i++;
1626            }
1627        }
1628
1629        if (!mEvents.isEmpty() && mEvents.get(0).mEvent != AnimationEvent.ANIMATION_START) {
1630            throw new UnsupportedOperationException(
1631                    "Sorting went bad, the start event should always be at index 0");
1632        }
1633
1634        // Add AnimatorSet's start delay node to the beginning
1635        mEvents.add(0, new AnimationEvent(mRootNode, AnimationEvent.ANIMATION_START));
1636        mEvents.add(1, new AnimationEvent(mRootNode, AnimationEvent.ANIMATION_DELAY_ENDED));
1637        mEvents.add(2, new AnimationEvent(mRootNode, AnimationEvent.ANIMATION_END));
1638
1639        if (mEvents.get(mEvents.size() - 1).mEvent == AnimationEvent.ANIMATION_START
1640                || mEvents.get(mEvents.size() - 1).mEvent == AnimationEvent.ANIMATION_DELAY_ENDED) {
1641            throw new UnsupportedOperationException(
1642                    "Something went wrong, the last event is not an end event");
1643        }
1644    }
1645
1646    /**
1647     * Based on parent's start/end time, calculate children's start/end time. If cycle exists in
1648     * the graph, all the nodes on the cycle will be marked to start at {@link #DURATION_INFINITE},
1649     * meaning they will ever play.
1650     */
1651    private void updatePlayTime(Node parent,  ArrayList<Node> visited) {
1652        if (parent.mChildNodes == null) {
1653            if (parent == mRootNode) {
1654                // All the animators are in a cycle
1655                for (int i = 0; i < mNodes.size(); i++) {
1656                    Node node = mNodes.get(i);
1657                    if (node != mRootNode) {
1658                        node.mStartTime = DURATION_INFINITE;
1659                        node.mEndTime = DURATION_INFINITE;
1660                    }
1661                }
1662            }
1663            return;
1664        }
1665
1666        visited.add(parent);
1667        int childrenSize = parent.mChildNodes.size();
1668        for (int i = 0; i < childrenSize; i++) {
1669            Node child = parent.mChildNodes.get(i);
1670            int index = visited.indexOf(child);
1671            if (index >= 0) {
1672                // Child has been visited, cycle found. Mark all the nodes in the cycle.
1673                for (int j = index; j < visited.size(); j++) {
1674                    visited.get(j).mLatestParent = null;
1675                    visited.get(j).mStartTime = DURATION_INFINITE;
1676                    visited.get(j).mEndTime = DURATION_INFINITE;
1677                }
1678                child.mStartTime = DURATION_INFINITE;
1679                child.mEndTime = DURATION_INFINITE;
1680                child.mLatestParent = null;
1681                Log.w(TAG, "Cycle found in AnimatorSet: " + this);
1682                continue;
1683            }
1684
1685            if (child.mStartTime != DURATION_INFINITE) {
1686                if (parent.mEndTime == DURATION_INFINITE) {
1687                    child.mLatestParent = parent;
1688                    child.mStartTime = DURATION_INFINITE;
1689                    child.mEndTime = DURATION_INFINITE;
1690                } else {
1691                    if (parent.mEndTime >= child.mStartTime) {
1692                        child.mLatestParent = parent;
1693                        child.mStartTime = parent.mEndTime;
1694                    }
1695
1696                    long duration = child.mAnimation.getTotalDuration();
1697                    child.mEndTime = duration == DURATION_INFINITE ?
1698                            DURATION_INFINITE : child.mStartTime + duration;
1699                }
1700            }
1701            updatePlayTime(child, visited);
1702        }
1703        visited.remove(parent);
1704    }
1705
1706    // Recursively find all the siblings
1707    private void findSiblings(Node node, ArrayList<Node> siblings) {
1708        if (!siblings.contains(node)) {
1709            siblings.add(node);
1710            if (node.mSiblings == null) {
1711                return;
1712            }
1713            for (int i = 0; i < node.mSiblings.size(); i++) {
1714                findSiblings(node.mSiblings.get(i), siblings);
1715            }
1716        }
1717    }
1718
1719    /**
1720     * @hide
1721     * TODO: For animatorSet defined in XML, we can use a flag to indicate what the play order
1722     * if defined (i.e. sequential or together), then we can use the flag instead of calculating
1723     * dynamically. Note that when AnimatorSet is empty this method returns true.
1724     * @return whether all the animators in the set are supposed to play together
1725     */
1726    public boolean shouldPlayTogether() {
1727        updateAnimatorsDuration();
1728        createDependencyGraph();
1729        // All the child nodes are set out to play right after the delay animation
1730        return mRootNode.mChildNodes == null || mRootNode.mChildNodes.size() == mNodes.size() - 1;
1731    }
1732
1733    @Override
1734    public long getTotalDuration() {
1735        updateAnimatorsDuration();
1736        createDependencyGraph();
1737        return mTotalDuration;
1738    }
1739
1740    private Node getNodeForAnimation(Animator anim) {
1741        Node node = mNodeMap.get(anim);
1742        if (node == null) {
1743            node = new Node(anim);
1744            mNodeMap.put(anim, node);
1745            mNodes.add(node);
1746        }
1747        return node;
1748    }
1749
1750    /**
1751     * A Node is an embodiment of both the Animator that it wraps as well as
1752     * any dependencies that are associated with that Animation. This includes
1753     * both dependencies upon other nodes (in the dependencies list) as
1754     * well as dependencies of other nodes upon this (in the nodeDependents list).
1755     */
1756    private static class Node implements Cloneable {
1757        Animator mAnimation;
1758
1759        /**
1760         * Child nodes are the nodes associated with animations that will be played immediately
1761         * after current node.
1762         */
1763        ArrayList<Node> mChildNodes = null;
1764
1765        /**
1766         * Flag indicating whether the animation in this node is finished. This flag
1767         * is used by AnimatorSet to check, as each animation ends, whether all child animations
1768         * are mEnded and it's time to send out an end event for the entire AnimatorSet.
1769         */
1770        boolean mEnded = false;
1771
1772        /**
1773         * Nodes with animations that are defined to play simultaneously with the animation
1774         * associated with this current node.
1775         */
1776        ArrayList<Node> mSiblings;
1777
1778        /**
1779         * Parent nodes are the nodes with animations preceding current node's animation. Parent
1780         * nodes here are derived from user defined animation sequence.
1781         */
1782        ArrayList<Node> mParents;
1783
1784        /**
1785         * Latest parent is the parent node associated with a animation that finishes after all
1786         * the other parents' animations.
1787         */
1788        Node mLatestParent = null;
1789
1790        boolean mParentsAdded = false;
1791        long mStartTime = 0;
1792        long mEndTime = 0;
1793        long mTotalDuration = 0;
1794
1795        /**
1796         * Constructs the Node with the animation that it encapsulates. A Node has no
1797         * dependencies by default; dependencies are added via the addDependency()
1798         * method.
1799         *
1800         * @param animation The animation that the Node encapsulates.
1801         */
1802        public Node(Animator animation) {
1803            this.mAnimation = animation;
1804        }
1805
1806        @Override
1807        public Node clone() {
1808            try {
1809                Node node = (Node) super.clone();
1810                node.mAnimation = mAnimation.clone();
1811                if (mChildNodes != null) {
1812                    node.mChildNodes = new ArrayList<>(mChildNodes);
1813                }
1814                if (mSiblings != null) {
1815                    node.mSiblings = new ArrayList<>(mSiblings);
1816                }
1817                if (mParents != null) {
1818                    node.mParents = new ArrayList<>(mParents);
1819                }
1820                node.mEnded = false;
1821                return node;
1822            } catch (CloneNotSupportedException e) {
1823               throw new AssertionError();
1824            }
1825        }
1826
1827        void addChild(Node node) {
1828            if (mChildNodes == null) {
1829                mChildNodes = new ArrayList<>();
1830            }
1831            if (!mChildNodes.contains(node)) {
1832                mChildNodes.add(node);
1833                node.addParent(this);
1834            }
1835        }
1836
1837        public void addSibling(Node node) {
1838            if (mSiblings == null) {
1839                mSiblings = new ArrayList<Node>();
1840            }
1841            if (!mSiblings.contains(node)) {
1842                mSiblings.add(node);
1843                node.addSibling(this);
1844            }
1845        }
1846
1847        public void addParent(Node node) {
1848            if (mParents == null) {
1849                mParents =  new ArrayList<Node>();
1850            }
1851            if (!mParents.contains(node)) {
1852                mParents.add(node);
1853                node.addChild(this);
1854            }
1855        }
1856
1857        public void addParents(ArrayList<Node> parents) {
1858            if (parents == null) {
1859                return;
1860            }
1861            int size = parents.size();
1862            for (int i = 0; i < size; i++) {
1863                addParent(parents.get(i));
1864            }
1865        }
1866    }
1867
1868    /**
1869     * This class is a wrapper around a node and an event for the animation corresponding to the
1870     * node. The 3 types of events represent the start of an animation, the end of a start delay of
1871     * an animation, and the end of an animation. When playing forward (i.e. in the non-reverse
1872     * direction), start event marks when start() should be called, and end event corresponds to
1873     * when the animation should finish. When playing in reverse, start delay will not be a part
1874     * of the animation. Therefore, reverse() is called at the end event, and animation should end
1875     * at the delay ended event.
1876     */
1877    private static class AnimationEvent {
1878        static final int ANIMATION_START = 0;
1879        static final int ANIMATION_DELAY_ENDED = 1;
1880        static final int ANIMATION_END = 2;
1881        final Node mNode;
1882        final int mEvent;
1883
1884        AnimationEvent(Node node, int event) {
1885            mNode = node;
1886            mEvent = event;
1887        }
1888
1889        long getTime() {
1890            if (mEvent == ANIMATION_START) {
1891                return mNode.mStartTime;
1892            } else if (mEvent == ANIMATION_DELAY_ENDED) {
1893                return mNode.mStartTime == DURATION_INFINITE
1894                        ? DURATION_INFINITE : mNode.mStartTime + mNode.mAnimation.getStartDelay();
1895            } else {
1896                return mNode.mEndTime;
1897            }
1898        }
1899
1900        public String toString() {
1901            String eventStr = mEvent == ANIMATION_START ? "start" : (
1902                    mEvent == ANIMATION_DELAY_ENDED ? "delay ended" : "end");
1903            return eventStr + " " + mNode.mAnimation.toString();
1904        }
1905    }
1906
1907    private class SeekState {
1908        private long mPlayTime = -1;
1909        private boolean mSeekingInReverse = false;
1910        void reset() {
1911            mPlayTime = -1;
1912            mSeekingInReverse = false;
1913        }
1914
1915        void setPlayTime(long playTime, boolean inReverse) {
1916            // TODO: This can be simplified.
1917
1918            // Clamp the play time
1919            if (getTotalDuration() != DURATION_INFINITE) {
1920                mPlayTime = Math.min(playTime, getTotalDuration() - mStartDelay);
1921            }
1922            mPlayTime = Math.max(0, mPlayTime);
1923            mSeekingInReverse = inReverse;
1924        }
1925
1926        void updateSeekDirection(boolean inReverse) {
1927            // Change seek direction without changing the overall fraction
1928            if (inReverse && getTotalDuration() == DURATION_INFINITE) {
1929                throw new UnsupportedOperationException("Error: Cannot reverse infinite animator"
1930                        + " set");
1931            }
1932            if (mPlayTime >= 0) {
1933                if (inReverse != mSeekingInReverse) {
1934                    mPlayTime = getTotalDuration() - mStartDelay - mPlayTime;
1935                    mSeekingInReverse = inReverse;
1936                }
1937            }
1938        }
1939
1940        long getPlayTime() {
1941            return mPlayTime;
1942        }
1943
1944        /**
1945         * Returns the playtime assuming the animation is forward playing
1946         */
1947        long getPlayTimeNormalized() {
1948            if (mReversing) {
1949                return getTotalDuration() - mStartDelay - mPlayTime;
1950            }
1951            return mPlayTime;
1952        }
1953
1954        boolean isActive() {
1955            return mPlayTime != -1;
1956        }
1957    }
1958
1959    /**
1960     * The <code>Builder</code> object is a utility class to facilitate adding animations to a
1961     * <code>AnimatorSet</code> along with the relationships between the various animations. The
1962     * intention of the <code>Builder</code> methods, along with the {@link
1963     * AnimatorSet#play(Animator) play()} method of <code>AnimatorSet</code> is to make it possible
1964     * to express the dependency relationships of animations in a natural way. Developers can also
1965     * use the {@link AnimatorSet#playTogether(Animator[]) playTogether()} and {@link
1966     * AnimatorSet#playSequentially(Animator[]) playSequentially()} methods if these suit the need,
1967     * but it might be easier in some situations to express the AnimatorSet of animations in pairs.
1968     * <p/>
1969     * <p>The <code>Builder</code> object cannot be constructed directly, but is rather constructed
1970     * internally via a call to {@link AnimatorSet#play(Animator)}.</p>
1971     * <p/>
1972     * <p>For example, this sets up a AnimatorSet to play anim1 and anim2 at the same time, anim3 to
1973     * play when anim2 finishes, and anim4 to play when anim3 finishes:</p>
1974     * <pre>
1975     *     AnimatorSet s = new AnimatorSet();
1976     *     s.play(anim1).with(anim2);
1977     *     s.play(anim2).before(anim3);
1978     *     s.play(anim4).after(anim3);
1979     * </pre>
1980     * <p/>
1981     * <p>Note in the example that both {@link Builder#before(Animator)} and {@link
1982     * Builder#after(Animator)} are used. These are just different ways of expressing the same
1983     * relationship and are provided to make it easier to say things in a way that is more natural,
1984     * depending on the situation.</p>
1985     * <p/>
1986     * <p>It is possible to make several calls into the same <code>Builder</code> object to express
1987     * multiple relationships. However, note that it is only the animation passed into the initial
1988     * {@link AnimatorSet#play(Animator)} method that is the dependency in any of the successive
1989     * calls to the <code>Builder</code> object. For example, the following code starts both anim2
1990     * and anim3 when anim1 ends; there is no direct dependency relationship between anim2 and
1991     * anim3:
1992     * <pre>
1993     *   AnimatorSet s = new AnimatorSet();
1994     *   s.play(anim1).before(anim2).before(anim3);
1995     * </pre>
1996     * If the desired result is to play anim1 then anim2 then anim3, this code expresses the
1997     * relationship correctly:</p>
1998     * <pre>
1999     *   AnimatorSet s = new AnimatorSet();
2000     *   s.play(anim1).before(anim2);
2001     *   s.play(anim2).before(anim3);
2002     * </pre>
2003     * <p/>
2004     * <p>Note that it is possible to express relationships that cannot be resolved and will not
2005     * result in sensible results. For example, <code>play(anim1).after(anim1)</code> makes no
2006     * sense. In general, circular dependencies like this one (or more indirect ones where a depends
2007     * on b, which depends on c, which depends on a) should be avoided. Only create AnimatorSets
2008     * that can boil down to a simple, one-way relationship of animations starting with, before, and
2009     * after other, different, animations.</p>
2010     */
2011    public class Builder {
2012
2013        /**
2014         * This tracks the current node being processed. It is supplied to the play() method
2015         * of AnimatorSet and passed into the constructor of Builder.
2016         */
2017        private Node mCurrentNode;
2018
2019        /**
2020         * package-private constructor. Builders are only constructed by AnimatorSet, when the
2021         * play() method is called.
2022         *
2023         * @param anim The animation that is the dependency for the other animations passed into
2024         * the other methods of this Builder object.
2025         */
2026        Builder(Animator anim) {
2027            mDependencyDirty = true;
2028            mCurrentNode = getNodeForAnimation(anim);
2029        }
2030
2031        /**
2032         * Sets up the given animation to play at the same time as the animation supplied in the
2033         * {@link AnimatorSet#play(Animator)} call that created this <code>Builder</code> object.
2034         *
2035         * @param anim The animation that will play when the animation supplied to the
2036         * {@link AnimatorSet#play(Animator)} method starts.
2037         */
2038        public Builder with(Animator anim) {
2039            Node node = getNodeForAnimation(anim);
2040            mCurrentNode.addSibling(node);
2041            return this;
2042        }
2043
2044        /**
2045         * Sets up the given animation to play when the animation supplied in the
2046         * {@link AnimatorSet#play(Animator)} call that created this <code>Builder</code> object
2047         * ends.
2048         *
2049         * @param anim The animation that will play when the animation supplied to the
2050         * {@link AnimatorSet#play(Animator)} method ends.
2051         */
2052        public Builder before(Animator anim) {
2053            Node node = getNodeForAnimation(anim);
2054            mCurrentNode.addChild(node);
2055            return this;
2056        }
2057
2058        /**
2059         * Sets up the given animation to play when the animation supplied in the
2060         * {@link AnimatorSet#play(Animator)} call that created this <code>Builder</code> object
2061         * to start when the animation supplied in this method call ends.
2062         *
2063         * @param anim The animation whose end will cause the animation supplied to the
2064         * {@link AnimatorSet#play(Animator)} method to play.
2065         */
2066        public Builder after(Animator anim) {
2067            Node node = getNodeForAnimation(anim);
2068            mCurrentNode.addParent(node);
2069            return this;
2070        }
2071
2072        /**
2073         * Sets up the animation supplied in the
2074         * {@link AnimatorSet#play(Animator)} call that created this <code>Builder</code> object
2075         * to play when the given amount of time elapses.
2076         *
2077         * @param delay The number of milliseconds that should elapse before the
2078         * animation starts.
2079         */
2080        public Builder after(long delay) {
2081            // setup dummy ValueAnimator just to run the clock
2082            ValueAnimator anim = ValueAnimator.ofFloat(0f, 1f);
2083            anim.setDuration(delay);
2084            after(anim);
2085            return this;
2086        }
2087
2088    }
2089
2090}
2091