blob: 8ff38bb8647bd59e8ecf7468572a94b598ac047b [file] [log] [blame]
Chet Haase17fb4b02010-06-28 17:55:07 -07001/*
2 * Copyright (C) 2010 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package android.animation;
18
Doris Liu58606db2016-04-13 13:28:38 -070019import android.app.ActivityThread;
20import android.app.Application;
21import android.os.Build;
Doris Liud7444422015-05-11 13:23:31 -070022import android.util.ArrayMap;
Doris Liu13099142015-07-10 17:32:41 -070023import android.util.Log;
Doris Liud7444422015-05-11 13:23:31 -070024
Chet Haase17fb4b02010-06-28 17:55:07 -070025import java.util.ArrayList;
Chet Haase37a7bec2010-11-30 15:55:39 -080026import java.util.Collection;
Chet Haase37a7bec2010-11-30 15:55:39 -080027import java.util.List;
Chet Haase17fb4b02010-06-28 17:55:07 -070028
29/**
Chet Haasea18a86b2010-09-07 13:20:00 -070030 * This class plays a set of {@link Animator} objects in the specified order. Animations
Chet Haase17fb4b02010-06-28 17:55:07 -070031 * can be set up to play together, in sequence, or after a specified delay.
32 *
Chet Haasea18a86b2010-09-07 13:20:00 -070033 * <p>There are two different approaches to adding animations to a <code>AnimatorSet</code>:
34 * either the {@link AnimatorSet#playTogether(Animator[]) playTogether()} or
35 * {@link AnimatorSet#playSequentially(Animator[]) playSequentially()} methods can be called to add
36 * a set of animations all at once, or the {@link AnimatorSet#play(Animator)} can be
37 * used in conjunction with methods in the {@link AnimatorSet.Builder Builder}
Chet Haase17fb4b02010-06-28 17:55:07 -070038 * class to add animations
39 * one by one.</p>
40 *
Chet Haasea18a86b2010-09-07 13:20:00 -070041 * <p>It is possible to set up a <code>AnimatorSet</code> with circular dependencies between
Chet Haase17fb4b02010-06-28 17:55:07 -070042 * its animations. For example, an animation a1 could be set up to start before animation a2, a2
43 * before a3, and a3 before a1. The results of this configuration are undefined, but will typically
44 * result in none of the affected animations being played. Because of this (and because
45 * circular dependencies do not make logical sense anyway), circular dependencies
46 * should be avoided, and the dependency flow of animations should only be in one direction.
Joe Fernandez3aef8e1d2011-12-20 10:38:34 -080047 *
48 * <div class="special reference">
49 * <h3>Developer Guides</h3>
50 * <p>For more information about animating with {@code AnimatorSet}, read the
51 * <a href="{@docRoot}guide/topics/graphics/prop-animation.html#choreography">Property
52 * Animation</a> developer guide.</p>
53 * </div>
Chet Haase17fb4b02010-06-28 17:55:07 -070054 */
Chet Haasea18a86b2010-09-07 13:20:00 -070055public final class AnimatorSet extends Animator {
Chet Haase17fb4b02010-06-28 17:55:07 -070056
Doris Liu13099142015-07-10 17:32:41 -070057 private static final String TAG = "AnimatorSet";
Chet Haase17fb4b02010-06-28 17:55:07 -070058 /**
Chet Haase49afa5b2010-08-23 11:39:53 -070059 * Internal variables
60 * NOTE: This object implements the clone() method, making a deep copy of any referenced
61 * objects. As other non-trivial fields are added to this class, make sure to add logic
62 * to clone() to make deep copies of them.
63 */
64
65 /**
Chet Haase3b69b6f2010-07-29 09:09:05 -070066 * Tracks animations currently being played, so that we know what to
Chet Haasea18a86b2010-09-07 13:20:00 -070067 * cancel or end when cancel() or end() is called on this AnimatorSet
Chet Haase17fb4b02010-06-28 17:55:07 -070068 */
Chet Haasea18a86b2010-09-07 13:20:00 -070069 private ArrayList<Animator> mPlayingSet = new ArrayList<Animator>();
Chet Haase17fb4b02010-06-28 17:55:07 -070070
71 /**
Chet Haasea18a86b2010-09-07 13:20:00 -070072 * Contains all nodes, mapped to their respective Animators. When new
73 * dependency information is added for an Animator, we want to add it
74 * to a single node representing that Animator, not create a new Node
Chet Haase17fb4b02010-06-28 17:55:07 -070075 * if one already exists.
76 */
Doris Liud7444422015-05-11 13:23:31 -070077 private ArrayMap<Animator, Node> mNodeMap = new ArrayMap<Animator, Node>();
Chet Haase17fb4b02010-06-28 17:55:07 -070078
79 /**
Chet Haasea18a86b2010-09-07 13:20:00 -070080 * Set of all nodes created for this AnimatorSet. This list is used upon
81 * starting the set, and the nodes are placed in sorted order into the
Chet Haase17fb4b02010-06-28 17:55:07 -070082 * sortedNodes collection.
83 */
Chet Haase49afa5b2010-08-23 11:39:53 -070084 private ArrayList<Node> mNodes = new ArrayList<Node>();
Chet Haase17fb4b02010-06-28 17:55:07 -070085
86 /**
Doris Liu13099142015-07-10 17:32:41 -070087 * Animator Listener that tracks the lifecycle of each Animator in the set. It will be added
88 * to each Animator before they start and removed after they end.
Chet Haase17fb4b02010-06-28 17:55:07 -070089 */
Doris Liu13099142015-07-10 17:32:41 -070090 private AnimatorSetListener mSetListener = new AnimatorSetListener(this);
Chet Haase17fb4b02010-06-28 17:55:07 -070091
92 /**
Chet Haase7dfacdb2011-07-11 17:01:56 -070093 * Flag indicating that the AnimatorSet has been manually
94 * terminated (by calling cancel() or end()).
Chet Haase010dbaa2010-07-19 17:29:49 -070095 * This flag is used to avoid starting other animations when currently-playing
Chet Haase7dfacdb2011-07-11 17:01:56 -070096 * child animations of this AnimatorSet end. It also determines whether cancel/end
97 * notifications are sent out via the normal AnimatorSetListener mechanism.
Chet Haase010dbaa2010-07-19 17:29:49 -070098 */
Doris Liu13099142015-07-10 17:32:41 -070099 private boolean mTerminated = false;
100
101 /**
102 * Tracks whether any change has been made to the AnimatorSet, which is then used to
103 * determine whether the dependency graph should be re-constructed.
104 */
105 private boolean mDependencyDirty = false;
Chet Haase010dbaa2010-07-19 17:29:49 -0700106
Chet Haase8b699792011-08-05 15:20:19 -0700107 /**
108 * Indicates whether an AnimatorSet has been start()'d, whether or
109 * not there is a nonzero startDelay.
110 */
111 private boolean mStarted = false;
112
Chet Haase21cd1382010-09-01 17:42:29 -0700113 // The amount of time in ms to delay starting the animation after start() is called
114 private long mStartDelay = 0;
115
Chet Haasee2ab7cc2010-12-06 16:10:07 -0800116 // Animator used for a nonzero startDelay
Doris Liu13099142015-07-10 17:32:41 -0700117 private ValueAnimator mDelayAnim = ValueAnimator.ofFloat(0f, 1f).setDuration(0);
Chet Haasee2ab7cc2010-12-06 16:10:07 -0800118
Doris Liu13099142015-07-10 17:32:41 -0700119 // Root of the dependency tree of all the animators in the set. In this tree, parent-child
120 // relationship captures the order of animation (i.e. parent and child will play sequentially),
121 // and sibling relationship indicates "with" relationship, as sibling animators start at the
122 // same time.
123 private Node mRootNode = new Node(mDelayAnim);
Chet Haase21cd1382010-09-01 17:42:29 -0700124
125 // How long the child animations should last in ms. The default value is negative, which
Chet Haasea18a86b2010-09-07 13:20:00 -0700126 // simply means that there is no duration set on the AnimatorSet. When a real duration is
Chet Haase21cd1382010-09-01 17:42:29 -0700127 // set, it is passed along to the child animations.
128 private long mDuration = -1;
129
Chet Haase430742f2013-04-12 11:18:36 -0700130 // Records the interpolator for the set. Null value indicates that no interpolator
131 // was set on this AnimatorSet, so it should not be passed down to the children.
132 private TimeInterpolator mInterpolator = null;
133
Doris Liu13099142015-07-10 17:32:41 -0700134 // Whether the AnimatorSet can be reversed.
ztenghui7bc6a3f2014-07-15 15:12:12 -0700135 private boolean mReversible = true;
Doris Liu13099142015-07-10 17:32:41 -0700136 // The total duration of finishing all the Animators in the set.
137 private long mTotalDuration = 0;
138
Doris Liu58606db2016-04-13 13:28:38 -0700139 // In pre-N releases, calling end() before start() on an animator set is no-op. But that is not
140 // consistent with the behavior for other animator types. In order to keep the behavior
141 // consistent within Animation framework, when end() is called without start(), we will start
142 // the animator set and immediately end it for N and forward.
143 private final boolean mShouldIgnoreEndWithoutStart;
144
Doris Liu13099142015-07-10 17:32:41 -0700145 public AnimatorSet() {
146 super();
147 mNodeMap.put(mDelayAnim, mRootNode);
148 mNodes.add(mRootNode);
Doris Liu58606db2016-04-13 13:28:38 -0700149 // Set the flag to ignore calling end() without start() for pre-N releases
150 Application app = ActivityThread.currentApplication();
151 if (app == null || app.getApplicationInfo() == null) {
152 mShouldIgnoreEndWithoutStart = true;
153 } else if (app.getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.N) {
154 mShouldIgnoreEndWithoutStart = true;
155 } else {
156 mShouldIgnoreEndWithoutStart = false;
157 }
Doris Liu13099142015-07-10 17:32:41 -0700158 }
159
Chet Haase010dbaa2010-07-19 17:29:49 -0700160 /**
Chet Haasea18a86b2010-09-07 13:20:00 -0700161 * Sets up this AnimatorSet to play all of the supplied animations at the same time.
Chet Haase430742f2013-04-12 11:18:36 -0700162 * This is equivalent to calling {@link #play(Animator)} with the first animator in the
163 * set and then {@link Builder#with(Animator)} with each of the other animators. Note that
164 * an Animator with a {@link Animator#setStartDelay(long) startDelay} will not actually
165 * start until that delay elapses, which means that if the first animator in the list
166 * supplied to this constructor has a startDelay, none of the other animators will start
167 * until that first animator's startDelay has elapsed.
Chet Haase17fb4b02010-06-28 17:55:07 -0700168 *
Chet Haasea18a86b2010-09-07 13:20:00 -0700169 * @param items The animations that will be started simultaneously.
Chet Haase17fb4b02010-06-28 17:55:07 -0700170 */
Chet Haasea18a86b2010-09-07 13:20:00 -0700171 public void playTogether(Animator... items) {
172 if (items != null) {
Chet Haasea18a86b2010-09-07 13:20:00 -0700173 Builder builder = play(items[0]);
174 for (int i = 1; i < items.length; ++i) {
175 builder.with(items[i]);
Chet Haase17fb4b02010-06-28 17:55:07 -0700176 }
177 }
178 }
179
180 /**
Chet Haase37a7bec2010-11-30 15:55:39 -0800181 * Sets up this AnimatorSet to play all of the supplied animations at the same time.
182 *
183 * @param items The animations that will be started simultaneously.
184 */
185 public void playTogether(Collection<Animator> items) {
186 if (items != null && items.size() > 0) {
Chet Haase37a7bec2010-11-30 15:55:39 -0800187 Builder builder = null;
188 for (Animator anim : items) {
189 if (builder == null) {
190 builder = play(anim);
191 } else {
192 builder.with(anim);
193 }
194 }
195 }
196 }
197
198 /**
Chet Haasea18a86b2010-09-07 13:20:00 -0700199 * Sets up this AnimatorSet to play each of the supplied animations when the
Chet Haase17fb4b02010-06-28 17:55:07 -0700200 * previous animation ends.
201 *
Chet Haase37a7bec2010-11-30 15:55:39 -0800202 * @param items The animations that will be started one after another.
Chet Haase17fb4b02010-06-28 17:55:07 -0700203 */
Chet Haasea18a86b2010-09-07 13:20:00 -0700204 public void playSequentially(Animator... items) {
205 if (items != null) {
Chet Haasea18a86b2010-09-07 13:20:00 -0700206 if (items.length == 1) {
207 play(items[0]);
Chet Haase17fb4b02010-06-28 17:55:07 -0700208 } else {
ztenghui7bc6a3f2014-07-15 15:12:12 -0700209 mReversible = false;
Chet Haasea18a86b2010-09-07 13:20:00 -0700210 for (int i = 0; i < items.length - 1; ++i) {
Doris Liu13099142015-07-10 17:32:41 -0700211 play(items[i]).before(items[i + 1]);
Chet Haase17fb4b02010-06-28 17:55:07 -0700212 }
213 }
214 }
215 }
216
217 /**
Chet Haase37a7bec2010-11-30 15:55:39 -0800218 * Sets up this AnimatorSet to play each of the supplied animations when the
219 * previous animation ends.
220 *
221 * @param items The animations that will be started one after another.
222 */
223 public void playSequentially(List<Animator> items) {
224 if (items != null && items.size() > 0) {
Chet Haase37a7bec2010-11-30 15:55:39 -0800225 if (items.size() == 1) {
226 play(items.get(0));
227 } else {
ztenghui7bc6a3f2014-07-15 15:12:12 -0700228 mReversible = false;
Chet Haase37a7bec2010-11-30 15:55:39 -0800229 for (int i = 0; i < items.size() - 1; ++i) {
Doris Liu13099142015-07-10 17:32:41 -0700230 play(items.get(i)).before(items.get(i + 1));
Chet Haase37a7bec2010-11-30 15:55:39 -0800231 }
232 }
233 }
234 }
235
236 /**
Chet Haasea18a86b2010-09-07 13:20:00 -0700237 * Returns the current list of child Animator objects controlled by this
238 * AnimatorSet. This is a copy of the internal list; modifications to the returned list
239 * will not affect the AnimatorSet, although changes to the underlying Animator objects
240 * will affect those objects being managed by the AnimatorSet.
Chet Haasef54a8d72010-07-22 14:44:59 -0700241 *
Chet Haasea18a86b2010-09-07 13:20:00 -0700242 * @return ArrayList<Animator> The list of child animations of this AnimatorSet.
Chet Haasef54a8d72010-07-22 14:44:59 -0700243 */
Chet Haasea18a86b2010-09-07 13:20:00 -0700244 public ArrayList<Animator> getChildAnimations() {
245 ArrayList<Animator> childList = new ArrayList<Animator>();
Doris Liu13099142015-07-10 17:32:41 -0700246 int size = mNodes.size();
247 for (int i = 0; i < size; i++) {
248 Node node = mNodes.get(i);
Doris Liudbf69e42015-08-05 20:00:11 -0700249 if (node != mRootNode) {
250 childList.add(node.mAnimation);
251 }
Chet Haasef54a8d72010-07-22 14:44:59 -0700252 }
253 return childList;
254 }
255
256 /**
Chet Haase811ed1062010-08-06 10:38:15 -0700257 * Sets the target object for all current {@link #getChildAnimations() child animations}
Chet Haasea18a86b2010-09-07 13:20:00 -0700258 * of this AnimatorSet that take targets ({@link ObjectAnimator} and
259 * AnimatorSet).
Chet Haase811ed1062010-08-06 10:38:15 -0700260 *
261 * @param target The object being animated
262 */
Chet Haase21cd1382010-09-01 17:42:29 -0700263 @Override
Chet Haase811ed1062010-08-06 10:38:15 -0700264 public void setTarget(Object target) {
Doris Liu13099142015-07-10 17:32:41 -0700265 int size = mNodes.size();
266 for (int i = 0; i < size; i++) {
267 Node node = mNodes.get(i);
268 Animator animation = node.mAnimation;
Chet Haasea18a86b2010-09-07 13:20:00 -0700269 if (animation instanceof AnimatorSet) {
270 ((AnimatorSet)animation).setTarget(target);
271 } else if (animation instanceof ObjectAnimator) {
272 ((ObjectAnimator)animation).setTarget(target);
Chet Haase811ed1062010-08-06 10:38:15 -0700273 }
274 }
275 }
276
277 /**
Yigit Boyard422dc32014-09-25 12:23:35 -0700278 * @hide
279 */
280 @Override
281 public int getChangingConfigurations() {
282 int conf = super.getChangingConfigurations();
283 final int nodeCount = mNodes.size();
284 for (int i = 0; i < nodeCount; i ++) {
Doris Liu13099142015-07-10 17:32:41 -0700285 conf |= mNodes.get(i).mAnimation.getChangingConfigurations();
Yigit Boyard422dc32014-09-25 12:23:35 -0700286 }
287 return conf;
288 }
289
290 /**
Chet Haasee0ee2e92010-10-07 09:06:18 -0700291 * Sets the TimeInterpolator for all current {@link #getChildAnimations() child animations}
Chet Haase430742f2013-04-12 11:18:36 -0700292 * of this AnimatorSet. The default value is null, which means that no interpolator
293 * is set on this AnimatorSet. Setting the interpolator to any non-null value
294 * will cause that interpolator to be set on the child animations
295 * when the set is started.
Chet Haase21cd1382010-09-01 17:42:29 -0700296 *
Chet Haasea18a86b2010-09-07 13:20:00 -0700297 * @param interpolator the interpolator to be used by each child animation of this AnimatorSet
Chet Haase21cd1382010-09-01 17:42:29 -0700298 */
299 @Override
Chet Haasee0ee2e92010-10-07 09:06:18 -0700300 public void setInterpolator(TimeInterpolator interpolator) {
Chet Haase430742f2013-04-12 11:18:36 -0700301 mInterpolator = interpolator;
302 }
303
304 @Override
305 public TimeInterpolator getInterpolator() {
306 return mInterpolator;
Chet Haase21cd1382010-09-01 17:42:29 -0700307 }
308
309 /**
Chet Haase17fb4b02010-06-28 17:55:07 -0700310 * This method creates a <code>Builder</code> object, which is used to
311 * set up playing constraints. This initial <code>play()</code> method
312 * tells the <code>Builder</code> the animation that is the dependency for
313 * the succeeding commands to the <code>Builder</code>. For example,
Chet Haasea18a86b2010-09-07 13:20:00 -0700314 * calling <code>play(a1).with(a2)</code> sets up the AnimatorSet to play
Chet Haase17fb4b02010-06-28 17:55:07 -0700315 * <code>a1</code> and <code>a2</code> at the same time,
Chet Haasea18a86b2010-09-07 13:20:00 -0700316 * <code>play(a1).before(a2)</code> sets up the AnimatorSet to play
Chet Haase17fb4b02010-06-28 17:55:07 -0700317 * <code>a1</code> first, followed by <code>a2</code>, and
Chet Haasea18a86b2010-09-07 13:20:00 -0700318 * <code>play(a1).after(a2)</code> sets up the AnimatorSet to play
Chet Haase17fb4b02010-06-28 17:55:07 -0700319 * <code>a2</code> first, followed by <code>a1</code>.
320 *
321 * <p>Note that <code>play()</code> is the only way to tell the
322 * <code>Builder</code> the animation upon which the dependency is created,
323 * so successive calls to the various functions in <code>Builder</code>
324 * will all refer to the initial parameter supplied in <code>play()</code>
325 * as the dependency of the other animations. For example, calling
326 * <code>play(a1).before(a2).before(a3)</code> will play both <code>a2</code>
327 * and <code>a3</code> when a1 ends; it does not set up a dependency between
328 * <code>a2</code> and <code>a3</code>.</p>
329 *
330 * @param anim The animation that is the dependency used in later calls to the
331 * methods in the returned <code>Builder</code> object. A null parameter will result
332 * in a null <code>Builder</code> return value.
Chet Haasea18a86b2010-09-07 13:20:00 -0700333 * @return Builder The object that constructs the AnimatorSet based on the dependencies
Chet Haase17fb4b02010-06-28 17:55:07 -0700334 * outlined in the calls to <code>play</code> and the other methods in the
335 * <code>Builder</code object.
336 */
Chet Haasea18a86b2010-09-07 13:20:00 -0700337 public Builder play(Animator anim) {
Chet Haase17fb4b02010-06-28 17:55:07 -0700338 if (anim != null) {
Chet Haase17fb4b02010-06-28 17:55:07 -0700339 return new Builder(anim);
340 }
341 return null;
342 }
343
344 /**
345 * {@inheritDoc}
346 *
Chet Haase8b699792011-08-05 15:20:19 -0700347 * <p>Note that canceling a <code>AnimatorSet</code> also cancels all of the animations that it
348 * is responsible for.</p>
Chet Haase17fb4b02010-06-28 17:55:07 -0700349 */
350 @SuppressWarnings("unchecked")
351 @Override
352 public void cancel() {
Chet Haase7dfacdb2011-07-11 17:01:56 -0700353 mTerminated = true;
Chet Haase8b699792011-08-05 15:20:19 -0700354 if (isStarted()) {
Chet Haase7dfacdb2011-07-11 17:01:56 -0700355 ArrayList<AnimatorListener> tmpListeners = null;
Chet Haasee2ab7cc2010-12-06 16:10:07 -0800356 if (mListeners != null) {
Chet Haase7dfacdb2011-07-11 17:01:56 -0700357 tmpListeners = (ArrayList<AnimatorListener>) mListeners.clone();
Doris Liu13099142015-07-10 17:32:41 -0700358 int size = tmpListeners.size();
359 for (int i = 0; i < size; i++) {
360 tmpListeners.get(i).onAnimationCancel(this);
Chet Haase7dfacdb2011-07-11 17:01:56 -0700361 }
362 }
Doris Liu13099142015-07-10 17:32:41 -0700363 ArrayList<Animator> playingSet = new ArrayList<>(mPlayingSet);
364 int setSize = playingSet.size();
365 for (int i = 0; i < setSize; i++) {
366 playingSet.get(i).cancel();
Chet Haase7dfacdb2011-07-11 17:01:56 -0700367 }
368 if (tmpListeners != null) {
Doris Liu13099142015-07-10 17:32:41 -0700369 int size = tmpListeners.size();
370 for (int i = 0; i < size; i++) {
371 tmpListeners.get(i).onAnimationEnd(this);
Chet Haasee2ab7cc2010-12-06 16:10:07 -0800372 }
373 }
Chet Haase8b699792011-08-05 15:20:19 -0700374 mStarted = false;
Chet Haase17fb4b02010-06-28 17:55:07 -0700375 }
376 }
377
378 /**
379 * {@inheritDoc}
380 *
Chet Haasea18a86b2010-09-07 13:20:00 -0700381 * <p>Note that ending a <code>AnimatorSet</code> also ends all of the animations that it is
Chet Haase17fb4b02010-06-28 17:55:07 -0700382 * responsible for.</p>
383 */
384 @Override
385 public void end() {
Doris Liu58606db2016-04-13 13:28:38 -0700386 if (mShouldIgnoreEndWithoutStart && !isStarted()) {
387 return;
388 }
Chet Haase7dfacdb2011-07-11 17:01:56 -0700389 mTerminated = true;
Chet Haase8b699792011-08-05 15:20:19 -0700390 if (isStarted()) {
Doris Liu13099142015-07-10 17:32:41 -0700391 endRemainingAnimations();
392 }
393 if (mListeners != null) {
394 ArrayList<AnimatorListener> tmpListeners =
395 (ArrayList<AnimatorListener>) mListeners.clone();
396 for (int i = 0; i < tmpListeners.size(); i++) {
397 tmpListeners.get(i).onAnimationEnd(this);
398 }
399 }
400 mStarted = false;
401 }
402
403 /**
404 * Iterate the animations that haven't finished or haven't started, and end them.
405 */
406 private void endRemainingAnimations() {
407 ArrayList<Animator> remainingList = new ArrayList<Animator>(mNodes.size());
408 remainingList.addAll(mPlayingSet);
409
410 int index = 0;
411 while (index < remainingList.size()) {
412 Animator anim = remainingList.get(index);
413 anim.end();
414 index++;
415 Node node = mNodeMap.get(anim);
416 if (node.mChildNodes != null) {
417 int childSize = node.mChildNodes.size();
418 for (int i = 0; i < childSize; i++) {
419 Node child = node.mChildNodes.get(i);
420 if (child.mLatestParent != node) {
421 continue;
Chet Haase7dfacdb2011-07-11 17:01:56 -0700422 }
Doris Liu13099142015-07-10 17:32:41 -0700423 remainingList.add(child.mAnimation);
Chet Haase1e0ac5a2010-08-27 08:32:11 -0700424 }
Chet Haase1e0ac5a2010-08-27 08:32:11 -0700425 }
Chet Haase17fb4b02010-06-28 17:55:07 -0700426 }
427 }
428
Doris Liu13099142015-07-10 17:32:41 -0700429
Chet Haase17fb4b02010-06-28 17:55:07 -0700430 /**
Chet Haase8b699792011-08-05 15:20:19 -0700431 * Returns true if any of the child animations of this AnimatorSet have been started and have
Doris Liuee684552015-08-28 13:21:03 -0700432 * not yet ended. Child animations will not be started until the AnimatorSet has gone past
433 * its initial delay set through {@link #setStartDelay(long)}.
434 *
435 * @return Whether this AnimatorSet has gone past the initial delay, and at least one child
436 * animation has been started and not yet ended.
Chet Haase673e42f2010-08-25 16:32:37 -0700437 */
438 @Override
439 public boolean isRunning() {
Doris Liu13099142015-07-10 17:32:41 -0700440 int size = mNodes.size();
441 for (int i = 0; i < size; i++) {
Doris Liu458f20e2015-08-10 17:32:54 -0700442 Node node = mNodes.get(i);
Doris Liuee684552015-08-28 13:21:03 -0700443 if (node != mRootNode && node.mAnimation.isStarted()) {
Chet Haase673e42f2010-08-25 16:32:37 -0700444 return true;
445 }
446 }
Chet Haase8b699792011-08-05 15:20:19 -0700447 return false;
448 }
449
450 @Override
451 public boolean isStarted() {
452 return mStarted;
Chet Haase673e42f2010-08-25 16:32:37 -0700453 }
454
455 /**
Chet Haase21cd1382010-09-01 17:42:29 -0700456 * The amount of time, in milliseconds, to delay starting the animation after
457 * {@link #start()} is called.
458 *
459 * @return the number of milliseconds to delay running the animation
460 */
461 @Override
462 public long getStartDelay() {
463 return mStartDelay;
464 }
465
466 /**
467 * The amount of time, in milliseconds, to delay starting the animation after
468 * {@link #start()} is called.
469
470 * @param startDelay The amount of the delay, in milliseconds
471 */
472 @Override
473 public void setStartDelay(long startDelay) {
ztenghui7bc6a3f2014-07-15 15:12:12 -0700474 if (mStartDelay > 0) {
475 mReversible = false;
476 }
Doris Liu13099142015-07-10 17:32:41 -0700477 long delta = startDelay - mStartDelay;
Doris Liu49db4242015-08-10 13:33:06 -0700478 if (delta == 0) {
479 return;
480 }
Chet Haase21cd1382010-09-01 17:42:29 -0700481 mStartDelay = startDelay;
Doris Liu13099142015-07-10 17:32:41 -0700482 if (!mDependencyDirty) {
483 // Dependency graph already constructed, update all the nodes' start/end time
484 int size = mNodes.size();
485 for (int i = 0; i < size; i++) {
486 Node node = mNodes.get(i);
487 if (node == mRootNode) {
488 node.mEndTime = mStartDelay;
489 } else {
490 node.mStartTime = node.mStartTime == DURATION_INFINITE ?
491 DURATION_INFINITE : node.mStartTime + delta;
492 node.mEndTime = node.mEndTime == DURATION_INFINITE ?
493 DURATION_INFINITE : node.mEndTime + delta;
Doris Liu13099142015-07-10 17:32:41 -0700494 }
495 }
Doris Liu49db4242015-08-10 13:33:06 -0700496 // Update total duration, if necessary.
497 if (mTotalDuration != DURATION_INFINITE) {
498 mTotalDuration += delta;
499 }
Doris Liu13099142015-07-10 17:32:41 -0700500 }
Chet Haase21cd1382010-09-01 17:42:29 -0700501 }
502
503 /**
Chet Haasea18a86b2010-09-07 13:20:00 -0700504 * Gets the length of each of the child animations of this AnimatorSet. This value may
505 * be less than 0, which indicates that no duration has been set on this AnimatorSet
Chet Haase21cd1382010-09-01 17:42:29 -0700506 * and each of the child animations will use their own duration.
507 *
508 * @return The length of the animation, in milliseconds, of each of the child
Chet Haasea18a86b2010-09-07 13:20:00 -0700509 * animations of this AnimatorSet.
Chet Haase21cd1382010-09-01 17:42:29 -0700510 */
511 @Override
512 public long getDuration() {
513 return mDuration;
514 }
515
516 /**
Chet Haasea18a86b2010-09-07 13:20:00 -0700517 * Sets the length of each of the current child animations of this AnimatorSet. By default,
518 * each child animation will use its own duration. If the duration is set on the AnimatorSet,
Chet Haase21cd1382010-09-01 17:42:29 -0700519 * then each child animation inherits this duration.
520 *
521 * @param duration The length of the animation, in milliseconds, of each of the child
Chet Haasea18a86b2010-09-07 13:20:00 -0700522 * animations of this AnimatorSet.
Chet Haase21cd1382010-09-01 17:42:29 -0700523 */
524 @Override
Chet Haase2794eb32010-10-12 16:29:28 -0700525 public AnimatorSet setDuration(long duration) {
Chet Haase21cd1382010-09-01 17:42:29 -0700526 if (duration < 0) {
527 throw new IllegalArgumentException("duration must be a value of zero or greater");
528 }
Doris Liu13099142015-07-10 17:32:41 -0700529 mDependencyDirty = true;
Chet Haasec299a332012-04-12 07:51:50 -0700530 // Just record the value for now - it will be used later when the AnimatorSet starts
Chet Haase21cd1382010-09-01 17:42:29 -0700531 mDuration = duration;
Chet Haase2794eb32010-10-12 16:29:28 -0700532 return this;
Chet Haase21cd1382010-09-01 17:42:29 -0700533 }
534
Chet Haase2970c492010-11-09 13:58:04 -0800535 @Override
536 public void setupStartValues() {
Doris Liu13099142015-07-10 17:32:41 -0700537 int size = mNodes.size();
538 for (int i = 0; i < size; i++) {
539 Node node = mNodes.get(i);
Doris Liu458f20e2015-08-10 17:32:54 -0700540 if (node != mRootNode) {
541 node.mAnimation.setupStartValues();
542 }
Chet Haase2970c492010-11-09 13:58:04 -0800543 }
544 }
545
546 @Override
547 public void setupEndValues() {
Doris Liu13099142015-07-10 17:32:41 -0700548 int size = mNodes.size();
549 for (int i = 0; i < size; i++) {
550 Node node = mNodes.get(i);
Doris Liu458f20e2015-08-10 17:32:54 -0700551 if (node != mRootNode) {
552 node.mAnimation.setupEndValues();
553 }
Chet Haase2970c492010-11-09 13:58:04 -0800554 }
555 }
556
Chet Haase8aa1ffb2013-08-08 14:00:00 -0700557 @Override
558 public void pause() {
559 boolean previouslyPaused = mPaused;
560 super.pause();
561 if (!previouslyPaused && mPaused) {
Doris Liu2987dff2016-04-18 17:31:24 -0700562 if (mDelayAnim.isStarted()) {
563 // If delay hasn't passed, pause the start delay animator.
Chet Haase8aa1ffb2013-08-08 14:00:00 -0700564 mDelayAnim.pause();
565 } else {
Doris Liu13099142015-07-10 17:32:41 -0700566 int size = mNodes.size();
567 for (int i = 0; i < size; i++) {
568 Node node = mNodes.get(i);
Doris Liu458f20e2015-08-10 17:32:54 -0700569 if (node != mRootNode) {
570 node.mAnimation.pause();
571 }
Chet Haase8aa1ffb2013-08-08 14:00:00 -0700572 }
573 }
574 }
575 }
576
577 @Override
578 public void resume() {
579 boolean previouslyPaused = mPaused;
580 super.resume();
581 if (previouslyPaused && !mPaused) {
Doris Liu2987dff2016-04-18 17:31:24 -0700582 if (mDelayAnim.isStarted()) {
583 // If start delay hasn't passed, resume the previously paused start delay animator
Chet Haase8aa1ffb2013-08-08 14:00:00 -0700584 mDelayAnim.resume();
585 } else {
Doris Liu13099142015-07-10 17:32:41 -0700586 int size = mNodes.size();
587 for (int i = 0; i < size; i++) {
588 Node node = mNodes.get(i);
Doris Liu458f20e2015-08-10 17:32:54 -0700589 if (node != mRootNode) {
590 node.mAnimation.resume();
591 }
Chet Haase8aa1ffb2013-08-08 14:00:00 -0700592 }
593 }
594 }
595 }
596
Chet Haase21cd1382010-09-01 17:42:29 -0700597 /**
Chet Haase17fb4b02010-06-28 17:55:07 -0700598 * {@inheritDoc}
599 *
Chet Haasea18a86b2010-09-07 13:20:00 -0700600 * <p>Starting this <code>AnimatorSet</code> will, in turn, start the animations for which
Chet Haase17fb4b02010-06-28 17:55:07 -0700601 * it is responsible. The details of when exactly those animations are started depends on
602 * the dependency relationships that have been set up between the animations.
603 */
604 @SuppressWarnings("unchecked")
605 @Override
606 public void start() {
Chet Haase7dfacdb2011-07-11 17:01:56 -0700607 mTerminated = false;
Chet Haase8b699792011-08-05 15:20:19 -0700608 mStarted = true;
Chet Haase8aa1ffb2013-08-08 14:00:00 -0700609 mPaused = false;
Chet Haase010dbaa2010-07-19 17:29:49 -0700610
Doris Liu13099142015-07-10 17:32:41 -0700611 int size = mNodes.size();
612 for (int i = 0; i < size; i++) {
613 Node node = mNodes.get(i);
614 node.mEnded = false;
615 node.mAnimation.setAllowRunningAsynchronously(false);
John Reckf5945a02014-09-05 15:57:47 -0700616 }
617
Chet Haase430742f2013-04-12 11:18:36 -0700618 if (mInterpolator != null) {
Doris Liu13099142015-07-10 17:32:41 -0700619 for (int i = 0; i < size; i++) {
620 Node node = mNodes.get(i);
621 node.mAnimation.setInterpolator(mInterpolator);
Chet Haasee2ab7cc2010-12-06 16:10:07 -0800622 }
623 }
624
Doris Liu13099142015-07-10 17:32:41 -0700625 updateAnimatorsDuration();
626 createDependencyGraph();
627
Chet Haase17fb4b02010-06-28 17:55:07 -0700628 // Now that all dependencies are set up, start the animations that should be started.
Doris Liuf57bfe22015-10-01 13:26:01 -0700629 boolean setIsEmpty = false;
630 if (mStartDelay > 0) {
631 start(mRootNode);
632 } else if (mNodes.size() > 1) {
633 // No delay, but there are other animators in the set
634 onChildAnimatorEnded(mDelayAnim);
635 } else {
636 // Set is empty, no delay, no other animation. Skip to end in this case
637 setIsEmpty = true;
638 }
639
Chet Haase17fb4b02010-06-28 17:55:07 -0700640 if (mListeners != null) {
Chet Haasea18a86b2010-09-07 13:20:00 -0700641 ArrayList<AnimatorListener> tmpListeners =
642 (ArrayList<AnimatorListener>) mListeners.clone();
Chet Haase7c608f22010-10-22 17:54:04 -0700643 int numListeners = tmpListeners.size();
644 for (int i = 0; i < numListeners; ++i) {
645 tmpListeners.get(i).onAnimationStart(this);
Chet Haase8b699792011-08-05 15:20:19 -0700646 }
647 }
Doris Liuf57bfe22015-10-01 13:26:01 -0700648 if (setIsEmpty) {
649 // In the case of empty AnimatorSet, we will trigger the onAnimationEnd() right away.
650 onChildAnimatorEnded(mDelayAnim);
Chet Haase17fb4b02010-06-28 17:55:07 -0700651 }
652 }
653
Doris Liu13099142015-07-10 17:32:41 -0700654 private void updateAnimatorsDuration() {
655 if (mDuration >= 0) {
656 // If the duration was set on this AnimatorSet, pass it along to all child animations
657 int size = mNodes.size();
658 for (int i = 0; i < size; i++) {
659 Node node = mNodes.get(i);
660 // TODO: don't set the duration of the timing-only nodes created by AnimatorSet to
661 // insert "play-after" delays
662 node.mAnimation.setDuration(mDuration);
663 }
664 }
665 mDelayAnim.setDuration(mStartDelay);
666 }
667
668 void start(final Node node) {
669 final Animator anim = node.mAnimation;
670 mPlayingSet.add(anim);
671 anim.addListener(mSetListener);
672 anim.start();
673 }
674
Chet Haase49afa5b2010-08-23 11:39:53 -0700675 @Override
Chet Haasea18a86b2010-09-07 13:20:00 -0700676 public AnimatorSet clone() {
677 final AnimatorSet anim = (AnimatorSet) super.clone();
Chet Haase49afa5b2010-08-23 11:39:53 -0700678 /*
679 * The basic clone() operation copies all items. This doesn't work very well for
Chet Haasea18a86b2010-09-07 13:20:00 -0700680 * AnimatorSet, because it will copy references that need to be recreated and state
Chet Haase49afa5b2010-08-23 11:39:53 -0700681 * that may not apply. What we need to do now is put the clone in an uninitialized
682 * state, with fresh, empty data structures. Then we will build up the nodes list
683 * manually, as we clone each Node (and its animation). The clone will then be sorted,
684 * and will populate any appropriate lists, when it is started.
685 */
Yigit Boyard422dc32014-09-25 12:23:35 -0700686 final int nodeCount = mNodes.size();
Chet Haase7dfacdb2011-07-11 17:01:56 -0700687 anim.mTerminated = false;
Chet Haase8b699792011-08-05 15:20:19 -0700688 anim.mStarted = false;
Chet Haasea18a86b2010-09-07 13:20:00 -0700689 anim.mPlayingSet = new ArrayList<Animator>();
Doris Liud7444422015-05-11 13:23:31 -0700690 anim.mNodeMap = new ArrayMap<Animator, Node>();
Yigit Boyard422dc32014-09-25 12:23:35 -0700691 anim.mNodes = new ArrayList<Node>(nodeCount);
ztenghui7bc6a3f2014-07-15 15:12:12 -0700692 anim.mReversible = mReversible;
Doris Liu13099142015-07-10 17:32:41 -0700693 anim.mSetListener = new AnimatorSetListener(anim);
Chet Haase49afa5b2010-08-23 11:39:53 -0700694
695 // Walk through the old nodes list, cloning each node and adding it to the new nodemap.
Chet Haasea18a86b2010-09-07 13:20:00 -0700696 // One problem is that the old node dependencies point to nodes in the old AnimatorSet.
Chet Haase49afa5b2010-08-23 11:39:53 -0700697 // We need to track the old/new nodes in order to reconstruct the dependencies in the clone.
Yigit Boyard422dc32014-09-25 12:23:35 -0700698
699 for (int n = 0; n < nodeCount; n++) {
700 final Node node = mNodes.get(n);
Chet Haase49afa5b2010-08-23 11:39:53 -0700701 Node nodeClone = node.clone();
Yigit Boyard422dc32014-09-25 12:23:35 -0700702 node.mTmpClone = nodeClone;
Chet Haase49afa5b2010-08-23 11:39:53 -0700703 anim.mNodes.add(nodeClone);
Doris Liu13099142015-07-10 17:32:41 -0700704 anim.mNodeMap.put(nodeClone.mAnimation, nodeClone);
Yigit Boyard422dc32014-09-25 12:23:35 -0700705
Doris Liu13099142015-07-10 17:32:41 -0700706 // clear out any listeners that were set up by the AnimatorSet
707 final ArrayList<AnimatorListener> cloneListeners = nodeClone.mAnimation.getListeners();
Chet Haase49afa5b2010-08-23 11:39:53 -0700708 if (cloneListeners != null) {
Yigit Boyard422dc32014-09-25 12:23:35 -0700709 for (int i = cloneListeners.size() - 1; i >= 0; i--) {
710 final AnimatorListener listener = cloneListeners.get(i);
Chet Haasea18a86b2010-09-07 13:20:00 -0700711 if (listener instanceof AnimatorSetListener) {
Yigit Boyard422dc32014-09-25 12:23:35 -0700712 cloneListeners.remove(i);
Chet Haase49afa5b2010-08-23 11:39:53 -0700713 }
714 }
715 }
716 }
Doris Liu13099142015-07-10 17:32:41 -0700717
718 anim.mRootNode = mRootNode.mTmpClone;
719 anim.mDelayAnim = (ValueAnimator) anim.mRootNode.mAnimation;
720
Chet Haase49afa5b2010-08-23 11:39:53 -0700721 // Now that we've cloned all of the nodes, we're ready to walk through their
722 // dependencies, mapping the old dependencies to the new nodes
Doris Liu13099142015-07-10 17:32:41 -0700723 for (int i = 0; i < nodeCount; i++) {
724 Node node = mNodes.get(i);
725 // Update dependencies for node's clone
726 node.mTmpClone.mLatestParent = node.mLatestParent == null ?
727 null : node.mLatestParent.mTmpClone;
728 int size = node.mChildNodes == null ? 0 : node.mChildNodes.size();
729 for (int j = 0; j < size; j++) {
730 node.mTmpClone.mChildNodes.set(j, node.mChildNodes.get(j).mTmpClone);
Yigit Boyard422dc32014-09-25 12:23:35 -0700731 }
Doris Liu13099142015-07-10 17:32:41 -0700732 size = node.mSiblings == null ? 0 : node.mSiblings.size();
733 for (int j = 0; j < size; j++) {
734 node.mTmpClone.mSiblings.set(j, node.mSiblings.get(j).mTmpClone);
Yigit Boyard422dc32014-09-25 12:23:35 -0700735 }
Doris Liu13099142015-07-10 17:32:41 -0700736 size = node.mParents == null ? 0 : node.mParents.size();
737 for (int j = 0; j < size; j++) {
738 node.mTmpClone.mParents.set(j, node.mParents.get(j).mTmpClone);
Chet Haase49afa5b2010-08-23 11:39:53 -0700739 }
740 }
Doris Liu13099142015-07-10 17:32:41 -0700741
Yigit Boyard422dc32014-09-25 12:23:35 -0700742 for (int n = 0; n < nodeCount; n++) {
743 mNodes.get(n).mTmpClone = null;
744 }
Chet Haase49afa5b2010-08-23 11:39:53 -0700745 return anim;
746 }
747
Chet Haase17fb4b02010-06-28 17:55:07 -0700748
Doris Liu6f2276b2015-08-06 13:51:31 -0700749 private static class AnimatorSetListener implements AnimatorListener {
Chet Haase17fb4b02010-06-28 17:55:07 -0700750
Chet Haasea18a86b2010-09-07 13:20:00 -0700751 private AnimatorSet mAnimatorSet;
Chet Haase17fb4b02010-06-28 17:55:07 -0700752
Chet Haasea18a86b2010-09-07 13:20:00 -0700753 AnimatorSetListener(AnimatorSet animatorSet) {
754 mAnimatorSet = animatorSet;
Chet Haase17fb4b02010-06-28 17:55:07 -0700755 }
756
Chet Haasea18a86b2010-09-07 13:20:00 -0700757 public void onAnimationCancel(Animator animation) {
Doris Liu13099142015-07-10 17:32:41 -0700758
Doris Liu6f2276b2015-08-06 13:51:31 -0700759 if (!mAnimatorSet.mTerminated) {
Chet Haase7dfacdb2011-07-11 17:01:56 -0700760 // Listeners are already notified of the AnimatorSet canceling in cancel().
761 // The logic below only kicks in when animations end normally
Doris Liu13099142015-07-10 17:32:41 -0700762 if (mAnimatorSet.mPlayingSet.size() == 0) {
763 ArrayList<AnimatorListener> listeners = mAnimatorSet.mListeners;
764 if (listeners != null) {
765 int numListeners = listeners.size();
Chet Haase7dfacdb2011-07-11 17:01:56 -0700766 for (int i = 0; i < numListeners; ++i) {
Doris Liu13099142015-07-10 17:32:41 -0700767 listeners.get(i).onAnimationCancel(mAnimatorSet);
Chet Haase7dfacdb2011-07-11 17:01:56 -0700768 }
Chet Haase17fb4b02010-06-28 17:55:07 -0700769 }
770 }
771 }
772 }
773
774 @SuppressWarnings("unchecked")
Chet Haasea18a86b2010-09-07 13:20:00 -0700775 public void onAnimationEnd(Animator animation) {
Chet Haase17fb4b02010-06-28 17:55:07 -0700776 animation.removeListener(this);
Doris Liu13099142015-07-10 17:32:41 -0700777 mAnimatorSet.mPlayingSet.remove(animation);
Doris Liuf57bfe22015-10-01 13:26:01 -0700778 mAnimatorSet.onChildAnimatorEnded(animation);
Chet Haase17fb4b02010-06-28 17:55:07 -0700779 }
780
781 // Nothing to do
Chet Haasea18a86b2010-09-07 13:20:00 -0700782 public void onAnimationRepeat(Animator animation) {
Chet Haase17fb4b02010-06-28 17:55:07 -0700783 }
784
785 // Nothing to do
Chet Haasea18a86b2010-09-07 13:20:00 -0700786 public void onAnimationStart(Animator animation) {
Chet Haase17fb4b02010-06-28 17:55:07 -0700787 }
788
789 }
790
Doris Liuf57bfe22015-10-01 13:26:01 -0700791 private void onChildAnimatorEnded(Animator animation) {
792 Node animNode = mNodeMap.get(animation);
793 animNode.mEnded = true;
794
795 if (!mTerminated) {
796 List<Node> children = animNode.mChildNodes;
797 // Start children animations, if any.
798 int childrenSize = children == null ? 0 : children.size();
799 for (int i = 0; i < childrenSize; i++) {
800 if (children.get(i).mLatestParent == animNode) {
801 start(children.get(i));
802 }
803 }
804 // Listeners are already notified of the AnimatorSet ending in cancel() or
805 // end(); the logic below only kicks in when animations end normally
806 boolean allDone = true;
807 // Traverse the tree and find if there's any unfinished node
808 int size = mNodes.size();
809 for (int i = 0; i < size; i++) {
810 if (!mNodes.get(i).mEnded) {
811 allDone = false;
812 break;
813 }
814 }
815 if (allDone) {
816 // If this was the last child animation to end, then notify listeners that this
817 // AnimatorSet has ended
818 if (mListeners != null) {
819 ArrayList<AnimatorListener> tmpListeners =
820 (ArrayList<AnimatorListener>) mListeners.clone();
821 int numListeners = tmpListeners.size();
822 for (int i = 0; i < numListeners; ++i) {
823 tmpListeners.get(i).onAnimationEnd(this);
824 }
825 }
826 mStarted = false;
827 mPaused = false;
828 }
829 }
830 }
831
Chet Haase17fb4b02010-06-28 17:55:07 -0700832 /**
Doris Liuc4bb1852016-02-19 21:39:21 +0000833 * AnimatorSet is only reversible when the set contains no sequential animation, and no child
834 * animators have a start delay.
ztenghui7bc6a3f2014-07-15 15:12:12 -0700835 * @hide
836 */
837 @Override
838 public boolean canReverse() {
839 if (!mReversible) {
840 return false;
841 }
842 // Loop to make sure all the Nodes can reverse.
Doris Liu13099142015-07-10 17:32:41 -0700843 int size = mNodes.size();
844 for (int i = 0; i < size; i++) {
845 Node node = mNodes.get(i);
846 if (!node.mAnimation.canReverse() || node.mAnimation.getStartDelay() > 0) {
ztenghui7bc6a3f2014-07-15 15:12:12 -0700847 return false;
848 }
849 }
850 return true;
851 }
852
853 /**
854 * @hide
855 */
856 @Override
857 public void reverse() {
858 if (canReverse()) {
Doris Liu13099142015-07-10 17:32:41 -0700859 int size = mNodes.size();
860 for (int i = 0; i < size; i++) {
861 Node node = mNodes.get(i);
862 node.mAnimation.reverse();
ztenghui7bc6a3f2014-07-15 15:12:12 -0700863 }
864 }
865 }
866
Chet Haased4307532014-12-01 06:32:38 -0800867 @Override
868 public String toString() {
869 String returnVal = "AnimatorSet@" + Integer.toHexString(hashCode()) + "{";
Doris Liu13099142015-07-10 17:32:41 -0700870 int size = mNodes.size();
871 for (int i = 0; i < size; i++) {
872 Node node = mNodes.get(i);
873 returnVal += "\n " + node.mAnimation.toString();
Chet Haased4307532014-12-01 06:32:38 -0800874 }
875 return returnVal + "\n}";
876 }
877
Doris Liu13099142015-07-10 17:32:41 -0700878 private void printChildCount() {
879 // Print out the child count through a level traverse.
880 ArrayList<Node> list = new ArrayList<>(mNodes.size());
881 list.add(mRootNode);
882 Log.d(TAG, "Current tree: ");
883 int index = 0;
884 while (index < list.size()) {
885 int listSize = list.size();
886 StringBuilder builder = new StringBuilder();
887 for (; index < listSize; index++) {
888 Node node = list.get(index);
889 int num = 0;
890 if (node.mChildNodes != null) {
891 for (int i = 0; i < node.mChildNodes.size(); i++) {
892 Node child = node.mChildNodes.get(i);
893 if (child.mLatestParent == node) {
894 num++;
895 list.add(child);
896 }
897 }
898 }
899 builder.append(" ");
900 builder.append(num);
901 }
902 Log.d(TAG, builder.toString());
Chet Haase17fb4b02010-06-28 17:55:07 -0700903 }
904 }
905
Doris Liu13099142015-07-10 17:32:41 -0700906 private void createDependencyGraph() {
907 if (!mDependencyDirty) {
Doris Liu49db4242015-08-10 13:33:06 -0700908 // Check whether any duration of the child animations has changed
909 boolean durationChanged = false;
910 for (int i = 0; i < mNodes.size(); i++) {
911 Animator anim = mNodes.get(i).mAnimation;
912 if (mNodes.get(i).mTotalDuration != anim.getTotalDuration()) {
913 durationChanged = true;
914 break;
915 }
916 }
917 if (!durationChanged) {
918 return;
919 }
Doris Liu13099142015-07-10 17:32:41 -0700920 }
921
Doris Liu13099142015-07-10 17:32:41 -0700922 mDependencyDirty = false;
923 // Traverse all the siblings and make sure they have all the parents
924 int size = mNodes.size();
925 for (int i = 0; i < size; i++) {
926 mNodes.get(i).mParentsAdded = false;
927 }
928 for (int i = 0; i < size; i++) {
929 Node node = mNodes.get(i);
930 if (node.mParentsAdded) {
931 continue;
932 }
933
934 node.mParentsAdded = true;
935 if (node.mSiblings == null) {
936 continue;
937 }
938
939 // Find all the siblings
940 findSiblings(node, node.mSiblings);
941 node.mSiblings.remove(node);
942
943 // Get parents from all siblings
944 int siblingSize = node.mSiblings.size();
945 for (int j = 0; j < siblingSize; j++) {
946 node.addParents(node.mSiblings.get(j).mParents);
947 }
948
949 // Now make sure all siblings share the same set of parents
950 for (int j = 0; j < siblingSize; j++) {
951 Node sibling = node.mSiblings.get(j);
952 sibling.addParents(node.mParents);
953 sibling.mParentsAdded = true;
954 }
955 }
956
957 for (int i = 0; i < size; i++) {
958 Node node = mNodes.get(i);
959 if (node != mRootNode && node.mParents == null) {
960 node.addParent(mRootNode);
961 }
962 }
963
964 // Do a DFS on the tree
965 ArrayList<Node> visited = new ArrayList<Node>(mNodes.size());
966 // Assign start/end time
967 mRootNode.mStartTime = 0;
968 mRootNode.mEndTime = mDelayAnim.getDuration();
969 updatePlayTime(mRootNode, visited);
970
971 long maxEndTime = 0;
972 for (int i = 0; i < size; i++) {
973 Node node = mNodes.get(i);
Doris Liu49db4242015-08-10 13:33:06 -0700974 node.mTotalDuration = node.mAnimation.getTotalDuration();
Doris Liu13099142015-07-10 17:32:41 -0700975 if (node.mEndTime == DURATION_INFINITE) {
976 maxEndTime = DURATION_INFINITE;
977 break;
978 } else {
979 maxEndTime = node.mEndTime > maxEndTime ? node.mEndTime : maxEndTime;
980 }
981 }
982 mTotalDuration = maxEndTime;
983 }
984
985 /**
986 * Based on parent's start/end time, calculate children's start/end time. If cycle exists in
987 * the graph, all the nodes on the cycle will be marked to start at {@link #DURATION_INFINITE},
988 * meaning they will ever play.
989 */
990 private void updatePlayTime(Node parent, ArrayList<Node> visited) {
991 if (parent.mChildNodes == null) {
Doris Liu49db4242015-08-10 13:33:06 -0700992 if (parent == mRootNode) {
993 // All the animators are in a cycle
994 for (int i = 0; i < mNodes.size(); i++) {
995 Node node = mNodes.get(i);
996 if (node != mRootNode) {
997 node.mStartTime = DURATION_INFINITE;
998 node.mEndTime = DURATION_INFINITE;
999 }
1000 }
1001 }
Doris Liu13099142015-07-10 17:32:41 -07001002 return;
1003 }
1004
1005 visited.add(parent);
1006 int childrenSize = parent.mChildNodes.size();
1007 for (int i = 0; i < childrenSize; i++) {
1008 Node child = parent.mChildNodes.get(i);
1009 int index = visited.indexOf(child);
1010 if (index >= 0) {
1011 // Child has been visited, cycle found. Mark all the nodes in the cycle.
Doris Liu49db4242015-08-10 13:33:06 -07001012 for (int j = index; j < visited.size(); j++) {
Doris Liu13099142015-07-10 17:32:41 -07001013 visited.get(j).mLatestParent = null;
1014 visited.get(j).mStartTime = DURATION_INFINITE;
1015 visited.get(j).mEndTime = DURATION_INFINITE;
1016 }
1017 child.mStartTime = DURATION_INFINITE;
1018 child.mEndTime = DURATION_INFINITE;
1019 child.mLatestParent = null;
1020 Log.w(TAG, "Cycle found in AnimatorSet: " + this);
1021 continue;
1022 }
1023
1024 if (child.mStartTime != DURATION_INFINITE) {
1025 if (parent.mEndTime == DURATION_INFINITE) {
1026 child.mLatestParent = parent;
1027 child.mStartTime = DURATION_INFINITE;
1028 child.mEndTime = DURATION_INFINITE;
1029 } else {
1030 if (parent.mEndTime >= child.mStartTime) {
1031 child.mLatestParent = parent;
1032 child.mStartTime = parent.mEndTime;
1033 }
1034
1035 long duration = child.mAnimation.getTotalDuration();
1036 child.mEndTime = duration == DURATION_INFINITE ?
1037 DURATION_INFINITE : child.mStartTime + duration;
1038 }
1039 }
1040 updatePlayTime(child, visited);
1041 }
1042 visited.remove(parent);
1043 }
1044
1045 // Recursively find all the siblings
1046 private void findSiblings(Node node, ArrayList<Node> siblings) {
1047 if (!siblings.contains(node)) {
1048 siblings.add(node);
1049 if (node.mSiblings == null) {
1050 return;
1051 }
1052 for (int i = 0; i < node.mSiblings.size(); i++) {
1053 findSiblings(node.mSiblings.get(i), siblings);
1054 }
1055 }
1056 }
1057
Doris Liu766431a2016-02-04 22:17:11 +00001058 /**
1059 * @hide
1060 * TODO: For animatorSet defined in XML, we can use a flag to indicate what the play order
1061 * if defined (i.e. sequential or together), then we can use the flag instead of calculate
1062 * dynamically.
1063 * @return whether all the animators in the set are supposed to play together
1064 */
1065 public boolean shouldPlayTogether() {
1066 updateAnimatorsDuration();
1067 createDependencyGraph();
1068 // All the child nodes are set out to play right after the delay animation
1069 return mRootNode.mChildNodes.size() == mNodes.size() - 1;
1070 }
1071
Doris Liu13099142015-07-10 17:32:41 -07001072 @Override
1073 public long getTotalDuration() {
1074 updateAnimatorsDuration();
1075 createDependencyGraph();
1076 return mTotalDuration;
1077 }
1078
1079 private Node getNodeForAnimation(Animator anim) {
1080 Node node = mNodeMap.get(anim);
1081 if (node == null) {
1082 node = new Node(anim);
1083 mNodeMap.put(anim, node);
1084 mNodes.add(node);
1085 }
1086 return node;
1087 }
1088
Chet Haase17fb4b02010-06-28 17:55:07 -07001089 /**
Chet Haasea18a86b2010-09-07 13:20:00 -07001090 * A Node is an embodiment of both the Animator that it wraps as well as
Chet Haase17fb4b02010-06-28 17:55:07 -07001091 * any dependencies that are associated with that Animation. This includes
1092 * both dependencies upon other nodes (in the dependencies list) as
1093 * well as dependencies of other nodes upon this (in the nodeDependents list).
1094 */
Chet Haase49afa5b2010-08-23 11:39:53 -07001095 private static class Node implements Cloneable {
Doris Liu13099142015-07-10 17:32:41 -07001096 Animator mAnimation;
Chet Haase17fb4b02010-06-28 17:55:07 -07001097
1098 /**
Doris Liu13099142015-07-10 17:32:41 -07001099 * Child nodes are the nodes associated with animations that will be played immediately
1100 * after current node.
Chet Haase17fb4b02010-06-28 17:55:07 -07001101 */
Doris Liu13099142015-07-10 17:32:41 -07001102 ArrayList<Node> mChildNodes = null;
Chet Haase1e0ac5a2010-08-27 08:32:11 -07001103
1104 /**
Yigit Boyard422dc32014-09-25 12:23:35 -07001105 * Temporary field to hold the clone in AnimatorSet#clone. Cleaned after clone is complete
1106 */
1107 private Node mTmpClone = null;
1108
1109 /**
Doris Liu13099142015-07-10 17:32:41 -07001110 * Flag indicating whether the animation in this node is finished. This flag
1111 * is used by AnimatorSet to check, as each animation ends, whether all child animations
1112 * are mEnded and it's time to send out an end event for the entire AnimatorSet.
1113 */
1114 boolean mEnded = false;
1115
1116 /**
1117 * Nodes with animations that are defined to play simultaneously with the animation
1118 * associated with this current node.
1119 */
1120 ArrayList<Node> mSiblings;
1121
1122 /**
1123 * Parent nodes are the nodes with animations preceding current node's animation. Parent
1124 * nodes here are derived from user defined animation sequence.
1125 */
1126 ArrayList<Node> mParents;
1127
1128 /**
1129 * Latest parent is the parent node associated with a animation that finishes after all
1130 * the other parents' animations.
1131 */
1132 Node mLatestParent = null;
1133
1134 boolean mParentsAdded = false;
1135 long mStartTime = 0;
1136 long mEndTime = 0;
Doris Liu49db4242015-08-10 13:33:06 -07001137 long mTotalDuration = 0;
Doris Liu13099142015-07-10 17:32:41 -07001138
1139 /**
Chet Haase17fb4b02010-06-28 17:55:07 -07001140 * Constructs the Node with the animation that it encapsulates. A Node has no
1141 * dependencies by default; dependencies are added via the addDependency()
1142 * method.
1143 *
1144 * @param animation The animation that the Node encapsulates.
1145 */
Chet Haasea18a86b2010-09-07 13:20:00 -07001146 public Node(Animator animation) {
Doris Liu13099142015-07-10 17:32:41 -07001147 this.mAnimation = animation;
Chet Haase17fb4b02010-06-28 17:55:07 -07001148 }
Chet Haase49afa5b2010-08-23 11:39:53 -07001149
1150 @Override
Chet Haase21cd1382010-09-01 17:42:29 -07001151 public Node clone() {
1152 try {
1153 Node node = (Node) super.clone();
Doris Liu13099142015-07-10 17:32:41 -07001154 node.mAnimation = mAnimation.clone();
1155 if (mChildNodes != null) {
1156 node.mChildNodes = new ArrayList<>(mChildNodes);
1157 }
Doris Liu5e666162015-08-03 16:06:56 -07001158 if (mSiblings != null) {
1159 node.mSiblings = new ArrayList<>(mSiblings);
1160 }
1161 if (mParents != null) {
1162 node.mParents = new ArrayList<>(mParents);
1163 }
Doris Liu13099142015-07-10 17:32:41 -07001164 node.mEnded = false;
Chet Haase21cd1382010-09-01 17:42:29 -07001165 return node;
1166 } catch (CloneNotSupportedException e) {
1167 throw new AssertionError();
1168 }
Chet Haase49afa5b2010-08-23 11:39:53 -07001169 }
Doris Liu13099142015-07-10 17:32:41 -07001170
1171 void addChild(Node node) {
1172 if (mChildNodes == null) {
1173 mChildNodes = new ArrayList<>();
1174 }
1175 if (!mChildNodes.contains(node)) {
1176 mChildNodes.add(node);
1177 node.addParent(this);
1178 }
1179 }
1180
1181 public void addSibling(Node node) {
1182 if (mSiblings == null) {
1183 mSiblings = new ArrayList<Node>();
1184 }
1185 if (!mSiblings.contains(node)) {
1186 mSiblings.add(node);
1187 node.addSibling(this);
1188 }
1189 }
1190
1191 public void addParent(Node node) {
1192 if (mParents == null) {
1193 mParents = new ArrayList<Node>();
1194 }
1195 if (!mParents.contains(node)) {
1196 mParents.add(node);
1197 node.addChild(this);
1198 }
1199 }
1200
1201 public void addParents(ArrayList<Node> parents) {
1202 if (parents == null) {
1203 return;
1204 }
1205 int size = parents.size();
1206 for (int i = 0; i < size; i++) {
1207 addParent(parents.get(i));
1208 }
1209 }
Chet Haase17fb4b02010-06-28 17:55:07 -07001210 }
1211
1212 /**
1213 * The <code>Builder</code> object is a utility class to facilitate adding animations to a
Chet Haasea18a86b2010-09-07 13:20:00 -07001214 * <code>AnimatorSet</code> along with the relationships between the various animations. The
Chet Haase17fb4b02010-06-28 17:55:07 -07001215 * intention of the <code>Builder</code> methods, along with the {@link
Chet Haase8b699792011-08-05 15:20:19 -07001216 * AnimatorSet#play(Animator) play()} method of <code>AnimatorSet</code> is to make it possible
1217 * to express the dependency relationships of animations in a natural way. Developers can also
1218 * use the {@link AnimatorSet#playTogether(Animator[]) playTogether()} and {@link
Chet Haasea18a86b2010-09-07 13:20:00 -07001219 * AnimatorSet#playSequentially(Animator[]) playSequentially()} methods if these suit the need,
1220 * but it might be easier in some situations to express the AnimatorSet of animations in pairs.
Chet Haase17fb4b02010-06-28 17:55:07 -07001221 * <p/>
1222 * <p>The <code>Builder</code> object cannot be constructed directly, but is rather constructed
Chet Haasea18a86b2010-09-07 13:20:00 -07001223 * internally via a call to {@link AnimatorSet#play(Animator)}.</p>
Chet Haase17fb4b02010-06-28 17:55:07 -07001224 * <p/>
Chet Haasea18a86b2010-09-07 13:20:00 -07001225 * <p>For example, this sets up a AnimatorSet to play anim1 and anim2 at the same time, anim3 to
Chet Haase17fb4b02010-06-28 17:55:07 -07001226 * play when anim2 finishes, and anim4 to play when anim3 finishes:</p>
1227 * <pre>
Chet Haasea18a86b2010-09-07 13:20:00 -07001228 * AnimatorSet s = new AnimatorSet();
Chet Haase17fb4b02010-06-28 17:55:07 -07001229 * s.play(anim1).with(anim2);
1230 * s.play(anim2).before(anim3);
1231 * s.play(anim4).after(anim3);
1232 * </pre>
1233 * <p/>
Chet Haasea18a86b2010-09-07 13:20:00 -07001234 * <p>Note in the example that both {@link Builder#before(Animator)} and {@link
1235 * Builder#after(Animator)} are used. These are just different ways of expressing the same
Chet Haase17fb4b02010-06-28 17:55:07 -07001236 * relationship and are provided to make it easier to say things in a way that is more natural,
1237 * depending on the situation.</p>
1238 * <p/>
1239 * <p>It is possible to make several calls into the same <code>Builder</code> object to express
1240 * multiple relationships. However, note that it is only the animation passed into the initial
Chet Haasea18a86b2010-09-07 13:20:00 -07001241 * {@link AnimatorSet#play(Animator)} method that is the dependency in any of the successive
Chet Haase17fb4b02010-06-28 17:55:07 -07001242 * calls to the <code>Builder</code> object. For example, the following code starts both anim2
1243 * and anim3 when anim1 ends; there is no direct dependency relationship between anim2 and
1244 * anim3:
1245 * <pre>
Chet Haasea18a86b2010-09-07 13:20:00 -07001246 * AnimatorSet s = new AnimatorSet();
Chet Haase17fb4b02010-06-28 17:55:07 -07001247 * s.play(anim1).before(anim2).before(anim3);
1248 * </pre>
1249 * If the desired result is to play anim1 then anim2 then anim3, this code expresses the
1250 * relationship correctly:</p>
1251 * <pre>
Chet Haasea18a86b2010-09-07 13:20:00 -07001252 * AnimatorSet s = new AnimatorSet();
Chet Haase17fb4b02010-06-28 17:55:07 -07001253 * s.play(anim1).before(anim2);
1254 * s.play(anim2).before(anim3);
1255 * </pre>
1256 * <p/>
1257 * <p>Note that it is possible to express relationships that cannot be resolved and will not
1258 * result in sensible results. For example, <code>play(anim1).after(anim1)</code> makes no
1259 * sense. In general, circular dependencies like this one (or more indirect ones where a depends
Chet Haasea18a86b2010-09-07 13:20:00 -07001260 * on b, which depends on c, which depends on a) should be avoided. Only create AnimatorSets
1261 * that can boil down to a simple, one-way relationship of animations starting with, before, and
Chet Haase17fb4b02010-06-28 17:55:07 -07001262 * after other, different, animations.</p>
1263 */
1264 public class Builder {
1265
1266 /**
1267 * This tracks the current node being processed. It is supplied to the play() method
Chet Haasea18a86b2010-09-07 13:20:00 -07001268 * of AnimatorSet and passed into the constructor of Builder.
Chet Haase17fb4b02010-06-28 17:55:07 -07001269 */
1270 private Node mCurrentNode;
1271
1272 /**
Chet Haasea18a86b2010-09-07 13:20:00 -07001273 * package-private constructor. Builders are only constructed by AnimatorSet, when the
Chet Haase17fb4b02010-06-28 17:55:07 -07001274 * play() method is called.
1275 *
1276 * @param anim The animation that is the dependency for the other animations passed into
1277 * the other methods of this Builder object.
1278 */
Chet Haasea18a86b2010-09-07 13:20:00 -07001279 Builder(Animator anim) {
Doris Liu13099142015-07-10 17:32:41 -07001280 mDependencyDirty = true;
1281 mCurrentNode = getNodeForAnimation(anim);
Chet Haase17fb4b02010-06-28 17:55:07 -07001282 }
1283
1284 /**
1285 * Sets up the given animation to play at the same time as the animation supplied in the
Chet Haasea18a86b2010-09-07 13:20:00 -07001286 * {@link AnimatorSet#play(Animator)} call that created this <code>Builder</code> object.
Chet Haase17fb4b02010-06-28 17:55:07 -07001287 *
1288 * @param anim The animation that will play when the animation supplied to the
Chet Haasea18a86b2010-09-07 13:20:00 -07001289 * {@link AnimatorSet#play(Animator)} method starts.
Chet Haase17fb4b02010-06-28 17:55:07 -07001290 */
Chet Haase2970c492010-11-09 13:58:04 -08001291 public Builder with(Animator anim) {
Doris Liu13099142015-07-10 17:32:41 -07001292 Node node = getNodeForAnimation(anim);
1293 mCurrentNode.addSibling(node);
Chet Haase2970c492010-11-09 13:58:04 -08001294 return this;
Chet Haase17fb4b02010-06-28 17:55:07 -07001295 }
1296
1297 /**
1298 * Sets up the given animation to play when the animation supplied in the
Chet Haasea18a86b2010-09-07 13:20:00 -07001299 * {@link AnimatorSet#play(Animator)} call that created this <code>Builder</code> object
Chet Haase17fb4b02010-06-28 17:55:07 -07001300 * ends.
1301 *
1302 * @param anim The animation that will play when the animation supplied to the
Chet Haasea18a86b2010-09-07 13:20:00 -07001303 * {@link AnimatorSet#play(Animator)} method ends.
Chet Haase17fb4b02010-06-28 17:55:07 -07001304 */
Chet Haase2970c492010-11-09 13:58:04 -08001305 public Builder before(Animator anim) {
ztenghui7bc6a3f2014-07-15 15:12:12 -07001306 mReversible = false;
Doris Liu13099142015-07-10 17:32:41 -07001307 Node node = getNodeForAnimation(anim);
1308 mCurrentNode.addChild(node);
Chet Haase2970c492010-11-09 13:58:04 -08001309 return this;
Chet Haase17fb4b02010-06-28 17:55:07 -07001310 }
1311
1312 /**
1313 * Sets up the given animation to play when the animation supplied in the
Chet Haasea18a86b2010-09-07 13:20:00 -07001314 * {@link AnimatorSet#play(Animator)} call that created this <code>Builder</code> object
Chet Haase17fb4b02010-06-28 17:55:07 -07001315 * to start when the animation supplied in this method call ends.
1316 *
1317 * @param anim The animation whose end will cause the animation supplied to the
Chet Haasea18a86b2010-09-07 13:20:00 -07001318 * {@link AnimatorSet#play(Animator)} method to play.
Chet Haase17fb4b02010-06-28 17:55:07 -07001319 */
Chet Haase2970c492010-11-09 13:58:04 -08001320 public Builder after(Animator anim) {
ztenghui7bc6a3f2014-07-15 15:12:12 -07001321 mReversible = false;
Doris Liu13099142015-07-10 17:32:41 -07001322 Node node = getNodeForAnimation(anim);
1323 mCurrentNode.addParent(node);
Chet Haase2970c492010-11-09 13:58:04 -08001324 return this;
Chet Haase17fb4b02010-06-28 17:55:07 -07001325 }
1326
1327 /**
1328 * Sets up the animation supplied in the
Chet Haasea18a86b2010-09-07 13:20:00 -07001329 * {@link AnimatorSet#play(Animator)} call that created this <code>Builder</code> object
Chet Haase17fb4b02010-06-28 17:55:07 -07001330 * to play when the given amount of time elapses.
1331 *
1332 * @param delay The number of milliseconds that should elapse before the
1333 * animation starts.
1334 */
Chet Haase2970c492010-11-09 13:58:04 -08001335 public Builder after(long delay) {
Chet Haasea18a86b2010-09-07 13:20:00 -07001336 // setup dummy ValueAnimator just to run the clock
Chet Haase2794eb32010-10-12 16:29:28 -07001337 ValueAnimator anim = ValueAnimator.ofFloat(0f, 1f);
1338 anim.setDuration(delay);
1339 after(anim);
Chet Haase2970c492010-11-09 13:58:04 -08001340 return this;
Chet Haase17fb4b02010-06-28 17:55:07 -07001341 }
1342
1343 }
1344
1345}