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