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