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