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