AnimatorSet.java revision e2ab7ccd385cdb6517955c719e1d2b49771bedb6
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 java.util.ArrayList;
20import java.util.Collection;
21import java.util.HashMap;
22import java.util.List;
23
24/**
25 * This class plays a set of {@link Animator} objects in the specified order. Animations
26 * can be set up to play together, in sequence, or after a specified delay.
27 *
28 * <p>There are two different approaches to adding animations to a <code>AnimatorSet</code>:
29 * either the {@link AnimatorSet#playTogether(Animator[]) playTogether()} or
30 * {@link AnimatorSet#playSequentially(Animator[]) playSequentially()} methods can be called to add
31 * a set of animations all at once, or the {@link AnimatorSet#play(Animator)} can be
32 * used in conjunction with methods in the {@link AnimatorSet.Builder Builder}
33 * class to add animations
34 * one by one.</p>
35 *
36 * <p>It is possible to set up a <code>AnimatorSet</code> with circular dependencies between
37 * its animations. For example, an animation a1 could be set up to start before animation a2, a2
38 * before a3, and a3 before a1. The results of this configuration are undefined, but will typically
39 * result in none of the affected animations being played. Because of this (and because
40 * circular dependencies do not make logical sense anyway), circular dependencies
41 * should be avoided, and the dependency flow of animations should only be in one direction.
42 */
43public final class AnimatorSet extends Animator {
44
45    /**
46     * Internal variables
47     * NOTE: This object implements the clone() method, making a deep copy of any referenced
48     * objects. As other non-trivial fields are added to this class, make sure to add logic
49     * to clone() to make deep copies of them.
50     */
51
52    /**
53     * Tracks animations currently being played, so that we know what to
54     * cancel or end when cancel() or end() is called on this AnimatorSet
55     */
56    private ArrayList<Animator> mPlayingSet = new ArrayList<Animator>();
57
58    /**
59     * Contains all nodes, mapped to their respective Animators. When new
60     * dependency information is added for an Animator, we want to add it
61     * to a single node representing that Animator, not create a new Node
62     * if one already exists.
63     */
64    private HashMap<Animator, Node> mNodeMap = new HashMap<Animator, Node>();
65
66    /**
67     * Set of all nodes created for this AnimatorSet. This list is used upon
68     * starting the set, and the nodes are placed in sorted order into the
69     * sortedNodes collection.
70     */
71    private ArrayList<Node> mNodes = new ArrayList<Node>();
72
73    /**
74     * The sorted list of nodes. This is the order in which the animations will
75     * be played. The details about when exactly they will be played depend
76     * on the dependency relationships of the nodes.
77     */
78    private ArrayList<Node> mSortedNodes = new ArrayList<Node>();
79
80    /**
81     * Flag indicating whether the nodes should be sorted prior to playing. This
82     * flag allows us to cache the previous sorted nodes so that if the sequence
83     * is replayed with no changes, it does not have to re-sort the nodes again.
84     */
85    private boolean mNeedsSort = true;
86
87    private AnimatorSetListener mSetListener = null;
88
89    /**
90     * Flag indicating that the AnimatorSet has been canceled (by calling cancel() or end()).
91     * This flag is used to avoid starting other animations when currently-playing
92     * child animations of this AnimatorSet end.
93     */
94    boolean mCanceled = false;
95
96    // The amount of time in ms to delay starting the animation after start() is called
97    private long mStartDelay = 0;
98
99    // Animator used for a nonzero startDelay
100    private ValueAnimator mDelayAnim = null;
101
102
103    // How long the child animations should last in ms. The default value is negative, which
104    // simply means that there is no duration set on the AnimatorSet. When a real duration is
105    // set, it is passed along to the child animations.
106    private long mDuration = -1;
107
108
109    /**
110     * Sets up this AnimatorSet to play all of the supplied animations at the same time.
111     *
112     * @param items The animations that will be started simultaneously.
113     */
114    public void playTogether(Animator... items) {
115        if (items != null) {
116            mNeedsSort = true;
117            Builder builder = play(items[0]);
118            for (int i = 1; i < items.length; ++i) {
119                builder.with(items[i]);
120            }
121        }
122    }
123
124    /**
125     * Sets up this AnimatorSet to play all of the supplied animations at the same time.
126     *
127     * @param items The animations that will be started simultaneously.
128     */
129    public void playTogether(Collection<Animator> items) {
130        if (items != null && items.size() > 0) {
131            mNeedsSort = true;
132            Builder builder = null;
133            for (Animator anim : items) {
134                if (builder == null) {
135                    builder = play(anim);
136                } else {
137                    builder.with(anim);
138                }
139            }
140        }
141    }
142
143    /**
144     * Sets up this AnimatorSet to play each of the supplied animations when the
145     * previous animation ends.
146     *
147     * @param items The animations that will be started one after another.
148     */
149    public void playSequentially(Animator... items) {
150        if (items != null) {
151            mNeedsSort = true;
152            if (items.length == 1) {
153                play(items[0]);
154            } else {
155                for (int i = 0; i < items.length - 1; ++i) {
156                    play(items[i]).before(items[i+1]);
157                }
158            }
159        }
160    }
161
162    /**
163     * Sets up this AnimatorSet to play each of the supplied animations when the
164     * previous animation ends.
165     *
166     * @param items The animations that will be started one after another.
167     */
168    public void playSequentially(List<Animator> items) {
169        if (items != null && items.size() > 0) {
170            mNeedsSort = true;
171            if (items.size() == 1) {
172                play(items.get(0));
173            } else {
174                for (int i = 0; i < items.size() - 1; ++i) {
175                    play(items.get(i)).before(items.get(i+1));
176                }
177            }
178        }
179    }
180
181    /**
182     * Returns the current list of child Animator objects controlled by this
183     * AnimatorSet. This is a copy of the internal list; modifications to the returned list
184     * will not affect the AnimatorSet, although changes to the underlying Animator objects
185     * will affect those objects being managed by the AnimatorSet.
186     *
187     * @return ArrayList<Animator> The list of child animations of this AnimatorSet.
188     */
189    public ArrayList<Animator> getChildAnimations() {
190        ArrayList<Animator> childList = new ArrayList<Animator>();
191        for (Node node : mNodes) {
192            childList.add(node.animation);
193        }
194        return childList;
195    }
196
197    /**
198     * Sets the target object for all current {@link #getChildAnimations() child animations}
199     * of this AnimatorSet that take targets ({@link ObjectAnimator} and
200     * AnimatorSet).
201     *
202     * @param target The object being animated
203     */
204    @Override
205    public void setTarget(Object target) {
206        for (Node node : mNodes) {
207            Animator animation = node.animation;
208            if (animation instanceof AnimatorSet) {
209                ((AnimatorSet)animation).setTarget(target);
210            } else if (animation instanceof ObjectAnimator) {
211                ((ObjectAnimator)animation).setTarget(target);
212            }
213        }
214    }
215
216    /**
217     * Sets the TimeInterpolator for all current {@link #getChildAnimations() child animations}
218     * of this AnimatorSet.
219     *
220     * @param interpolator the interpolator to be used by each child animation of this AnimatorSet
221     */
222    @Override
223    public void setInterpolator(TimeInterpolator interpolator) {
224        for (Node node : mNodes) {
225            node.animation.setInterpolator(interpolator);
226        }
227    }
228
229    /**
230     * This method creates a <code>Builder</code> object, which is used to
231     * set up playing constraints. This initial <code>play()</code> method
232     * tells the <code>Builder</code> the animation that is the dependency for
233     * the succeeding commands to the <code>Builder</code>. For example,
234     * calling <code>play(a1).with(a2)</code> sets up the AnimatorSet to play
235     * <code>a1</code> and <code>a2</code> at the same time,
236     * <code>play(a1).before(a2)</code> sets up the AnimatorSet to play
237     * <code>a1</code> first, followed by <code>a2</code>, and
238     * <code>play(a1).after(a2)</code> sets up the AnimatorSet to play
239     * <code>a2</code> first, followed by <code>a1</code>.
240     *
241     * <p>Note that <code>play()</code> is the only way to tell the
242     * <code>Builder</code> the animation upon which the dependency is created,
243     * so successive calls to the various functions in <code>Builder</code>
244     * will all refer to the initial parameter supplied in <code>play()</code>
245     * as the dependency of the other animations. For example, calling
246     * <code>play(a1).before(a2).before(a3)</code> will play both <code>a2</code>
247     * and <code>a3</code> when a1 ends; it does not set up a dependency between
248     * <code>a2</code> and <code>a3</code>.</p>
249     *
250     * @param anim The animation that is the dependency used in later calls to the
251     * methods in the returned <code>Builder</code> object. A null parameter will result
252     * in a null <code>Builder</code> return value.
253     * @return Builder The object that constructs the AnimatorSet based on the dependencies
254     * outlined in the calls to <code>play</code> and the other methods in the
255     * <code>Builder</code object.
256     */
257    public Builder play(Animator anim) {
258        if (anim != null) {
259            mNeedsSort = true;
260            return new Builder(anim);
261        }
262        return null;
263    }
264
265    /**
266     * {@inheritDoc}
267     *
268     * <p>Note that canceling a <code>AnimatorSet</code> also cancels all of the animations that it is
269     * responsible for.</p>
270     */
271    @SuppressWarnings("unchecked")
272    @Override
273    public void cancel() {
274        mCanceled = true;
275        if (mListeners != null) {
276            ArrayList<AnimatorListener> tmpListeners =
277                    (ArrayList<AnimatorListener>) mListeners.clone();
278            for (AnimatorListener listener : tmpListeners) {
279                listener.onAnimationCancel(this);
280            }
281        }
282        if (mDelayAnim != null && mDelayAnim.isRunning()) {
283            // If we're currently in the startDelay period, just cancel that animator and
284            // send out the end event to all listeners
285            mDelayAnim.cancel();
286            if (mListeners != null) {
287                ArrayList<AnimatorListener> tmpListeners =
288                        (ArrayList<AnimatorListener>) mListeners.clone();
289                for (AnimatorListener listener : tmpListeners) {
290                    listener.onAnimationEnd(this);
291                }
292            }
293            return;
294        }
295        if (mSortedNodes.size() > 0) {
296            for (Node node : mSortedNodes) {
297                node.animation.cancel();
298            }
299        }
300    }
301
302    /**
303     * {@inheritDoc}
304     *
305     * <p>Note that ending a <code>AnimatorSet</code> also ends all of the animations that it is
306     * responsible for.</p>
307     */
308    @Override
309    public void end() {
310        mCanceled = true;
311        if (mSortedNodes.size() != mNodes.size()) {
312            // hasn't been started yet - sort the nodes now, then end them
313            sortNodes();
314            for (Node node : mSortedNodes) {
315                if (mSetListener == null) {
316                    mSetListener = new AnimatorSetListener(this);
317                }
318                node.animation.addListener(mSetListener);
319            }
320        }
321        if (mDelayAnim != null) {
322            mDelayAnim.cancel();
323        }
324        if (mSortedNodes.size() > 0) {
325            for (Node node : mSortedNodes) {
326                node.animation.end();
327            }
328        }
329    }
330
331    /**
332     * Returns true if any of the child animations of this AnimatorSet have been started and have not
333     * yet ended.
334     * @return Whether this AnimatorSet has been started and has not yet ended.
335     */
336    @Override
337    public boolean isRunning() {
338        for (Node node : mNodes) {
339            if (node.animation.isRunning()) {
340                return true;
341            }
342        }
343        return false;
344    }
345
346    /**
347     * The amount of time, in milliseconds, to delay starting the animation after
348     * {@link #start()} is called.
349     *
350     * @return the number of milliseconds to delay running the animation
351     */
352    @Override
353    public long getStartDelay() {
354        return mStartDelay;
355    }
356
357    /**
358     * The amount of time, in milliseconds, to delay starting the animation after
359     * {@link #start()} is called.
360
361     * @param startDelay The amount of the delay, in milliseconds
362     */
363    @Override
364    public void setStartDelay(long startDelay) {
365        mStartDelay = startDelay;
366    }
367
368    /**
369     * Gets the length of each of the child animations of this AnimatorSet. This value may
370     * be less than 0, which indicates that no duration has been set on this AnimatorSet
371     * and each of the child animations will use their own duration.
372     *
373     * @return The length of the animation, in milliseconds, of each of the child
374     * animations of this AnimatorSet.
375     */
376    @Override
377    public long getDuration() {
378        return mDuration;
379    }
380
381    /**
382     * Sets the length of each of the current child animations of this AnimatorSet. By default,
383     * each child animation will use its own duration. If the duration is set on the AnimatorSet,
384     * then each child animation inherits this duration.
385     *
386     * @param duration The length of the animation, in milliseconds, of each of the child
387     * animations of this AnimatorSet.
388     */
389    @Override
390    public AnimatorSet setDuration(long duration) {
391        if (duration < 0) {
392            throw new IllegalArgumentException("duration must be a value of zero or greater");
393        }
394        for (Node node : mNodes) {
395            // TODO: don't set the duration of the timing-only nodes created by AnimatorSet to
396            // insert "play-after" delays
397            node.animation.setDuration(duration);
398        }
399        mDuration = duration;
400        return this;
401    }
402
403    @Override
404    public void setupStartValues() {
405        for (Node node : mNodes) {
406            node.animation.setupStartValues();
407        }
408    }
409
410    @Override
411    public void setupEndValues() {
412        for (Node node : mNodes) {
413            node.animation.setupEndValues();
414        }
415    }
416
417    /**
418     * {@inheritDoc}
419     *
420     * <p>Starting this <code>AnimatorSet</code> will, in turn, start the animations for which
421     * it is responsible. The details of when exactly those animations are started depends on
422     * the dependency relationships that have been set up between the animations.
423     */
424    @SuppressWarnings("unchecked")
425    @Override
426    public void start() {
427        mCanceled = false;
428
429        // First, sort the nodes (if necessary). This will ensure that sortedNodes
430        // contains the animation nodes in the correct order.
431        sortNodes();
432
433        int numSortedNodes = mSortedNodes.size();
434        for (int i = 0; i < numSortedNodes; ++i) {
435            Node node = mSortedNodes.get(i);
436            // First, clear out the old listeners
437            ArrayList<AnimatorListener> oldListeners = node.animation.getListeners();
438            if (oldListeners != null && oldListeners.size() > 0) {
439                for (AnimatorListener listener : oldListeners) {
440                    if (listener instanceof DependencyListener) {
441                        node.animation.removeListener(listener);
442                    }
443                }
444            }
445        }
446
447        // nodesToStart holds the list of nodes to be started immediately. We don't want to
448        // start the animations in the loop directly because we first need to set up
449        // dependencies on all of the nodes. For example, we don't want to start an animation
450        // when some other animation also wants to start when the first animation begins.
451        final ArrayList<Node> nodesToStart = new ArrayList<Node>();
452        for (int i = 0; i < numSortedNodes; ++i) {
453            Node node = mSortedNodes.get(i);
454            if (mSetListener == null) {
455                mSetListener = new AnimatorSetListener(this);
456            }
457            if (node.dependencies == null || node.dependencies.size() == 0) {
458                nodesToStart.add(node);
459            } else {
460                int numDependencies = node.dependencies.size();
461                for (int j = 0; j < numDependencies; ++j) {
462                    Dependency dependency = node.dependencies.get(j);
463                    dependency.node.animation.addListener(
464                            new DependencyListener(this, node, dependency.rule));
465                }
466                node.tmpDependencies = (ArrayList<Dependency>) node.dependencies.clone();
467            }
468            node.animation.addListener(mSetListener);
469        }
470        // Now that all dependencies are set up, start the animations that should be started.
471        if (mStartDelay <= 0) {
472            for (Node node : nodesToStart) {
473                node.animation.start();
474                mPlayingSet.add(node.animation);
475            }
476        } else {
477            // TODO: Need to cancel out of the delay appropriately
478            mDelayAnim = ValueAnimator.ofFloat(0f, 1f);
479            mDelayAnim.setDuration(mStartDelay);
480            mDelayAnim.addListener(new AnimatorListenerAdapter() {
481                boolean canceled = false;
482                public void onAnimationCancel(Animator anim) {
483                    canceled = true;
484                }
485                public void onAnimationEnd(Animator anim) {
486                    if (!canceled) {
487                        int numNodes = nodesToStart.size();
488                        for (int i = 0; i < numNodes; ++i) {
489                            Node node = nodesToStart.get(i);
490                            node.animation.start();
491                            mPlayingSet.add(node.animation);
492                        }
493                    }
494                }
495            });
496            mDelayAnim.start();
497        }
498        if (mListeners != null) {
499            ArrayList<AnimatorListener> tmpListeners =
500                    (ArrayList<AnimatorListener>) mListeners.clone();
501            int numListeners = tmpListeners.size();
502            for (int i = 0; i < numListeners; ++i) {
503                tmpListeners.get(i).onAnimationStart(this);
504                if (mNodes.size() == 0) {
505                    // Handle unusual case where empty AnimatorSet is started - should send out
506                    // end event immediately since the event will not be sent out at all otherwise
507                    tmpListeners.get(i).onAnimationEnd(this);
508                }
509            }
510        }
511    }
512
513    @Override
514    public AnimatorSet clone() {
515        final AnimatorSet anim = (AnimatorSet) super.clone();
516        /*
517         * The basic clone() operation copies all items. This doesn't work very well for
518         * AnimatorSet, because it will copy references that need to be recreated and state
519         * that may not apply. What we need to do now is put the clone in an uninitialized
520         * state, with fresh, empty data structures. Then we will build up the nodes list
521         * manually, as we clone each Node (and its animation). The clone will then be sorted,
522         * and will populate any appropriate lists, when it is started.
523         */
524        anim.mNeedsSort = true;
525        anim.mCanceled = false;
526        anim.mPlayingSet = new ArrayList<Animator>();
527        anim.mNodeMap = new HashMap<Animator, Node>();
528        anim.mNodes = new ArrayList<Node>();
529        anim.mSortedNodes = new ArrayList<Node>();
530
531        // Walk through the old nodes list, cloning each node and adding it to the new nodemap.
532        // One problem is that the old node dependencies point to nodes in the old AnimatorSet.
533        // We need to track the old/new nodes in order to reconstruct the dependencies in the clone.
534        HashMap<Node, Node> nodeCloneMap = new HashMap<Node, Node>(); // <old, new>
535        for (Node node : mNodes) {
536            Node nodeClone = node.clone();
537            nodeCloneMap.put(node, nodeClone);
538            anim.mNodes.add(nodeClone);
539            anim.mNodeMap.put(nodeClone.animation, nodeClone);
540            // Clear out the dependencies in the clone; we'll set these up manually later
541            nodeClone.dependencies = null;
542            nodeClone.tmpDependencies = null;
543            nodeClone.nodeDependents = null;
544            nodeClone.nodeDependencies = null;
545            // clear out any listeners that were set up by the AnimatorSet; these will
546            // be set up when the clone's nodes are sorted
547            ArrayList<AnimatorListener> cloneListeners = nodeClone.animation.getListeners();
548            if (cloneListeners != null) {
549                ArrayList<AnimatorListener> listenersToRemove = null;
550                for (AnimatorListener listener : cloneListeners) {
551                    if (listener instanceof AnimatorSetListener) {
552                        if (listenersToRemove == null) {
553                            listenersToRemove = new ArrayList<AnimatorListener>();
554                        }
555                        listenersToRemove.add(listener);
556                    }
557                }
558                if (listenersToRemove != null) {
559                    for (AnimatorListener listener : listenersToRemove) {
560                        cloneListeners.remove(listener);
561                    }
562                }
563            }
564        }
565        // Now that we've cloned all of the nodes, we're ready to walk through their
566        // dependencies, mapping the old dependencies to the new nodes
567        for (Node node : mNodes) {
568            Node nodeClone = nodeCloneMap.get(node);
569            if (node.dependencies != null) {
570                for (Dependency dependency : node.dependencies) {
571                    Node clonedDependencyNode = nodeCloneMap.get(dependency.node);
572                    Dependency cloneDependency = new Dependency(clonedDependencyNode,
573                            dependency.rule);
574                    nodeClone.addDependency(cloneDependency);
575                }
576            }
577        }
578
579        return anim;
580    }
581
582    /**
583     * This class is the mechanism by which animations are started based on events in other
584     * animations. If an animation has multiple dependencies on other animations, then
585     * all dependencies must be satisfied before the animation is started.
586     */
587    private static class DependencyListener implements AnimatorListener {
588
589        private AnimatorSet mAnimatorSet;
590
591        // The node upon which the dependency is based.
592        private Node mNode;
593
594        // The Dependency rule (WITH or AFTER) that the listener should wait for on
595        // the node
596        private int mRule;
597
598        public DependencyListener(AnimatorSet animatorSet, Node node, int rule) {
599            this.mAnimatorSet = animatorSet;
600            this.mNode = node;
601            this.mRule = rule;
602        }
603
604        /**
605         * Ignore cancel events for now. We may want to handle this eventually,
606         * to prevent follow-on animations from running when some dependency
607         * animation is canceled.
608         */
609        public void onAnimationCancel(Animator animation) {
610        }
611
612        /**
613         * An end event is received - see if this is an event we are listening for
614         */
615        public void onAnimationEnd(Animator animation) {
616            if (mRule == Dependency.AFTER) {
617                startIfReady(animation);
618            }
619        }
620
621        /**
622         * Ignore repeat events for now
623         */
624        public void onAnimationRepeat(Animator animation) {
625        }
626
627        /**
628         * A start event is received - see if this is an event we are listening for
629         */
630        public void onAnimationStart(Animator animation) {
631            if (mRule == Dependency.WITH) {
632                startIfReady(animation);
633            }
634        }
635
636        /**
637         * Check whether the event received is one that the node was waiting for.
638         * If so, mark it as complete and see whether it's time to start
639         * the animation.
640         * @param dependencyAnimation the animation that sent the event.
641         */
642        private void startIfReady(Animator dependencyAnimation) {
643            if (mAnimatorSet.mCanceled) {
644                // if the parent AnimatorSet was canceled, then don't start any dependent anims
645                return;
646            }
647            Dependency dependencyToRemove = null;
648            int numDependencies = mNode.tmpDependencies.size();
649            for (int i = 0; i < numDependencies; ++i) {
650                Dependency dependency = mNode.tmpDependencies.get(i);
651                if (dependency.rule == mRule &&
652                        dependency.node.animation == dependencyAnimation) {
653                    // rule fired - remove the dependency and listener and check to
654                    // see whether it's time to start the animation
655                    dependencyToRemove = dependency;
656                    dependencyAnimation.removeListener(this);
657                    break;
658                }
659            }
660            mNode.tmpDependencies.remove(dependencyToRemove);
661            if (mNode.tmpDependencies.size() == 0) {
662                // all dependencies satisfied: start the animation
663                mNode.animation.start();
664                mAnimatorSet.mPlayingSet.add(mNode.animation);
665            }
666        }
667
668    }
669
670    private class AnimatorSetListener implements AnimatorListener {
671
672        private AnimatorSet mAnimatorSet;
673
674        AnimatorSetListener(AnimatorSet animatorSet) {
675            mAnimatorSet = animatorSet;
676        }
677
678        public void onAnimationCancel(Animator animation) {
679            if (mPlayingSet.size() == 0) {
680                if (mListeners != null) {
681                    int numListeners = mListeners.size();
682                    for (int i = 0; i < numListeners; ++i) {
683                        mListeners.get(i).onAnimationCancel(mAnimatorSet);
684                    }
685                }
686            }
687        }
688
689        @SuppressWarnings("unchecked")
690        public void onAnimationEnd(Animator animation) {
691            animation.removeListener(this);
692            mPlayingSet.remove(animation);
693            Node animNode = mAnimatorSet.mNodeMap.get(animation);
694            animNode.done = true;
695            ArrayList<Node> sortedNodes = mAnimatorSet.mSortedNodes;
696            boolean allDone = true;
697            int numSortedNodes = sortedNodes.size();
698            for (int i = 0; i < numSortedNodes; ++i) {
699                if (!sortedNodes.get(i).done) {
700                    allDone = false;
701                    break;
702                }
703            }
704            if (allDone) {
705                // If this was the last child animation to end, then notify listeners that this
706                // AnimatorSet has ended
707                if (mListeners != null) {
708                    ArrayList<AnimatorListener> tmpListeners =
709                            (ArrayList<AnimatorListener>) mListeners.clone();
710                    int numListeners = tmpListeners.size();
711                    for (int i = 0; i < numListeners; ++i) {
712                        tmpListeners.get(i).onAnimationEnd(mAnimatorSet);
713                    }
714                }
715            }
716        }
717
718        // Nothing to do
719        public void onAnimationRepeat(Animator animation) {
720        }
721
722        // Nothing to do
723        public void onAnimationStart(Animator animation) {
724        }
725
726    }
727
728    /**
729     * This method sorts the current set of nodes, if needed. The sort is a simple
730     * DependencyGraph sort, which goes like this:
731     * - All nodes without dependencies become 'roots'
732     * - while roots list is not null
733     * -   for each root r
734     * -     add r to sorted list
735     * -     remove r as a dependency from any other node
736     * -   any nodes with no dependencies are added to the roots list
737     */
738    private void sortNodes() {
739        if (mNeedsSort) {
740            mSortedNodes.clear();
741            ArrayList<Node> roots = new ArrayList<Node>();
742            int numNodes = mNodes.size();
743            for (int i = 0; i < numNodes; ++i) {
744                Node node = mNodes.get(i);
745                if (node.dependencies == null || node.dependencies.size() == 0) {
746                    roots.add(node);
747                }
748            }
749            ArrayList<Node> tmpRoots = new ArrayList<Node>();
750            while (roots.size() > 0) {
751                int numRoots = roots.size();
752                for (int i = 0; i < numRoots; ++i) {
753                    Node root = roots.get(i);
754                    mSortedNodes.add(root);
755                    if (root.nodeDependents != null) {
756                        int numDependents = root.nodeDependents.size();
757                        for (int j = 0; j < numDependents; ++j) {
758                            Node node = root.nodeDependents.get(j);
759                            node.nodeDependencies.remove(root);
760                            if (node.nodeDependencies.size() == 0) {
761                                tmpRoots.add(node);
762                            }
763                        }
764                    }
765                }
766                roots.clear();
767                roots.addAll(tmpRoots);
768                tmpRoots.clear();
769            }
770            mNeedsSort = false;
771            if (mSortedNodes.size() != mNodes.size()) {
772                throw new IllegalStateException("Circular dependencies cannot exist"
773                        + " in AnimatorSet");
774            }
775        } else {
776            // Doesn't need sorting, but still need to add in the nodeDependencies list
777            // because these get removed as the event listeners fire and the dependencies
778            // are satisfied
779            int numNodes = mNodes.size();
780            for (int i = 0; i < numNodes; ++i) {
781                Node node = mNodes.get(i);
782                if (node.dependencies != null && node.dependencies.size() > 0) {
783                    int numDependencies = node.dependencies.size();
784                    for (int j = 0; j < numDependencies; ++j) {
785                        Dependency dependency = node.dependencies.get(j);
786                        if (node.nodeDependencies == null) {
787                            node.nodeDependencies = new ArrayList<Node>();
788                        }
789                        if (!node.nodeDependencies.contains(dependency.node)) {
790                            node.nodeDependencies.add(dependency.node);
791                        }
792                    }
793                }
794                node.done = false;
795            }
796        }
797    }
798
799    /**
800     * Dependency holds information about the node that some other node is
801     * dependent upon and the nature of that dependency.
802     *
803     */
804    private static class Dependency {
805        static final int WITH = 0; // dependent node must start with this dependency node
806        static final int AFTER = 1; // dependent node must start when this dependency node finishes
807
808        // The node that the other node with this Dependency is dependent upon
809        public Node node;
810
811        // The nature of the dependency (WITH or AFTER)
812        public int rule;
813
814        public Dependency(Node node, int rule) {
815            this.node = node;
816            this.rule = rule;
817        }
818    }
819
820    /**
821     * A Node is an embodiment of both the Animator that it wraps as well as
822     * any dependencies that are associated with that Animation. This includes
823     * both dependencies upon other nodes (in the dependencies list) as
824     * well as dependencies of other nodes upon this (in the nodeDependents list).
825     */
826    private static class Node implements Cloneable {
827        public Animator animation;
828
829        /**
830         *  These are the dependencies that this node's animation has on other
831         *  nodes. For example, if this node's animation should begin with some
832         *  other animation ends, then there will be an item in this node's
833         *  dependencies list for that other animation's node.
834         */
835        public ArrayList<Dependency> dependencies = null;
836
837        /**
838         * tmpDependencies is a runtime detail. We use the dependencies list for sorting.
839         * But we also use the list to keep track of when multiple dependencies are satisfied,
840         * but removing each dependency as it is satisfied. We do not want to remove
841         * the dependency itself from the list, because we need to retain that information
842         * if the AnimatorSet is launched in the future. So we create a copy of the dependency
843         * list when the AnimatorSet starts and use this tmpDependencies list to track the
844         * list of satisfied dependencies.
845         */
846        public ArrayList<Dependency> tmpDependencies = null;
847
848        /**
849         * nodeDependencies is just a list of the nodes that this Node is dependent upon.
850         * This information is used in sortNodes(), to determine when a node is a root.
851         */
852        public ArrayList<Node> nodeDependencies = null;
853
854        /**
855         * nodeDepdendents is the list of nodes that have this node as a dependency. This
856         * is a utility field used in sortNodes to facilitate removing this node as a
857         * dependency when it is a root node.
858         */
859        public ArrayList<Node> nodeDependents = null;
860
861        /**
862         * Flag indicating whether the animation in this node is finished. This flag
863         * is used by AnimatorSet to check, as each animation ends, whether all child animations
864         * are done and it's time to send out an end event for the entire AnimatorSet.
865         */
866        public boolean done = false;
867
868        /**
869         * Constructs the Node with the animation that it encapsulates. A Node has no
870         * dependencies by default; dependencies are added via the addDependency()
871         * method.
872         *
873         * @param animation The animation that the Node encapsulates.
874         */
875        public Node(Animator animation) {
876            this.animation = animation;
877        }
878
879        /**
880         * Add a dependency to this Node. The dependency includes information about the
881         * node that this node is dependency upon and the nature of the dependency.
882         * @param dependency
883         */
884        public void addDependency(Dependency dependency) {
885            if (dependencies == null) {
886                dependencies = new ArrayList<Dependency>();
887                nodeDependencies = new ArrayList<Node>();
888            }
889            dependencies.add(dependency);
890            if (!nodeDependencies.contains(dependency.node)) {
891                nodeDependencies.add(dependency.node);
892            }
893            Node dependencyNode = dependency.node;
894            if (dependencyNode.nodeDependents == null) {
895                dependencyNode.nodeDependents = new ArrayList<Node>();
896            }
897            dependencyNode.nodeDependents.add(this);
898        }
899
900        @Override
901        public Node clone() {
902            try {
903                Node node = (Node) super.clone();
904                node.animation = (Animator) animation.clone();
905                return node;
906            } catch (CloneNotSupportedException e) {
907               throw new AssertionError();
908            }
909        }
910    }
911
912    /**
913     * The <code>Builder</code> object is a utility class to facilitate adding animations to a
914     * <code>AnimatorSet</code> along with the relationships between the various animations. The
915     * intention of the <code>Builder</code> methods, along with the {@link
916     * AnimatorSet#play(Animator) play()} method of <code>AnimatorSet</code> is to make it possible to
917     * express the dependency relationships of animations in a natural way. Developers can also use
918     * the {@link AnimatorSet#playTogether(Animator[]) playTogether()} and {@link
919     * AnimatorSet#playSequentially(Animator[]) playSequentially()} methods if these suit the need,
920     * but it might be easier in some situations to express the AnimatorSet of animations in pairs.
921     * <p/>
922     * <p>The <code>Builder</code> object cannot be constructed directly, but is rather constructed
923     * internally via a call to {@link AnimatorSet#play(Animator)}.</p>
924     * <p/>
925     * <p>For example, this sets up a AnimatorSet to play anim1 and anim2 at the same time, anim3 to
926     * play when anim2 finishes, and anim4 to play when anim3 finishes:</p>
927     * <pre>
928     *     AnimatorSet s = new AnimatorSet();
929     *     s.play(anim1).with(anim2);
930     *     s.play(anim2).before(anim3);
931     *     s.play(anim4).after(anim3);
932     * </pre>
933     * <p/>
934     * <p>Note in the example that both {@link Builder#before(Animator)} and {@link
935     * Builder#after(Animator)} are used. These are just different ways of expressing the same
936     * relationship and are provided to make it easier to say things in a way that is more natural,
937     * depending on the situation.</p>
938     * <p/>
939     * <p>It is possible to make several calls into the same <code>Builder</code> object to express
940     * multiple relationships. However, note that it is only the animation passed into the initial
941     * {@link AnimatorSet#play(Animator)} method that is the dependency in any of the successive
942     * calls to the <code>Builder</code> object. For example, the following code starts both anim2
943     * and anim3 when anim1 ends; there is no direct dependency relationship between anim2 and
944     * anim3:
945     * <pre>
946     *   AnimatorSet s = new AnimatorSet();
947     *   s.play(anim1).before(anim2).before(anim3);
948     * </pre>
949     * If the desired result is to play anim1 then anim2 then anim3, this code expresses the
950     * relationship correctly:</p>
951     * <pre>
952     *   AnimatorSet s = new AnimatorSet();
953     *   s.play(anim1).before(anim2);
954     *   s.play(anim2).before(anim3);
955     * </pre>
956     * <p/>
957     * <p>Note that it is possible to express relationships that cannot be resolved and will not
958     * result in sensible results. For example, <code>play(anim1).after(anim1)</code> makes no
959     * sense. In general, circular dependencies like this one (or more indirect ones where a depends
960     * on b, which depends on c, which depends on a) should be avoided. Only create AnimatorSets
961     * that can boil down to a simple, one-way relationship of animations starting with, before, and
962     * after other, different, animations.</p>
963     */
964    public class Builder {
965
966        /**
967         * This tracks the current node being processed. It is supplied to the play() method
968         * of AnimatorSet and passed into the constructor of Builder.
969         */
970        private Node mCurrentNode;
971
972        /**
973         * package-private constructor. Builders are only constructed by AnimatorSet, when the
974         * play() method is called.
975         *
976         * @param anim The animation that is the dependency for the other animations passed into
977         * the other methods of this Builder object.
978         */
979        Builder(Animator anim) {
980            mCurrentNode = mNodeMap.get(anim);
981            if (mCurrentNode == null) {
982                mCurrentNode = new Node(anim);
983                mNodeMap.put(anim, mCurrentNode);
984                mNodes.add(mCurrentNode);
985            }
986        }
987
988        /**
989         * Sets up the given animation to play at the same time as the animation supplied in the
990         * {@link AnimatorSet#play(Animator)} call that created this <code>Builder</code> object.
991         *
992         * @param anim The animation that will play when the animation supplied to the
993         * {@link AnimatorSet#play(Animator)} method starts.
994         */
995        public Builder with(Animator anim) {
996            Node node = mNodeMap.get(anim);
997            if (node == null) {
998                node = new Node(anim);
999                mNodeMap.put(anim, node);
1000                mNodes.add(node);
1001            }
1002            Dependency dependency = new Dependency(mCurrentNode, Dependency.WITH);
1003            node.addDependency(dependency);
1004            return this;
1005        }
1006
1007        /**
1008         * Sets up the given animation to play when the animation supplied in the
1009         * {@link AnimatorSet#play(Animator)} call that created this <code>Builder</code> object
1010         * ends.
1011         *
1012         * @param anim The animation that will play when the animation supplied to the
1013         * {@link AnimatorSet#play(Animator)} method ends.
1014         */
1015        public Builder before(Animator anim) {
1016            Node node = mNodeMap.get(anim);
1017            if (node == null) {
1018                node = new Node(anim);
1019                mNodeMap.put(anim, node);
1020                mNodes.add(node);
1021            }
1022            Dependency dependency = new Dependency(mCurrentNode, Dependency.AFTER);
1023            node.addDependency(dependency);
1024            return this;
1025        }
1026
1027        /**
1028         * Sets up the given animation to play when the animation supplied in the
1029         * {@link AnimatorSet#play(Animator)} call that created this <code>Builder</code> object
1030         * to start when the animation supplied in this method call ends.
1031         *
1032         * @param anim The animation whose end will cause the animation supplied to the
1033         * {@link AnimatorSet#play(Animator)} method to play.
1034         */
1035        public Builder after(Animator anim) {
1036            Node node = mNodeMap.get(anim);
1037            if (node == null) {
1038                node = new Node(anim);
1039                mNodeMap.put(anim, node);
1040                mNodes.add(node);
1041            }
1042            Dependency dependency = new Dependency(node, Dependency.AFTER);
1043            mCurrentNode.addDependency(dependency);
1044            return this;
1045        }
1046
1047        /**
1048         * Sets up the animation supplied in the
1049         * {@link AnimatorSet#play(Animator)} call that created this <code>Builder</code> object
1050         * to play when the given amount of time elapses.
1051         *
1052         * @param delay The number of milliseconds that should elapse before the
1053         * animation starts.
1054         */
1055        public Builder after(long delay) {
1056            // setup dummy ValueAnimator just to run the clock
1057            ValueAnimator anim = ValueAnimator.ofFloat(0f, 1f);
1058            anim.setDuration(delay);
1059            after(anim);
1060            return this;
1061        }
1062
1063    }
1064
1065}
1066