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