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