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