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