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