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