117fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase/*
217fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase * Copyright (C) 2010 The Android Open Source Project
317fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase *
417fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase * Licensed under the Apache License, Version 2.0 (the "License");
517fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase * you may not use this file except in compliance with the License.
617fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase * You may obtain a copy of the License at
717fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase *
817fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase *      http://www.apache.org/licenses/LICENSE-2.0
917fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase *
1017fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase * Unless required by applicable law or agreed to in writing, software
1117fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase * distributed under the License is distributed on an "AS IS" BASIS,
1217fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1317fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase * See the License for the specific language governing permissions and
1417fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase * limitations under the License.
1517fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase */
1617fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase
1717fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haasepackage android.animation;
1817fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase
1958606db8be7e64a4317955b87fba4ee51f353630Doris Liuimport android.app.ActivityThread;
2058606db8be7e64a4317955b87fba4ee51f353630Doris Liuimport android.app.Application;
2158606db8be7e64a4317955b87fba4ee51f353630Doris Liuimport android.os.Build;
2213351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liuimport android.os.Looper;
2313351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liuimport android.util.AndroidRuntimeException;
24d7444427d9f44b6b7448d4c21edca866132c8b59Doris Liuimport android.util.ArrayMap;
251309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liuimport android.util.Log;
2613351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liuimport android.view.animation.Animation;
27d7444427d9f44b6b7448d4c21edca866132c8b59Doris Liu
2817fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haaseimport java.util.ArrayList;
2937a7bec599e8d877d8a7f12ab2c2c160d1c2cf8aChet Haaseimport java.util.Collection;
3013351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liuimport java.util.Comparator;
3156b2df05f56b2c7b82a50b46341d055f6d70a913Doris Liuimport java.util.HashMap;
3237a7bec599e8d877d8a7f12ab2c2c160d1c2cf8aChet Haaseimport java.util.List;
3317fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase
3417fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase/**
35a18a86b43e40e3c15dcca0ae0148d641be9b25feChet Haase * This class plays a set of {@link Animator} objects in the specified order. Animations
3617fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase * can be set up to play together, in sequence, or after a specified delay.
3717fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase *
38a18a86b43e40e3c15dcca0ae0148d641be9b25feChet Haase * <p>There are two different approaches to adding animations to a <code>AnimatorSet</code>:
39a18a86b43e40e3c15dcca0ae0148d641be9b25feChet Haase * either the {@link AnimatorSet#playTogether(Animator[]) playTogether()} or
40a18a86b43e40e3c15dcca0ae0148d641be9b25feChet Haase * {@link AnimatorSet#playSequentially(Animator[]) playSequentially()} methods can be called to add
41a18a86b43e40e3c15dcca0ae0148d641be9b25feChet Haase * a set of animations all at once, or the {@link AnimatorSet#play(Animator)} can be
42a18a86b43e40e3c15dcca0ae0148d641be9b25feChet Haase * used in conjunction with methods in the {@link AnimatorSet.Builder Builder}
4317fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase * class to add animations
4417fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase * one by one.</p>
4517fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase *
46a18a86b43e40e3c15dcca0ae0148d641be9b25feChet Haase * <p>It is possible to set up a <code>AnimatorSet</code> with circular dependencies between
4717fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase * its animations. For example, an animation a1 could be set up to start before animation a2, a2
4817fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase * before a3, and a3 before a1. The results of this configuration are undefined, but will typically
4917fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase * result in none of the affected animations being played. Because of this (and because
5017fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase * circular dependencies do not make logical sense anyway), circular dependencies
5117fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase * should be avoided, and the dependency flow of animations should only be in one direction.
523aef8e1d1b2f0b87d470bcccf37ba4ebb6560c45Joe Fernandez *
533aef8e1d1b2f0b87d470bcccf37ba4ebb6560c45Joe Fernandez * <div class="special reference">
543aef8e1d1b2f0b87d470bcccf37ba4ebb6560c45Joe Fernandez * <h3>Developer Guides</h3>
553aef8e1d1b2f0b87d470bcccf37ba4ebb6560c45Joe Fernandez * <p>For more information about animating with {@code AnimatorSet}, read the
563aef8e1d1b2f0b87d470bcccf37ba4ebb6560c45Joe Fernandez * <a href="{@docRoot}guide/topics/graphics/prop-animation.html#choreography">Property
573aef8e1d1b2f0b87d470bcccf37ba4ebb6560c45Joe Fernandez * Animation</a> developer guide.</p>
583aef8e1d1b2f0b87d470bcccf37ba4ebb6560c45Joe Fernandez * </div>
5917fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase */
6013351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liupublic final class AnimatorSet extends Animator implements AnimationHandler.AnimationFrameCallback {
6117fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase
621309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu    private static final String TAG = "AnimatorSet";
6317fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase    /**
6449afa5bc100e5d4c069fea980dd6b09501f56397Chet Haase     * Internal variables
6549afa5bc100e5d4c069fea980dd6b09501f56397Chet Haase     * NOTE: This object implements the clone() method, making a deep copy of any referenced
6649afa5bc100e5d4c069fea980dd6b09501f56397Chet Haase     * objects. As other non-trivial fields are added to this class, make sure to add logic
6749afa5bc100e5d4c069fea980dd6b09501f56397Chet Haase     * to clone() to make deep copies of them.
6849afa5bc100e5d4c069fea980dd6b09501f56397Chet Haase     */
6949afa5bc100e5d4c069fea980dd6b09501f56397Chet Haase
7049afa5bc100e5d4c069fea980dd6b09501f56397Chet Haase    /**
713b69b6f0be85d1f97c1e6824cf986777ba4e5d00Chet Haase     * Tracks animations currently being played, so that we know what to
72a18a86b43e40e3c15dcca0ae0148d641be9b25feChet Haase     * cancel or end when cancel() or end() is called on this AnimatorSet
7317fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase     */
7413351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu    private ArrayList<Node> mPlayingSet = new ArrayList<Node>();
7517fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase
7617fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase    /**
77a18a86b43e40e3c15dcca0ae0148d641be9b25feChet Haase     * Contains all nodes, mapped to their respective Animators. When new
78a18a86b43e40e3c15dcca0ae0148d641be9b25feChet Haase     * dependency information is added for an Animator, we want to add it
79a18a86b43e40e3c15dcca0ae0148d641be9b25feChet Haase     * to a single node representing that Animator, not create a new Node
8017fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase     * if one already exists.
8117fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase     */
82d7444427d9f44b6b7448d4c21edca866132c8b59Doris Liu    private ArrayMap<Animator, Node> mNodeMap = new ArrayMap<Animator, Node>();
8317fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase
8417fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase    /**
8513351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu     * Contains the start and end events of all the nodes. All these events are sorted in this list.
8613351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu     */
8713351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu    private ArrayList<AnimationEvent> mEvents = new ArrayList<>();
8813351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu
8913351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu    /**
90a18a86b43e40e3c15dcca0ae0148d641be9b25feChet Haase     * Set of all nodes created for this AnimatorSet. This list is used upon
91a18a86b43e40e3c15dcca0ae0148d641be9b25feChet Haase     * starting the set, and the nodes are placed in sorted order into the
9217fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase     * sortedNodes collection.
9317fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase     */
9449afa5bc100e5d4c069fea980dd6b09501f56397Chet Haase    private ArrayList<Node> mNodes = new ArrayList<Node>();
9517fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase
9617fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase    /**
971309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu     * Tracks whether any change has been made to the AnimatorSet, which is then used to
981309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu     * determine whether the dependency graph should be re-constructed.
991309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu     */
1001309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu    private boolean mDependencyDirty = false;
101010dbaa1236cf2dcdc62c29049468e90188acaaeChet Haase
1028b699792b677bd4dd8442b32641ac09d48fdd79cChet Haase    /**
1038b699792b677bd4dd8442b32641ac09d48fdd79cChet Haase     * Indicates whether an AnimatorSet has been start()'d, whether or
1048b699792b677bd4dd8442b32641ac09d48fdd79cChet Haase     * not there is a nonzero startDelay.
1058b699792b677bd4dd8442b32641ac09d48fdd79cChet Haase     */
1068b699792b677bd4dd8442b32641ac09d48fdd79cChet Haase    private boolean mStarted = false;
1078b699792b677bd4dd8442b32641ac09d48fdd79cChet Haase
10821cd1389d2ef218b20994b617c57af120841a57fChet Haase    // The amount of time in ms to delay starting the animation after start() is called
10921cd1389d2ef218b20994b617c57af120841a57fChet Haase    private long mStartDelay = 0;
11021cd1389d2ef218b20994b617c57af120841a57fChet Haase
111e2ab7ccd385cdb6517955c719e1d2b49771bedb6Chet Haase    // Animator used for a nonzero startDelay
1121309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu    private ValueAnimator mDelayAnim = ValueAnimator.ofFloat(0f, 1f).setDuration(0);
113e2ab7ccd385cdb6517955c719e1d2b49771bedb6Chet Haase
1141309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu    // Root of the dependency tree of all the animators in the set. In this tree, parent-child
1151309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu    // relationship captures the order of animation (i.e. parent and child will play sequentially),
1161309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu    // and sibling relationship indicates "with" relationship, as sibling animators start at the
1171309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu    // same time.
1181309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu    private Node mRootNode = new Node(mDelayAnim);
11921cd1389d2ef218b20994b617c57af120841a57fChet Haase
12021cd1389d2ef218b20994b617c57af120841a57fChet Haase    // How long the child animations should last in ms. The default value is negative, which
121a18a86b43e40e3c15dcca0ae0148d641be9b25feChet Haase    // simply means that there is no duration set on the AnimatorSet. When a real duration is
12221cd1389d2ef218b20994b617c57af120841a57fChet Haase    // set, it is passed along to the child animations.
12321cd1389d2ef218b20994b617c57af120841a57fChet Haase    private long mDuration = -1;
12421cd1389d2ef218b20994b617c57af120841a57fChet Haase
125430742f09063574271e6c4091de13b9b9e762514Chet Haase    // Records the interpolator for the set. Null value indicates that no interpolator
126430742f09063574271e6c4091de13b9b9e762514Chet Haase    // was set on this AnimatorSet, so it should not be passed down to the children.
127430742f09063574271e6c4091de13b9b9e762514Chet Haase    private TimeInterpolator mInterpolator = null;
128430742f09063574271e6c4091de13b9b9e762514Chet Haase
1291309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu    // The total duration of finishing all the Animators in the set.
1301309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu    private long mTotalDuration = 0;
1311309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu
13258606db8be7e64a4317955b87fba4ee51f353630Doris Liu    // In pre-N releases, calling end() before start() on an animator set is no-op. But that is not
13358606db8be7e64a4317955b87fba4ee51f353630Doris Liu    // consistent with the behavior for other animator types. In order to keep the behavior
13458606db8be7e64a4317955b87fba4ee51f353630Doris Liu    // consistent within Animation framework, when end() is called without start(), we will start
13558606db8be7e64a4317955b87fba4ee51f353630Doris Liu    // the animator set and immediately end it for N and forward.
13658606db8be7e64a4317955b87fba4ee51f353630Doris Liu    private final boolean mShouldIgnoreEndWithoutStart;
13758606db8be7e64a4317955b87fba4ee51f353630Doris Liu
13813351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu    // In pre-O releases, calling start() doesn't reset all the animators values to start values.
13913351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu    // As a result, the start of the animation is inconsistent with what setCurrentPlayTime(0) would
14013351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu    // look like on O. Also it is inconsistent with what reverse() does on O, as reverse would
14113351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu    // advance all the animations to the right beginning values for before starting to reverse.
14213351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu    // From O and forward, we will add an additional step of resetting the animation values (unless
14313351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu    // the animation was previously seeked and therefore doesn't start from the beginning).
14413351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu    private final boolean mShouldResetValuesAtStart;
14513351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu
1466d4520920526cc44063ff2e665adb08e9a3c8a64Doris Liu    // In pre-O releases, end() may never explicitly called on a child animator. As a result, end()
1476d4520920526cc44063ff2e665adb08e9a3c8a64Doris Liu    // may not even be properly implemented in a lot of cases. After a few apps crashing on this,
1486d4520920526cc44063ff2e665adb08e9a3c8a64Doris Liu    // it became necessary to use an sdk target guard for calling end().
1496d4520920526cc44063ff2e665adb08e9a3c8a64Doris Liu    private final boolean mEndCanBeCalled;
1506d4520920526cc44063ff2e665adb08e9a3c8a64Doris Liu
15113351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu    // The time, in milliseconds, when last frame of the animation came in. -1 when the animation is
15213351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu    // not running.
15313351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu    private long mLastFrameTime = -1;
15413351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu
1556d4520920526cc44063ff2e665adb08e9a3c8a64Doris Liu    // The time, in milliseconds, when the first frame of the animation came in. This is the
1566d4520920526cc44063ff2e665adb08e9a3c8a64Doris Liu    // frame before we start counting down the start delay, if any.
15713351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu    // -1 when the animation is not running.
15813351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu    private long mFirstFrame = -1;
15913351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu
16013351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu    // The time, in milliseconds, when the first frame of the animation came in.
16113351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu    // -1 when the animation is not running.
16213351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu    private int mLastEventId = -1;
16313351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu
16413351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu    // Indicates whether the animation is reversing.
16513351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu    private boolean mReversing = false;
16613351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu
16713351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu    // Indicates whether the animation should register frame callbacks. If false, the animation will
16813351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu    // passively wait for an AnimatorSet to pulse it.
16913351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu    private boolean mSelfPulse = true;
17013351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu
17113351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu    // SeekState stores the last seeked play time as well as seek direction.
17213351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu    private SeekState mSeekState = new SeekState();
17313351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu
17413351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu    // Indicates where children animators are all initialized with their start values captured.
17513351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu    private boolean mChildrenInitialized = false;
17613351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu
17713351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu    /**
17813351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu     * Set on the next frame after pause() is called, used to calculate a new startTime
17913351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu     * or delayStartTime which allows the animator set to continue from the point at which
18013351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu     * it was paused. If negative, has not yet been set.
18113351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu     */
18213351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu    private long mPauseTime = -1;
18313351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu
1846ba5ed322c04f260c51d58f2e49c6189b672f2d3Doris Liu    // This is to work around a bug in b/34736819. This needs to be removed once app team
18566c564e390ea71576db7d4c1803b169cc98e987bDoris Liu    // fixes their side.
1866ba5ed322c04f260c51d58f2e49c6189b672f2d3Doris Liu    private AnimatorListenerAdapter mDummyListener = new AnimatorListenerAdapter() {
1876ba5ed322c04f260c51d58f2e49c6189b672f2d3Doris Liu        @Override
1886ba5ed322c04f260c51d58f2e49c6189b672f2d3Doris Liu        public void onAnimationEnd(Animator animation) {
1896ba5ed322c04f260c51d58f2e49c6189b672f2d3Doris Liu            if (mNodeMap.get(animation) == null) {
1906ba5ed322c04f260c51d58f2e49c6189b672f2d3Doris Liu                throw new AndroidRuntimeException("Error: animation ended is not in the node map");
1916ba5ed322c04f260c51d58f2e49c6189b672f2d3Doris Liu            }
1926ba5ed322c04f260c51d58f2e49c6189b672f2d3Doris Liu            mNodeMap.get(animation).mEnded = true;
1936ba5ed322c04f260c51d58f2e49c6189b672f2d3Doris Liu
1946ba5ed322c04f260c51d58f2e49c6189b672f2d3Doris Liu        }
1956ba5ed322c04f260c51d58f2e49c6189b672f2d3Doris Liu    };
19666c564e390ea71576db7d4c1803b169cc98e987bDoris Liu
1971309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu    public AnimatorSet() {
1981309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu        super();
1991309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu        mNodeMap.put(mDelayAnim, mRootNode);
2001309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu        mNodes.add(mRootNode);
2016d4520920526cc44063ff2e665adb08e9a3c8a64Doris Liu        boolean isPreO;
20258606db8be7e64a4317955b87fba4ee51f353630Doris Liu        // Set the flag to ignore calling end() without start() for pre-N releases
20358606db8be7e64a4317955b87fba4ee51f353630Doris Liu        Application app = ActivityThread.currentApplication();
20458606db8be7e64a4317955b87fba4ee51f353630Doris Liu        if (app == null || app.getApplicationInfo() == null) {
20558606db8be7e64a4317955b87fba4ee51f353630Doris Liu            mShouldIgnoreEndWithoutStart = true;
2066d4520920526cc44063ff2e665adb08e9a3c8a64Doris Liu            isPreO = true;
20758606db8be7e64a4317955b87fba4ee51f353630Doris Liu        } else {
20813351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu            if (app.getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.N) {
20913351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu                mShouldIgnoreEndWithoutStart = true;
21013351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu            } else {
21113351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu                mShouldIgnoreEndWithoutStart = false;
21213351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu            }
21313351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu
2146d4520920526cc44063ff2e665adb08e9a3c8a64Doris Liu            isPreO = app.getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.O;
21558606db8be7e64a4317955b87fba4ee51f353630Doris Liu        }
2166d4520920526cc44063ff2e665adb08e9a3c8a64Doris Liu        mShouldResetValuesAtStart = !isPreO;
2176d4520920526cc44063ff2e665adb08e9a3c8a64Doris Liu        mEndCanBeCalled = !isPreO;
2181309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu    }
2191309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu
220010dbaa1236cf2dcdc62c29049468e90188acaaeChet Haase    /**
221a18a86b43e40e3c15dcca0ae0148d641be9b25feChet Haase     * Sets up this AnimatorSet to play all of the supplied animations at the same time.
222430742f09063574271e6c4091de13b9b9e762514Chet Haase     * This is equivalent to calling {@link #play(Animator)} with the first animator in the
223430742f09063574271e6c4091de13b9b9e762514Chet Haase     * set and then {@link Builder#with(Animator)} with each of the other animators. Note that
224430742f09063574271e6c4091de13b9b9e762514Chet Haase     * an Animator with a {@link Animator#setStartDelay(long) startDelay} will not actually
225430742f09063574271e6c4091de13b9b9e762514Chet Haase     * start until that delay elapses, which means that if the first animator in the list
226430742f09063574271e6c4091de13b9b9e762514Chet Haase     * supplied to this constructor has a startDelay, none of the other animators will start
227430742f09063574271e6c4091de13b9b9e762514Chet Haase     * until that first animator's startDelay has elapsed.
22817fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase     *
229a18a86b43e40e3c15dcca0ae0148d641be9b25feChet Haase     * @param items The animations that will be started simultaneously.
23017fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase     */
231a18a86b43e40e3c15dcca0ae0148d641be9b25feChet Haase    public void playTogether(Animator... items) {
232a18a86b43e40e3c15dcca0ae0148d641be9b25feChet Haase        if (items != null) {
233a18a86b43e40e3c15dcca0ae0148d641be9b25feChet Haase            Builder builder = play(items[0]);
234a18a86b43e40e3c15dcca0ae0148d641be9b25feChet Haase            for (int i = 1; i < items.length; ++i) {
235a18a86b43e40e3c15dcca0ae0148d641be9b25feChet Haase                builder.with(items[i]);
23617fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase            }
23717fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase        }
23817fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase    }
23917fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase
24017fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase    /**
24137a7bec599e8d877d8a7f12ab2c2c160d1c2cf8aChet Haase     * Sets up this AnimatorSet to play all of the supplied animations at the same time.
24237a7bec599e8d877d8a7f12ab2c2c160d1c2cf8aChet Haase     *
24337a7bec599e8d877d8a7f12ab2c2c160d1c2cf8aChet Haase     * @param items The animations that will be started simultaneously.
24437a7bec599e8d877d8a7f12ab2c2c160d1c2cf8aChet Haase     */
24537a7bec599e8d877d8a7f12ab2c2c160d1c2cf8aChet Haase    public void playTogether(Collection<Animator> items) {
24637a7bec599e8d877d8a7f12ab2c2c160d1c2cf8aChet Haase        if (items != null && items.size() > 0) {
24737a7bec599e8d877d8a7f12ab2c2c160d1c2cf8aChet Haase            Builder builder = null;
24837a7bec599e8d877d8a7f12ab2c2c160d1c2cf8aChet Haase            for (Animator anim : items) {
24937a7bec599e8d877d8a7f12ab2c2c160d1c2cf8aChet Haase                if (builder == null) {
25037a7bec599e8d877d8a7f12ab2c2c160d1c2cf8aChet Haase                    builder = play(anim);
25137a7bec599e8d877d8a7f12ab2c2c160d1c2cf8aChet Haase                } else {
25237a7bec599e8d877d8a7f12ab2c2c160d1c2cf8aChet Haase                    builder.with(anim);
25337a7bec599e8d877d8a7f12ab2c2c160d1c2cf8aChet Haase                }
25437a7bec599e8d877d8a7f12ab2c2c160d1c2cf8aChet Haase            }
25537a7bec599e8d877d8a7f12ab2c2c160d1c2cf8aChet Haase        }
25637a7bec599e8d877d8a7f12ab2c2c160d1c2cf8aChet Haase    }
25737a7bec599e8d877d8a7f12ab2c2c160d1c2cf8aChet Haase
25837a7bec599e8d877d8a7f12ab2c2c160d1c2cf8aChet Haase    /**
259a18a86b43e40e3c15dcca0ae0148d641be9b25feChet Haase     * Sets up this AnimatorSet to play each of the supplied animations when the
26017fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase     * previous animation ends.
26117fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase     *
26237a7bec599e8d877d8a7f12ab2c2c160d1c2cf8aChet Haase     * @param items The animations that will be started one after another.
26317fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase     */
264a18a86b43e40e3c15dcca0ae0148d641be9b25feChet Haase    public void playSequentially(Animator... items) {
265a18a86b43e40e3c15dcca0ae0148d641be9b25feChet Haase        if (items != null) {
266a18a86b43e40e3c15dcca0ae0148d641be9b25feChet Haase            if (items.length == 1) {
267a18a86b43e40e3c15dcca0ae0148d641be9b25feChet Haase                play(items[0]);
26817fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase            } else {
269a18a86b43e40e3c15dcca0ae0148d641be9b25feChet Haase                for (int i = 0; i < items.length - 1; ++i) {
2701309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu                    play(items[i]).before(items[i + 1]);
27117fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase                }
27217fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase            }
27317fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase        }
27417fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase    }
27517fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase
27617fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase    /**
27737a7bec599e8d877d8a7f12ab2c2c160d1c2cf8aChet Haase     * Sets up this AnimatorSet to play each of the supplied animations when the
27837a7bec599e8d877d8a7f12ab2c2c160d1c2cf8aChet Haase     * previous animation ends.
27937a7bec599e8d877d8a7f12ab2c2c160d1c2cf8aChet Haase     *
28037a7bec599e8d877d8a7f12ab2c2c160d1c2cf8aChet Haase     * @param items The animations that will be started one after another.
28137a7bec599e8d877d8a7f12ab2c2c160d1c2cf8aChet Haase     */
28237a7bec599e8d877d8a7f12ab2c2c160d1c2cf8aChet Haase    public void playSequentially(List<Animator> items) {
28337a7bec599e8d877d8a7f12ab2c2c160d1c2cf8aChet Haase        if (items != null && items.size() > 0) {
28437a7bec599e8d877d8a7f12ab2c2c160d1c2cf8aChet Haase            if (items.size() == 1) {
28537a7bec599e8d877d8a7f12ab2c2c160d1c2cf8aChet Haase                play(items.get(0));
28637a7bec599e8d877d8a7f12ab2c2c160d1c2cf8aChet Haase            } else {
28737a7bec599e8d877d8a7f12ab2c2c160d1c2cf8aChet Haase                for (int i = 0; i < items.size() - 1; ++i) {
2881309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu                    play(items.get(i)).before(items.get(i + 1));
28937a7bec599e8d877d8a7f12ab2c2c160d1c2cf8aChet Haase                }
29037a7bec599e8d877d8a7f12ab2c2c160d1c2cf8aChet Haase            }
29137a7bec599e8d877d8a7f12ab2c2c160d1c2cf8aChet Haase        }
29237a7bec599e8d877d8a7f12ab2c2c160d1c2cf8aChet Haase    }
29337a7bec599e8d877d8a7f12ab2c2c160d1c2cf8aChet Haase
29437a7bec599e8d877d8a7f12ab2c2c160d1c2cf8aChet Haase    /**
295a18a86b43e40e3c15dcca0ae0148d641be9b25feChet Haase     * Returns the current list of child Animator objects controlled by this
296a18a86b43e40e3c15dcca0ae0148d641be9b25feChet Haase     * AnimatorSet. This is a copy of the internal list; modifications to the returned list
297a18a86b43e40e3c15dcca0ae0148d641be9b25feChet Haase     * will not affect the AnimatorSet, although changes to the underlying Animator objects
298a18a86b43e40e3c15dcca0ae0148d641be9b25feChet Haase     * will affect those objects being managed by the AnimatorSet.
299f54a8d7c479485174941c38f151ea7083c658da3Chet Haase     *
300a18a86b43e40e3c15dcca0ae0148d641be9b25feChet Haase     * @return ArrayList<Animator> The list of child animations of this AnimatorSet.
301f54a8d7c479485174941c38f151ea7083c658da3Chet Haase     */
302a18a86b43e40e3c15dcca0ae0148d641be9b25feChet Haase    public ArrayList<Animator> getChildAnimations() {
303a18a86b43e40e3c15dcca0ae0148d641be9b25feChet Haase        ArrayList<Animator> childList = new ArrayList<Animator>();
3041309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu        int size = mNodes.size();
3051309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu        for (int i = 0; i < size; i++) {
3061309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu            Node node = mNodes.get(i);
307dbf69e4652d439892080dff3353b1cfd5b4bf6bcDoris Liu            if (node != mRootNode) {
308dbf69e4652d439892080dff3353b1cfd5b4bf6bcDoris Liu                childList.add(node.mAnimation);
309dbf69e4652d439892080dff3353b1cfd5b4bf6bcDoris Liu            }
310f54a8d7c479485174941c38f151ea7083c658da3Chet Haase        }
311f54a8d7c479485174941c38f151ea7083c658da3Chet Haase        return childList;
312f54a8d7c479485174941c38f151ea7083c658da3Chet Haase    }
313f54a8d7c479485174941c38f151ea7083c658da3Chet Haase
314f54a8d7c479485174941c38f151ea7083c658da3Chet Haase    /**
315811ed1065f39469cf2cf6adba22cab397ed88d5eChet Haase     * Sets the target object for all current {@link #getChildAnimations() child animations}
316a18a86b43e40e3c15dcca0ae0148d641be9b25feChet Haase     * of this AnimatorSet that take targets ({@link ObjectAnimator} and
317a18a86b43e40e3c15dcca0ae0148d641be9b25feChet Haase     * AnimatorSet).
318811ed1065f39469cf2cf6adba22cab397ed88d5eChet Haase     *
319811ed1065f39469cf2cf6adba22cab397ed88d5eChet Haase     * @param target The object being animated
320811ed1065f39469cf2cf6adba22cab397ed88d5eChet Haase     */
32121cd1389d2ef218b20994b617c57af120841a57fChet Haase    @Override
322811ed1065f39469cf2cf6adba22cab397ed88d5eChet Haase    public void setTarget(Object target) {
3231309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu        int size = mNodes.size();
3241309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu        for (int i = 0; i < size; i++) {
3251309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu            Node node = mNodes.get(i);
3261309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu            Animator animation = node.mAnimation;
327a18a86b43e40e3c15dcca0ae0148d641be9b25feChet Haase            if (animation instanceof AnimatorSet) {
328a18a86b43e40e3c15dcca0ae0148d641be9b25feChet Haase                ((AnimatorSet)animation).setTarget(target);
329a18a86b43e40e3c15dcca0ae0148d641be9b25feChet Haase            } else if (animation instanceof ObjectAnimator) {
330a18a86b43e40e3c15dcca0ae0148d641be9b25feChet Haase                ((ObjectAnimator)animation).setTarget(target);
331811ed1065f39469cf2cf6adba22cab397ed88d5eChet Haase            }
332811ed1065f39469cf2cf6adba22cab397ed88d5eChet Haase        }
333811ed1065f39469cf2cf6adba22cab397ed88d5eChet Haase    }
334811ed1065f39469cf2cf6adba22cab397ed88d5eChet Haase
335811ed1065f39469cf2cf6adba22cab397ed88d5eChet Haase    /**
336d422dc358f0100106dc07d7b903201eb9b043b11Yigit Boyar     * @hide
337d422dc358f0100106dc07d7b903201eb9b043b11Yigit Boyar     */
338d422dc358f0100106dc07d7b903201eb9b043b11Yigit Boyar    @Override
339d422dc358f0100106dc07d7b903201eb9b043b11Yigit Boyar    public int getChangingConfigurations() {
340d422dc358f0100106dc07d7b903201eb9b043b11Yigit Boyar        int conf = super.getChangingConfigurations();
341d422dc358f0100106dc07d7b903201eb9b043b11Yigit Boyar        final int nodeCount = mNodes.size();
342d422dc358f0100106dc07d7b903201eb9b043b11Yigit Boyar        for (int i = 0; i < nodeCount; i ++) {
3431309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu            conf |= mNodes.get(i).mAnimation.getChangingConfigurations();
344d422dc358f0100106dc07d7b903201eb9b043b11Yigit Boyar        }
345d422dc358f0100106dc07d7b903201eb9b043b11Yigit Boyar        return conf;
346d422dc358f0100106dc07d7b903201eb9b043b11Yigit Boyar    }
347d422dc358f0100106dc07d7b903201eb9b043b11Yigit Boyar
348d422dc358f0100106dc07d7b903201eb9b043b11Yigit Boyar    /**
349e0ee2e9f3102c3c14c873a75a7b04e49787e0fb9Chet Haase     * Sets the TimeInterpolator for all current {@link #getChildAnimations() child animations}
350430742f09063574271e6c4091de13b9b9e762514Chet Haase     * of this AnimatorSet. The default value is null, which means that no interpolator
351430742f09063574271e6c4091de13b9b9e762514Chet Haase     * is set on this AnimatorSet. Setting the interpolator to any non-null value
352430742f09063574271e6c4091de13b9b9e762514Chet Haase     * will cause that interpolator to be set on the child animations
353430742f09063574271e6c4091de13b9b9e762514Chet Haase     * when the set is started.
35421cd1389d2ef218b20994b617c57af120841a57fChet Haase     *
355a18a86b43e40e3c15dcca0ae0148d641be9b25feChet Haase     * @param interpolator the interpolator to be used by each child animation of this AnimatorSet
35621cd1389d2ef218b20994b617c57af120841a57fChet Haase     */
35721cd1389d2ef218b20994b617c57af120841a57fChet Haase    @Override
358e0ee2e9f3102c3c14c873a75a7b04e49787e0fb9Chet Haase    public void setInterpolator(TimeInterpolator interpolator) {
359430742f09063574271e6c4091de13b9b9e762514Chet Haase        mInterpolator = interpolator;
360430742f09063574271e6c4091de13b9b9e762514Chet Haase    }
361430742f09063574271e6c4091de13b9b9e762514Chet Haase
362430742f09063574271e6c4091de13b9b9e762514Chet Haase    @Override
363430742f09063574271e6c4091de13b9b9e762514Chet Haase    public TimeInterpolator getInterpolator() {
364430742f09063574271e6c4091de13b9b9e762514Chet Haase        return mInterpolator;
36521cd1389d2ef218b20994b617c57af120841a57fChet Haase    }
36621cd1389d2ef218b20994b617c57af120841a57fChet Haase
36721cd1389d2ef218b20994b617c57af120841a57fChet Haase    /**
36817fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase     * This method creates a <code>Builder</code> object, which is used to
36917fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase     * set up playing constraints. This initial <code>play()</code> method
37017fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase     * tells the <code>Builder</code> the animation that is the dependency for
37117fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase     * the succeeding commands to the <code>Builder</code>. For example,
372a18a86b43e40e3c15dcca0ae0148d641be9b25feChet Haase     * calling <code>play(a1).with(a2)</code> sets up the AnimatorSet to play
37317fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase     * <code>a1</code> and <code>a2</code> at the same time,
374a18a86b43e40e3c15dcca0ae0148d641be9b25feChet Haase     * <code>play(a1).before(a2)</code> sets up the AnimatorSet to play
37517fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase     * <code>a1</code> first, followed by <code>a2</code>, and
376a18a86b43e40e3c15dcca0ae0148d641be9b25feChet Haase     * <code>play(a1).after(a2)</code> sets up the AnimatorSet to play
37717fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase     * <code>a2</code> first, followed by <code>a1</code>.
37817fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase     *
37917fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase     * <p>Note that <code>play()</code> is the only way to tell the
38017fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase     * <code>Builder</code> the animation upon which the dependency is created,
38117fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase     * so successive calls to the various functions in <code>Builder</code>
38217fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase     * will all refer to the initial parameter supplied in <code>play()</code>
38317fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase     * as the dependency of the other animations. For example, calling
38417fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase     * <code>play(a1).before(a2).before(a3)</code> will play both <code>a2</code>
38517fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase     * and <code>a3</code> when a1 ends; it does not set up a dependency between
38617fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase     * <code>a2</code> and <code>a3</code>.</p>
38717fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase     *
38817fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase     * @param anim The animation that is the dependency used in later calls to the
38917fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase     * methods in the returned <code>Builder</code> object. A null parameter will result
39017fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase     * in a null <code>Builder</code> return value.
391a18a86b43e40e3c15dcca0ae0148d641be9b25feChet Haase     * @return Builder The object that constructs the AnimatorSet based on the dependencies
39217fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase     * outlined in the calls to <code>play</code> and the other methods in the
39317fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase     * <code>Builder</code object.
39417fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase     */
395a18a86b43e40e3c15dcca0ae0148d641be9b25feChet Haase    public Builder play(Animator anim) {
39617fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase        if (anim != null) {
39717fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase            return new Builder(anim);
39817fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase        }
39917fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase        return null;
40017fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase    }
40117fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase
40217fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase    /**
40317fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase     * {@inheritDoc}
40417fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase     *
4058b699792b677bd4dd8442b32641ac09d48fdd79cChet Haase     * <p>Note that canceling a <code>AnimatorSet</code> also cancels all of the animations that it
4068b699792b677bd4dd8442b32641ac09d48fdd79cChet Haase     * is responsible for.</p>
40717fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase     */
40817fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase    @SuppressWarnings("unchecked")
40917fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase    @Override
41017fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase    public void cancel() {
41113351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu        if (Looper.myLooper() == null) {
41213351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu            throw new AndroidRuntimeException("Animators may only be run on Looper threads");
41313351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu        }
4148b699792b677bd4dd8442b32641ac09d48fdd79cChet Haase        if (isStarted()) {
4157dfacdb1c820f955cb3cd6032ff5fbc2dd7d9df5Chet Haase            ArrayList<AnimatorListener> tmpListeners = null;
416e2ab7ccd385cdb6517955c719e1d2b49771bedb6Chet Haase            if (mListeners != null) {
4177dfacdb1c820f955cb3cd6032ff5fbc2dd7d9df5Chet Haase                tmpListeners = (ArrayList<AnimatorListener>) mListeners.clone();
4181309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu                int size = tmpListeners.size();
4191309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu                for (int i = 0; i < size; i++) {
4201309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu                    tmpListeners.get(i).onAnimationCancel(this);
421e2ab7ccd385cdb6517955c719e1d2b49771bedb6Chet Haase                }
422e2ab7ccd385cdb6517955c719e1d2b49771bedb6Chet Haase            }
42313351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu            ArrayList<Node> playingSet = new ArrayList<>(mPlayingSet);
4241309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu            int setSize = playingSet.size();
4251309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu            for (int i = 0; i < setSize; i++) {
42613351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu                playingSet.get(i).mAnimation.cancel();
4277dfacdb1c820f955cb3cd6032ff5fbc2dd7d9df5Chet Haase            }
42813351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu            mPlayingSet.clear();
42913351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu            endAnimation();
43017fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase        }
43117fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase    }
43217fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase
4336d4520920526cc44063ff2e665adb08e9a3c8a64Doris Liu    // Force all the animations to end when the duration scale is 0.
4346d4520920526cc44063ff2e665adb08e9a3c8a64Doris Liu    private void forceToEnd() {
435ae570c04c0e43e0d66e2c563fe6d8cacb438961bDoris Liu        if (mEndCanBeCalled) {
436ae570c04c0e43e0d66e2c563fe6d8cacb438961bDoris Liu            end();
437ae570c04c0e43e0d66e2c563fe6d8cacb438961bDoris Liu            return;
438ae570c04c0e43e0d66e2c563fe6d8cacb438961bDoris Liu        }
439d7968dc174f60e3b8f5aaddd05703cf6ce9114adDoris Liu
440d7968dc174f60e3b8f5aaddd05703cf6ce9114adDoris Liu        // Note: we don't want to combine this case with the end() method below because in
441d7968dc174f60e3b8f5aaddd05703cf6ce9114adDoris Liu        // the case of developer calling end(), we still need to make sure end() is explicitly
442d7968dc174f60e3b8f5aaddd05703cf6ce9114adDoris Liu        // called on the child animators to maintain the old behavior.
443d7968dc174f60e3b8f5aaddd05703cf6ce9114adDoris Liu        if (mReversing) {
444d7968dc174f60e3b8f5aaddd05703cf6ce9114adDoris Liu            handleAnimationEvents(mLastEventId, 0, getTotalDuration());
4456d4520920526cc44063ff2e665adb08e9a3c8a64Doris Liu        } else {
446d7968dc174f60e3b8f5aaddd05703cf6ce9114adDoris Liu            long zeroScalePlayTime = getTotalDuration();
447d7968dc174f60e3b8f5aaddd05703cf6ce9114adDoris Liu            if (zeroScalePlayTime == DURATION_INFINITE) {
448d7968dc174f60e3b8f5aaddd05703cf6ce9114adDoris Liu                // Use a large number for the play time.
449d7968dc174f60e3b8f5aaddd05703cf6ce9114adDoris Liu                zeroScalePlayTime = Integer.MAX_VALUE;
4506d4520920526cc44063ff2e665adb08e9a3c8a64Doris Liu            }
451d7968dc174f60e3b8f5aaddd05703cf6ce9114adDoris Liu            handleAnimationEvents(mLastEventId, mEvents.size() - 1, zeroScalePlayTime);
4526d4520920526cc44063ff2e665adb08e9a3c8a64Doris Liu        }
453d7968dc174f60e3b8f5aaddd05703cf6ce9114adDoris Liu        mPlayingSet.clear();
454d7968dc174f60e3b8f5aaddd05703cf6ce9114adDoris Liu        endAnimation();
4556d4520920526cc44063ff2e665adb08e9a3c8a64Doris Liu    }
4566d4520920526cc44063ff2e665adb08e9a3c8a64Doris Liu
45717fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase    /**
45817fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase     * {@inheritDoc}
45917fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase     *
460a18a86b43e40e3c15dcca0ae0148d641be9b25feChet Haase     * <p>Note that ending a <code>AnimatorSet</code> also ends all of the animations that it is
46117fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase     * responsible for.</p>
46217fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase     */
46317fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase    @Override
46417fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase    public void end() {
46513351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu        if (Looper.myLooper() == null) {
46613351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu            throw new AndroidRuntimeException("Animators may only be run on Looper threads");
46713351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu        }
46858606db8be7e64a4317955b87fba4ee51f353630Doris Liu        if (mShouldIgnoreEndWithoutStart && !isStarted()) {
46958606db8be7e64a4317955b87fba4ee51f353630Doris Liu            return;
47058606db8be7e64a4317955b87fba4ee51f353630Doris Liu        }
4718b699792b677bd4dd8442b32641ac09d48fdd79cChet Haase        if (isStarted()) {
47213351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu            // Iterate the animations that haven't finished or haven't started, and end them.
47313351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu            if (mReversing) {
47413351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu                // Between start() and first frame, mLastEventId would be unset (i.e. -1)
47513351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu                mLastEventId = mLastEventId == -1 ? mEvents.size() : mLastEventId;
47688bb31b95467f3591f4911be673b57c6fb316134Doris Liu                while (mLastEventId > 0) {
47788bb31b95467f3591f4911be673b57c6fb316134Doris Liu                    mLastEventId = mLastEventId - 1;
47888bb31b95467f3591f4911be673b57c6fb316134Doris Liu                    AnimationEvent event = mEvents.get(mLastEventId);
4796d4520920526cc44063ff2e665adb08e9a3c8a64Doris Liu                    Animator anim = event.mNode.mAnimation;
48088bb31b95467f3591f4911be673b57c6fb316134Doris Liu                    if (mNodeMap.get(anim).mEnded) {
48188bb31b95467f3591f4911be673b57c6fb316134Doris Liu                        continue;
48288bb31b95467f3591f4911be673b57c6fb316134Doris Liu                    }
48313351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu                    if (event.mEvent == AnimationEvent.ANIMATION_END) {
4846d4520920526cc44063ff2e665adb08e9a3c8a64Doris Liu                        anim.reverse();
4856d4520920526cc44063ff2e665adb08e9a3c8a64Doris Liu                    } else if (event.mEvent == AnimationEvent.ANIMATION_DELAY_ENDED
4866d4520920526cc44063ff2e665adb08e9a3c8a64Doris Liu                            && anim.isStarted()) {
4876d4520920526cc44063ff2e665adb08e9a3c8a64Doris Liu                        // Make sure anim hasn't finished before calling end() so that we don't end
4886d4520920526cc44063ff2e665adb08e9a3c8a64Doris Liu                        // already ended animations, which will cause start and end callbacks to be
4896d4520920526cc44063ff2e665adb08e9a3c8a64Doris Liu                        // triggered again.
4906d4520920526cc44063ff2e665adb08e9a3c8a64Doris Liu                        anim.end();
49113351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu                    }
49213351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu                }
49313351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu            } else {
49488bb31b95467f3591f4911be673b57c6fb316134Doris Liu                while (mLastEventId < mEvents.size() - 1) {
49588bb31b95467f3591f4911be673b57c6fb316134Doris Liu                    // Avoid potential reentrant loop caused by child animators manipulating
49688bb31b95467f3591f4911be673b57c6fb316134Doris Liu                    // AnimatorSet's lifecycle (i.e. not a recommended approach).
49788bb31b95467f3591f4911be673b57c6fb316134Doris Liu                    mLastEventId = mLastEventId + 1;
49888bb31b95467f3591f4911be673b57c6fb316134Doris Liu                    AnimationEvent event = mEvents.get(mLastEventId);
4996d4520920526cc44063ff2e665adb08e9a3c8a64Doris Liu                    Animator anim = event.mNode.mAnimation;
50088bb31b95467f3591f4911be673b57c6fb316134Doris Liu                    if (mNodeMap.get(anim).mEnded) {
50188bb31b95467f3591f4911be673b57c6fb316134Doris Liu                        continue;
50288bb31b95467f3591f4911be673b57c6fb316134Doris Liu                    }
50313351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu                    if (event.mEvent == AnimationEvent.ANIMATION_START) {
5046d4520920526cc44063ff2e665adb08e9a3c8a64Doris Liu                        anim.start();
5056d4520920526cc44063ff2e665adb08e9a3c8a64Doris Liu                    } else if (event.mEvent == AnimationEvent.ANIMATION_END && anim.isStarted()) {
5066d4520920526cc44063ff2e665adb08e9a3c8a64Doris Liu                        // Make sure anim hasn't finished before calling end() so that we don't end
5076d4520920526cc44063ff2e665adb08e9a3c8a64Doris Liu                        // already ended animations, which will cause start and end callbacks to be
5086d4520920526cc44063ff2e665adb08e9a3c8a64Doris Liu                        // triggered again.
5096d4520920526cc44063ff2e665adb08e9a3c8a64Doris Liu                        anim.end();
5101309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu                    }
5117dfacdb1c820f955cb3cd6032ff5fbc2dd7d9df5Chet Haase                }
51217fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase            }
51313351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu            mPlayingSet.clear();
51417fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase        }
51513351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu        endAnimation();
51617fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase    }
51717fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase
51817fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase    /**
5198b699792b677bd4dd8442b32641ac09d48fdd79cChet Haase     * Returns true if any of the child animations of this AnimatorSet have been started and have
520ee684556ceb8b940ca2af06613a59728e9b78507Doris Liu     * not yet ended. Child animations will not be started until the AnimatorSet has gone past
521ee684556ceb8b940ca2af06613a59728e9b78507Doris Liu     * its initial delay set through {@link #setStartDelay(long)}.
522ee684556ceb8b940ca2af06613a59728e9b78507Doris Liu     *
523ee684556ceb8b940ca2af06613a59728e9b78507Doris Liu     * @return Whether this AnimatorSet has gone past the initial delay, and at least one child
524ee684556ceb8b940ca2af06613a59728e9b78507Doris Liu     *         animation has been started and not yet ended.
525673e42fafd4088970ec95e1f13c61dc83132c74eChet Haase     */
526673e42fafd4088970ec95e1f13c61dc83132c74eChet Haase    @Override
527673e42fafd4088970ec95e1f13c61dc83132c74eChet Haase    public boolean isRunning() {
5286d4520920526cc44063ff2e665adb08e9a3c8a64Doris Liu        if (mStartDelay == 0) {
52913351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu            return mStarted;
530673e42fafd4088970ec95e1f13c61dc83132c74eChet Haase        }
5316d4520920526cc44063ff2e665adb08e9a3c8a64Doris Liu        return mLastFrameTime > 0;
5328b699792b677bd4dd8442b32641ac09d48fdd79cChet Haase    }
5338b699792b677bd4dd8442b32641ac09d48fdd79cChet Haase
5348b699792b677bd4dd8442b32641ac09d48fdd79cChet Haase    @Override
5358b699792b677bd4dd8442b32641ac09d48fdd79cChet Haase    public boolean isStarted() {
5368b699792b677bd4dd8442b32641ac09d48fdd79cChet Haase        return mStarted;
537673e42fafd4088970ec95e1f13c61dc83132c74eChet Haase    }
538673e42fafd4088970ec95e1f13c61dc83132c74eChet Haase
539673e42fafd4088970ec95e1f13c61dc83132c74eChet Haase    /**
54021cd1389d2ef218b20994b617c57af120841a57fChet Haase     * The amount of time, in milliseconds, to delay starting the animation after
54121cd1389d2ef218b20994b617c57af120841a57fChet Haase     * {@link #start()} is called.
54221cd1389d2ef218b20994b617c57af120841a57fChet Haase     *
54321cd1389d2ef218b20994b617c57af120841a57fChet Haase     * @return the number of milliseconds to delay running the animation
54421cd1389d2ef218b20994b617c57af120841a57fChet Haase     */
54521cd1389d2ef218b20994b617c57af120841a57fChet Haase    @Override
54621cd1389d2ef218b20994b617c57af120841a57fChet Haase    public long getStartDelay() {
54721cd1389d2ef218b20994b617c57af120841a57fChet Haase        return mStartDelay;
54821cd1389d2ef218b20994b617c57af120841a57fChet Haase    }
54921cd1389d2ef218b20994b617c57af120841a57fChet Haase
55021cd1389d2ef218b20994b617c57af120841a57fChet Haase    /**
55121cd1389d2ef218b20994b617c57af120841a57fChet Haase     * The amount of time, in milliseconds, to delay starting the animation after
55261045c518b18a7cee30954fe45f9db8c14e705e1Doris Liu     * {@link #start()} is called. Note that the start delay should always be non-negative. Any
55361045c518b18a7cee30954fe45f9db8c14e705e1Doris Liu     * negative start delay will be clamped to 0 on N and above.
55461045c518b18a7cee30954fe45f9db8c14e705e1Doris Liu     *
55521cd1389d2ef218b20994b617c57af120841a57fChet Haase     * @param startDelay The amount of the delay, in milliseconds
55621cd1389d2ef218b20994b617c57af120841a57fChet Haase     */
55721cd1389d2ef218b20994b617c57af120841a57fChet Haase    @Override
55821cd1389d2ef218b20994b617c57af120841a57fChet Haase    public void setStartDelay(long startDelay) {
55961045c518b18a7cee30954fe45f9db8c14e705e1Doris Liu        // Clamp start delay to non-negative range.
56061045c518b18a7cee30954fe45f9db8c14e705e1Doris Liu        if (startDelay < 0) {
56161045c518b18a7cee30954fe45f9db8c14e705e1Doris Liu            Log.w(TAG, "Start delay should always be non-negative");
56261045c518b18a7cee30954fe45f9db8c14e705e1Doris Liu            startDelay = 0;
5637bc6a3f023ca3e1dde91fc97b6036dee3ba538a2ztenghui        }
5641309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu        long delta = startDelay - mStartDelay;
56549db4242b134ffee79392670f9e434f6e3c7cec7Doris Liu        if (delta == 0) {
56649db4242b134ffee79392670f9e434f6e3c7cec7Doris Liu            return;
56749db4242b134ffee79392670f9e434f6e3c7cec7Doris Liu        }
56821cd1389d2ef218b20994b617c57af120841a57fChet Haase        mStartDelay = startDelay;
5691309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu        if (!mDependencyDirty) {
5701309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu            // Dependency graph already constructed, update all the nodes' start/end time
5711309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu            int size = mNodes.size();
5721309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu            for (int i = 0; i < size; i++) {
5731309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu                Node node = mNodes.get(i);
5741309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu                if (node == mRootNode) {
5751309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu                    node.mEndTime = mStartDelay;
5761309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu                } else {
5771309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu                    node.mStartTime = node.mStartTime == DURATION_INFINITE ?
5781309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu                            DURATION_INFINITE : node.mStartTime + delta;
5791309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu                    node.mEndTime = node.mEndTime == DURATION_INFINITE ?
5801309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu                            DURATION_INFINITE : node.mEndTime + delta;
5811309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu                }
5821309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu            }
58349db4242b134ffee79392670f9e434f6e3c7cec7Doris Liu            // Update total duration, if necessary.
58449db4242b134ffee79392670f9e434f6e3c7cec7Doris Liu            if (mTotalDuration != DURATION_INFINITE) {
58549db4242b134ffee79392670f9e434f6e3c7cec7Doris Liu                mTotalDuration += delta;
58649db4242b134ffee79392670f9e434f6e3c7cec7Doris Liu            }
5871309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu        }
58821cd1389d2ef218b20994b617c57af120841a57fChet Haase    }
58921cd1389d2ef218b20994b617c57af120841a57fChet Haase
59021cd1389d2ef218b20994b617c57af120841a57fChet Haase    /**
591a18a86b43e40e3c15dcca0ae0148d641be9b25feChet Haase     * Gets the length of each of the child animations of this AnimatorSet. This value may
592a18a86b43e40e3c15dcca0ae0148d641be9b25feChet Haase     * be less than 0, which indicates that no duration has been set on this AnimatorSet
59321cd1389d2ef218b20994b617c57af120841a57fChet Haase     * and each of the child animations will use their own duration.
59421cd1389d2ef218b20994b617c57af120841a57fChet Haase     *
59521cd1389d2ef218b20994b617c57af120841a57fChet Haase     * @return The length of the animation, in milliseconds, of each of the child
596a18a86b43e40e3c15dcca0ae0148d641be9b25feChet Haase     * animations of this AnimatorSet.
59721cd1389d2ef218b20994b617c57af120841a57fChet Haase     */
59821cd1389d2ef218b20994b617c57af120841a57fChet Haase    @Override
59921cd1389d2ef218b20994b617c57af120841a57fChet Haase    public long getDuration() {
60021cd1389d2ef218b20994b617c57af120841a57fChet Haase        return mDuration;
60121cd1389d2ef218b20994b617c57af120841a57fChet Haase    }
60221cd1389d2ef218b20994b617c57af120841a57fChet Haase
60321cd1389d2ef218b20994b617c57af120841a57fChet Haase    /**
604a18a86b43e40e3c15dcca0ae0148d641be9b25feChet Haase     * Sets the length of each of the current child animations of this AnimatorSet. By default,
605a18a86b43e40e3c15dcca0ae0148d641be9b25feChet Haase     * each child animation will use its own duration. If the duration is set on the AnimatorSet,
60621cd1389d2ef218b20994b617c57af120841a57fChet Haase     * then each child animation inherits this duration.
60721cd1389d2ef218b20994b617c57af120841a57fChet Haase     *
60821cd1389d2ef218b20994b617c57af120841a57fChet Haase     * @param duration The length of the animation, in milliseconds, of each of the child
609a18a86b43e40e3c15dcca0ae0148d641be9b25feChet Haase     * animations of this AnimatorSet.
61021cd1389d2ef218b20994b617c57af120841a57fChet Haase     */
61121cd1389d2ef218b20994b617c57af120841a57fChet Haase    @Override
6122794eb3b02e2404d453d3ad22a8a85a138130a07Chet Haase    public AnimatorSet setDuration(long duration) {
61321cd1389d2ef218b20994b617c57af120841a57fChet Haase        if (duration < 0) {
61421cd1389d2ef218b20994b617c57af120841a57fChet Haase            throw new IllegalArgumentException("duration must be a value of zero or greater");
61521cd1389d2ef218b20994b617c57af120841a57fChet Haase        }
6161309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu        mDependencyDirty = true;
617c299a3384171e36fc9ab6d1011d8a589a7f344d1Chet Haase        // Just record the value for now - it will be used later when the AnimatorSet starts
61821cd1389d2ef218b20994b617c57af120841a57fChet Haase        mDuration = duration;
6192794eb3b02e2404d453d3ad22a8a85a138130a07Chet Haase        return this;
62021cd1389d2ef218b20994b617c57af120841a57fChet Haase    }
62121cd1389d2ef218b20994b617c57af120841a57fChet Haase
6222970c499388b4dcd1232cd622a9b80b395eeb2b4Chet Haase    @Override
6232970c499388b4dcd1232cd622a9b80b395eeb2b4Chet Haase    public void setupStartValues() {
6241309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu        int size = mNodes.size();
6251309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu        for (int i = 0; i < size; i++) {
6261309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu            Node node = mNodes.get(i);
627458f20ed5a3be51ab1166256dfeece37fa7e3c66Doris Liu            if (node != mRootNode) {
628458f20ed5a3be51ab1166256dfeece37fa7e3c66Doris Liu                node.mAnimation.setupStartValues();
629458f20ed5a3be51ab1166256dfeece37fa7e3c66Doris Liu            }
6302970c499388b4dcd1232cd622a9b80b395eeb2b4Chet Haase        }
6312970c499388b4dcd1232cd622a9b80b395eeb2b4Chet Haase    }
6322970c499388b4dcd1232cd622a9b80b395eeb2b4Chet Haase
6332970c499388b4dcd1232cd622a9b80b395eeb2b4Chet Haase    @Override
6342970c499388b4dcd1232cd622a9b80b395eeb2b4Chet Haase    public void setupEndValues() {
6351309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu        int size = mNodes.size();
6361309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu        for (int i = 0; i < size; i++) {
6371309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu            Node node = mNodes.get(i);
638458f20ed5a3be51ab1166256dfeece37fa7e3c66Doris Liu            if (node != mRootNode) {
639458f20ed5a3be51ab1166256dfeece37fa7e3c66Doris Liu                node.mAnimation.setupEndValues();
640458f20ed5a3be51ab1166256dfeece37fa7e3c66Doris Liu            }
6412970c499388b4dcd1232cd622a9b80b395eeb2b4Chet Haase        }
6422970c499388b4dcd1232cd622a9b80b395eeb2b4Chet Haase    }
6432970c499388b4dcd1232cd622a9b80b395eeb2b4Chet Haase
6448aa1ffb0ed292891030992c65df4e5dc8bd37524Chet Haase    @Override
6458aa1ffb0ed292891030992c65df4e5dc8bd37524Chet Haase    public void pause() {
64613351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu        if (Looper.myLooper() == null) {
64713351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu            throw new AndroidRuntimeException("Animators may only be run on Looper threads");
64813351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu        }
6498aa1ffb0ed292891030992c65df4e5dc8bd37524Chet Haase        boolean previouslyPaused = mPaused;
6508aa1ffb0ed292891030992c65df4e5dc8bd37524Chet Haase        super.pause();
6518aa1ffb0ed292891030992c65df4e5dc8bd37524Chet Haase        if (!previouslyPaused && mPaused) {
65213351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu            mPauseTime = -1;
6538aa1ffb0ed292891030992c65df4e5dc8bd37524Chet Haase        }
6548aa1ffb0ed292891030992c65df4e5dc8bd37524Chet Haase    }
6558aa1ffb0ed292891030992c65df4e5dc8bd37524Chet Haase
6568aa1ffb0ed292891030992c65df4e5dc8bd37524Chet Haase    @Override
6578aa1ffb0ed292891030992c65df4e5dc8bd37524Chet Haase    public void resume() {
65813351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu        if (Looper.myLooper() == null) {
65913351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu            throw new AndroidRuntimeException("Animators may only be run on Looper threads");
66013351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu        }
6618aa1ffb0ed292891030992c65df4e5dc8bd37524Chet Haase        boolean previouslyPaused = mPaused;
6628aa1ffb0ed292891030992c65df4e5dc8bd37524Chet Haase        super.resume();
6638aa1ffb0ed292891030992c65df4e5dc8bd37524Chet Haase        if (previouslyPaused && !mPaused) {
66413351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu            if (mPauseTime >= 0) {
66513351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu                addAnimationCallback(0);
6668aa1ffb0ed292891030992c65df4e5dc8bd37524Chet Haase            }
6678aa1ffb0ed292891030992c65df4e5dc8bd37524Chet Haase        }
6688aa1ffb0ed292891030992c65df4e5dc8bd37524Chet Haase    }
6698aa1ffb0ed292891030992c65df4e5dc8bd37524Chet Haase
67021cd1389d2ef218b20994b617c57af120841a57fChet Haase    /**
67117fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase     * {@inheritDoc}
67217fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase     *
673a18a86b43e40e3c15dcca0ae0148d641be9b25feChet Haase     * <p>Starting this <code>AnimatorSet</code> will, in turn, start the animations for which
67417fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase     * it is responsible. The details of when exactly those animations are started depends on
67517fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase     * the dependency relationships that have been set up between the animations.
67688bb31b95467f3591f4911be673b57c6fb316134Doris Liu     *
67788bb31b95467f3591f4911be673b57c6fb316134Doris Liu     * <b>Note:</b> Manipulating AnimatorSet's lifecycle in the child animators' listener callbacks
67888bb31b95467f3591f4911be673b57c6fb316134Doris Liu     * will lead to undefined behaviors. Also, AnimatorSet will ignore any seeking in the child
67988bb31b95467f3591f4911be673b57c6fb316134Doris Liu     * animators once {@link #start()} is called.
68017fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase     */
68117fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase    @SuppressWarnings("unchecked")
68217fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase    @Override
68317fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase    public void start() {
68413351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu        start(false, true);
68513351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu    }
68613351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu
68713351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu    @Override
68813351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu    void startWithoutPulsing(boolean inReverse) {
68913351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu        start(inReverse, false);
69013351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu    }
69113351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu
69213351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu    private void initAnimation() {
69313351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu        if (mInterpolator != null) {
69413351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu            for (int i = 0; i < mNodes.size(); i++) {
69513351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu                Node node = mNodes.get(i);
69613351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu                node.mAnimation.setInterpolator(mInterpolator);
69713351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu            }
69813351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu        }
69913351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu        updateAnimatorsDuration();
70013351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu        createDependencyGraph();
70113351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu    }
70213351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu
70313351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu    private void start(boolean inReverse, boolean selfPulse) {
70413351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu        if (Looper.myLooper() == null) {
70513351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu            throw new AndroidRuntimeException("Animators may only be run on Looper threads");
70613351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu        }
7078b699792b677bd4dd8442b32641ac09d48fdd79cChet Haase        mStarted = true;
70813351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu        mSelfPulse = selfPulse;
7098aa1ffb0ed292891030992c65df4e5dc8bd37524Chet Haase        mPaused = false;
71013351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu        mPauseTime = -1;
711010dbaa1236cf2dcdc62c29049468e90188acaaeChet Haase
7121309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu        int size = mNodes.size();
7131309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu        for (int i = 0; i < size; i++) {
7141309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu            Node node = mNodes.get(i);
7151309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu            node.mEnded = false;
7161309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu            node.mAnimation.setAllowRunningAsynchronously(false);
717f5945a0c8bb868f978d9d0d22043a8b44464a86eJohn Reck        }
718f5945a0c8bb868f978d9d0d22043a8b44464a86eJohn Reck
71913351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu        initAnimation();
72013351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu        if (inReverse && !canReverse()) {
72113351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu            throw new UnsupportedOperationException("Cannot reverse infinite AnimatorSet");
722e2ab7ccd385cdb6517955c719e1d2b49771bedb6Chet Haase        }
723e2ab7ccd385cdb6517955c719e1d2b49771bedb6Chet Haase
72413351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu        mReversing = inReverse;
7251309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu
72617fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase        // Now that all dependencies are set up, start the animations that should be started.
72721ba77f6b6c037e9507e210e22c18614fa9d2e9bDoris Liu        boolean isEmptySet = isEmptySet(this);
72821ba77f6b6c037e9507e210e22c18614fa9d2e9bDoris Liu        if (!isEmptySet) {
72913351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu            startAnimation();
730f57bfe2fefc87fdb1dcc27b0f4b3a11996c15da2Doris Liu        }
731f57bfe2fefc87fdb1dcc27b0f4b3a11996c15da2Doris Liu
73217fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase        if (mListeners != null) {
733a18a86b43e40e3c15dcca0ae0148d641be9b25feChet Haase            ArrayList<AnimatorListener> tmpListeners =
734a18a86b43e40e3c15dcca0ae0148d641be9b25feChet Haase                    (ArrayList<AnimatorListener>) mListeners.clone();
7357c608f25d494c8a0a671e7373efbb47ca635367eChet Haase            int numListeners = tmpListeners.size();
7367c608f25d494c8a0a671e7373efbb47ca635367eChet Haase            for (int i = 0; i < numListeners; ++i) {
73713351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu                tmpListeners.get(i).onAnimationStart(this, inReverse);
7388b699792b677bd4dd8442b32641ac09d48fdd79cChet Haase            }
7398b699792b677bd4dd8442b32641ac09d48fdd79cChet Haase        }
74021ba77f6b6c037e9507e210e22c18614fa9d2e9bDoris Liu        if (isEmptySet) {
7416d4520920526cc44063ff2e665adb08e9a3c8a64Doris Liu            // In the case of empty AnimatorSet, or 0 duration scale, we will trigger the
7426d4520920526cc44063ff2e665adb08e9a3c8a64Doris Liu            // onAnimationEnd() right away.
743d7968dc174f60e3b8f5aaddd05703cf6ce9114adDoris Liu            end();
74417fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase        }
74517fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase    }
74617fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase
747f66d2f6f31d6a9bc51c8fa52915906c3594db82cDoris Liu    // Returns true if set is empty or contains nothing but animator sets with no start delay.
748f66d2f6f31d6a9bc51c8fa52915906c3594db82cDoris Liu    private static boolean isEmptySet(AnimatorSet set) {
749f66d2f6f31d6a9bc51c8fa52915906c3594db82cDoris Liu        if (set.getStartDelay() > 0) {
750f66d2f6f31d6a9bc51c8fa52915906c3594db82cDoris Liu            return false;
751f66d2f6f31d6a9bc51c8fa52915906c3594db82cDoris Liu        }
752f66d2f6f31d6a9bc51c8fa52915906c3594db82cDoris Liu        for (int i = 0; i < set.getChildAnimations().size(); i++) {
753f66d2f6f31d6a9bc51c8fa52915906c3594db82cDoris Liu            Animator anim = set.getChildAnimations().get(i);
754f66d2f6f31d6a9bc51c8fa52915906c3594db82cDoris Liu            if (!(anim instanceof AnimatorSet)) {
755f66d2f6f31d6a9bc51c8fa52915906c3594db82cDoris Liu                // Contains non-AnimatorSet, not empty.
756f66d2f6f31d6a9bc51c8fa52915906c3594db82cDoris Liu                return false;
757f66d2f6f31d6a9bc51c8fa52915906c3594db82cDoris Liu            } else {
758f66d2f6f31d6a9bc51c8fa52915906c3594db82cDoris Liu                if (!isEmptySet((AnimatorSet) anim)) {
759f66d2f6f31d6a9bc51c8fa52915906c3594db82cDoris Liu                    return false;
760f66d2f6f31d6a9bc51c8fa52915906c3594db82cDoris Liu                }
761f66d2f6f31d6a9bc51c8fa52915906c3594db82cDoris Liu            }
762f66d2f6f31d6a9bc51c8fa52915906c3594db82cDoris Liu        }
763f66d2f6f31d6a9bc51c8fa52915906c3594db82cDoris Liu        return true;
764f66d2f6f31d6a9bc51c8fa52915906c3594db82cDoris Liu    }
765f66d2f6f31d6a9bc51c8fa52915906c3594db82cDoris Liu
7661309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu    private void updateAnimatorsDuration() {
7671309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu        if (mDuration >= 0) {
7681309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu            // If the duration was set on this AnimatorSet, pass it along to all child animations
7691309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu            int size = mNodes.size();
7701309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu            for (int i = 0; i < size; i++) {
7711309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu                Node node = mNodes.get(i);
7721309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu                // TODO: don't set the duration of the timing-only nodes created by AnimatorSet to
7731309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu                // insert "play-after" delays
7741309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu                node.mAnimation.setDuration(mDuration);
7751309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu            }
7761309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu        }
7771309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu        mDelayAnim.setDuration(mStartDelay);
7781309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu    }
7791309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu
78013351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu    @Override
78113351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu    void skipToEndValue(boolean inReverse) {
78213351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu        if (!isInitialized()) {
78313351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu            throw new UnsupportedOperationException("Children must be initialized.");
78413351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu        }
78513351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu
78613351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu        // This makes sure the animation events are sorted an up to date.
78713351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu        initAnimation();
78813351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu
78913351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu        // Calling skip to the end in the sequence that they would be called in a forward/reverse
79013351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu        // run, such that the sequential animations modifying the same property would have
79113351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu        // the right value in the end.
79213351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu        if (inReverse) {
79313351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu            for (int i = mEvents.size() - 1; i >= 0; i--) {
79413351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu                if (mEvents.get(i).mEvent == AnimationEvent.ANIMATION_DELAY_ENDED) {
79513351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu                    mEvents.get(i).mNode.mAnimation.skipToEndValue(true);
79613351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu                }
79713351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu            }
79813351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu        } else {
79913351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu            for (int i = 0; i < mEvents.size(); i++) {
80013351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu                if (mEvents.get(i).mEvent == AnimationEvent.ANIMATION_END) {
80113351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu                    mEvents.get(i).mNode.mAnimation.skipToEndValue(false);
80213351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu                }
80313351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu            }
80413351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu        }
80513351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu    }
80613351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu
80713351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu    /**
80813351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu     * Internal only.
80913351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu     *
81013351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu     * This method sets the animation values based on the play time. It also fast forward or
81113351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu     * backward all the child animations progress accordingly.
81213351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu     *
81313351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu     * This method is also responsible for calling
81413351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu     * {@link android.view.animation.Animation.AnimationListener#onAnimationRepeat(Animation)},
81513351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu     * as needed, based on the last play time and current play time.
81613351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu     */
81713351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu    @Override
81813351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu    void animateBasedOnPlayTime(long currentPlayTime, long lastPlayTime, boolean inReverse) {
81913351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu        if (currentPlayTime < 0 || lastPlayTime < 0) {
82013351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu            throw new UnsupportedOperationException("Error: Play time should never be negative.");
82113351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu        }
82213351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu        // TODO: take into account repeat counts and repeat callback when repeat is implemented.
82313351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu        // Clamp currentPlayTime and lastPlayTime
82413351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu
82513351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu        // TODO: Make this more efficient
82613351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu
82713351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu        // Convert the play times to the forward direction.
82813351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu        if (inReverse) {
82913351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu            if (getTotalDuration() == DURATION_INFINITE) {
83013351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu                throw new UnsupportedOperationException("Cannot reverse AnimatorSet with infinite"
83113351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu                        + " duration");
83213351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu            }
83313351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu            long duration = getTotalDuration() - mStartDelay;
83413351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu            currentPlayTime = Math.min(currentPlayTime, duration);
83513351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu            currentPlayTime = duration - currentPlayTime;
83613351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu            lastPlayTime = duration - lastPlayTime;
83713351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu            inReverse = false;
83813351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu        }
83913351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu        // Skip all values to start, and iterate mEvents to get animations to the right fraction.
84013351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu        skipToStartValue(false);
84113351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu
84213351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu        ArrayList<Node> unfinishedNodes = new ArrayList<>();
84313351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu        // Assumes forward playing from here on.
84413351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu        for (int i = 0; i < mEvents.size(); i++) {
84513351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu            AnimationEvent event = mEvents.get(i);
846290271695d66cb6be38b1ebe80dc3473e6163738Doris Liu            if (event.getTime() > currentPlayTime || event.getTime() == DURATION_INFINITE) {
84713351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu                break;
84813351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu            }
84913351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu
85013351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu            // This animation started prior to the current play time, and won't finish before the
85113351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu            // play time, add to the unfinished list.
85213351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu            if (event.mEvent == AnimationEvent.ANIMATION_DELAY_ENDED) {
85313351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu                if (event.mNode.mEndTime == DURATION_INFINITE
85413351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu                        || event.mNode.mEndTime > currentPlayTime) {
85513351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu                    unfinishedNodes.add(event.mNode);
85613351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu                }
85713351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu            }
85813351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu            // For animations that do finish before the play time, end them in the sequence that
85913351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu            // they would in a normal run.
86013351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu            if (event.mEvent == AnimationEvent.ANIMATION_END) {
86113351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu                // Skip to the end of the animation.
86213351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu                event.mNode.mAnimation.skipToEndValue(false);
86313351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu            }
86413351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu        }
86513351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu
86613351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu        // Seek unfinished animation to the right time.
86713351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu        for (int i = 0; i < unfinishedNodes.size(); i++) {
86813351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu            Node node = unfinishedNodes.get(i);
86913351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu            long playTime = getPlayTimeForNode(currentPlayTime, node, inReverse);
8706d4520920526cc44063ff2e665adb08e9a3c8a64Doris Liu            if (!inReverse) {
8716d4520920526cc44063ff2e665adb08e9a3c8a64Doris Liu                playTime -= node.mAnimation.getStartDelay();
8726d4520920526cc44063ff2e665adb08e9a3c8a64Doris Liu            }
87313351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu            node.mAnimation.animateBasedOnPlayTime(playTime, lastPlayTime, inReverse);
87413351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu        }
87513351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu    }
87613351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu
87713351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu    @Override
87813351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu    boolean isInitialized() {
87913351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu        if (mChildrenInitialized) {
88013351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu            return true;
88113351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu        }
88213351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu
88313351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu        boolean allInitialized = true;
88413351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu        for (int i = 0; i < mNodes.size(); i++) {
88513351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu            if (!mNodes.get(i).mAnimation.isInitialized()) {
88613351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu                allInitialized = false;
88713351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu                break;
88813351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu            }
88913351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu        }
89013351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu        mChildrenInitialized = allInitialized;
89113351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu        return mChildrenInitialized;
89213351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu    }
89313351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu
89413351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu    private void skipToStartValue(boolean inReverse) {
89513351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu        skipToEndValue(!inReverse);
89613351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu    }
89713351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu
89813351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu    /**
89913351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu     * Sets the position of the animation to the specified point in time. This time should
90013351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu     * be between 0 and the total duration of the animation, including any repetition. If
90113351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu     * the animation has not yet been started, then it will not advance forward after it is
90213351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu     * set to this time; it will simply set the time to this value and perform any appropriate
90313351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu     * actions based on that time. If the animation is already running, then setCurrentPlayTime()
90413351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu     * will set the current playing time to this value and continue playing from that point.
90513351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu     *
90613351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu     * @param playTime The time, in milliseconds, to which the animation is advanced or rewound.
90713351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu     *                 Unless the animation is reversing, the playtime is considered the time since
90813351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu     *                 the end of the start delay of the AnimatorSet in a forward playing direction.
90913351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu     *
91013351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu     */
91113351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu    public void setCurrentPlayTime(long playTime) {
91213351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu        if (mReversing && getTotalDuration() == DURATION_INFINITE) {
91313351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu            // Should never get here
91413351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu            throw new UnsupportedOperationException("Error: Cannot seek in reverse in an infinite"
91513351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu                    + " AnimatorSet");
91613351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu        }
91713351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu
91813351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu        if ((getTotalDuration() != DURATION_INFINITE && playTime > getTotalDuration() - mStartDelay)
91913351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu                || playTime < 0) {
92013351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu            throw new UnsupportedOperationException("Error: Play time should always be in between"
92113351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu                    + "0 and duration.");
92213351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu        }
92313351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu
92413351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu        initAnimation();
92513351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu
92613351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu        if (!isStarted()) {
92713351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu            if (mReversing) {
92813351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu                throw new UnsupportedOperationException("Error: Something went wrong. mReversing"
92913351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu                        + " should not be set when AnimatorSet is not started.");
93013351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu            }
93113351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu            if (!mSeekState.isActive()) {
93213351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu                findLatestEventIdForTime(0);
93313351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu                // Set all the values to start values.
93413351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu                initChildren();
93513351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu                skipToStartValue(mReversing);
93613351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu                mSeekState.setPlayTime(0, mReversing);
93713351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu            }
93813351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu            animateBasedOnPlayTime(playTime, 0, mReversing);
93913351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu            mSeekState.setPlayTime(playTime, mReversing);
94013351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu        } else {
94113351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu            // If the animation is running, just set the seek time and wait until the next frame
94213351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu            // (i.e. doAnimationFrame(...)) to advance the animation.
94313351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu            mSeekState.setPlayTime(playTime, mReversing);
94413351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu        }
94513351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu    }
94613351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu
947dd65ab04071fd80007155e8cb4b205370acf759bDoris Liu    /**
94802eabdff6b8d4a8c6152a4ef69c2839e66595512Doris Liu     * Returns the milliseconds elapsed since the start of the animation.
949dd65ab04071fd80007155e8cb4b205370acf759bDoris Liu     *
95002eabdff6b8d4a8c6152a4ef69c2839e66595512Doris Liu     * <p>For ongoing animations, this method returns the current progress of the animation in
95102eabdff6b8d4a8c6152a4ef69c2839e66595512Doris Liu     * terms of play time. For an animation that has not yet been started: if the animation has been
95202eabdff6b8d4a8c6152a4ef69c2839e66595512Doris Liu     * seeked to a certain time via {@link #setCurrentPlayTime(long)}, the seeked play time will
95302eabdff6b8d4a8c6152a4ef69c2839e66595512Doris Liu     * be returned; otherwise, this method will return 0.
95402eabdff6b8d4a8c6152a4ef69c2839e66595512Doris Liu     *
95502eabdff6b8d4a8c6152a4ef69c2839e66595512Doris Liu     * @return the current position in time of the animation in milliseconds
956dd65ab04071fd80007155e8cb4b205370acf759bDoris Liu     */
957dd65ab04071fd80007155e8cb4b205370acf759bDoris Liu    public long getCurrentPlayTime() {
958dd65ab04071fd80007155e8cb4b205370acf759bDoris Liu        if (mSeekState.isActive()) {
959dd65ab04071fd80007155e8cb4b205370acf759bDoris Liu            return mSeekState.getPlayTime();
960dd65ab04071fd80007155e8cb4b205370acf759bDoris Liu        }
961dd65ab04071fd80007155e8cb4b205370acf759bDoris Liu        if (mLastFrameTime == -1) {
962dd65ab04071fd80007155e8cb4b205370acf759bDoris Liu            // Not yet started or during start delay
963dd65ab04071fd80007155e8cb4b205370acf759bDoris Liu            return 0;
964dd65ab04071fd80007155e8cb4b205370acf759bDoris Liu        }
965dd65ab04071fd80007155e8cb4b205370acf759bDoris Liu        float durationScale = ValueAnimator.getDurationScale();
966dd65ab04071fd80007155e8cb4b205370acf759bDoris Liu        durationScale = durationScale == 0 ? 1 : durationScale;
967dd65ab04071fd80007155e8cb4b205370acf759bDoris Liu        if (mReversing) {
968dd65ab04071fd80007155e8cb4b205370acf759bDoris Liu            return (long) ((mLastFrameTime - mFirstFrame) / durationScale);
969dd65ab04071fd80007155e8cb4b205370acf759bDoris Liu        } else {
970dd65ab04071fd80007155e8cb4b205370acf759bDoris Liu            return (long) ((mLastFrameTime - mFirstFrame - mStartDelay) / durationScale);
971dd65ab04071fd80007155e8cb4b205370acf759bDoris Liu        }
972dd65ab04071fd80007155e8cb4b205370acf759bDoris Liu    }
973dd65ab04071fd80007155e8cb4b205370acf759bDoris Liu
97413351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu    private void initChildren() {
97513351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu        if (!isInitialized()) {
97613351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu            mChildrenInitialized = true;
97713351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu            // Forcefully initialize all children based on their end time, so that if the start
97813351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu            // value of a child is dependent on a previous animation, the animation will be
97913351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu            // initialized after the the previous animations have been advanced to the end.
98013351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu            skipToEndValue(false);
98113351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu        }
98213351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu    }
98313351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu
98413351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu    /**
98513351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu     * @param frameTime The frame start time, in the {@link SystemClock#uptimeMillis()} time
98613351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu     *                  base.
98713351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu     * @return
98813351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu     * @hide
98913351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu     */
99013351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu    @Override
99113351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu    public boolean doAnimationFrame(long frameTime) {
9926d4520920526cc44063ff2e665adb08e9a3c8a64Doris Liu        float durationScale = ValueAnimator.getDurationScale();
9936d4520920526cc44063ff2e665adb08e9a3c8a64Doris Liu        if (durationScale == 0f) {
99421ba77f6b6c037e9507e210e22c18614fa9d2e9bDoris Liu            // Duration scale is 0, end the animation right away.
9956d4520920526cc44063ff2e665adb08e9a3c8a64Doris Liu            forceToEnd();
9966d4520920526cc44063ff2e665adb08e9a3c8a64Doris Liu            return true;
9976d4520920526cc44063ff2e665adb08e9a3c8a64Doris Liu        }
9986d4520920526cc44063ff2e665adb08e9a3c8a64Doris Liu
9996d4520920526cc44063ff2e665adb08e9a3c8a64Doris Liu        // After the first frame comes in, we need to wait for start delay to pass before updating
10006d4520920526cc44063ff2e665adb08e9a3c8a64Doris Liu        // any animation values.
10016d4520920526cc44063ff2e665adb08e9a3c8a64Doris Liu        if (mFirstFrame < 0) {
10026d4520920526cc44063ff2e665adb08e9a3c8a64Doris Liu            mFirstFrame = frameTime;
100313351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu        }
100413351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu
100513351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu        // Handle pause/resume
100613351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu        if (mPaused) {
100713351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu            // Note: Child animations don't receive pause events. Since it's never a contract that
100813351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu            // the child animators will be paused when set is paused, this is unlikely to be an
100913351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu            // issue.
101013351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu            mPauseTime = frameTime;
101113351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu            removeAnimationCallback();
101213351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu            return false;
101313351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu        } else if (mPauseTime > 0) {
101413351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu                // Offset by the duration that the animation was paused
101513351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu            mFirstFrame += (frameTime - mPauseTime);
101613351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu            mPauseTime = -1;
101713351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu        }
101813351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu
101913351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu        // Continue at seeked position
102013351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu        if (mSeekState.isActive()) {
102113351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu            mSeekState.updateSeekDirection(mReversing);
10226d4520920526cc44063ff2e665adb08e9a3c8a64Doris Liu            if (mReversing) {
10236d4520920526cc44063ff2e665adb08e9a3c8a64Doris Liu                mFirstFrame = (long) (frameTime - mSeekState.getPlayTime() * durationScale);
10246d4520920526cc44063ff2e665adb08e9a3c8a64Doris Liu            } else {
10256d4520920526cc44063ff2e665adb08e9a3c8a64Doris Liu                mFirstFrame = (long) (frameTime - (mSeekState.getPlayTime() + mStartDelay)
10266d4520920526cc44063ff2e665adb08e9a3c8a64Doris Liu                        * durationScale);
10276d4520920526cc44063ff2e665adb08e9a3c8a64Doris Liu            }
102813351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu            mSeekState.reset();
102913351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu        }
103013351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu
10316d4520920526cc44063ff2e665adb08e9a3c8a64Doris Liu        if (!mReversing && frameTime < mFirstFrame + mStartDelay * durationScale) {
10326d4520920526cc44063ff2e665adb08e9a3c8a64Doris Liu            // Still during start delay in a forward playing case.
10336d4520920526cc44063ff2e665adb08e9a3c8a64Doris Liu            return false;
10346d4520920526cc44063ff2e665adb08e9a3c8a64Doris Liu        }
10356d4520920526cc44063ff2e665adb08e9a3c8a64Doris Liu
10366d4520920526cc44063ff2e665adb08e9a3c8a64Doris Liu        // From here on, we always use unscaled play time. Note this unscaled playtime includes
10376d4520920526cc44063ff2e665adb08e9a3c8a64Doris Liu        // the start delay.
10386d4520920526cc44063ff2e665adb08e9a3c8a64Doris Liu        long unscaledPlayTime = (long) ((frameTime - mFirstFrame) / durationScale);
10396d4520920526cc44063ff2e665adb08e9a3c8a64Doris Liu        mLastFrameTime = frameTime;
104013351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu
104113351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu        // 1. Pulse the animators that will start or end in this frame
104213351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu        // 2. Pulse the animators that will finish in a later frame
10436d4520920526cc44063ff2e665adb08e9a3c8a64Doris Liu        int latestId = findLatestEventIdForTime(unscaledPlayTime);
104413351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu        int startId = mLastEventId;
104513351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu
10466d4520920526cc44063ff2e665adb08e9a3c8a64Doris Liu        handleAnimationEvents(startId, latestId, unscaledPlayTime);
104713351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu
104813351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu        mLastEventId = latestId;
104913351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu
105013351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu        // Pump a frame to the on-going animators
105113351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu        for (int i = 0; i < mPlayingSet.size(); i++) {
105213351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu            Node node = mPlayingSet.get(i);
105313351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu            if (!node.mEnded) {
10546d4520920526cc44063ff2e665adb08e9a3c8a64Doris Liu                pulseFrame(node, getPlayTimeForNode(unscaledPlayTime, node));
105513351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu            }
105613351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu        }
105713351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu
105813351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu        // Remove all the finished anims
105913351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu        for (int i = mPlayingSet.size() - 1; i >= 0; i--) {
106013351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu            if (mPlayingSet.get(i).mEnded) {
106113351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu                mPlayingSet.remove(i);
106213351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu            }
106313351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu        }
106413351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu
10656d4520920526cc44063ff2e665adb08e9a3c8a64Doris Liu        boolean finished = false;
10666d4520920526cc44063ff2e665adb08e9a3c8a64Doris Liu        if (mReversing) {
10676d4520920526cc44063ff2e665adb08e9a3c8a64Doris Liu            if (mPlayingSet.size() == 1 && mPlayingSet.get(0) == mRootNode) {
10686d4520920526cc44063ff2e665adb08e9a3c8a64Doris Liu                // The only animation that is running is the delay animation.
10696d4520920526cc44063ff2e665adb08e9a3c8a64Doris Liu                finished = true;
10706d4520920526cc44063ff2e665adb08e9a3c8a64Doris Liu            } else if (mPlayingSet.isEmpty() && mLastEventId < 3) {
10716d4520920526cc44063ff2e665adb08e9a3c8a64Doris Liu                // The only remaining animation is the delay animation
10726d4520920526cc44063ff2e665adb08e9a3c8a64Doris Liu                finished = true;
107313351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu            }
10746d4520920526cc44063ff2e665adb08e9a3c8a64Doris Liu        } else {
10756d4520920526cc44063ff2e665adb08e9a3c8a64Doris Liu            finished = mPlayingSet.isEmpty() && mLastEventId == mEvents.size() - 1;
10766d4520920526cc44063ff2e665adb08e9a3c8a64Doris Liu        }
10776d4520920526cc44063ff2e665adb08e9a3c8a64Doris Liu
10786d4520920526cc44063ff2e665adb08e9a3c8a64Doris Liu        if (finished) {
10796d4520920526cc44063ff2e665adb08e9a3c8a64Doris Liu            endAnimation();
10806d4520920526cc44063ff2e665adb08e9a3c8a64Doris Liu            return true;
108113351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu        }
108213351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu        return false;
108313351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu    }
108413351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu
108513351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu    /**
10865c71b8cc4ae3e76ce7c7462fff9426c8e96ea5f7Doris Liu     * @hide
10875c71b8cc4ae3e76ce7c7462fff9426c8e96ea5f7Doris Liu     */
10885c71b8cc4ae3e76ce7c7462fff9426c8e96ea5f7Doris Liu    @Override
10895c71b8cc4ae3e76ce7c7462fff9426c8e96ea5f7Doris Liu    public void commitAnimationFrame(long frameTime) {
10905c71b8cc4ae3e76ce7c7462fff9426c8e96ea5f7Doris Liu        // No op.
10915c71b8cc4ae3e76ce7c7462fff9426c8e96ea5f7Doris Liu    }
10925c71b8cc4ae3e76ce7c7462fff9426c8e96ea5f7Doris Liu
10935c71b8cc4ae3e76ce7c7462fff9426c8e96ea5f7Doris Liu    @Override
10945c71b8cc4ae3e76ce7c7462fff9426c8e96ea5f7Doris Liu    boolean pulseAnimationFrame(long frameTime) {
10955c71b8cc4ae3e76ce7c7462fff9426c8e96ea5f7Doris Liu        return doAnimationFrame(frameTime);
10965c71b8cc4ae3e76ce7c7462fff9426c8e96ea5f7Doris Liu    }
10975c71b8cc4ae3e76ce7c7462fff9426c8e96ea5f7Doris Liu
10985c71b8cc4ae3e76ce7c7462fff9426c8e96ea5f7Doris Liu    /**
109913351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu     * When playing forward, we call start() at the animation's scheduled start time, and make sure
110013351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu     * to pump a frame at the animation's scheduled end time.
110113351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu     *
110213351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu     * When playing in reverse, we should reverse the animation when we hit animation's end event,
110313351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu     * and expect the animation to end at the its delay ended event, rather than start event.
110413351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu     */
110513351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu    private void handleAnimationEvents(int startId, int latestId, long playTime) {
110613351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu        if (mReversing) {
110713351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu            startId = startId == -1 ? mEvents.size() : startId;
110813351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu            for (int i = startId - 1; i >= latestId; i--) {
110913351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu                AnimationEvent event = mEvents.get(i);
111013351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu                Node node = event.mNode;
111113351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu                if (event.mEvent == AnimationEvent.ANIMATION_END) {
11125fb129c2abcf62ba09bff7322cad62adf9449887Doris Liu                    if (node.mAnimation.isStarted()) {
11135fb129c2abcf62ba09bff7322cad62adf9449887Doris Liu                        // If the animation has already been started before its due time (i.e.
11145fb129c2abcf62ba09bff7322cad62adf9449887Doris Liu                        // the child animator is being manipulated outside of the AnimatorSet), we
11155fb129c2abcf62ba09bff7322cad62adf9449887Doris Liu                        // need to cancel the animation to reset the internal state (e.g. frame
11165fb129c2abcf62ba09bff7322cad62adf9449887Doris Liu                        // time tracking) and remove the self pulsing callbacks
11175fb129c2abcf62ba09bff7322cad62adf9449887Doris Liu                        node.mAnimation.cancel();
11185fb129c2abcf62ba09bff7322cad62adf9449887Doris Liu                    }
11195fb129c2abcf62ba09bff7322cad62adf9449887Doris Liu                    node.mEnded = false;
112013351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu                    mPlayingSet.add(event.mNode);
112113351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu                    node.mAnimation.startWithoutPulsing(true);
11225c71b8cc4ae3e76ce7c7462fff9426c8e96ea5f7Doris Liu                    pulseFrame(node, 0);
112313351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu                } else if (event.mEvent == AnimationEvent.ANIMATION_DELAY_ENDED && !node.mEnded) {
112413351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu                    // end event:
11255c71b8cc4ae3e76ce7c7462fff9426c8e96ea5f7Doris Liu                    pulseFrame(node, getPlayTimeForNode(playTime, node));
112613351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu                }
112713351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu            }
112813351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu        } else {
112913351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu            for (int i = startId + 1; i <= latestId; i++) {
113013351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu                AnimationEvent event = mEvents.get(i);
113113351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu                Node node = event.mNode;
113213351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu                if (event.mEvent == AnimationEvent.ANIMATION_START) {
113313351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu                    mPlayingSet.add(event.mNode);
11345fb129c2abcf62ba09bff7322cad62adf9449887Doris Liu                    if (node.mAnimation.isStarted()) {
11355fb129c2abcf62ba09bff7322cad62adf9449887Doris Liu                        // If the animation has already been started before its due time (i.e.
11365fb129c2abcf62ba09bff7322cad62adf9449887Doris Liu                        // the child animator is being manipulated outside of the AnimatorSet), we
11375fb129c2abcf62ba09bff7322cad62adf9449887Doris Liu                        // need to cancel the animation to reset the internal state (e.g. frame
11385fb129c2abcf62ba09bff7322cad62adf9449887Doris Liu                        // time tracking) and remove the self pulsing callbacks
11395fb129c2abcf62ba09bff7322cad62adf9449887Doris Liu                        node.mAnimation.cancel();
11405fb129c2abcf62ba09bff7322cad62adf9449887Doris Liu                    }
11415fb129c2abcf62ba09bff7322cad62adf9449887Doris Liu                    node.mEnded = false;
114213351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu                    node.mAnimation.startWithoutPulsing(false);
11435c71b8cc4ae3e76ce7c7462fff9426c8e96ea5f7Doris Liu                    pulseFrame(node, 0);
114413351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu                } else if (event.mEvent == AnimationEvent.ANIMATION_END && !node.mEnded) {
114513351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu                    // start event:
11465c71b8cc4ae3e76ce7c7462fff9426c8e96ea5f7Doris Liu                    pulseFrame(node, getPlayTimeForNode(playTime, node));
114713351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu                }
114813351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu            }
114913351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu        }
115013351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu    }
115113351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu
11526d4520920526cc44063ff2e665adb08e9a3c8a64Doris Liu    /**
11536d4520920526cc44063ff2e665adb08e9a3c8a64Doris Liu     * This method pulses frames into child animations. It scales the input animation play time
11546d4520920526cc44063ff2e665adb08e9a3c8a64Doris Liu     * with the duration scale and pass that to the child animation via pulseAnimationFrame(long).
11556d4520920526cc44063ff2e665adb08e9a3c8a64Doris Liu     *
11566d4520920526cc44063ff2e665adb08e9a3c8a64Doris Liu     * @param node child animator node
11576d4520920526cc44063ff2e665adb08e9a3c8a64Doris Liu     * @param animPlayTime unscaled play time (including start delay) for the child animator
11586d4520920526cc44063ff2e665adb08e9a3c8a64Doris Liu     */
11596d4520920526cc44063ff2e665adb08e9a3c8a64Doris Liu    private void pulseFrame(Node node, long animPlayTime) {
11605c71b8cc4ae3e76ce7c7462fff9426c8e96ea5f7Doris Liu        if (!node.mEnded) {
1161d7968dc174f60e3b8f5aaddd05703cf6ce9114adDoris Liu            float durationScale = ValueAnimator.getDurationScale();
1162d7968dc174f60e3b8f5aaddd05703cf6ce9114adDoris Liu            durationScale = durationScale == 0  ? 1 : durationScale;
11636d4520920526cc44063ff2e665adb08e9a3c8a64Doris Liu            node.mEnded = node.mAnimation.pulseAnimationFrame(
1164d7968dc174f60e3b8f5aaddd05703cf6ce9114adDoris Liu                    (long) (animPlayTime * durationScale));
11655c71b8cc4ae3e76ce7c7462fff9426c8e96ea5f7Doris Liu        }
11665c71b8cc4ae3e76ce7c7462fff9426c8e96ea5f7Doris Liu    }
11675c71b8cc4ae3e76ce7c7462fff9426c8e96ea5f7Doris Liu
116813351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu    private long getPlayTimeForNode(long overallPlayTime, Node node) {
116913351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu        return getPlayTimeForNode(overallPlayTime, node, mReversing);
117013351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu    }
117113351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu
117213351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu    private long getPlayTimeForNode(long overallPlayTime, Node node, boolean inReverse) {
117313351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu        if (inReverse) {
117413351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu            overallPlayTime = getTotalDuration() - overallPlayTime;
117513351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu            return node.mEndTime - overallPlayTime;
117613351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu        } else {
117713351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu            return overallPlayTime - node.mStartTime;
117813351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu        }
117913351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu    }
118013351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu
118113351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu    private void startAnimation() {
118266c564e390ea71576db7d4c1803b169cc98e987bDoris Liu        addDummyListener();
118366c564e390ea71576db7d4c1803b169cc98e987bDoris Liu
118413351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu        // Register animation callback
11856d4520920526cc44063ff2e665adb08e9a3c8a64Doris Liu        addAnimationCallback(0);
118613351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu
118713351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu        if (mSeekState.getPlayTimeNormalized() == 0 && mReversing) {
118813351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu            // Maintain old behavior, if seeked to 0 then call reverse, we'll treat the case
118913351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu            // the same as no seeking at all.
119013351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu            mSeekState.reset();
119113351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu        }
119213351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu        // Set the child animators to the right end:
119313351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu        if (mShouldResetValuesAtStart) {
11946d4520920526cc44063ff2e665adb08e9a3c8a64Doris Liu            if (isInitialized()) {
11956d4520920526cc44063ff2e665adb08e9a3c8a64Doris Liu                skipToEndValue(!mReversing);
11966d4520920526cc44063ff2e665adb08e9a3c8a64Doris Liu            } else if (mReversing) {
11976d4520920526cc44063ff2e665adb08e9a3c8a64Doris Liu                // Reversing but haven't initialized all the children yet.
11986d4520920526cc44063ff2e665adb08e9a3c8a64Doris Liu                initChildren();
119913351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu                skipToEndValue(!mReversing);
120013351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu            } else {
120113351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu                // If not all children are initialized and play direction is forward
120213351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu                for (int i = mEvents.size() - 1; i >= 0; i--) {
120313351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu                    if (mEvents.get(i).mEvent == AnimationEvent.ANIMATION_DELAY_ENDED) {
120413351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu                        Animator anim = mEvents.get(i).mNode.mAnimation;
120513351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu                        // Only reset the animations that have been initialized to start value,
120613351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu                        // so that if they are defined without a start value, they will get the
120713351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu                        // values set at the right time (i.e. the next animation run)
120813351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu                        if (anim.isInitialized()) {
120913351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu                            anim.skipToEndValue(true);
121013351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu                        }
121113351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu                    }
121213351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu                }
121313351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu            }
121413351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu        }
121513351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu
121613351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu        if (mReversing || mStartDelay == 0 || mSeekState.isActive()) {
121713351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu            long playTime;
121813351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu            // If no delay, we need to call start on the first animations to be consistent with old
121913351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu            // behavior.
122013351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu            if (mSeekState.isActive()) {
122113351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu                mSeekState.updateSeekDirection(mReversing);
122213351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu                playTime = mSeekState.getPlayTime();
122313351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu            } else {
122413351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu                playTime = 0;
122513351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu            }
122613351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu            int toId = findLatestEventIdForTime(playTime);
122713351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu            handleAnimationEvents(-1, toId, playTime);
12286d4520920526cc44063ff2e665adb08e9a3c8a64Doris Liu            for (int i = mPlayingSet.size() - 1; i >= 0; i--) {
12296d4520920526cc44063ff2e665adb08e9a3c8a64Doris Liu                if (mPlayingSet.get(i).mEnded) {
12306d4520920526cc44063ff2e665adb08e9a3c8a64Doris Liu                    mPlayingSet.remove(i);
12316d4520920526cc44063ff2e665adb08e9a3c8a64Doris Liu                }
12326d4520920526cc44063ff2e665adb08e9a3c8a64Doris Liu            }
123313351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu            mLastEventId = toId;
123413351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu        }
123513351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu    }
123613351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu
123766c564e390ea71576db7d4c1803b169cc98e987bDoris Liu    // This is to work around the issue in b/34736819, as the old behavior in AnimatorSet had
123866c564e390ea71576db7d4c1803b169cc98e987bDoris Liu    // masked a real bug in play movies. TODO: remove this and below once the root cause is fixed.
123966c564e390ea71576db7d4c1803b169cc98e987bDoris Liu    private void addDummyListener() {
124066c564e390ea71576db7d4c1803b169cc98e987bDoris Liu        for (int i = 1; i < mNodes.size(); i++) {
124166c564e390ea71576db7d4c1803b169cc98e987bDoris Liu            mNodes.get(i).mAnimation.addListener(mDummyListener);
124266c564e390ea71576db7d4c1803b169cc98e987bDoris Liu        }
124366c564e390ea71576db7d4c1803b169cc98e987bDoris Liu    }
124466c564e390ea71576db7d4c1803b169cc98e987bDoris Liu
124566c564e390ea71576db7d4c1803b169cc98e987bDoris Liu    private void removeDummyListener() {
124666c564e390ea71576db7d4c1803b169cc98e987bDoris Liu        for (int i = 1; i < mNodes.size(); i++) {
124766c564e390ea71576db7d4c1803b169cc98e987bDoris Liu            mNodes.get(i).mAnimation.removeListener(mDummyListener);
124866c564e390ea71576db7d4c1803b169cc98e987bDoris Liu        }
124966c564e390ea71576db7d4c1803b169cc98e987bDoris Liu    }
125066c564e390ea71576db7d4c1803b169cc98e987bDoris Liu
125113351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu    private int findLatestEventIdForTime(long currentPlayTime) {
125213351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu        int size = mEvents.size();
125313351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu        int latestId = mLastEventId;
125413351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu        // Call start on the first animations now to be consistent with the old behavior
125513351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu        if (mReversing) {
125613351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu            currentPlayTime = getTotalDuration() - currentPlayTime;
125713351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu            mLastEventId = mLastEventId == -1 ? size : mLastEventId;
125813351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu            for (int j = mLastEventId - 1; j >= 0; j--) {
125913351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu                AnimationEvent event = mEvents.get(j);
126013351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu                if (event.getTime() >= currentPlayTime) {
126113351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu                    latestId = j;
126213351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu                }
126313351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu            }
126413351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu        } else {
126513351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu            for (int i = mLastEventId + 1; i < size; i++) {
126613351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu                AnimationEvent event = mEvents.get(i);
1267290271695d66cb6be38b1ebe80dc3473e6163738Doris Liu                // TODO: need a function that accounts for infinite duration to compare time
1268290271695d66cb6be38b1ebe80dc3473e6163738Doris Liu                if (event.getTime() != DURATION_INFINITE && event.getTime() <= currentPlayTime) {
126913351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu                    latestId = i;
127013351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu                }
127113351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu            }
127213351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu        }
127313351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu        return latestId;
127413351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu    }
127513351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu
127613351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu    private void endAnimation() {
127713351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu        mStarted = false;
127813351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu        mLastFrameTime = -1;
127913351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu        mFirstFrame = -1;
128013351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu        mLastEventId = -1;
128113351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu        mPaused = false;
128213351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu        mPauseTime = -1;
128313351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu        mSeekState.reset();
128413351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu        mPlayingSet.clear();
128513351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu
128613351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu        // No longer receive callbacks
128713351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu        removeAnimationCallback();
128813351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu        // Call end listener
128913351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu        if (mListeners != null) {
129013351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu            ArrayList<AnimatorListener> tmpListeners =
129113351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu                    (ArrayList<AnimatorListener>) mListeners.clone();
129213351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu            int numListeners = tmpListeners.size();
129313351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu            for (int i = 0; i < numListeners; ++i) {
129413351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu                tmpListeners.get(i).onAnimationEnd(this, mReversing);
129513351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu            }
129613351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu        }
129766c564e390ea71576db7d4c1803b169cc98e987bDoris Liu        removeDummyListener();
129813351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu        mSelfPulse = true;
129913351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu        mReversing = false;
130013351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu    }
130113351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu
130213351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu    private void removeAnimationCallback() {
130313351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu        if (!mSelfPulse) {
130413351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu            return;
130513351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu        }
130613351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu        AnimationHandler handler = AnimationHandler.getInstance();
130713351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu        handler.removeCallback(this);
130813351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu    }
130913351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu
131013351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu    private void addAnimationCallback(long delay) {
131113351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu        if (!mSelfPulse) {
131213351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu            return;
131313351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu        }
131413351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu        AnimationHandler handler = AnimationHandler.getInstance();
131513351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu        handler.addAnimationFrameCallback(this, delay);
13161309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu    }
13171309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu
131849afa5bc100e5d4c069fea980dd6b09501f56397Chet Haase    @Override
1319a18a86b43e40e3c15dcca0ae0148d641be9b25feChet Haase    public AnimatorSet clone() {
1320a18a86b43e40e3c15dcca0ae0148d641be9b25feChet Haase        final AnimatorSet anim = (AnimatorSet) super.clone();
132149afa5bc100e5d4c069fea980dd6b09501f56397Chet Haase        /*
132249afa5bc100e5d4c069fea980dd6b09501f56397Chet Haase         * The basic clone() operation copies all items. This doesn't work very well for
1323a18a86b43e40e3c15dcca0ae0148d641be9b25feChet Haase         * AnimatorSet, because it will copy references that need to be recreated and state
132449afa5bc100e5d4c069fea980dd6b09501f56397Chet Haase         * that may not apply. What we need to do now is put the clone in an uninitialized
132549afa5bc100e5d4c069fea980dd6b09501f56397Chet Haase         * state, with fresh, empty data structures. Then we will build up the nodes list
132649afa5bc100e5d4c069fea980dd6b09501f56397Chet Haase         * manually, as we clone each Node (and its animation). The clone will then be sorted,
132749afa5bc100e5d4c069fea980dd6b09501f56397Chet Haase         * and will populate any appropriate lists, when it is started.
132849afa5bc100e5d4c069fea980dd6b09501f56397Chet Haase         */
1329d422dc358f0100106dc07d7b903201eb9b043b11Yigit Boyar        final int nodeCount = mNodes.size();
13308b699792b677bd4dd8442b32641ac09d48fdd79cChet Haase        anim.mStarted = false;
133113351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu        anim.mLastFrameTime = -1;
133213351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu        anim.mFirstFrame = -1;
133313351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu        anim.mLastEventId = -1;
133413351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu        anim.mPaused = false;
133513351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu        anim.mPauseTime = -1;
133613351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu        anim.mSeekState = new SeekState();
133713351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu        anim.mSelfPulse = true;
133813351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu        anim.mPlayingSet = new ArrayList<Node>();
1339d7444427d9f44b6b7448d4c21edca866132c8b59Doris Liu        anim.mNodeMap = new ArrayMap<Animator, Node>();
1340d422dc358f0100106dc07d7b903201eb9b043b11Yigit Boyar        anim.mNodes = new ArrayList<Node>(nodeCount);
134113351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu        anim.mEvents = new ArrayList<AnimationEvent>();
13426ba5ed322c04f260c51d58f2e49c6189b672f2d3Doris Liu        anim.mDummyListener = new AnimatorListenerAdapter() {
13436ba5ed322c04f260c51d58f2e49c6189b672f2d3Doris Liu            @Override
13446ba5ed322c04f260c51d58f2e49c6189b672f2d3Doris Liu            public void onAnimationEnd(Animator animation) {
13456ba5ed322c04f260c51d58f2e49c6189b672f2d3Doris Liu                if (anim.mNodeMap.get(animation) == null) {
13466ba5ed322c04f260c51d58f2e49c6189b672f2d3Doris Liu                    throw new AndroidRuntimeException("Error: animation ended is not in the node"
13476ba5ed322c04f260c51d58f2e49c6189b672f2d3Doris Liu                            + " map");
13486ba5ed322c04f260c51d58f2e49c6189b672f2d3Doris Liu                }
13496ba5ed322c04f260c51d58f2e49c6189b672f2d3Doris Liu                anim.mNodeMap.get(animation).mEnded = true;
13506ba5ed322c04f260c51d58f2e49c6189b672f2d3Doris Liu
13516ba5ed322c04f260c51d58f2e49c6189b672f2d3Doris Liu            }
13526ba5ed322c04f260c51d58f2e49c6189b672f2d3Doris Liu        };
135313351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu        anim.mReversing = false;
135413351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu        anim.mDependencyDirty = true;
135549afa5bc100e5d4c069fea980dd6b09501f56397Chet Haase
135649afa5bc100e5d4c069fea980dd6b09501f56397Chet Haase        // Walk through the old nodes list, cloning each node and adding it to the new nodemap.
1357a18a86b43e40e3c15dcca0ae0148d641be9b25feChet Haase        // One problem is that the old node dependencies point to nodes in the old AnimatorSet.
135849afa5bc100e5d4c069fea980dd6b09501f56397Chet Haase        // We need to track the old/new nodes in order to reconstruct the dependencies in the clone.
1359d422dc358f0100106dc07d7b903201eb9b043b11Yigit Boyar
136056b2df05f56b2c7b82a50b46341d055f6d70a913Doris Liu        HashMap<Node, Node> clonesMap = new HashMap<>(nodeCount);
1361d422dc358f0100106dc07d7b903201eb9b043b11Yigit Boyar        for (int n = 0; n < nodeCount; n++) {
1362d422dc358f0100106dc07d7b903201eb9b043b11Yigit Boyar            final Node node = mNodes.get(n);
136349afa5bc100e5d4c069fea980dd6b09501f56397Chet Haase            Node nodeClone = node.clone();
1364c457547cbd26e754d126f4f3651b4a7b2a262799Doris Liu            // Remove the old internal listener from the cloned child
1365c457547cbd26e754d126f4f3651b4a7b2a262799Doris Liu            nodeClone.mAnimation.removeListener(mDummyListener);
136656b2df05f56b2c7b82a50b46341d055f6d70a913Doris Liu            clonesMap.put(node, nodeClone);
136749afa5bc100e5d4c069fea980dd6b09501f56397Chet Haase            anim.mNodes.add(nodeClone);
13681309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu            anim.mNodeMap.put(nodeClone.mAnimation, nodeClone);
136949afa5bc100e5d4c069fea980dd6b09501f56397Chet Haase        }
13701309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu
137156b2df05f56b2c7b82a50b46341d055f6d70a913Doris Liu        anim.mRootNode = clonesMap.get(mRootNode);
13721309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu        anim.mDelayAnim = (ValueAnimator) anim.mRootNode.mAnimation;
13731309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu
137449afa5bc100e5d4c069fea980dd6b09501f56397Chet Haase        // Now that we've cloned all of the nodes, we're ready to walk through their
137549afa5bc100e5d4c069fea980dd6b09501f56397Chet Haase        // dependencies, mapping the old dependencies to the new nodes
13761309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu        for (int i = 0; i < nodeCount; i++) {
13771309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu            Node node = mNodes.get(i);
13781309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu            // Update dependencies for node's clone
137956b2df05f56b2c7b82a50b46341d055f6d70a913Doris Liu            Node nodeClone = clonesMap.get(node);
138056b2df05f56b2c7b82a50b46341d055f6d70a913Doris Liu            nodeClone.mLatestParent = node.mLatestParent == null
138156b2df05f56b2c7b82a50b46341d055f6d70a913Doris Liu                    ? null : clonesMap.get(node.mLatestParent);
13821309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu            int size = node.mChildNodes == null ? 0 : node.mChildNodes.size();
13831309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu            for (int j = 0; j < size; j++) {
138456b2df05f56b2c7b82a50b46341d055f6d70a913Doris Liu                nodeClone.mChildNodes.set(j, clonesMap.get(node.mChildNodes.get(j)));
1385d422dc358f0100106dc07d7b903201eb9b043b11Yigit Boyar            }
13861309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu            size = node.mSiblings == null ? 0 : node.mSiblings.size();
13871309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu            for (int j = 0; j < size; j++) {
138856b2df05f56b2c7b82a50b46341d055f6d70a913Doris Liu                nodeClone.mSiblings.set(j, clonesMap.get(node.mSiblings.get(j)));
1389d422dc358f0100106dc07d7b903201eb9b043b11Yigit Boyar            }
13901309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu            size = node.mParents == null ? 0 : node.mParents.size();
13911309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu            for (int j = 0; j < size; j++) {
139256b2df05f56b2c7b82a50b46341d055f6d70a913Doris Liu                nodeClone.mParents.set(j, clonesMap.get(node.mParents.get(j)));
139349afa5bc100e5d4c069fea980dd6b09501f56397Chet Haase            }
139449afa5bc100e5d4c069fea980dd6b09501f56397Chet Haase        }
139549afa5bc100e5d4c069fea980dd6b09501f56397Chet Haase        return anim;
139649afa5bc100e5d4c069fea980dd6b09501f56397Chet Haase    }
139749afa5bc100e5d4c069fea980dd6b09501f56397Chet Haase
139817fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase
139917fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase    /**
1400c4bb185d41cfb960ed9a3178a4f8974c351abdb0Doris Liu     * AnimatorSet is only reversible when the set contains no sequential animation, and no child
1401c4bb185d41cfb960ed9a3178a4f8974c351abdb0Doris Liu     * animators have a start delay.
14027bc6a3f023ca3e1dde91fc97b6036dee3ba538a2ztenghui     * @hide
14037bc6a3f023ca3e1dde91fc97b6036dee3ba538a2ztenghui     */
14047bc6a3f023ca3e1dde91fc97b6036dee3ba538a2ztenghui    @Override
14057bc6a3f023ca3e1dde91fc97b6036dee3ba538a2ztenghui    public boolean canReverse() {
140613351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu        return getTotalDuration() != DURATION_INFINITE;
14077bc6a3f023ca3e1dde91fc97b6036dee3ba538a2ztenghui    }
14087bc6a3f023ca3e1dde91fc97b6036dee3ba538a2ztenghui
14097bc6a3f023ca3e1dde91fc97b6036dee3ba538a2ztenghui    /**
141013351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu     * Plays the AnimatorSet in reverse. If the animation has been seeked to a specific play time
141113351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu     * using {@link #setCurrentPlayTime(long)}, it will play backwards from the point seeked when
141213351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu     * reverse was called. Otherwise, then it will start from the end and play backwards. This
141313351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu     * behavior is only set for the current animation; future playing of the animation will use the
141413351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu     * default behavior of playing forward.
141513351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu     * <p>
141613351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu     * Note: reverse is not supported for infinite AnimatorSet.
14177bc6a3f023ca3e1dde91fc97b6036dee3ba538a2ztenghui     */
14187bc6a3f023ca3e1dde91fc97b6036dee3ba538a2ztenghui    @Override
14197bc6a3f023ca3e1dde91fc97b6036dee3ba538a2ztenghui    public void reverse() {
142013351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu        start(true, true);
14217bc6a3f023ca3e1dde91fc97b6036dee3ba538a2ztenghui    }
14227bc6a3f023ca3e1dde91fc97b6036dee3ba538a2ztenghui
1423d430753cba09acb07af8b313286f247c78a41a32Chet Haase    @Override
1424d430753cba09acb07af8b313286f247c78a41a32Chet Haase    public String toString() {
1425d430753cba09acb07af8b313286f247c78a41a32Chet Haase        String returnVal = "AnimatorSet@" + Integer.toHexString(hashCode()) + "{";
14261309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu        int size = mNodes.size();
14271309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu        for (int i = 0; i < size; i++) {
14281309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu            Node node = mNodes.get(i);
14291309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu            returnVal += "\n    " + node.mAnimation.toString();
1430d430753cba09acb07af8b313286f247c78a41a32Chet Haase        }
1431d430753cba09acb07af8b313286f247c78a41a32Chet Haase        return returnVal + "\n}";
1432d430753cba09acb07af8b313286f247c78a41a32Chet Haase    }
1433d430753cba09acb07af8b313286f247c78a41a32Chet Haase
14341309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu    private void printChildCount() {
14351309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu        // Print out the child count through a level traverse.
14361309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu        ArrayList<Node> list = new ArrayList<>(mNodes.size());
14371309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu        list.add(mRootNode);
14381309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu        Log.d(TAG, "Current tree: ");
14391309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu        int index = 0;
14401309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu        while (index < list.size()) {
14411309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu            int listSize = list.size();
14421309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu            StringBuilder builder = new StringBuilder();
14431309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu            for (; index < listSize; index++) {
14441309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu                Node node = list.get(index);
14451309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu                int num = 0;
14461309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu                if (node.mChildNodes != null) {
14471309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu                    for (int i = 0; i < node.mChildNodes.size(); i++) {
14481309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu                        Node child = node.mChildNodes.get(i);
14491309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu                        if (child.mLatestParent == node) {
14501309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu                            num++;
14511309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu                            list.add(child);
14521309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu                        }
14531309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu                    }
14541309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu                }
14551309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu                builder.append(" ");
14561309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu                builder.append(num);
14571309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu            }
14581309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu            Log.d(TAG, builder.toString());
14591309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu        }
14601309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu    }
14611309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu
14621309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu    private void createDependencyGraph() {
14631309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu        if (!mDependencyDirty) {
146449db4242b134ffee79392670f9e434f6e3c7cec7Doris Liu            // Check whether any duration of the child animations has changed
146549db4242b134ffee79392670f9e434f6e3c7cec7Doris Liu            boolean durationChanged = false;
146649db4242b134ffee79392670f9e434f6e3c7cec7Doris Liu            for (int i = 0; i < mNodes.size(); i++) {
146749db4242b134ffee79392670f9e434f6e3c7cec7Doris Liu                Animator anim = mNodes.get(i).mAnimation;
146849db4242b134ffee79392670f9e434f6e3c7cec7Doris Liu                if (mNodes.get(i).mTotalDuration != anim.getTotalDuration()) {
146949db4242b134ffee79392670f9e434f6e3c7cec7Doris Liu                    durationChanged = true;
147049db4242b134ffee79392670f9e434f6e3c7cec7Doris Liu                    break;
147149db4242b134ffee79392670f9e434f6e3c7cec7Doris Liu                }
147249db4242b134ffee79392670f9e434f6e3c7cec7Doris Liu            }
147349db4242b134ffee79392670f9e434f6e3c7cec7Doris Liu            if (!durationChanged) {
147449db4242b134ffee79392670f9e434f6e3c7cec7Doris Liu                return;
147549db4242b134ffee79392670f9e434f6e3c7cec7Doris Liu            }
14761309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu        }
14771309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu
14781309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu        mDependencyDirty = false;
14791309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu        // Traverse all the siblings and make sure they have all the parents
14801309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu        int size = mNodes.size();
14811309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu        for (int i = 0; i < size; i++) {
14821309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu            mNodes.get(i).mParentsAdded = false;
14831309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu        }
14841309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu        for (int i = 0; i < size; i++) {
14851309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu            Node node = mNodes.get(i);
14861309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu            if (node.mParentsAdded) {
14871309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu                continue;
14881309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu            }
14891309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu
14901309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu            node.mParentsAdded = true;
14911309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu            if (node.mSiblings == null) {
14921309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu                continue;
14931309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu            }
14941309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu
14951309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu            // Find all the siblings
14961309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu            findSiblings(node, node.mSiblings);
14971309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu            node.mSiblings.remove(node);
14981309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu
14991309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu            // Get parents from all siblings
15001309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu            int siblingSize = node.mSiblings.size();
15011309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu            for (int j = 0; j < siblingSize; j++) {
15021309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu                node.addParents(node.mSiblings.get(j).mParents);
15031309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu            }
15041309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu
15051309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu            // Now make sure all siblings share the same set of parents
15061309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu            for (int j = 0; j < siblingSize; j++) {
15071309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu                Node sibling = node.mSiblings.get(j);
15081309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu                sibling.addParents(node.mParents);
15091309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu                sibling.mParentsAdded = true;
15101309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu            }
15111309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu        }
15121309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu
15131309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu        for (int i = 0; i < size; i++) {
15141309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu            Node node = mNodes.get(i);
15151309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu            if (node != mRootNode && node.mParents == null) {
15161309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu                node.addParent(mRootNode);
15171309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu            }
15181309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu        }
15191309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu
15201309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu        // Do a DFS on the tree
15211309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu        ArrayList<Node> visited = new ArrayList<Node>(mNodes.size());
15221309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu        // Assign start/end time
15231309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu        mRootNode.mStartTime = 0;
15241309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu        mRootNode.mEndTime = mDelayAnim.getDuration();
15251309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu        updatePlayTime(mRootNode, visited);
15261309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu
152713351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu        sortAnimationEvents();
152813351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu        mTotalDuration = mEvents.get(mEvents.size() - 1).getTime();
152913351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu    }
153013351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu
153113351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu    private void sortAnimationEvents() {
153213351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu        // Sort the list of events in ascending order of their time
153313351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu        // Create the list including the delay animation.
153413351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu        mEvents.clear();
153540e8469789c197e36b3d008039c7c2842ab2bfc5Doris Liu        for (int i = 1; i < mNodes.size(); i++) {
15361309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu            Node node = mNodes.get(i);
153713351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu            mEvents.add(new AnimationEvent(node, AnimationEvent.ANIMATION_START));
153813351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu            mEvents.add(new AnimationEvent(node, AnimationEvent.ANIMATION_DELAY_ENDED));
153913351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu            mEvents.add(new AnimationEvent(node, AnimationEvent.ANIMATION_END));
154013351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu        }
154113351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu        mEvents.sort(new Comparator<AnimationEvent>() {
154213351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu            @Override
154313351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu            public int compare(AnimationEvent e1, AnimationEvent e2) {
154413351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu                long t1 = e1.getTime();
154513351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu                long t2 = e2.getTime();
154613351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu                if (t1 == t2) {
154740e8469789c197e36b3d008039c7c2842ab2bfc5Doris Liu                    // For events that happen at the same time, we need them to be in the sequence
154840e8469789c197e36b3d008039c7c2842ab2bfc5Doris Liu                    // (end, start, start delay ended)
154940e8469789c197e36b3d008039c7c2842ab2bfc5Doris Liu                    if (e2.mEvent + e1.mEvent == AnimationEvent.ANIMATION_START
155040e8469789c197e36b3d008039c7c2842ab2bfc5Doris Liu                            + AnimationEvent.ANIMATION_DELAY_ENDED) {
155140e8469789c197e36b3d008039c7c2842ab2bfc5Doris Liu                        // Ensure start delay happens after start
155213351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu                        return e1.mEvent - e2.mEvent;
155340e8469789c197e36b3d008039c7c2842ab2bfc5Doris Liu                    } else {
155440e8469789c197e36b3d008039c7c2842ab2bfc5Doris Liu                        return e2.mEvent - e1.mEvent;
155513351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu                    }
155613351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu                }
155713351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu                if (t2 == DURATION_INFINITE) {
155813351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu                    return -1;
155913351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu                }
156013351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu                if (t1 == DURATION_INFINITE) {
156113351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu                    return 1;
156213351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu                }
156313351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu                // When neither event happens at INFINITE time:
156413351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu                return (int) (t1 - t2);
15651309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu            }
156613351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu        });
156713351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu
156840e8469789c197e36b3d008039c7c2842ab2bfc5Doris Liu        int eventSize = mEvents.size();
156940e8469789c197e36b3d008039c7c2842ab2bfc5Doris Liu        // For the same animation, start event has to happen before end.
157040e8469789c197e36b3d008039c7c2842ab2bfc5Doris Liu        for (int i = 0; i < eventSize;) {
157140e8469789c197e36b3d008039c7c2842ab2bfc5Doris Liu            AnimationEvent event = mEvents.get(i);
157240e8469789c197e36b3d008039c7c2842ab2bfc5Doris Liu            if (event.mEvent == AnimationEvent.ANIMATION_END) {
157340e8469789c197e36b3d008039c7c2842ab2bfc5Doris Liu                boolean needToSwapStart;
157440e8469789c197e36b3d008039c7c2842ab2bfc5Doris Liu                if (event.mNode.mStartTime == event.mNode.mEndTime) {
157540e8469789c197e36b3d008039c7c2842ab2bfc5Doris Liu                    needToSwapStart = true;
157640e8469789c197e36b3d008039c7c2842ab2bfc5Doris Liu                } else if (event.mNode.mEndTime == event.mNode.mStartTime
157740e8469789c197e36b3d008039c7c2842ab2bfc5Doris Liu                        + event.mNode.mAnimation.getStartDelay()) {
157840e8469789c197e36b3d008039c7c2842ab2bfc5Doris Liu                    // Swapping start delay
157940e8469789c197e36b3d008039c7c2842ab2bfc5Doris Liu                    needToSwapStart = false;
158040e8469789c197e36b3d008039c7c2842ab2bfc5Doris Liu                } else {
158140e8469789c197e36b3d008039c7c2842ab2bfc5Doris Liu                    i++;
158240e8469789c197e36b3d008039c7c2842ab2bfc5Doris Liu                    continue;
158340e8469789c197e36b3d008039c7c2842ab2bfc5Doris Liu                }
158440e8469789c197e36b3d008039c7c2842ab2bfc5Doris Liu
158540e8469789c197e36b3d008039c7c2842ab2bfc5Doris Liu                int startEventId = eventSize;
158640e8469789c197e36b3d008039c7c2842ab2bfc5Doris Liu                int startDelayEndId = eventSize;
158740e8469789c197e36b3d008039c7c2842ab2bfc5Doris Liu                for (int j = i + 1; j < eventSize; j++) {
158840e8469789c197e36b3d008039c7c2842ab2bfc5Doris Liu                    if (startEventId < eventSize && startDelayEndId < eventSize) {
158940e8469789c197e36b3d008039c7c2842ab2bfc5Doris Liu                        break;
159040e8469789c197e36b3d008039c7c2842ab2bfc5Doris Liu                    }
159140e8469789c197e36b3d008039c7c2842ab2bfc5Doris Liu                    if (mEvents.get(j).mNode == event.mNode) {
159240e8469789c197e36b3d008039c7c2842ab2bfc5Doris Liu                        if (mEvents.get(j).mEvent == AnimationEvent.ANIMATION_START) {
159340e8469789c197e36b3d008039c7c2842ab2bfc5Doris Liu                            // Found start event
159440e8469789c197e36b3d008039c7c2842ab2bfc5Doris Liu                            startEventId = j;
159540e8469789c197e36b3d008039c7c2842ab2bfc5Doris Liu                        } else if (mEvents.get(j).mEvent == AnimationEvent.ANIMATION_DELAY_ENDED) {
159640e8469789c197e36b3d008039c7c2842ab2bfc5Doris Liu                            startDelayEndId = j;
159740e8469789c197e36b3d008039c7c2842ab2bfc5Doris Liu                        }
159840e8469789c197e36b3d008039c7c2842ab2bfc5Doris Liu                    }
159940e8469789c197e36b3d008039c7c2842ab2bfc5Doris Liu
160040e8469789c197e36b3d008039c7c2842ab2bfc5Doris Liu                }
160140e8469789c197e36b3d008039c7c2842ab2bfc5Doris Liu                if (needToSwapStart && startEventId == mEvents.size()) {
160240e8469789c197e36b3d008039c7c2842ab2bfc5Doris Liu                    throw new UnsupportedOperationException("Something went wrong, no start is"
160340e8469789c197e36b3d008039c7c2842ab2bfc5Doris Liu                            + "found after stop for an animation that has the same start and end"
160440e8469789c197e36b3d008039c7c2842ab2bfc5Doris Liu                            + "time.");
160540e8469789c197e36b3d008039c7c2842ab2bfc5Doris Liu
160640e8469789c197e36b3d008039c7c2842ab2bfc5Doris Liu                }
160740e8469789c197e36b3d008039c7c2842ab2bfc5Doris Liu                if (startDelayEndId == mEvents.size()) {
160840e8469789c197e36b3d008039c7c2842ab2bfc5Doris Liu                    throw new UnsupportedOperationException("Something went wrong, no start"
160940e8469789c197e36b3d008039c7c2842ab2bfc5Doris Liu                            + "delay end is found after stop for an animation");
161040e8469789c197e36b3d008039c7c2842ab2bfc5Doris Liu
161140e8469789c197e36b3d008039c7c2842ab2bfc5Doris Liu                }
161240e8469789c197e36b3d008039c7c2842ab2bfc5Doris Liu
161340e8469789c197e36b3d008039c7c2842ab2bfc5Doris Liu                // We need to make sure start is inserted before start delay ended event,
161440e8469789c197e36b3d008039c7c2842ab2bfc5Doris Liu                // because otherwise inserting start delay ended events first would change
161540e8469789c197e36b3d008039c7c2842ab2bfc5Doris Liu                // the start event index.
161640e8469789c197e36b3d008039c7c2842ab2bfc5Doris Liu                if (needToSwapStart) {
161740e8469789c197e36b3d008039c7c2842ab2bfc5Doris Liu                    AnimationEvent startEvent = mEvents.remove(startEventId);
161840e8469789c197e36b3d008039c7c2842ab2bfc5Doris Liu                    mEvents.add(i, startEvent);
161940e8469789c197e36b3d008039c7c2842ab2bfc5Doris Liu                    i++;
162040e8469789c197e36b3d008039c7c2842ab2bfc5Doris Liu                }
162140e8469789c197e36b3d008039c7c2842ab2bfc5Doris Liu
162240e8469789c197e36b3d008039c7c2842ab2bfc5Doris Liu                AnimationEvent startDelayEndEvent = mEvents.remove(startDelayEndId);
162340e8469789c197e36b3d008039c7c2842ab2bfc5Doris Liu                mEvents.add(i, startDelayEndEvent);
162440e8469789c197e36b3d008039c7c2842ab2bfc5Doris Liu                i += 2;
162540e8469789c197e36b3d008039c7c2842ab2bfc5Doris Liu            } else {
162640e8469789c197e36b3d008039c7c2842ab2bfc5Doris Liu                i++;
162740e8469789c197e36b3d008039c7c2842ab2bfc5Doris Liu            }
162813351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu        }
162940e8469789c197e36b3d008039c7c2842ab2bfc5Doris Liu
163040e8469789c197e36b3d008039c7c2842ab2bfc5Doris Liu        if (!mEvents.isEmpty() && mEvents.get(0).mEvent != AnimationEvent.ANIMATION_START) {
163113351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu            throw new UnsupportedOperationException(
163240e8469789c197e36b3d008039c7c2842ab2bfc5Doris Liu                    "Sorting went bad, the start event should always be at index 0");
163313351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu        }
163440e8469789c197e36b3d008039c7c2842ab2bfc5Doris Liu
163540e8469789c197e36b3d008039c7c2842ab2bfc5Doris Liu        // Add AnimatorSet's start delay node to the beginning
163640e8469789c197e36b3d008039c7c2842ab2bfc5Doris Liu        mEvents.add(0, new AnimationEvent(mRootNode, AnimationEvent.ANIMATION_START));
163740e8469789c197e36b3d008039c7c2842ab2bfc5Doris Liu        mEvents.add(1, new AnimationEvent(mRootNode, AnimationEvent.ANIMATION_DELAY_ENDED));
163840e8469789c197e36b3d008039c7c2842ab2bfc5Doris Liu        mEvents.add(2, new AnimationEvent(mRootNode, AnimationEvent.ANIMATION_END));
163940e8469789c197e36b3d008039c7c2842ab2bfc5Doris Liu
164040e8469789c197e36b3d008039c7c2842ab2bfc5Doris Liu        if (mEvents.get(mEvents.size() - 1).mEvent == AnimationEvent.ANIMATION_START
164140e8469789c197e36b3d008039c7c2842ab2bfc5Doris Liu                || mEvents.get(mEvents.size() - 1).mEvent == AnimationEvent.ANIMATION_DELAY_ENDED) {
164213351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu            throw new UnsupportedOperationException(
164340e8469789c197e36b3d008039c7c2842ab2bfc5Doris Liu                    "Something went wrong, the last event is not an end event");
16441309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu        }
16451309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu    }
16461309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu
16477bc6a3f023ca3e1dde91fc97b6036dee3ba538a2ztenghui    /**
16481309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu     * Based on parent's start/end time, calculate children's start/end time. If cycle exists in
16491309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu     * the graph, all the nodes on the cycle will be marked to start at {@link #DURATION_INFINITE},
16501309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu     * meaning they will ever play.
165117fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase     */
16521309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu    private void updatePlayTime(Node parent,  ArrayList<Node> visited) {
16531309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu        if (parent.mChildNodes == null) {
165449db4242b134ffee79392670f9e434f6e3c7cec7Doris Liu            if (parent == mRootNode) {
165549db4242b134ffee79392670f9e434f6e3c7cec7Doris Liu                // All the animators are in a cycle
165649db4242b134ffee79392670f9e434f6e3c7cec7Doris Liu                for (int i = 0; i < mNodes.size(); i++) {
165749db4242b134ffee79392670f9e434f6e3c7cec7Doris Liu                    Node node = mNodes.get(i);
165849db4242b134ffee79392670f9e434f6e3c7cec7Doris Liu                    if (node != mRootNode) {
165949db4242b134ffee79392670f9e434f6e3c7cec7Doris Liu                        node.mStartTime = DURATION_INFINITE;
166049db4242b134ffee79392670f9e434f6e3c7cec7Doris Liu                        node.mEndTime = DURATION_INFINITE;
166149db4242b134ffee79392670f9e434f6e3c7cec7Doris Liu                    }
166249db4242b134ffee79392670f9e434f6e3c7cec7Doris Liu                }
166349db4242b134ffee79392670f9e434f6e3c7cec7Doris Liu            }
16641309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu            return;
16651309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu        }
166617fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase
16671309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu        visited.add(parent);
16681309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu        int childrenSize = parent.mChildNodes.size();
16691309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu        for (int i = 0; i < childrenSize; i++) {
16701309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu            Node child = parent.mChildNodes.get(i);
16717fb80f7d277bcde8a685088b97f52a39be567c7cPhilip Quinn            child.mTotalDuration = child.mAnimation.getTotalDuration();  // Update cached duration.
16727fb80f7d277bcde8a685088b97f52a39be567c7cPhilip Quinn
16731309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu            int index = visited.indexOf(child);
16741309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu            if (index >= 0) {
16751309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu                // Child has been visited, cycle found. Mark all the nodes in the cycle.
167649db4242b134ffee79392670f9e434f6e3c7cec7Doris Liu                for (int j = index; j < visited.size(); j++) {
16771309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu                    visited.get(j).mLatestParent = null;
16781309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu                    visited.get(j).mStartTime = DURATION_INFINITE;
16791309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu                    visited.get(j).mEndTime = DURATION_INFINITE;
16801309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu                }
16811309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu                child.mStartTime = DURATION_INFINITE;
16821309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu                child.mEndTime = DURATION_INFINITE;
16831309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu                child.mLatestParent = null;
16841309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu                Log.w(TAG, "Cycle found in AnimatorSet: " + this);
16851309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu                continue;
16861309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu            }
168717fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase
16881309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu            if (child.mStartTime != DURATION_INFINITE) {
16891309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu                if (parent.mEndTime == DURATION_INFINITE) {
16901309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu                    child.mLatestParent = parent;
16911309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu                    child.mStartTime = DURATION_INFINITE;
16921309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu                    child.mEndTime = DURATION_INFINITE;
16931309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu                } else {
16941309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu                    if (parent.mEndTime >= child.mStartTime) {
16951309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu                        child.mLatestParent = parent;
16961309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu                        child.mStartTime = parent.mEndTime;
16971309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu                    }
16981309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu
16997fb80f7d277bcde8a685088b97f52a39be567c7cPhilip Quinn                    child.mEndTime = child.mTotalDuration == DURATION_INFINITE
17007fb80f7d277bcde8a685088b97f52a39be567c7cPhilip Quinn                            ? DURATION_INFINITE : child.mStartTime + child.mTotalDuration;
17011309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu                }
17021309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu            }
17031309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu            updatePlayTime(child, visited);
17041309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu        }
17051309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu        visited.remove(parent);
17061309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu    }
17071309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu
17081309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu    // Recursively find all the siblings
17091309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu    private void findSiblings(Node node, ArrayList<Node> siblings) {
17101309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu        if (!siblings.contains(node)) {
17111309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu            siblings.add(node);
17121309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu            if (node.mSiblings == null) {
17131309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu                return;
17141309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu            }
17151309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu            for (int i = 0; i < node.mSiblings.size(); i++) {
17161309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu                findSiblings(node.mSiblings.get(i), siblings);
17171309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu            }
17181309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu        }
17191309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu    }
17201309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu
1721766431aa57c16ece8842287a92b2e7208e3b8ac3Doris Liu    /**
1722766431aa57c16ece8842287a92b2e7208e3b8ac3Doris Liu     * @hide
1723766431aa57c16ece8842287a92b2e7208e3b8ac3Doris Liu     * TODO: For animatorSet defined in XML, we can use a flag to indicate what the play order
1724c470466d7c89b55d8c5a13d79076fa2f8d624da1Doris Liu     * if defined (i.e. sequential or together), then we can use the flag instead of calculating
1725c470466d7c89b55d8c5a13d79076fa2f8d624da1Doris Liu     * dynamically. Note that when AnimatorSet is empty this method returns true.
1726766431aa57c16ece8842287a92b2e7208e3b8ac3Doris Liu     * @return whether all the animators in the set are supposed to play together
1727766431aa57c16ece8842287a92b2e7208e3b8ac3Doris Liu     */
1728766431aa57c16ece8842287a92b2e7208e3b8ac3Doris Liu    public boolean shouldPlayTogether() {
1729766431aa57c16ece8842287a92b2e7208e3b8ac3Doris Liu        updateAnimatorsDuration();
1730766431aa57c16ece8842287a92b2e7208e3b8ac3Doris Liu        createDependencyGraph();
1731766431aa57c16ece8842287a92b2e7208e3b8ac3Doris Liu        // All the child nodes are set out to play right after the delay animation
1732c470466d7c89b55d8c5a13d79076fa2f8d624da1Doris Liu        return mRootNode.mChildNodes == null || mRootNode.mChildNodes.size() == mNodes.size() - 1;
1733766431aa57c16ece8842287a92b2e7208e3b8ac3Doris Liu    }
1734766431aa57c16ece8842287a92b2e7208e3b8ac3Doris Liu
17351309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu    @Override
17361309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu    public long getTotalDuration() {
17371309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu        updateAnimatorsDuration();
17381309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu        createDependencyGraph();
17391309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu        return mTotalDuration;
17401309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu    }
174117fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase
17421309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu    private Node getNodeForAnimation(Animator anim) {
17431309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu        Node node = mNodeMap.get(anim);
17441309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu        if (node == null) {
17451309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu            node = new Node(anim);
17461309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu            mNodeMap.put(anim, node);
17471309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu            mNodes.add(node);
174817fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase        }
17491309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu        return node;
175017fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase    }
175117fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase
175217fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase    /**
1753a18a86b43e40e3c15dcca0ae0148d641be9b25feChet Haase     * A Node is an embodiment of both the Animator that it wraps as well as
175417fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase     * any dependencies that are associated with that Animation. This includes
175517fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase     * both dependencies upon other nodes (in the dependencies list) as
175617fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase     * well as dependencies of other nodes upon this (in the nodeDependents list).
175717fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase     */
175849afa5bc100e5d4c069fea980dd6b09501f56397Chet Haase    private static class Node implements Cloneable {
17591309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu        Animator mAnimation;
176017fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase
176117fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase        /**
17621309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu         * Child nodes are the nodes associated with animations that will be played immediately
17631309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu         * after current node.
176417fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase         */
17651309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu        ArrayList<Node> mChildNodes = null;
176617fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase
176717fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase        /**
17681309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu         * Flag indicating whether the animation in this node is finished. This flag
17691309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu         * is used by AnimatorSet to check, as each animation ends, whether all child animations
17701309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu         * are mEnded and it's time to send out an end event for the entire AnimatorSet.
177117fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase         */
17721309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu        boolean mEnded = false;
177317fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase
177417fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase        /**
17751309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu         * Nodes with animations that are defined to play simultaneously with the animation
17761309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu         * associated with this current node.
177717fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase         */
17781309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu        ArrayList<Node> mSiblings;
177917fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase
178017fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase        /**
17811309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu         * Parent nodes are the nodes with animations preceding current node's animation. Parent
17821309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu         * nodes here are derived from user defined animation sequence.
17831e0ac5a1063d00670f4dc7ca5b120ef2836f9e8fChet Haase         */
17841309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu        ArrayList<Node> mParents;
17851e0ac5a1063d00670f4dc7ca5b120ef2836f9e8fChet Haase
17861e0ac5a1063d00670f4dc7ca5b120ef2836f9e8fChet Haase        /**
17871309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu         * Latest parent is the parent node associated with a animation that finishes after all
17881309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu         * the other parents' animations.
1789d422dc358f0100106dc07d7b903201eb9b043b11Yigit Boyar         */
17901309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu        Node mLatestParent = null;
17911309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu
17921309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu        boolean mParentsAdded = false;
17931309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu        long mStartTime = 0;
17941309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu        long mEndTime = 0;
179549db4242b134ffee79392670f9e434f6e3c7cec7Doris Liu        long mTotalDuration = 0;
1796d422dc358f0100106dc07d7b903201eb9b043b11Yigit Boyar
1797d422dc358f0100106dc07d7b903201eb9b043b11Yigit Boyar        /**
179817fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase         * Constructs the Node with the animation that it encapsulates. A Node has no
179917fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase         * dependencies by default; dependencies are added via the addDependency()
180017fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase         * method.
180117fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase         *
180217fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase         * @param animation The animation that the Node encapsulates.
180317fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase         */
1804a18a86b43e40e3c15dcca0ae0148d641be9b25feChet Haase        public Node(Animator animation) {
18051309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu            this.mAnimation = animation;
180617fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase        }
180749afa5bc100e5d4c069fea980dd6b09501f56397Chet Haase
180849afa5bc100e5d4c069fea980dd6b09501f56397Chet Haase        @Override
180921cd1389d2ef218b20994b617c57af120841a57fChet Haase        public Node clone() {
181021cd1389d2ef218b20994b617c57af120841a57fChet Haase            try {
181121cd1389d2ef218b20994b617c57af120841a57fChet Haase                Node node = (Node) super.clone();
18121309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu                node.mAnimation = mAnimation.clone();
18131309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu                if (mChildNodes != null) {
18141309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu                    node.mChildNodes = new ArrayList<>(mChildNodes);
18151309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu                }
18165e66616c04e4a54df7e8783bc7ffe6c22bcb3040Doris Liu                if (mSiblings != null) {
18175e66616c04e4a54df7e8783bc7ffe6c22bcb3040Doris Liu                    node.mSiblings = new ArrayList<>(mSiblings);
18185e66616c04e4a54df7e8783bc7ffe6c22bcb3040Doris Liu                }
18195e66616c04e4a54df7e8783bc7ffe6c22bcb3040Doris Liu                if (mParents != null) {
18205e66616c04e4a54df7e8783bc7ffe6c22bcb3040Doris Liu                    node.mParents = new ArrayList<>(mParents);
18215e66616c04e4a54df7e8783bc7ffe6c22bcb3040Doris Liu                }
18221309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu                node.mEnded = false;
182321cd1389d2ef218b20994b617c57af120841a57fChet Haase                return node;
182421cd1389d2ef218b20994b617c57af120841a57fChet Haase            } catch (CloneNotSupportedException e) {
182521cd1389d2ef218b20994b617c57af120841a57fChet Haase               throw new AssertionError();
182621cd1389d2ef218b20994b617c57af120841a57fChet Haase            }
182749afa5bc100e5d4c069fea980dd6b09501f56397Chet Haase        }
18281309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu
18291309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu        void addChild(Node node) {
18301309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu            if (mChildNodes == null) {
18311309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu                mChildNodes = new ArrayList<>();
18321309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu            }
18331309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu            if (!mChildNodes.contains(node)) {
18341309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu                mChildNodes.add(node);
18351309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu                node.addParent(this);
18361309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu            }
18371309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu        }
18381309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu
18391309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu        public void addSibling(Node node) {
18401309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu            if (mSiblings == null) {
18411309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu                mSiblings = new ArrayList<Node>();
18421309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu            }
18431309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu            if (!mSiblings.contains(node)) {
18441309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu                mSiblings.add(node);
18451309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu                node.addSibling(this);
18461309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu            }
18471309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu        }
18481309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu
18491309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu        public void addParent(Node node) {
18501309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu            if (mParents == null) {
18511309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu                mParents =  new ArrayList<Node>();
18521309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu            }
18531309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu            if (!mParents.contains(node)) {
18541309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu                mParents.add(node);
18551309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu                node.addChild(this);
18561309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu            }
18571309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu        }
18581309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu
18591309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu        public void addParents(ArrayList<Node> parents) {
18601309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu            if (parents == null) {
18611309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu                return;
18621309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu            }
18631309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu            int size = parents.size();
18641309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu            for (int i = 0; i < size; i++) {
18651309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu                addParent(parents.get(i));
18661309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu            }
18671309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu        }
186817fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase    }
186917fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase
187017fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase    /**
187113351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu     * This class is a wrapper around a node and an event for the animation corresponding to the
187213351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu     * node. The 3 types of events represent the start of an animation, the end of a start delay of
187313351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu     * an animation, and the end of an animation. When playing forward (i.e. in the non-reverse
187413351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu     * direction), start event marks when start() should be called, and end event corresponds to
187513351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu     * when the animation should finish. When playing in reverse, start delay will not be a part
187613351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu     * of the animation. Therefore, reverse() is called at the end event, and animation should end
187713351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu     * at the delay ended event.
187813351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu     */
187913351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu    private static class AnimationEvent {
188013351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu        static final int ANIMATION_START = 0;
188113351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu        static final int ANIMATION_DELAY_ENDED = 1;
188213351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu        static final int ANIMATION_END = 2;
188313351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu        final Node mNode;
188413351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu        final int mEvent;
188513351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu
188613351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu        AnimationEvent(Node node, int event) {
188713351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu            mNode = node;
188813351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu            mEvent = event;
188913351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu        }
189013351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu
189113351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu        long getTime() {
189213351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu            if (mEvent == ANIMATION_START) {
189313351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu                return mNode.mStartTime;
189413351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu            } else if (mEvent == ANIMATION_DELAY_ENDED) {
1895e8a5d8d211983522661cd2a059b66e71b09a5e07Doris Liu                return mNode.mStartTime == DURATION_INFINITE
1896c9b5034178809a0c51f368735fded79bd73527bdDoris Liu                        ? DURATION_INFINITE : mNode.mStartTime + mNode.mAnimation.getStartDelay();
189713351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu            } else {
189813351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu                return mNode.mEndTime;
189913351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu            }
190013351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu        }
190113351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu
190213351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu        public String toString() {
190313351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu            String eventStr = mEvent == ANIMATION_START ? "start" : (
190413351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu                    mEvent == ANIMATION_DELAY_ENDED ? "delay ended" : "end");
190513351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu            return eventStr + " " + mNode.mAnimation.toString();
190613351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu        }
190713351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu    }
190813351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu
190913351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu    private class SeekState {
191013351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu        private long mPlayTime = -1;
191113351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu        private boolean mSeekingInReverse = false;
191213351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu        void reset() {
191313351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu            mPlayTime = -1;
191413351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu            mSeekingInReverse = false;
191513351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu        }
191613351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu
191713351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu        void setPlayTime(long playTime, boolean inReverse) {
191813351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu            // TODO: This can be simplified.
191913351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu
192013351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu            // Clamp the play time
192113351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu            if (getTotalDuration() != DURATION_INFINITE) {
192213351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu                mPlayTime = Math.min(playTime, getTotalDuration() - mStartDelay);
192313351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu            }
192413351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu            mPlayTime = Math.max(0, mPlayTime);
192513351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu            mSeekingInReverse = inReverse;
192613351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu        }
192713351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu
192813351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu        void updateSeekDirection(boolean inReverse) {
192913351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu            // Change seek direction without changing the overall fraction
193013351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu            if (inReverse && getTotalDuration() == DURATION_INFINITE) {
193113351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu                throw new UnsupportedOperationException("Error: Cannot reverse infinite animator"
193213351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu                        + " set");
193313351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu            }
193413351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu            if (mPlayTime >= 0) {
193513351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu                if (inReverse != mSeekingInReverse) {
193613351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu                    mPlayTime = getTotalDuration() - mStartDelay - mPlayTime;
19376d4520920526cc44063ff2e665adb08e9a3c8a64Doris Liu                    mSeekingInReverse = inReverse;
193813351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu                }
193913351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu            }
194013351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu        }
194113351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu
194213351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu        long getPlayTime() {
194313351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu            return mPlayTime;
194413351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu        }
194513351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu
194613351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu        /**
194713351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu         * Returns the playtime assuming the animation is forward playing
194813351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu         */
194913351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu        long getPlayTimeNormalized() {
195013351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu            if (mReversing) {
195113351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu                return getTotalDuration() - mStartDelay - mPlayTime;
195213351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu            }
195313351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu            return mPlayTime;
195413351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu        }
195513351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu
195613351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu        boolean isActive() {
195713351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu            return mPlayTime != -1;
195813351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu        }
195913351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu    }
196013351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu
196113351997aa36eb53e5ff0fcee3f5e3da83787278Doris Liu    /**
196217fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase     * The <code>Builder</code> object is a utility class to facilitate adding animations to a
1963a18a86b43e40e3c15dcca0ae0148d641be9b25feChet Haase     * <code>AnimatorSet</code> along with the relationships between the various animations. The
196417fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase     * intention of the <code>Builder</code> methods, along with the {@link
19658b699792b677bd4dd8442b32641ac09d48fdd79cChet Haase     * AnimatorSet#play(Animator) play()} method of <code>AnimatorSet</code> is to make it possible
19668b699792b677bd4dd8442b32641ac09d48fdd79cChet Haase     * to express the dependency relationships of animations in a natural way. Developers can also
19678b699792b677bd4dd8442b32641ac09d48fdd79cChet Haase     * use the {@link AnimatorSet#playTogether(Animator[]) playTogether()} and {@link
1968a18a86b43e40e3c15dcca0ae0148d641be9b25feChet Haase     * AnimatorSet#playSequentially(Animator[]) playSequentially()} methods if these suit the need,
1969a18a86b43e40e3c15dcca0ae0148d641be9b25feChet Haase     * but it might be easier in some situations to express the AnimatorSet of animations in pairs.
197017fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase     * <p/>
197117fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase     * <p>The <code>Builder</code> object cannot be constructed directly, but is rather constructed
1972a18a86b43e40e3c15dcca0ae0148d641be9b25feChet Haase     * internally via a call to {@link AnimatorSet#play(Animator)}.</p>
197317fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase     * <p/>
1974a18a86b43e40e3c15dcca0ae0148d641be9b25feChet Haase     * <p>For example, this sets up a AnimatorSet to play anim1 and anim2 at the same time, anim3 to
197517fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase     * play when anim2 finishes, and anim4 to play when anim3 finishes:</p>
197617fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase     * <pre>
1977a18a86b43e40e3c15dcca0ae0148d641be9b25feChet Haase     *     AnimatorSet s = new AnimatorSet();
197817fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase     *     s.play(anim1).with(anim2);
197917fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase     *     s.play(anim2).before(anim3);
198017fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase     *     s.play(anim4).after(anim3);
198117fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase     * </pre>
198217fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase     * <p/>
1983a18a86b43e40e3c15dcca0ae0148d641be9b25feChet Haase     * <p>Note in the example that both {@link Builder#before(Animator)} and {@link
1984a18a86b43e40e3c15dcca0ae0148d641be9b25feChet Haase     * Builder#after(Animator)} are used. These are just different ways of expressing the same
198517fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase     * relationship and are provided to make it easier to say things in a way that is more natural,
198617fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase     * depending on the situation.</p>
198717fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase     * <p/>
198817fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase     * <p>It is possible to make several calls into the same <code>Builder</code> object to express
198917fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase     * multiple relationships. However, note that it is only the animation passed into the initial
1990a18a86b43e40e3c15dcca0ae0148d641be9b25feChet Haase     * {@link AnimatorSet#play(Animator)} method that is the dependency in any of the successive
199117fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase     * calls to the <code>Builder</code> object. For example, the following code starts both anim2
199217fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase     * and anim3 when anim1 ends; there is no direct dependency relationship between anim2 and
199317fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase     * anim3:
199417fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase     * <pre>
1995a18a86b43e40e3c15dcca0ae0148d641be9b25feChet Haase     *   AnimatorSet s = new AnimatorSet();
199617fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase     *   s.play(anim1).before(anim2).before(anim3);
199717fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase     * </pre>
199817fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase     * If the desired result is to play anim1 then anim2 then anim3, this code expresses the
199917fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase     * relationship correctly:</p>
200017fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase     * <pre>
2001a18a86b43e40e3c15dcca0ae0148d641be9b25feChet Haase     *   AnimatorSet s = new AnimatorSet();
200217fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase     *   s.play(anim1).before(anim2);
200317fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase     *   s.play(anim2).before(anim3);
200417fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase     * </pre>
200517fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase     * <p/>
200617fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase     * <p>Note that it is possible to express relationships that cannot be resolved and will not
200717fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase     * result in sensible results. For example, <code>play(anim1).after(anim1)</code> makes no
200817fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase     * sense. In general, circular dependencies like this one (or more indirect ones where a depends
2009a18a86b43e40e3c15dcca0ae0148d641be9b25feChet Haase     * on b, which depends on c, which depends on a) should be avoided. Only create AnimatorSets
2010a18a86b43e40e3c15dcca0ae0148d641be9b25feChet Haase     * that can boil down to a simple, one-way relationship of animations starting with, before, and
201117fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase     * after other, different, animations.</p>
201217fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase     */
201317fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase    public class Builder {
201417fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase
201517fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase        /**
201617fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase         * This tracks the current node being processed. It is supplied to the play() method
2017a18a86b43e40e3c15dcca0ae0148d641be9b25feChet Haase         * of AnimatorSet and passed into the constructor of Builder.
201817fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase         */
201917fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase        private Node mCurrentNode;
202017fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase
202117fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase        /**
2022a18a86b43e40e3c15dcca0ae0148d641be9b25feChet Haase         * package-private constructor. Builders are only constructed by AnimatorSet, when the
202317fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase         * play() method is called.
202417fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase         *
202517fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase         * @param anim The animation that is the dependency for the other animations passed into
202617fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase         * the other methods of this Builder object.
202717fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase         */
2028a18a86b43e40e3c15dcca0ae0148d641be9b25feChet Haase        Builder(Animator anim) {
20291309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu            mDependencyDirty = true;
20301309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu            mCurrentNode = getNodeForAnimation(anim);
203117fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase        }
203217fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase
203317fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase        /**
203417fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase         * Sets up the given animation to play at the same time as the animation supplied in the
2035a18a86b43e40e3c15dcca0ae0148d641be9b25feChet Haase         * {@link AnimatorSet#play(Animator)} call that created this <code>Builder</code> object.
203617fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase         *
203717fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase         * @param anim The animation that will play when the animation supplied to the
2038a18a86b43e40e3c15dcca0ae0148d641be9b25feChet Haase         * {@link AnimatorSet#play(Animator)} method starts.
203917fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase         */
20402970c499388b4dcd1232cd622a9b80b395eeb2b4Chet Haase        public Builder with(Animator anim) {
20411309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu            Node node = getNodeForAnimation(anim);
20421309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu            mCurrentNode.addSibling(node);
20432970c499388b4dcd1232cd622a9b80b395eeb2b4Chet Haase            return this;
204417fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase        }
204517fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase
204617fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase        /**
204717fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase         * Sets up the given animation to play when the animation supplied in the
2048a18a86b43e40e3c15dcca0ae0148d641be9b25feChet Haase         * {@link AnimatorSet#play(Animator)} call that created this <code>Builder</code> object
204917fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase         * ends.
205017fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase         *
205117fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase         * @param anim The animation that will play when the animation supplied to the
2052a18a86b43e40e3c15dcca0ae0148d641be9b25feChet Haase         * {@link AnimatorSet#play(Animator)} method ends.
205317fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase         */
20542970c499388b4dcd1232cd622a9b80b395eeb2b4Chet Haase        public Builder before(Animator anim) {
20551309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu            Node node = getNodeForAnimation(anim);
20561309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu            mCurrentNode.addChild(node);
20572970c499388b4dcd1232cd622a9b80b395eeb2b4Chet Haase            return this;
205817fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase        }
205917fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase
206017fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase        /**
206117fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase         * Sets up the given animation to play when the animation supplied in the
2062a18a86b43e40e3c15dcca0ae0148d641be9b25feChet Haase         * {@link AnimatorSet#play(Animator)} call that created this <code>Builder</code> object
206317fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase         * to start when the animation supplied in this method call ends.
206417fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase         *
206517fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase         * @param anim The animation whose end will cause the animation supplied to the
2066a18a86b43e40e3c15dcca0ae0148d641be9b25feChet Haase         * {@link AnimatorSet#play(Animator)} method to play.
206717fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase         */
20682970c499388b4dcd1232cd622a9b80b395eeb2b4Chet Haase        public Builder after(Animator anim) {
20691309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu            Node node = getNodeForAnimation(anim);
20701309914ebf21e705fc59d7d44014124d8a21a2d2Doris Liu            mCurrentNode.addParent(node);
20712970c499388b4dcd1232cd622a9b80b395eeb2b4Chet Haase            return this;
207217fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase        }
207317fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase
207417fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase        /**
207517fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase         * Sets up the animation supplied in the
2076a18a86b43e40e3c15dcca0ae0148d641be9b25feChet Haase         * {@link AnimatorSet#play(Animator)} call that created this <code>Builder</code> object
207717fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase         * to play when the given amount of time elapses.
207817fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase         *
207917fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase         * @param delay The number of milliseconds that should elapse before the
208017fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase         * animation starts.
208117fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase         */
20822970c499388b4dcd1232cd622a9b80b395eeb2b4Chet Haase        public Builder after(long delay) {
2083a18a86b43e40e3c15dcca0ae0148d641be9b25feChet Haase            // setup dummy ValueAnimator just to run the clock
20842794eb3b02e2404d453d3ad22a8a85a138130a07Chet Haase            ValueAnimator anim = ValueAnimator.ofFloat(0f, 1f);
20852794eb3b02e2404d453d3ad22a8a85a138130a07Chet Haase            anim.setDuration(delay);
20862794eb3b02e2404d453d3ad22a8a85a138130a07Chet Haase            after(anim);
20872970c499388b4dcd1232cd622a9b80b395eeb2b4Chet Haase            return this;
208817fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase        }
208917fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase
209017fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase    }
209117fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase
209217fb4b0d1cfbad1f026fec704c86640f070b4c2fChet Haase}
2093