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