Home | History | Annotate | Download | only in animation
      1 /*
      2  * Copyright (C) 2010 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package android.animation;
     18 
     19 import android.app.ActivityThread;
     20 import android.app.Application;
     21 import android.os.Build;
     22 import android.os.Looper;
     23 import android.util.AndroidRuntimeException;
     24 import android.util.ArrayMap;
     25 import android.util.Log;
     26 import android.view.animation.Animation;
     27 
     28 import java.util.ArrayList;
     29 import java.util.Collection;
     30 import java.util.Comparator;
     31 import java.util.HashMap;
     32 import java.util.List;
     33 
     34 /**
     35  * This class plays a set of {@link Animator} objects in the specified order. Animations
     36  * can be set up to play together, in sequence, or after a specified delay.
     37  *
     38  * <p>There are two different approaches to adding animations to a <code>AnimatorSet</code>:
     39  * either the {@link AnimatorSet#playTogether(Animator[]) playTogether()} or
     40  * {@link AnimatorSet#playSequentially(Animator[]) playSequentially()} methods can be called to add
     41  * a set of animations all at once, or the {@link AnimatorSet#play(Animator)} can be
     42  * used in conjunction with methods in the {@link AnimatorSet.Builder Builder}
     43  * class to add animations
     44  * one by one.</p>
     45  *
     46  * <p>It is possible to set up a <code>AnimatorSet</code> with circular dependencies between
     47  * its animations. For example, an animation a1 could be set up to start before animation a2, a2
     48  * before a3, and a3 before a1. The results of this configuration are undefined, but will typically
     49  * result in none of the affected animations being played. Because of this (and because
     50  * circular dependencies do not make logical sense anyway), circular dependencies
     51  * should be avoided, and the dependency flow of animations should only be in one direction.
     52  *
     53  * <div class="special reference">
     54  * <h3>Developer Guides</h3>
     55  * <p>For more information about animating with {@code AnimatorSet}, read the
     56  * <a href="{@docRoot}guide/topics/graphics/prop-animation.html#choreography">Property
     57  * Animation</a> developer guide.</p>
     58  * </div>
     59  */
     60 public final class AnimatorSet extends Animator implements AnimationHandler.AnimationFrameCallback {
     61 
     62     private static final String TAG = "AnimatorSet";
     63     /**
     64      * Internal variables
     65      * NOTE: This object implements the clone() method, making a deep copy of any referenced
     66      * objects. As other non-trivial fields are added to this class, make sure to add logic
     67      * to clone() to make deep copies of them.
     68      */
     69 
     70     /**
     71      * Tracks animations currently being played, so that we know what to
     72      * cancel or end when cancel() or end() is called on this AnimatorSet
     73      */
     74     private ArrayList<Node> mPlayingSet = new ArrayList<Node>();
     75 
     76     /**
     77      * Contains all nodes, mapped to their respective Animators. When new
     78      * dependency information is added for an Animator, we want to add it
     79      * to a single node representing that Animator, not create a new Node
     80      * if one already exists.
     81      */
     82     private ArrayMap<Animator, Node> mNodeMap = new ArrayMap<Animator, Node>();
     83 
     84     /**
     85      * Contains the start and end events of all the nodes. All these events are sorted in this list.
     86      */
     87     private ArrayList<AnimationEvent> mEvents = new ArrayList<>();
     88 
     89     /**
     90      * Set of all nodes created for this AnimatorSet. This list is used upon
     91      * starting the set, and the nodes are placed in sorted order into the
     92      * sortedNodes collection.
     93      */
     94     private ArrayList<Node> mNodes = new ArrayList<Node>();
     95 
     96     /**
     97      * Tracks whether any change has been made to the AnimatorSet, which is then used to
     98      * determine whether the dependency graph should be re-constructed.
     99      */
    100     private boolean mDependencyDirty = false;
    101 
    102     /**
    103      * Indicates whether an AnimatorSet has been start()'d, whether or
    104      * not there is a nonzero startDelay.
    105      */
    106     private boolean mStarted = false;
    107 
    108     // The amount of time in ms to delay starting the animation after start() is called
    109     private long mStartDelay = 0;
    110 
    111     // Animator used for a nonzero startDelay
    112     private ValueAnimator mDelayAnim = ValueAnimator.ofFloat(0f, 1f).setDuration(0);
    113 
    114     // Root of the dependency tree of all the animators in the set. In this tree, parent-child
    115     // relationship captures the order of animation (i.e. parent and child will play sequentially),
    116     // and sibling relationship indicates "with" relationship, as sibling animators start at the
    117     // same time.
    118     private Node mRootNode = new Node(mDelayAnim);
    119 
    120     // How long the child animations should last in ms. The default value is negative, which
    121     // simply means that there is no duration set on the AnimatorSet. When a real duration is
    122     // set, it is passed along to the child animations.
    123     private long mDuration = -1;
    124 
    125     // Records the interpolator for the set. Null value indicates that no interpolator
    126     // was set on this AnimatorSet, so it should not be passed down to the children.
    127     private TimeInterpolator mInterpolator = null;
    128 
    129     // The total duration of finishing all the Animators in the set.
    130     private long mTotalDuration = 0;
    131 
    132     // In pre-N releases, calling end() before start() on an animator set is no-op. But that is not
    133     // consistent with the behavior for other animator types. In order to keep the behavior
    134     // consistent within Animation framework, when end() is called without start(), we will start
    135     // the animator set and immediately end it for N and forward.
    136     private final boolean mShouldIgnoreEndWithoutStart;
    137 
    138     // In pre-O releases, calling start() doesn't reset all the animators values to start values.
    139     // As a result, the start of the animation is inconsistent with what setCurrentPlayTime(0) would
    140     // look like on O. Also it is inconsistent with what reverse() does on O, as reverse would
    141     // advance all the animations to the right beginning values for before starting to reverse.
    142     // From O and forward, we will add an additional step of resetting the animation values (unless
    143     // the animation was previously seeked and therefore doesn't start from the beginning).
    144     private final boolean mShouldResetValuesAtStart;
    145 
    146     // In pre-O releases, end() may never explicitly called on a child animator. As a result, end()
    147     // may not even be properly implemented in a lot of cases. After a few apps crashing on this,
    148     // it became necessary to use an sdk target guard for calling end().
    149     private final boolean mEndCanBeCalled;
    150 
    151     // The time, in milliseconds, when last frame of the animation came in. -1 when the animation is
    152     // not running.
    153     private long mLastFrameTime = -1;
    154 
    155     // The time, in milliseconds, when the first frame of the animation came in. This is the
    156     // frame before we start counting down the start delay, if any.
    157     // -1 when the animation is not running.
    158     private long mFirstFrame = -1;
    159 
    160     // The time, in milliseconds, when the first frame of the animation came in.
    161     // -1 when the animation is not running.
    162     private int mLastEventId = -1;
    163 
    164     // Indicates whether the animation is reversing.
    165     private boolean mReversing = false;
    166 
    167     // Indicates whether the animation should register frame callbacks. If false, the animation will
    168     // passively wait for an AnimatorSet to pulse it.
    169     private boolean mSelfPulse = true;
    170 
    171     // SeekState stores the last seeked play time as well as seek direction.
    172     private SeekState mSeekState = new SeekState();
    173 
    174     // Indicates where children animators are all initialized with their start values captured.
    175     private boolean mChildrenInitialized = false;
    176 
    177     /**
    178      * Set on the next frame after pause() is called, used to calculate a new startTime
    179      * or delayStartTime which allows the animator set to continue from the point at which
    180      * it was paused. If negative, has not yet been set.
    181      */
    182     private long mPauseTime = -1;
    183 
    184     // This is to work around a bug in b/34736819. This needs to be removed once app team
    185     // fixes their side.
    186     private AnimatorListenerAdapter mDummyListener = new AnimatorListenerAdapter() {
    187         @Override
    188         public void onAnimationEnd(Animator animation) {
    189             if (mNodeMap.get(animation) == null) {
    190                 throw new AndroidRuntimeException("Error: animation ended is not in the node map");
    191             }
    192             mNodeMap.get(animation).mEnded = true;
    193 
    194         }
    195     };
    196 
    197     public AnimatorSet() {
    198         super();
    199         mNodeMap.put(mDelayAnim, mRootNode);
    200         mNodes.add(mRootNode);
    201         boolean isPreO;
    202         // Set the flag to ignore calling end() without start() for pre-N releases
    203         Application app = ActivityThread.currentApplication();
    204         if (app == null || app.getApplicationInfo() == null) {
    205             mShouldIgnoreEndWithoutStart = true;
    206             isPreO = true;
    207         } else {
    208             if (app.getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.N) {
    209                 mShouldIgnoreEndWithoutStart = true;
    210             } else {
    211                 mShouldIgnoreEndWithoutStart = false;
    212             }
    213 
    214             isPreO = app.getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.O;
    215         }
    216         mShouldResetValuesAtStart = !isPreO;
    217         mEndCanBeCalled = !isPreO;
    218     }
    219 
    220     /**
    221      * Sets up this AnimatorSet to play all of the supplied animations at the same time.
    222      * This is equivalent to calling {@link #play(Animator)} with the first animator in the
    223      * set and then {@link Builder#with(Animator)} with each of the other animators. Note that
    224      * an Animator with a {@link Animator#setStartDelay(long) startDelay} will not actually
    225      * start until that delay elapses, which means that if the first animator in the list
    226      * supplied to this constructor has a startDelay, none of the other animators will start
    227      * until that first animator's startDelay has elapsed.
    228      *
    229      * @param items The animations that will be started simultaneously.
    230      */
    231     public void playTogether(Animator... items) {
    232         if (items != null) {
    233             Builder builder = play(items[0]);
    234             for (int i = 1; i < items.length; ++i) {
    235                 builder.with(items[i]);
    236             }
    237         }
    238     }
    239 
    240     /**
    241      * Sets up this AnimatorSet to play all of the supplied animations at the same time.
    242      *
    243      * @param items The animations that will be started simultaneously.
    244      */
    245     public void playTogether(Collection<Animator> items) {
    246         if (items != null && items.size() > 0) {
    247             Builder builder = null;
    248             for (Animator anim : items) {
    249                 if (builder == null) {
    250                     builder = play(anim);
    251                 } else {
    252                     builder.with(anim);
    253                 }
    254             }
    255         }
    256     }
    257 
    258     /**
    259      * Sets up this AnimatorSet to play each of the supplied animations when the
    260      * previous animation ends.
    261      *
    262      * @param items The animations that will be started one after another.
    263      */
    264     public void playSequentially(Animator... items) {
    265         if (items != null) {
    266             if (items.length == 1) {
    267                 play(items[0]);
    268             } else {
    269                 for (int i = 0; i < items.length - 1; ++i) {
    270                     play(items[i]).before(items[i + 1]);
    271                 }
    272             }
    273         }
    274     }
    275 
    276     /**
    277      * Sets up this AnimatorSet to play each of the supplied animations when the
    278      * previous animation ends.
    279      *
    280      * @param items The animations that will be started one after another.
    281      */
    282     public void playSequentially(List<Animator> items) {
    283         if (items != null && items.size() > 0) {
    284             if (items.size() == 1) {
    285                 play(items.get(0));
    286             } else {
    287                 for (int i = 0; i < items.size() - 1; ++i) {
    288                     play(items.get(i)).before(items.get(i + 1));
    289                 }
    290             }
    291         }
    292     }
    293 
    294     /**
    295      * Returns the current list of child Animator objects controlled by this
    296      * AnimatorSet. This is a copy of the internal list; modifications to the returned list
    297      * will not affect the AnimatorSet, although changes to the underlying Animator objects
    298      * will affect those objects being managed by the AnimatorSet.
    299      *
    300      * @return ArrayList<Animator> The list of child animations of this AnimatorSet.
    301      */
    302     public ArrayList<Animator> getChildAnimations() {
    303         ArrayList<Animator> childList = new ArrayList<Animator>();
    304         int size = mNodes.size();
    305         for (int i = 0; i < size; i++) {
    306             Node node = mNodes.get(i);
    307             if (node != mRootNode) {
    308                 childList.add(node.mAnimation);
    309             }
    310         }
    311         return childList;
    312     }
    313 
    314     /**
    315      * Sets the target object for all current {@link #getChildAnimations() child animations}
    316      * of this AnimatorSet that take targets ({@link ObjectAnimator} and
    317      * AnimatorSet).
    318      *
    319      * @param target The object being animated
    320      */
    321     @Override
    322     public void setTarget(Object target) {
    323         int size = mNodes.size();
    324         for (int i = 0; i < size; i++) {
    325             Node node = mNodes.get(i);
    326             Animator animation = node.mAnimation;
    327             if (animation instanceof AnimatorSet) {
    328                 ((AnimatorSet)animation).setTarget(target);
    329             } else if (animation instanceof ObjectAnimator) {
    330                 ((ObjectAnimator)animation).setTarget(target);
    331             }
    332         }
    333     }
    334 
    335     /**
    336      * @hide
    337      */
    338     @Override
    339     public int getChangingConfigurations() {
    340         int conf = super.getChangingConfigurations();
    341         final int nodeCount = mNodes.size();
    342         for (int i = 0; i < nodeCount; i ++) {
    343             conf |= mNodes.get(i).mAnimation.getChangingConfigurations();
    344         }
    345         return conf;
    346     }
    347 
    348     /**
    349      * Sets the TimeInterpolator for all current {@link #getChildAnimations() child animations}
    350      * of this AnimatorSet. The default value is null, which means that no interpolator
    351      * is set on this AnimatorSet. Setting the interpolator to any non-null value
    352      * will cause that interpolator to be set on the child animations
    353      * when the set is started.
    354      *
    355      * @param interpolator the interpolator to be used by each child animation of this AnimatorSet
    356      */
    357     @Override
    358     public void setInterpolator(TimeInterpolator interpolator) {
    359         mInterpolator = interpolator;
    360     }
    361 
    362     @Override
    363     public TimeInterpolator getInterpolator() {
    364         return mInterpolator;
    365     }
    366 
    367     /**
    368      * This method creates a <code>Builder</code> object, which is used to
    369      * set up playing constraints. This initial <code>play()</code> method
    370      * tells the <code>Builder</code> the animation that is the dependency for
    371      * the succeeding commands to the <code>Builder</code>. For example,
    372      * calling <code>play(a1).with(a2)</code> sets up the AnimatorSet to play
    373      * <code>a1</code> and <code>a2</code> at the same time,
    374      * <code>play(a1).before(a2)</code> sets up the AnimatorSet to play
    375      * <code>a1</code> first, followed by <code>a2</code>, and
    376      * <code>play(a1).after(a2)</code> sets up the AnimatorSet to play
    377      * <code>a2</code> first, followed by <code>a1</code>.
    378      *
    379      * <p>Note that <code>play()</code> is the only way to tell the
    380      * <code>Builder</code> the animation upon which the dependency is created,
    381      * so successive calls to the various functions in <code>Builder</code>
    382      * will all refer to the initial parameter supplied in <code>play()</code>
    383      * as the dependency of the other animations. For example, calling
    384      * <code>play(a1).before(a2).before(a3)</code> will play both <code>a2</code>
    385      * and <code>a3</code> when a1 ends; it does not set up a dependency between
    386      * <code>a2</code> and <code>a3</code>.</p>
    387      *
    388      * @param anim The animation that is the dependency used in later calls to the
    389      * methods in the returned <code>Builder</code> object. A null parameter will result
    390      * in a null <code>Builder</code> return value.
    391      * @return Builder The object that constructs the AnimatorSet based on the dependencies
    392      * outlined in the calls to <code>play</code> and the other methods in the
    393      * <code>Builder</code object.
    394      */
    395     public Builder play(Animator anim) {
    396         if (anim != null) {
    397             return new Builder(anim);
    398         }
    399         return null;
    400     }
    401 
    402     /**
    403      * {@inheritDoc}
    404      *
    405      * <p>Note that canceling a <code>AnimatorSet</code> also cancels all of the animations that it
    406      * is responsible for.</p>
    407      */
    408     @SuppressWarnings("unchecked")
    409     @Override
    410     public void cancel() {
    411         if (Looper.myLooper() == null) {
    412             throw new AndroidRuntimeException("Animators may only be run on Looper threads");
    413         }
    414         if (isStarted()) {
    415             ArrayList<AnimatorListener> tmpListeners = null;
    416             if (mListeners != null) {
    417                 tmpListeners = (ArrayList<AnimatorListener>) mListeners.clone();
    418                 int size = tmpListeners.size();
    419                 for (int i = 0; i < size; i++) {
    420                     tmpListeners.get(i).onAnimationCancel(this);
    421                 }
    422             }
    423             ArrayList<Node> playingSet = new ArrayList<>(mPlayingSet);
    424             int setSize = playingSet.size();
    425             for (int i = 0; i < setSize; i++) {
    426                 playingSet.get(i).mAnimation.cancel();
    427             }
    428             mPlayingSet.clear();
    429             endAnimation();
    430         }
    431     }
    432 
    433     // Force all the animations to end when the duration scale is 0.
    434     private void forceToEnd() {
    435         if (mEndCanBeCalled) {
    436             end();
    437             return;
    438         }
    439 
    440         // Note: we don't want to combine this case with the end() method below because in
    441         // the case of developer calling end(), we still need to make sure end() is explicitly
    442         // called on the child animators to maintain the old behavior.
    443         if (mReversing) {
    444             handleAnimationEvents(mLastEventId, 0, getTotalDuration());
    445         } else {
    446             long zeroScalePlayTime = getTotalDuration();
    447             if (zeroScalePlayTime == DURATION_INFINITE) {
    448                 // Use a large number for the play time.
    449                 zeroScalePlayTime = Integer.MAX_VALUE;
    450             }
    451             handleAnimationEvents(mLastEventId, mEvents.size() - 1, zeroScalePlayTime);
    452         }
    453         mPlayingSet.clear();
    454         endAnimation();
    455     }
    456 
    457     /**
    458      * {@inheritDoc}
    459      *
    460      * <p>Note that ending a <code>AnimatorSet</code> also ends all of the animations that it is
    461      * responsible for.</p>
    462      */
    463     @Override
    464     public void end() {
    465         if (Looper.myLooper() == null) {
    466             throw new AndroidRuntimeException("Animators may only be run on Looper threads");
    467         }
    468         if (mShouldIgnoreEndWithoutStart && !isStarted()) {
    469             return;
    470         }
    471         if (isStarted()) {
    472             // Iterate the animations that haven't finished or haven't started, and end them.
    473             if (mReversing) {
    474                 // Between start() and first frame, mLastEventId would be unset (i.e. -1)
    475                 mLastEventId = mLastEventId == -1 ? mEvents.size() : mLastEventId;
    476                 while (mLastEventId > 0) {
    477                     mLastEventId = mLastEventId - 1;
    478                     AnimationEvent event = mEvents.get(mLastEventId);
    479                     Animator anim = event.mNode.mAnimation;
    480                     if (mNodeMap.get(anim).mEnded) {
    481                         continue;
    482                     }
    483                     if (event.mEvent == AnimationEvent.ANIMATION_END) {
    484                         anim.reverse();
    485                     } else if (event.mEvent == AnimationEvent.ANIMATION_DELAY_ENDED
    486                             && anim.isStarted()) {
    487                         // Make sure anim hasn't finished before calling end() so that we don't end
    488                         // already ended animations, which will cause start and end callbacks to be
    489                         // triggered again.
    490                         anim.end();
    491                     }
    492                 }
    493             } else {
    494                 while (mLastEventId < mEvents.size() - 1) {
    495                     // Avoid potential reentrant loop caused by child animators manipulating
    496                     // AnimatorSet's lifecycle (i.e. not a recommended approach).
    497                     mLastEventId = mLastEventId + 1;
    498                     AnimationEvent event = mEvents.get(mLastEventId);
    499                     Animator anim = event.mNode.mAnimation;
    500                     if (mNodeMap.get(anim).mEnded) {
    501                         continue;
    502                     }
    503                     if (event.mEvent == AnimationEvent.ANIMATION_START) {
    504                         anim.start();
    505                     } else if (event.mEvent == AnimationEvent.ANIMATION_END && anim.isStarted()) {
    506                         // Make sure anim hasn't finished before calling end() so that we don't end
    507                         // already ended animations, which will cause start and end callbacks to be
    508                         // triggered again.
    509                         anim.end();
    510                     }
    511                 }
    512             }
    513             mPlayingSet.clear();
    514         }
    515         endAnimation();
    516     }
    517 
    518     /**
    519      * Returns true if any of the child animations of this AnimatorSet have been started and have
    520      * not yet ended. Child animations will not be started until the AnimatorSet has gone past
    521      * its initial delay set through {@link #setStartDelay(long)}.
    522      *
    523      * @return Whether this AnimatorSet has gone past the initial delay, and at least one child
    524      *         animation has been started and not yet ended.
    525      */
    526     @Override
    527     public boolean isRunning() {
    528         if (mStartDelay == 0) {
    529             return mStarted;
    530         }
    531         return mLastFrameTime > 0;
    532     }
    533 
    534     @Override
    535     public boolean isStarted() {
    536         return mStarted;
    537     }
    538 
    539     /**
    540      * The amount of time, in milliseconds, to delay starting the animation after
    541      * {@link #start()} is called.
    542      *
    543      * @return the number of milliseconds to delay running the animation
    544      */
    545     @Override
    546     public long getStartDelay() {
    547         return mStartDelay;
    548     }
    549 
    550     /**
    551      * The amount of time, in milliseconds, to delay starting the animation after
    552      * {@link #start()} is called. Note that the start delay should always be non-negative. Any
    553      * negative start delay will be clamped to 0 on N and above.
    554      *
    555      * @param startDelay The amount of the delay, in milliseconds
    556      */
    557     @Override
    558     public void setStartDelay(long startDelay) {
    559         // Clamp start delay to non-negative range.
    560         if (startDelay < 0) {
    561             Log.w(TAG, "Start delay should always be non-negative");
    562             startDelay = 0;
    563         }
    564         long delta = startDelay - mStartDelay;
    565         if (delta == 0) {
    566             return;
    567         }
    568         mStartDelay = startDelay;
    569         if (!mDependencyDirty) {
    570             // Dependency graph already constructed, update all the nodes' start/end time
    571             int size = mNodes.size();
    572             for (int i = 0; i < size; i++) {
    573                 Node node = mNodes.get(i);
    574                 if (node == mRootNode) {
    575                     node.mEndTime = mStartDelay;
    576                 } else {
    577                     node.mStartTime = node.mStartTime == DURATION_INFINITE ?
    578                             DURATION_INFINITE : node.mStartTime + delta;
    579                     node.mEndTime = node.mEndTime == DURATION_INFINITE ?
    580                             DURATION_INFINITE : node.mEndTime + delta;
    581                 }
    582             }
    583             // Update total duration, if necessary.
    584             if (mTotalDuration != DURATION_INFINITE) {
    585                 mTotalDuration += delta;
    586             }
    587         }
    588     }
    589 
    590     /**
    591      * Gets the length of each of the child animations of this AnimatorSet. This value may
    592      * be less than 0, which indicates that no duration has been set on this AnimatorSet
    593      * and each of the child animations will use their own duration.
    594      *
    595      * @return The length of the animation, in milliseconds, of each of the child
    596      * animations of this AnimatorSet.
    597      */
    598     @Override
    599     public long getDuration() {
    600         return mDuration;
    601     }
    602 
    603     /**
    604      * Sets the length of each of the current child animations of this AnimatorSet. By default,
    605      * each child animation will use its own duration. If the duration is set on the AnimatorSet,
    606      * then each child animation inherits this duration.
    607      *
    608      * @param duration The length of the animation, in milliseconds, of each of the child
    609      * animations of this AnimatorSet.
    610      */
    611     @Override
    612     public AnimatorSet setDuration(long duration) {
    613         if (duration < 0) {
    614             throw new IllegalArgumentException("duration must be a value of zero or greater");
    615         }
    616         mDependencyDirty = true;
    617         // Just record the value for now - it will be used later when the AnimatorSet starts
    618         mDuration = duration;
    619         return this;
    620     }
    621 
    622     @Override
    623     public void setupStartValues() {
    624         int size = mNodes.size();
    625         for (int i = 0; i < size; i++) {
    626             Node node = mNodes.get(i);
    627             if (node != mRootNode) {
    628                 node.mAnimation.setupStartValues();
    629             }
    630         }
    631     }
    632 
    633     @Override
    634     public void setupEndValues() {
    635         int size = mNodes.size();
    636         for (int i = 0; i < size; i++) {
    637             Node node = mNodes.get(i);
    638             if (node != mRootNode) {
    639                 node.mAnimation.setupEndValues();
    640             }
    641         }
    642     }
    643 
    644     @Override
    645     public void pause() {
    646         if (Looper.myLooper() == null) {
    647             throw new AndroidRuntimeException("Animators may only be run on Looper threads");
    648         }
    649         boolean previouslyPaused = mPaused;
    650         super.pause();
    651         if (!previouslyPaused && mPaused) {
    652             mPauseTime = -1;
    653         }
    654     }
    655 
    656     @Override
    657     public void resume() {
    658         if (Looper.myLooper() == null) {
    659             throw new AndroidRuntimeException("Animators may only be run on Looper threads");
    660         }
    661         boolean previouslyPaused = mPaused;
    662         super.resume();
    663         if (previouslyPaused && !mPaused) {
    664             if (mPauseTime >= 0) {
    665                 addAnimationCallback(0);
    666             }
    667         }
    668     }
    669 
    670     /**
    671      * {@inheritDoc}
    672      *
    673      * <p>Starting this <code>AnimatorSet</code> will, in turn, start the animations for which
    674      * it is responsible. The details of when exactly those animations are started depends on
    675      * the dependency relationships that have been set up between the animations.
    676      *
    677      * <b>Note:</b> Manipulating AnimatorSet's lifecycle in the child animators' listener callbacks
    678      * will lead to undefined behaviors. Also, AnimatorSet will ignore any seeking in the child
    679      * animators once {@link #start()} is called.
    680      */
    681     @SuppressWarnings("unchecked")
    682     @Override
    683     public void start() {
    684         start(false, true);
    685     }
    686 
    687     @Override
    688     void startWithoutPulsing(boolean inReverse) {
    689         start(inReverse, false);
    690     }
    691 
    692     private void initAnimation() {
    693         if (mInterpolator != null) {
    694             for (int i = 0; i < mNodes.size(); i++) {
    695                 Node node = mNodes.get(i);
    696                 node.mAnimation.setInterpolator(mInterpolator);
    697             }
    698         }
    699         updateAnimatorsDuration();
    700         createDependencyGraph();
    701     }
    702 
    703     private void start(boolean inReverse, boolean selfPulse) {
    704         if (Looper.myLooper() == null) {
    705             throw new AndroidRuntimeException("Animators may only be run on Looper threads");
    706         }
    707         mStarted = true;
    708         mSelfPulse = selfPulse;
    709         mPaused = false;
    710         mPauseTime = -1;
    711 
    712         int size = mNodes.size();
    713         for (int i = 0; i < size; i++) {
    714             Node node = mNodes.get(i);
    715             node.mEnded = false;
    716             node.mAnimation.setAllowRunningAsynchronously(false);
    717         }
    718 
    719         initAnimation();
    720         if (inReverse && !canReverse()) {
    721             throw new UnsupportedOperationException("Cannot reverse infinite AnimatorSet");
    722         }
    723 
    724         mReversing = inReverse;
    725 
    726         // Now that all dependencies are set up, start the animations that should be started.
    727         boolean isEmptySet = isEmptySet(this);
    728         if (!isEmptySet) {
    729             startAnimation();
    730         }
    731 
    732         if (mListeners != null) {
    733             ArrayList<AnimatorListener> tmpListeners =
    734                     (ArrayList<AnimatorListener>) mListeners.clone();
    735             int numListeners = tmpListeners.size();
    736             for (int i = 0; i < numListeners; ++i) {
    737                 tmpListeners.get(i).onAnimationStart(this, inReverse);
    738             }
    739         }
    740         if (isEmptySet) {
    741             // In the case of empty AnimatorSet, or 0 duration scale, we will trigger the
    742             // onAnimationEnd() right away.
    743             end();
    744         }
    745     }
    746 
    747     // Returns true if set is empty or contains nothing but animator sets with no start delay.
    748     private static boolean isEmptySet(AnimatorSet set) {
    749         if (set.getStartDelay() > 0) {
    750             return false;
    751         }
    752         for (int i = 0; i < set.getChildAnimations().size(); i++) {
    753             Animator anim = set.getChildAnimations().get(i);
    754             if (!(anim instanceof AnimatorSet)) {
    755                 // Contains non-AnimatorSet, not empty.
    756                 return false;
    757             } else {
    758                 if (!isEmptySet((AnimatorSet) anim)) {
    759                     return false;
    760                 }
    761             }
    762         }
    763         return true;
    764     }
    765 
    766     private void updateAnimatorsDuration() {
    767         if (mDuration >= 0) {
    768             // If the duration was set on this AnimatorSet, pass it along to all child animations
    769             int size = mNodes.size();
    770             for (int i = 0; i < size; i++) {
    771                 Node node = mNodes.get(i);
    772                 // TODO: don't set the duration of the timing-only nodes created by AnimatorSet to
    773                 // insert "play-after" delays
    774                 node.mAnimation.setDuration(mDuration);
    775             }
    776         }
    777         mDelayAnim.setDuration(mStartDelay);
    778     }
    779 
    780     @Override
    781     void skipToEndValue(boolean inReverse) {
    782         if (!isInitialized()) {
    783             throw new UnsupportedOperationException("Children must be initialized.");
    784         }
    785 
    786         // This makes sure the animation events are sorted an up to date.
    787         initAnimation();
    788 
    789         // Calling skip to the end in the sequence that they would be called in a forward/reverse
    790         // run, such that the sequential animations modifying the same property would have
    791         // the right value in the end.
    792         if (inReverse) {
    793             for (int i = mEvents.size() - 1; i >= 0; i--) {
    794                 if (mEvents.get(i).mEvent == AnimationEvent.ANIMATION_DELAY_ENDED) {
    795                     mEvents.get(i).mNode.mAnimation.skipToEndValue(true);
    796                 }
    797             }
    798         } else {
    799             for (int i = 0; i < mEvents.size(); i++) {
    800                 if (mEvents.get(i).mEvent == AnimationEvent.ANIMATION_END) {
    801                     mEvents.get(i).mNode.mAnimation.skipToEndValue(false);
    802                 }
    803             }
    804         }
    805     }
    806 
    807     /**
    808      * Internal only.
    809      *
    810      * This method sets the animation values based on the play time. It also fast forward or
    811      * backward all the child animations progress accordingly.
    812      *
    813      * This method is also responsible for calling
    814      * {@link android.view.animation.Animation.AnimationListener#onAnimationRepeat(Animation)},
    815      * as needed, based on the last play time and current play time.
    816      */
    817     @Override
    818     void animateBasedOnPlayTime(long currentPlayTime, long lastPlayTime, boolean inReverse) {
    819         if (currentPlayTime < 0 || lastPlayTime < 0) {
    820             throw new UnsupportedOperationException("Error: Play time should never be negative.");
    821         }
    822         // TODO: take into account repeat counts and repeat callback when repeat is implemented.
    823         // Clamp currentPlayTime and lastPlayTime
    824 
    825         // TODO: Make this more efficient
    826 
    827         // Convert the play times to the forward direction.
    828         if (inReverse) {
    829             if (getTotalDuration() == DURATION_INFINITE) {
    830                 throw new UnsupportedOperationException("Cannot reverse AnimatorSet with infinite"
    831                         + " duration");
    832             }
    833             long duration = getTotalDuration() - mStartDelay;
    834             currentPlayTime = Math.min(currentPlayTime, duration);
    835             currentPlayTime = duration - currentPlayTime;
    836             lastPlayTime = duration - lastPlayTime;
    837             inReverse = false;
    838         }
    839         // Skip all values to start, and iterate mEvents to get animations to the right fraction.
    840         skipToStartValue(false);
    841 
    842         ArrayList<Node> unfinishedNodes = new ArrayList<>();
    843         // Assumes forward playing from here on.
    844         for (int i = 0; i < mEvents.size(); i++) {
    845             AnimationEvent event = mEvents.get(i);
    846             if (event.getTime() > currentPlayTime || event.getTime() == DURATION_INFINITE) {
    847                 break;
    848             }
    849 
    850             // This animation started prior to the current play time, and won't finish before the
    851             // play time, add to the unfinished list.
    852             if (event.mEvent == AnimationEvent.ANIMATION_DELAY_ENDED) {
    853                 if (event.mNode.mEndTime == DURATION_INFINITE
    854                         || event.mNode.mEndTime > currentPlayTime) {
    855                     unfinishedNodes.add(event.mNode);
    856                 }
    857             }
    858             // For animations that do finish before the play time, end them in the sequence that
    859             // they would in a normal run.
    860             if (event.mEvent == AnimationEvent.ANIMATION_END) {
    861                 // Skip to the end of the animation.
    862                 event.mNode.mAnimation.skipToEndValue(false);
    863             }
    864         }
    865 
    866         // Seek unfinished animation to the right time.
    867         for (int i = 0; i < unfinishedNodes.size(); i++) {
    868             Node node = unfinishedNodes.get(i);
    869             long playTime = getPlayTimeForNode(currentPlayTime, node, inReverse);
    870             if (!inReverse) {
    871                 playTime -= node.mAnimation.getStartDelay();
    872             }
    873             node.mAnimation.animateBasedOnPlayTime(playTime, lastPlayTime, inReverse);
    874         }
    875     }
    876 
    877     @Override
    878     boolean isInitialized() {
    879         if (mChildrenInitialized) {
    880             return true;
    881         }
    882 
    883         boolean allInitialized = true;
    884         for (int i = 0; i < mNodes.size(); i++) {
    885             if (!mNodes.get(i).mAnimation.isInitialized()) {
    886                 allInitialized = false;
    887                 break;
    888             }
    889         }
    890         mChildrenInitialized = allInitialized;
    891         return mChildrenInitialized;
    892     }
    893 
    894     private void skipToStartValue(boolean inReverse) {
    895         skipToEndValue(!inReverse);
    896     }
    897 
    898     /**
    899      * Sets the position of the animation to the specified point in time. This time should
    900      * be between 0 and the total duration of the animation, including any repetition. If
    901      * the animation has not yet been started, then it will not advance forward after it is
    902      * set to this time; it will simply set the time to this value and perform any appropriate
    903      * actions based on that time. If the animation is already running, then setCurrentPlayTime()
    904      * will set the current playing time to this value and continue playing from that point.
    905      *
    906      * @param playTime The time, in milliseconds, to which the animation is advanced or rewound.
    907      *                 Unless the animation is reversing, the playtime is considered the time since
    908      *                 the end of the start delay of the AnimatorSet in a forward playing direction.
    909      *
    910      */
    911     public void setCurrentPlayTime(long playTime) {
    912         if (mReversing && getTotalDuration() == DURATION_INFINITE) {
    913             // Should never get here
    914             throw new UnsupportedOperationException("Error: Cannot seek in reverse in an infinite"
    915                     + " AnimatorSet");
    916         }
    917 
    918         if ((getTotalDuration() != DURATION_INFINITE && playTime > getTotalDuration() - mStartDelay)
    919                 || playTime < 0) {
    920             throw new UnsupportedOperationException("Error: Play time should always be in between"
    921                     + "0 and duration.");
    922         }
    923 
    924         initAnimation();
    925 
    926         if (!isStarted()) {
    927             if (mReversing) {
    928                 throw new UnsupportedOperationException("Error: Something went wrong. mReversing"
    929                         + " should not be set when AnimatorSet is not started.");
    930             }
    931             if (!mSeekState.isActive()) {
    932                 findLatestEventIdForTime(0);
    933                 // Set all the values to start values.
    934                 initChildren();
    935                 skipToStartValue(mReversing);
    936                 mSeekState.setPlayTime(0, mReversing);
    937             }
    938             animateBasedOnPlayTime(playTime, 0, mReversing);
    939             mSeekState.setPlayTime(playTime, mReversing);
    940         } else {
    941             // If the animation is running, just set the seek time and wait until the next frame
    942             // (i.e. doAnimationFrame(...)) to advance the animation.
    943             mSeekState.setPlayTime(playTime, mReversing);
    944         }
    945     }
    946 
    947     /**
    948      * Returns the milliseconds elapsed since the start of the animation.
    949      *
    950      * <p>For ongoing animations, this method returns the current progress of the animation in
    951      * terms of play time. For an animation that has not yet been started: if the animation has been
    952      * seeked to a certain time via {@link #setCurrentPlayTime(long)}, the seeked play time will
    953      * be returned; otherwise, this method will return 0.
    954      *
    955      * @return the current position in time of the animation in milliseconds
    956      */
    957     public long getCurrentPlayTime() {
    958         if (mSeekState.isActive()) {
    959             return mSeekState.getPlayTime();
    960         }
    961         if (mLastFrameTime == -1) {
    962             // Not yet started or during start delay
    963             return 0;
    964         }
    965         float durationScale = ValueAnimator.getDurationScale();
    966         durationScale = durationScale == 0 ? 1 : durationScale;
    967         if (mReversing) {
    968             return (long) ((mLastFrameTime - mFirstFrame) / durationScale);
    969         } else {
    970             return (long) ((mLastFrameTime - mFirstFrame - mStartDelay) / durationScale);
    971         }
    972     }
    973 
    974     private void initChildren() {
    975         if (!isInitialized()) {
    976             mChildrenInitialized = true;
    977             // Forcefully initialize all children based on their end time, so that if the start
    978             // value of a child is dependent on a previous animation, the animation will be
    979             // initialized after the the previous animations have been advanced to the end.
    980             skipToEndValue(false);
    981         }
    982     }
    983 
    984     /**
    985      * @param frameTime The frame start time, in the {@link SystemClock#uptimeMillis()} time
    986      *                  base.
    987      * @return
    988      * @hide
    989      */
    990     @Override
    991     public boolean doAnimationFrame(long frameTime) {
    992         float durationScale = ValueAnimator.getDurationScale();
    993         if (durationScale == 0f) {
    994             // Duration scale is 0, end the animation right away.
    995             forceToEnd();
    996             return true;
    997         }
    998 
    999         // After the first frame comes in, we need to wait for start delay to pass before updating
   1000         // any animation values.
   1001         if (mFirstFrame < 0) {
   1002             mFirstFrame = frameTime;
   1003         }
   1004 
   1005         // Handle pause/resume
   1006         if (mPaused) {
   1007             // Note: Child animations don't receive pause events. Since it's never a contract that
   1008             // the child animators will be paused when set is paused, this is unlikely to be an
   1009             // issue.
   1010             mPauseTime = frameTime;
   1011             removeAnimationCallback();
   1012             return false;
   1013         } else if (mPauseTime > 0) {
   1014                 // Offset by the duration that the animation was paused
   1015             mFirstFrame += (frameTime - mPauseTime);
   1016             mPauseTime = -1;
   1017         }
   1018 
   1019         // Continue at seeked position
   1020         if (mSeekState.isActive()) {
   1021             mSeekState.updateSeekDirection(mReversing);
   1022             if (mReversing) {
   1023                 mFirstFrame = (long) (frameTime - mSeekState.getPlayTime() * durationScale);
   1024             } else {
   1025                 mFirstFrame = (long) (frameTime - (mSeekState.getPlayTime() + mStartDelay)
   1026                         * durationScale);
   1027             }
   1028             mSeekState.reset();
   1029         }
   1030 
   1031         if (!mReversing && frameTime < mFirstFrame + mStartDelay * durationScale) {
   1032             // Still during start delay in a forward playing case.
   1033             return false;
   1034         }
   1035 
   1036         // From here on, we always use unscaled play time. Note this unscaled playtime includes
   1037         // the start delay.
   1038         long unscaledPlayTime = (long) ((frameTime - mFirstFrame) / durationScale);
   1039         mLastFrameTime = frameTime;
   1040 
   1041         // 1. Pulse the animators that will start or end in this frame
   1042         // 2. Pulse the animators that will finish in a later frame
   1043         int latestId = findLatestEventIdForTime(unscaledPlayTime);
   1044         int startId = mLastEventId;
   1045 
   1046         handleAnimationEvents(startId, latestId, unscaledPlayTime);
   1047 
   1048         mLastEventId = latestId;
   1049 
   1050         // Pump a frame to the on-going animators
   1051         for (int i = 0; i < mPlayingSet.size(); i++) {
   1052             Node node = mPlayingSet.get(i);
   1053             if (!node.mEnded) {
   1054                 pulseFrame(node, getPlayTimeForNode(unscaledPlayTime, node));
   1055             }
   1056         }
   1057 
   1058         // Remove all the finished anims
   1059         for (int i = mPlayingSet.size() - 1; i >= 0; i--) {
   1060             if (mPlayingSet.get(i).mEnded) {
   1061                 mPlayingSet.remove(i);
   1062             }
   1063         }
   1064 
   1065         boolean finished = false;
   1066         if (mReversing) {
   1067             if (mPlayingSet.size() == 1 && mPlayingSet.get(0) == mRootNode) {
   1068                 // The only animation that is running is the delay animation.
   1069                 finished = true;
   1070             } else if (mPlayingSet.isEmpty() && mLastEventId < 3) {
   1071                 // The only remaining animation is the delay animation
   1072                 finished = true;
   1073             }
   1074         } else {
   1075             finished = mPlayingSet.isEmpty() && mLastEventId == mEvents.size() - 1;
   1076         }
   1077 
   1078         if (finished) {
   1079             endAnimation();
   1080             return true;
   1081         }
   1082         return false;
   1083     }
   1084 
   1085     /**
   1086      * @hide
   1087      */
   1088     @Override
   1089     public void commitAnimationFrame(long frameTime) {
   1090         // No op.
   1091     }
   1092 
   1093     @Override
   1094     boolean pulseAnimationFrame(long frameTime) {
   1095         return doAnimationFrame(frameTime);
   1096     }
   1097 
   1098     /**
   1099      * When playing forward, we call start() at the animation's scheduled start time, and make sure
   1100      * to pump a frame at the animation's scheduled end time.
   1101      *
   1102      * When playing in reverse, we should reverse the animation when we hit animation's end event,
   1103      * and expect the animation to end at the its delay ended event, rather than start event.
   1104      */
   1105     private void handleAnimationEvents(int startId, int latestId, long playTime) {
   1106         if (mReversing) {
   1107             startId = startId == -1 ? mEvents.size() : startId;
   1108             for (int i = startId - 1; i >= latestId; i--) {
   1109                 AnimationEvent event = mEvents.get(i);
   1110                 Node node = event.mNode;
   1111                 if (event.mEvent == AnimationEvent.ANIMATION_END) {
   1112                     if (node.mAnimation.isStarted()) {
   1113                         // If the animation has already been started before its due time (i.e.
   1114                         // the child animator is being manipulated outside of the AnimatorSet), we
   1115                         // need to cancel the animation to reset the internal state (e.g. frame
   1116                         // time tracking) and remove the self pulsing callbacks
   1117                         node.mAnimation.cancel();
   1118                     }
   1119                     node.mEnded = false;
   1120                     mPlayingSet.add(event.mNode);
   1121                     node.mAnimation.startWithoutPulsing(true);
   1122                     pulseFrame(node, 0);
   1123                 } else if (event.mEvent == AnimationEvent.ANIMATION_DELAY_ENDED && !node.mEnded) {
   1124                     // end event:
   1125                     pulseFrame(node, getPlayTimeForNode(playTime, node));
   1126                 }
   1127             }
   1128         } else {
   1129             for (int i = startId + 1; i <= latestId; i++) {
   1130                 AnimationEvent event = mEvents.get(i);
   1131                 Node node = event.mNode;
   1132                 if (event.mEvent == AnimationEvent.ANIMATION_START) {
   1133                     mPlayingSet.add(event.mNode);
   1134                     if (node.mAnimation.isStarted()) {
   1135                         // If the animation has already been started before its due time (i.e.
   1136                         // the child animator is being manipulated outside of the AnimatorSet), we
   1137                         // need to cancel the animation to reset the internal state (e.g. frame
   1138                         // time tracking) and remove the self pulsing callbacks
   1139                         node.mAnimation.cancel();
   1140                     }
   1141                     node.mEnded = false;
   1142                     node.mAnimation.startWithoutPulsing(false);
   1143                     pulseFrame(node, 0);
   1144                 } else if (event.mEvent == AnimationEvent.ANIMATION_END && !node.mEnded) {
   1145                     // start event:
   1146                     pulseFrame(node, getPlayTimeForNode(playTime, node));
   1147                 }
   1148             }
   1149         }
   1150     }
   1151 
   1152     /**
   1153      * This method pulses frames into child animations. It scales the input animation play time
   1154      * with the duration scale and pass that to the child animation via pulseAnimationFrame(long).
   1155      *
   1156      * @param node child animator node
   1157      * @param animPlayTime unscaled play time (including start delay) for the child animator
   1158      */
   1159     private void pulseFrame(Node node, long animPlayTime) {
   1160         if (!node.mEnded) {
   1161             float durationScale = ValueAnimator.getDurationScale();
   1162             durationScale = durationScale == 0  ? 1 : durationScale;
   1163             node.mEnded = node.mAnimation.pulseAnimationFrame(
   1164                     (long) (animPlayTime * durationScale));
   1165         }
   1166     }
   1167 
   1168     private long getPlayTimeForNode(long overallPlayTime, Node node) {
   1169         return getPlayTimeForNode(overallPlayTime, node, mReversing);
   1170     }
   1171 
   1172     private long getPlayTimeForNode(long overallPlayTime, Node node, boolean inReverse) {
   1173         if (inReverse) {
   1174             overallPlayTime = getTotalDuration() - overallPlayTime;
   1175             return node.mEndTime - overallPlayTime;
   1176         } else {
   1177             return overallPlayTime - node.mStartTime;
   1178         }
   1179     }
   1180 
   1181     private void startAnimation() {
   1182         addDummyListener();
   1183 
   1184         // Register animation callback
   1185         addAnimationCallback(0);
   1186 
   1187         if (mSeekState.getPlayTimeNormalized() == 0 && mReversing) {
   1188             // Maintain old behavior, if seeked to 0 then call reverse, we'll treat the case
   1189             // the same as no seeking at all.
   1190             mSeekState.reset();
   1191         }
   1192         // Set the child animators to the right end:
   1193         if (mShouldResetValuesAtStart) {
   1194             if (isInitialized()) {
   1195                 skipToEndValue(!mReversing);
   1196             } else if (mReversing) {
   1197                 // Reversing but haven't initialized all the children yet.
   1198                 initChildren();
   1199                 skipToEndValue(!mReversing);
   1200             } else {
   1201                 // If not all children are initialized and play direction is forward
   1202                 for (int i = mEvents.size() - 1; i >= 0; i--) {
   1203                     if (mEvents.get(i).mEvent == AnimationEvent.ANIMATION_DELAY_ENDED) {
   1204                         Animator anim = mEvents.get(i).mNode.mAnimation;
   1205                         // Only reset the animations that have been initialized to start value,
   1206                         // so that if they are defined without a start value, they will get the
   1207                         // values set at the right time (i.e. the next animation run)
   1208                         if (anim.isInitialized()) {
   1209                             anim.skipToEndValue(true);
   1210                         }
   1211                     }
   1212                 }
   1213             }
   1214         }
   1215 
   1216         if (mReversing || mStartDelay == 0 || mSeekState.isActive()) {
   1217             long playTime;
   1218             // If no delay, we need to call start on the first animations to be consistent with old
   1219             // behavior.
   1220             if (mSeekState.isActive()) {
   1221                 mSeekState.updateSeekDirection(mReversing);
   1222                 playTime = mSeekState.getPlayTime();
   1223             } else {
   1224                 playTime = 0;
   1225             }
   1226             int toId = findLatestEventIdForTime(playTime);
   1227             handleAnimationEvents(-1, toId, playTime);
   1228             for (int i = mPlayingSet.size() - 1; i >= 0; i--) {
   1229                 if (mPlayingSet.get(i).mEnded) {
   1230                     mPlayingSet.remove(i);
   1231                 }
   1232             }
   1233             mLastEventId = toId;
   1234         }
   1235     }
   1236 
   1237     // This is to work around the issue in b/34736819, as the old behavior in AnimatorSet had
   1238     // masked a real bug in play movies. TODO: remove this and below once the root cause is fixed.
   1239     private void addDummyListener() {
   1240         for (int i = 1; i < mNodes.size(); i++) {
   1241             mNodes.get(i).mAnimation.addListener(mDummyListener);
   1242         }
   1243     }
   1244 
   1245     private void removeDummyListener() {
   1246         for (int i = 1; i < mNodes.size(); i++) {
   1247             mNodes.get(i).mAnimation.removeListener(mDummyListener);
   1248         }
   1249     }
   1250 
   1251     private int findLatestEventIdForTime(long currentPlayTime) {
   1252         int size = mEvents.size();
   1253         int latestId = mLastEventId;
   1254         // Call start on the first animations now to be consistent with the old behavior
   1255         if (mReversing) {
   1256             currentPlayTime = getTotalDuration() - currentPlayTime;
   1257             mLastEventId = mLastEventId == -1 ? size : mLastEventId;
   1258             for (int j = mLastEventId - 1; j >= 0; j--) {
   1259                 AnimationEvent event = mEvents.get(j);
   1260                 if (event.getTime() >= currentPlayTime) {
   1261                     latestId = j;
   1262                 }
   1263             }
   1264         } else {
   1265             for (int i = mLastEventId + 1; i < size; i++) {
   1266                 AnimationEvent event = mEvents.get(i);
   1267                 // TODO: need a function that accounts for infinite duration to compare time
   1268                 if (event.getTime() != DURATION_INFINITE && event.getTime() <= currentPlayTime) {
   1269                     latestId = i;
   1270                 }
   1271             }
   1272         }
   1273         return latestId;
   1274     }
   1275 
   1276     private void endAnimation() {
   1277         mStarted = false;
   1278         mLastFrameTime = -1;
   1279         mFirstFrame = -1;
   1280         mLastEventId = -1;
   1281         mPaused = false;
   1282         mPauseTime = -1;
   1283         mSeekState.reset();
   1284         mPlayingSet.clear();
   1285 
   1286         // No longer receive callbacks
   1287         removeAnimationCallback();
   1288         // Call end listener
   1289         if (mListeners != null) {
   1290             ArrayList<AnimatorListener> tmpListeners =
   1291                     (ArrayList<AnimatorListener>) mListeners.clone();
   1292             int numListeners = tmpListeners.size();
   1293             for (int i = 0; i < numListeners; ++i) {
   1294                 tmpListeners.get(i).onAnimationEnd(this, mReversing);
   1295             }
   1296         }
   1297         removeDummyListener();
   1298         mSelfPulse = true;
   1299         mReversing = false;
   1300     }
   1301 
   1302     private void removeAnimationCallback() {
   1303         if (!mSelfPulse) {
   1304             return;
   1305         }
   1306         AnimationHandler handler = AnimationHandler.getInstance();
   1307         handler.removeCallback(this);
   1308     }
   1309 
   1310     private void addAnimationCallback(long delay) {
   1311         if (!mSelfPulse) {
   1312             return;
   1313         }
   1314         AnimationHandler handler = AnimationHandler.getInstance();
   1315         handler.addAnimationFrameCallback(this, delay);
   1316     }
   1317 
   1318     @Override
   1319     public AnimatorSet clone() {
   1320         final AnimatorSet anim = (AnimatorSet) super.clone();
   1321         /*
   1322          * The basic clone() operation copies all items. This doesn't work very well for
   1323          * AnimatorSet, because it will copy references that need to be recreated and state
   1324          * that may not apply. What we need to do now is put the clone in an uninitialized
   1325          * state, with fresh, empty data structures. Then we will build up the nodes list
   1326          * manually, as we clone each Node (and its animation). The clone will then be sorted,
   1327          * and will populate any appropriate lists, when it is started.
   1328          */
   1329         final int nodeCount = mNodes.size();
   1330         anim.mStarted = false;
   1331         anim.mLastFrameTime = -1;
   1332         anim.mFirstFrame = -1;
   1333         anim.mLastEventId = -1;
   1334         anim.mPaused = false;
   1335         anim.mPauseTime = -1;
   1336         anim.mSeekState = new SeekState();
   1337         anim.mSelfPulse = true;
   1338         anim.mPlayingSet = new ArrayList<Node>();
   1339         anim.mNodeMap = new ArrayMap<Animator, Node>();
   1340         anim.mNodes = new ArrayList<Node>(nodeCount);
   1341         anim.mEvents = new ArrayList<AnimationEvent>();
   1342         anim.mDummyListener = new AnimatorListenerAdapter() {
   1343             @Override
   1344             public void onAnimationEnd(Animator animation) {
   1345                 if (anim.mNodeMap.get(animation) == null) {
   1346                     throw new AndroidRuntimeException("Error: animation ended is not in the node"
   1347                             + " map");
   1348                 }
   1349                 anim.mNodeMap.get(animation).mEnded = true;
   1350 
   1351             }
   1352         };
   1353         anim.mReversing = false;
   1354         anim.mDependencyDirty = true;
   1355 
   1356         // Walk through the old nodes list, cloning each node and adding it to the new nodemap.
   1357         // One problem is that the old node dependencies point to nodes in the old AnimatorSet.
   1358         // We need to track the old/new nodes in order to reconstruct the dependencies in the clone.
   1359 
   1360         HashMap<Node, Node> clonesMap = new HashMap<>(nodeCount);
   1361         for (int n = 0; n < nodeCount; n++) {
   1362             final Node node = mNodes.get(n);
   1363             Node nodeClone = node.clone();
   1364             // Remove the old internal listener from the cloned child
   1365             nodeClone.mAnimation.removeListener(mDummyListener);
   1366             clonesMap.put(node, nodeClone);
   1367             anim.mNodes.add(nodeClone);
   1368             anim.mNodeMap.put(nodeClone.mAnimation, nodeClone);
   1369         }
   1370 
   1371         anim.mRootNode = clonesMap.get(mRootNode);
   1372         anim.mDelayAnim = (ValueAnimator) anim.mRootNode.mAnimation;
   1373 
   1374         // Now that we've cloned all of the nodes, we're ready to walk through their
   1375         // dependencies, mapping the old dependencies to the new nodes
   1376         for (int i = 0; i < nodeCount; i++) {
   1377             Node node = mNodes.get(i);
   1378             // Update dependencies for node's clone
   1379             Node nodeClone = clonesMap.get(node);
   1380             nodeClone.mLatestParent = node.mLatestParent == null
   1381                     ? null : clonesMap.get(node.mLatestParent);
   1382             int size = node.mChildNodes == null ? 0 : node.mChildNodes.size();
   1383             for (int j = 0; j < size; j++) {
   1384                 nodeClone.mChildNodes.set(j, clonesMap.get(node.mChildNodes.get(j)));
   1385             }
   1386             size = node.mSiblings == null ? 0 : node.mSiblings.size();
   1387             for (int j = 0; j < size; j++) {
   1388                 nodeClone.mSiblings.set(j, clonesMap.get(node.mSiblings.get(j)));
   1389             }
   1390             size = node.mParents == null ? 0 : node.mParents.size();
   1391             for (int j = 0; j < size; j++) {
   1392                 nodeClone.mParents.set(j, clonesMap.get(node.mParents.get(j)));
   1393             }
   1394         }
   1395         return anim;
   1396     }
   1397 
   1398 
   1399     /**
   1400      * AnimatorSet is only reversible when the set contains no sequential animation, and no child
   1401      * animators have a start delay.
   1402      * @hide
   1403      */
   1404     @Override
   1405     public boolean canReverse() {
   1406         return getTotalDuration() != DURATION_INFINITE;
   1407     }
   1408 
   1409     /**
   1410      * Plays the AnimatorSet in reverse. If the animation has been seeked to a specific play time
   1411      * using {@link #setCurrentPlayTime(long)}, it will play backwards from the point seeked when
   1412      * reverse was called. Otherwise, then it will start from the end and play backwards. This
   1413      * behavior is only set for the current animation; future playing of the animation will use the
   1414      * default behavior of playing forward.
   1415      * <p>
   1416      * Note: reverse is not supported for infinite AnimatorSet.
   1417      */
   1418     @Override
   1419     public void reverse() {
   1420         start(true, true);
   1421     }
   1422 
   1423     @Override
   1424     public String toString() {
   1425         String returnVal = "AnimatorSet@" + Integer.toHexString(hashCode()) + "{";
   1426         int size = mNodes.size();
   1427         for (int i = 0; i < size; i++) {
   1428             Node node = mNodes.get(i);
   1429             returnVal += "\n    " + node.mAnimation.toString();
   1430         }
   1431         return returnVal + "\n}";
   1432     }
   1433 
   1434     private void printChildCount() {
   1435         // Print out the child count through a level traverse.
   1436         ArrayList<Node> list = new ArrayList<>(mNodes.size());
   1437         list.add(mRootNode);
   1438         Log.d(TAG, "Current tree: ");
   1439         int index = 0;
   1440         while (index < list.size()) {
   1441             int listSize = list.size();
   1442             StringBuilder builder = new StringBuilder();
   1443             for (; index < listSize; index++) {
   1444                 Node node = list.get(index);
   1445                 int num = 0;
   1446                 if (node.mChildNodes != null) {
   1447                     for (int i = 0; i < node.mChildNodes.size(); i++) {
   1448                         Node child = node.mChildNodes.get(i);
   1449                         if (child.mLatestParent == node) {
   1450                             num++;
   1451                             list.add(child);
   1452                         }
   1453                     }
   1454                 }
   1455                 builder.append(" ");
   1456                 builder.append(num);
   1457             }
   1458             Log.d(TAG, builder.toString());
   1459         }
   1460     }
   1461 
   1462     private void createDependencyGraph() {
   1463         if (!mDependencyDirty) {
   1464             // Check whether any duration of the child animations has changed
   1465             boolean durationChanged = false;
   1466             for (int i = 0; i < mNodes.size(); i++) {
   1467                 Animator anim = mNodes.get(i).mAnimation;
   1468                 if (mNodes.get(i).mTotalDuration != anim.getTotalDuration()) {
   1469                     durationChanged = true;
   1470                     break;
   1471                 }
   1472             }
   1473             if (!durationChanged) {
   1474                 return;
   1475             }
   1476         }
   1477 
   1478         mDependencyDirty = false;
   1479         // Traverse all the siblings and make sure they have all the parents
   1480         int size = mNodes.size();
   1481         for (int i = 0; i < size; i++) {
   1482             mNodes.get(i).mParentsAdded = false;
   1483         }
   1484         for (int i = 0; i < size; i++) {
   1485             Node node = mNodes.get(i);
   1486             if (node.mParentsAdded) {
   1487                 continue;
   1488             }
   1489 
   1490             node.mParentsAdded = true;
   1491             if (node.mSiblings == null) {
   1492                 continue;
   1493             }
   1494 
   1495             // Find all the siblings
   1496             findSiblings(node, node.mSiblings);
   1497             node.mSiblings.remove(node);
   1498 
   1499             // Get parents from all siblings
   1500             int siblingSize = node.mSiblings.size();
   1501             for (int j = 0; j < siblingSize; j++) {
   1502                 node.addParents(node.mSiblings.get(j).mParents);
   1503             }
   1504 
   1505             // Now make sure all siblings share the same set of parents
   1506             for (int j = 0; j < siblingSize; j++) {
   1507                 Node sibling = node.mSiblings.get(j);
   1508                 sibling.addParents(node.mParents);
   1509                 sibling.mParentsAdded = true;
   1510             }
   1511         }
   1512 
   1513         for (int i = 0; i < size; i++) {
   1514             Node node = mNodes.get(i);
   1515             if (node != mRootNode && node.mParents == null) {
   1516                 node.addParent(mRootNode);
   1517             }
   1518         }
   1519 
   1520         // Do a DFS on the tree
   1521         ArrayList<Node> visited = new ArrayList<Node>(mNodes.size());
   1522         // Assign start/end time
   1523         mRootNode.mStartTime = 0;
   1524         mRootNode.mEndTime = mDelayAnim.getDuration();
   1525         updatePlayTime(mRootNode, visited);
   1526 
   1527         sortAnimationEvents();
   1528         mTotalDuration = mEvents.get(mEvents.size() - 1).getTime();
   1529     }
   1530 
   1531     private void sortAnimationEvents() {
   1532         // Sort the list of events in ascending order of their time
   1533         // Create the list including the delay animation.
   1534         mEvents.clear();
   1535         for (int i = 1; i < mNodes.size(); i++) {
   1536             Node node = mNodes.get(i);
   1537             mEvents.add(new AnimationEvent(node, AnimationEvent.ANIMATION_START));
   1538             mEvents.add(new AnimationEvent(node, AnimationEvent.ANIMATION_DELAY_ENDED));
   1539             mEvents.add(new AnimationEvent(node, AnimationEvent.ANIMATION_END));
   1540         }
   1541         mEvents.sort(new Comparator<AnimationEvent>() {
   1542             @Override
   1543             public int compare(AnimationEvent e1, AnimationEvent e2) {
   1544                 long t1 = e1.getTime();
   1545                 long t2 = e2.getTime();
   1546                 if (t1 == t2) {
   1547                     // For events that happen at the same time, we need them to be in the sequence
   1548                     // (end, start, start delay ended)
   1549                     if (e2.mEvent + e1.mEvent == AnimationEvent.ANIMATION_START
   1550                             + AnimationEvent.ANIMATION_DELAY_ENDED) {
   1551                         // Ensure start delay happens after start
   1552                         return e1.mEvent - e2.mEvent;
   1553                     } else {
   1554                         return e2.mEvent - e1.mEvent;
   1555                     }
   1556                 }
   1557                 if (t2 == DURATION_INFINITE) {
   1558                     return -1;
   1559                 }
   1560                 if (t1 == DURATION_INFINITE) {
   1561                     return 1;
   1562                 }
   1563                 // When neither event happens at INFINITE time:
   1564                 return (int) (t1 - t2);
   1565             }
   1566         });
   1567 
   1568         int eventSize = mEvents.size();
   1569         // For the same animation, start event has to happen before end.
   1570         for (int i = 0; i < eventSize;) {
   1571             AnimationEvent event = mEvents.get(i);
   1572             if (event.mEvent == AnimationEvent.ANIMATION_END) {
   1573                 boolean needToSwapStart;
   1574                 if (event.mNode.mStartTime == event.mNode.mEndTime) {
   1575                     needToSwapStart = true;
   1576                 } else if (event.mNode.mEndTime == event.mNode.mStartTime
   1577                         + event.mNode.mAnimation.getStartDelay()) {
   1578                     // Swapping start delay
   1579                     needToSwapStart = false;
   1580                 } else {
   1581                     i++;
   1582                     continue;
   1583                 }
   1584 
   1585                 int startEventId = eventSize;
   1586                 int startDelayEndId = eventSize;
   1587                 for (int j = i + 1; j < eventSize; j++) {
   1588                     if (startEventId < eventSize && startDelayEndId < eventSize) {
   1589                         break;
   1590                     }
   1591                     if (mEvents.get(j).mNode == event.mNode) {
   1592                         if (mEvents.get(j).mEvent == AnimationEvent.ANIMATION_START) {
   1593                             // Found start event
   1594                             startEventId = j;
   1595                         } else if (mEvents.get(j).mEvent == AnimationEvent.ANIMATION_DELAY_ENDED) {
   1596                             startDelayEndId = j;
   1597                         }
   1598                     }
   1599 
   1600                 }
   1601                 if (needToSwapStart && startEventId == mEvents.size()) {
   1602                     throw new UnsupportedOperationException("Something went wrong, no start is"
   1603                             + "found after stop for an animation that has the same start and end"
   1604                             + "time.");
   1605 
   1606                 }
   1607                 if (startDelayEndId == mEvents.size()) {
   1608                     throw new UnsupportedOperationException("Something went wrong, no start"
   1609                             + "delay end is found after stop for an animation");
   1610 
   1611                 }
   1612 
   1613                 // We need to make sure start is inserted before start delay ended event,
   1614                 // because otherwise inserting start delay ended events first would change
   1615                 // the start event index.
   1616                 if (needToSwapStart) {
   1617                     AnimationEvent startEvent = mEvents.remove(startEventId);
   1618                     mEvents.add(i, startEvent);
   1619                     i++;
   1620                 }
   1621 
   1622                 AnimationEvent startDelayEndEvent = mEvents.remove(startDelayEndId);
   1623                 mEvents.add(i, startDelayEndEvent);
   1624                 i += 2;
   1625             } else {
   1626                 i++;
   1627             }
   1628         }
   1629 
   1630         if (!mEvents.isEmpty() && mEvents.get(0).mEvent != AnimationEvent.ANIMATION_START) {
   1631             throw new UnsupportedOperationException(
   1632                     "Sorting went bad, the start event should always be at index 0");
   1633         }
   1634 
   1635         // Add AnimatorSet's start delay node to the beginning
   1636         mEvents.add(0, new AnimationEvent(mRootNode, AnimationEvent.ANIMATION_START));
   1637         mEvents.add(1, new AnimationEvent(mRootNode, AnimationEvent.ANIMATION_DELAY_ENDED));
   1638         mEvents.add(2, new AnimationEvent(mRootNode, AnimationEvent.ANIMATION_END));
   1639 
   1640         if (mEvents.get(mEvents.size() - 1).mEvent == AnimationEvent.ANIMATION_START
   1641                 || mEvents.get(mEvents.size() - 1).mEvent == AnimationEvent.ANIMATION_DELAY_ENDED) {
   1642             throw new UnsupportedOperationException(
   1643                     "Something went wrong, the last event is not an end event");
   1644         }
   1645     }
   1646 
   1647     /**
   1648      * Based on parent's start/end time, calculate children's start/end time. If cycle exists in
   1649      * the graph, all the nodes on the cycle will be marked to start at {@link #DURATION_INFINITE},
   1650      * meaning they will ever play.
   1651      */
   1652     private void updatePlayTime(Node parent,  ArrayList<Node> visited) {
   1653         if (parent.mChildNodes == null) {
   1654             if (parent == mRootNode) {
   1655                 // All the animators are in a cycle
   1656                 for (int i = 0; i < mNodes.size(); i++) {
   1657                     Node node = mNodes.get(i);
   1658                     if (node != mRootNode) {
   1659                         node.mStartTime = DURATION_INFINITE;
   1660                         node.mEndTime = DURATION_INFINITE;
   1661                     }
   1662                 }
   1663             }
   1664             return;
   1665         }
   1666 
   1667         visited.add(parent);
   1668         int childrenSize = parent.mChildNodes.size();
   1669         for (int i = 0; i < childrenSize; i++) {
   1670             Node child = parent.mChildNodes.get(i);
   1671             child.mTotalDuration = child.mAnimation.getTotalDuration();  // Update cached duration.
   1672 
   1673             int index = visited.indexOf(child);
   1674             if (index >= 0) {
   1675                 // Child has been visited, cycle found. Mark all the nodes in the cycle.
   1676                 for (int j = index; j < visited.size(); j++) {
   1677                     visited.get(j).mLatestParent = null;
   1678                     visited.get(j).mStartTime = DURATION_INFINITE;
   1679                     visited.get(j).mEndTime = DURATION_INFINITE;
   1680                 }
   1681                 child.mStartTime = DURATION_INFINITE;
   1682                 child.mEndTime = DURATION_INFINITE;
   1683                 child.mLatestParent = null;
   1684                 Log.w(TAG, "Cycle found in AnimatorSet: " + this);
   1685                 continue;
   1686             }
   1687 
   1688             if (child.mStartTime != DURATION_INFINITE) {
   1689                 if (parent.mEndTime == DURATION_INFINITE) {
   1690                     child.mLatestParent = parent;
   1691                     child.mStartTime = DURATION_INFINITE;
   1692                     child.mEndTime = DURATION_INFINITE;
   1693                 } else {
   1694                     if (parent.mEndTime >= child.mStartTime) {
   1695                         child.mLatestParent = parent;
   1696                         child.mStartTime = parent.mEndTime;
   1697                     }
   1698 
   1699                     child.mEndTime = child.mTotalDuration == DURATION_INFINITE
   1700                             ? DURATION_INFINITE : child.mStartTime + child.mTotalDuration;
   1701                 }
   1702             }
   1703             updatePlayTime(child, visited);
   1704         }
   1705         visited.remove(parent);
   1706     }
   1707 
   1708     // Recursively find all the siblings
   1709     private void findSiblings(Node node, ArrayList<Node> siblings) {
   1710         if (!siblings.contains(node)) {
   1711             siblings.add(node);
   1712             if (node.mSiblings == null) {
   1713                 return;
   1714             }
   1715             for (int i = 0; i < node.mSiblings.size(); i++) {
   1716                 findSiblings(node.mSiblings.get(i), siblings);
   1717             }
   1718         }
   1719     }
   1720 
   1721     /**
   1722      * @hide
   1723      * TODO: For animatorSet defined in XML, we can use a flag to indicate what the play order
   1724      * if defined (i.e. sequential or together), then we can use the flag instead of calculating
   1725      * dynamically. Note that when AnimatorSet is empty this method returns true.
   1726      * @return whether all the animators in the set are supposed to play together
   1727      */
   1728     public boolean shouldPlayTogether() {
   1729         updateAnimatorsDuration();
   1730         createDependencyGraph();
   1731         // All the child nodes are set out to play right after the delay animation
   1732         return mRootNode.mChildNodes == null || mRootNode.mChildNodes.size() == mNodes.size() - 1;
   1733     }
   1734 
   1735     @Override
   1736     public long getTotalDuration() {
   1737         updateAnimatorsDuration();
   1738         createDependencyGraph();
   1739         return mTotalDuration;
   1740     }
   1741 
   1742     private Node getNodeForAnimation(Animator anim) {
   1743         Node node = mNodeMap.get(anim);
   1744         if (node == null) {
   1745             node = new Node(anim);
   1746             mNodeMap.put(anim, node);
   1747             mNodes.add(node);
   1748         }
   1749         return node;
   1750     }
   1751 
   1752     /**
   1753      * A Node is an embodiment of both the Animator that it wraps as well as
   1754      * any dependencies that are associated with that Animation. This includes
   1755      * both dependencies upon other nodes (in the dependencies list) as
   1756      * well as dependencies of other nodes upon this (in the nodeDependents list).
   1757      */
   1758     private static class Node implements Cloneable {
   1759         Animator mAnimation;
   1760 
   1761         /**
   1762          * Child nodes are the nodes associated with animations that will be played immediately
   1763          * after current node.
   1764          */
   1765         ArrayList<Node> mChildNodes = null;
   1766 
   1767         /**
   1768          * Flag indicating whether the animation in this node is finished. This flag
   1769          * is used by AnimatorSet to check, as each animation ends, whether all child animations
   1770          * are mEnded and it's time to send out an end event for the entire AnimatorSet.
   1771          */
   1772         boolean mEnded = false;
   1773 
   1774         /**
   1775          * Nodes with animations that are defined to play simultaneously with the animation
   1776          * associated with this current node.
   1777          */
   1778         ArrayList<Node> mSiblings;
   1779 
   1780         /**
   1781          * Parent nodes are the nodes with animations preceding current node's animation. Parent
   1782          * nodes here are derived from user defined animation sequence.
   1783          */
   1784         ArrayList<Node> mParents;
   1785 
   1786         /**
   1787          * Latest parent is the parent node associated with a animation that finishes after all
   1788          * the other parents' animations.
   1789          */
   1790         Node mLatestParent = null;
   1791 
   1792         boolean mParentsAdded = false;
   1793         long mStartTime = 0;
   1794         long mEndTime = 0;
   1795         long mTotalDuration = 0;
   1796 
   1797         /**
   1798          * Constructs the Node with the animation that it encapsulates. A Node has no
   1799          * dependencies by default; dependencies are added via the addDependency()
   1800          * method.
   1801          *
   1802          * @param animation The animation that the Node encapsulates.
   1803          */
   1804         public Node(Animator animation) {
   1805             this.mAnimation = animation;
   1806         }
   1807 
   1808         @Override
   1809         public Node clone() {
   1810             try {
   1811                 Node node = (Node) super.clone();
   1812                 node.mAnimation = mAnimation.clone();
   1813                 if (mChildNodes != null) {
   1814                     node.mChildNodes = new ArrayList<>(mChildNodes);
   1815                 }
   1816                 if (mSiblings != null) {
   1817                     node.mSiblings = new ArrayList<>(mSiblings);
   1818                 }
   1819                 if (mParents != null) {
   1820                     node.mParents = new ArrayList<>(mParents);
   1821                 }
   1822                 node.mEnded = false;
   1823                 return node;
   1824             } catch (CloneNotSupportedException e) {
   1825                throw new AssertionError();
   1826             }
   1827         }
   1828 
   1829         void addChild(Node node) {
   1830             if (mChildNodes == null) {
   1831                 mChildNodes = new ArrayList<>();
   1832             }
   1833             if (!mChildNodes.contains(node)) {
   1834                 mChildNodes.add(node);
   1835                 node.addParent(this);
   1836             }
   1837         }
   1838 
   1839         public void addSibling(Node node) {
   1840             if (mSiblings == null) {
   1841                 mSiblings = new ArrayList<Node>();
   1842             }
   1843             if (!mSiblings.contains(node)) {
   1844                 mSiblings.add(node);
   1845                 node.addSibling(this);
   1846             }
   1847         }
   1848 
   1849         public void addParent(Node node) {
   1850             if (mParents == null) {
   1851                 mParents =  new ArrayList<Node>();
   1852             }
   1853             if (!mParents.contains(node)) {
   1854                 mParents.add(node);
   1855                 node.addChild(this);
   1856             }
   1857         }
   1858 
   1859         public void addParents(ArrayList<Node> parents) {
   1860             if (parents == null) {
   1861                 return;
   1862             }
   1863             int size = parents.size();
   1864             for (int i = 0; i < size; i++) {
   1865                 addParent(parents.get(i));
   1866             }
   1867         }
   1868     }
   1869 
   1870     /**
   1871      * This class is a wrapper around a node and an event for the animation corresponding to the
   1872      * node. The 3 types of events represent the start of an animation, the end of a start delay of
   1873      * an animation, and the end of an animation. When playing forward (i.e. in the non-reverse
   1874      * direction), start event marks when start() should be called, and end event corresponds to
   1875      * when the animation should finish. When playing in reverse, start delay will not be a part
   1876      * of the animation. Therefore, reverse() is called at the end event, and animation should end
   1877      * at the delay ended event.
   1878      */
   1879     private static class AnimationEvent {
   1880         static final int ANIMATION_START = 0;
   1881         static final int ANIMATION_DELAY_ENDED = 1;
   1882         static final int ANIMATION_END = 2;
   1883         final Node mNode;
   1884         final int mEvent;
   1885 
   1886         AnimationEvent(Node node, int event) {
   1887             mNode = node;
   1888             mEvent = event;
   1889         }
   1890 
   1891         long getTime() {
   1892             if (mEvent == ANIMATION_START) {
   1893                 return mNode.mStartTime;
   1894             } else if (mEvent == ANIMATION_DELAY_ENDED) {
   1895                 return mNode.mStartTime == DURATION_INFINITE
   1896                         ? DURATION_INFINITE : mNode.mStartTime + mNode.mAnimation.getStartDelay();
   1897             } else {
   1898                 return mNode.mEndTime;
   1899             }
   1900         }
   1901 
   1902         public String toString() {
   1903             String eventStr = mEvent == ANIMATION_START ? "start" : (
   1904                     mEvent == ANIMATION_DELAY_ENDED ? "delay ended" : "end");
   1905             return eventStr + " " + mNode.mAnimation.toString();
   1906         }
   1907     }
   1908 
   1909     private class SeekState {
   1910         private long mPlayTime = -1;
   1911         private boolean mSeekingInReverse = false;
   1912         void reset() {
   1913             mPlayTime = -1;
   1914             mSeekingInReverse = false;
   1915         }
   1916 
   1917         void setPlayTime(long playTime, boolean inReverse) {
   1918             // TODO: This can be simplified.
   1919 
   1920             // Clamp the play time
   1921             if (getTotalDuration() != DURATION_INFINITE) {
   1922                 mPlayTime = Math.min(playTime, getTotalDuration() - mStartDelay);
   1923             }
   1924             mPlayTime = Math.max(0, mPlayTime);
   1925             mSeekingInReverse = inReverse;
   1926         }
   1927 
   1928         void updateSeekDirection(boolean inReverse) {
   1929             // Change seek direction without changing the overall fraction
   1930             if (inReverse && getTotalDuration() == DURATION_INFINITE) {
   1931                 throw new UnsupportedOperationException("Error: Cannot reverse infinite animator"
   1932                         + " set");
   1933             }
   1934             if (mPlayTime >= 0) {
   1935                 if (inReverse != mSeekingInReverse) {
   1936                     mPlayTime = getTotalDuration() - mStartDelay - mPlayTime;
   1937                     mSeekingInReverse = inReverse;
   1938                 }
   1939             }
   1940         }
   1941 
   1942         long getPlayTime() {
   1943             return mPlayTime;
   1944         }
   1945 
   1946         /**
   1947          * Returns the playtime assuming the animation is forward playing
   1948          */
   1949         long getPlayTimeNormalized() {
   1950             if (mReversing) {
   1951                 return getTotalDuration() - mStartDelay - mPlayTime;
   1952             }
   1953             return mPlayTime;
   1954         }
   1955 
   1956         boolean isActive() {
   1957             return mPlayTime != -1;
   1958         }
   1959     }
   1960 
   1961     /**
   1962      * The <code>Builder</code> object is a utility class to facilitate adding animations to a
   1963      * <code>AnimatorSet</code> along with the relationships between the various animations. The
   1964      * intention of the <code>Builder</code> methods, along with the {@link
   1965      * AnimatorSet#play(Animator) play()} method of <code>AnimatorSet</code> is to make it possible
   1966      * to express the dependency relationships of animations in a natural way. Developers can also
   1967      * use the {@link AnimatorSet#playTogether(Animator[]) playTogether()} and {@link
   1968      * AnimatorSet#playSequentially(Animator[]) playSequentially()} methods if these suit the need,
   1969      * but it might be easier in some situations to express the AnimatorSet of animations in pairs.
   1970      * <p/>
   1971      * <p>The <code>Builder</code> object cannot be constructed directly, but is rather constructed
   1972      * internally via a call to {@link AnimatorSet#play(Animator)}.</p>
   1973      * <p/>
   1974      * <p>For example, this sets up a AnimatorSet to play anim1 and anim2 at the same time, anim3 to
   1975      * play when anim2 finishes, and anim4 to play when anim3 finishes:</p>
   1976      * <pre>
   1977      *     AnimatorSet s = new AnimatorSet();
   1978      *     s.play(anim1).with(anim2);
   1979      *     s.play(anim2).before(anim3);
   1980      *     s.play(anim4).after(anim3);
   1981      * </pre>
   1982      * <p/>
   1983      * <p>Note in the example that both {@link Builder#before(Animator)} and {@link
   1984      * Builder#after(Animator)} are used. These are just different ways of expressing the same
   1985      * relationship and are provided to make it easier to say things in a way that is more natural,
   1986      * depending on the situation.</p>
   1987      * <p/>
   1988      * <p>It is possible to make several calls into the same <code>Builder</code> object to express
   1989      * multiple relationships. However, note that it is only the animation passed into the initial
   1990      * {@link AnimatorSet#play(Animator)} method that is the dependency in any of the successive
   1991      * calls to the <code>Builder</code> object. For example, the following code starts both anim2
   1992      * and anim3 when anim1 ends; there is no direct dependency relationship between anim2 and
   1993      * anim3:
   1994      * <pre>
   1995      *   AnimatorSet s = new AnimatorSet();
   1996      *   s.play(anim1).before(anim2).before(anim3);
   1997      * </pre>
   1998      * If the desired result is to play anim1 then anim2 then anim3, this code expresses the
   1999      * relationship correctly:</p>
   2000      * <pre>
   2001      *   AnimatorSet s = new AnimatorSet();
   2002      *   s.play(anim1).before(anim2);
   2003      *   s.play(anim2).before(anim3);
   2004      * </pre>
   2005      * <p/>
   2006      * <p>Note that it is possible to express relationships that cannot be resolved and will not
   2007      * result in sensible results. For example, <code>play(anim1).after(anim1)</code> makes no
   2008      * sense. In general, circular dependencies like this one (or more indirect ones where a depends
   2009      * on b, which depends on c, which depends on a) should be avoided. Only create AnimatorSets
   2010      * that can boil down to a simple, one-way relationship of animations starting with, before, and
   2011      * after other, different, animations.</p>
   2012      */
   2013     public class Builder {
   2014 
   2015         /**
   2016          * This tracks the current node being processed. It is supplied to the play() method
   2017          * of AnimatorSet and passed into the constructor of Builder.
   2018          */
   2019         private Node mCurrentNode;
   2020 
   2021         /**
   2022          * package-private constructor. Builders are only constructed by AnimatorSet, when the
   2023          * play() method is called.
   2024          *
   2025          * @param anim The animation that is the dependency for the other animations passed into
   2026          * the other methods of this Builder object.
   2027          */
   2028         Builder(Animator anim) {
   2029             mDependencyDirty = true;
   2030             mCurrentNode = getNodeForAnimation(anim);
   2031         }
   2032 
   2033         /**
   2034          * Sets up the given animation to play at the same time as the animation supplied in the
   2035          * {@link AnimatorSet#play(Animator)} call that created this <code>Builder</code> object.
   2036          *
   2037          * @param anim The animation that will play when the animation supplied to the
   2038          * {@link AnimatorSet#play(Animator)} method starts.
   2039          */
   2040         public Builder with(Animator anim) {
   2041             Node node = getNodeForAnimation(anim);
   2042             mCurrentNode.addSibling(node);
   2043             return this;
   2044         }
   2045 
   2046         /**
   2047          * Sets up the given animation to play when the animation supplied in the
   2048          * {@link AnimatorSet#play(Animator)} call that created this <code>Builder</code> object
   2049          * ends.
   2050          *
   2051          * @param anim The animation that will play when the animation supplied to the
   2052          * {@link AnimatorSet#play(Animator)} method ends.
   2053          */
   2054         public Builder before(Animator anim) {
   2055             Node node = getNodeForAnimation(anim);
   2056             mCurrentNode.addChild(node);
   2057             return this;
   2058         }
   2059 
   2060         /**
   2061          * Sets up the given animation to play when the animation supplied in the
   2062          * {@link AnimatorSet#play(Animator)} call that created this <code>Builder</code> object
   2063          * to start when the animation supplied in this method call ends.
   2064          *
   2065          * @param anim The animation whose end will cause the animation supplied to the
   2066          * {@link AnimatorSet#play(Animator)} method to play.
   2067          */
   2068         public Builder after(Animator anim) {
   2069             Node node = getNodeForAnimation(anim);
   2070             mCurrentNode.addParent(node);
   2071             return this;
   2072         }
   2073 
   2074         /**
   2075          * Sets up the animation supplied in the
   2076          * {@link AnimatorSet#play(Animator)} call that created this <code>Builder</code> object
   2077          * to play when the given amount of time elapses.
   2078          *
   2079          * @param delay The number of milliseconds that should elapse before the
   2080          * animation starts.
   2081          */
   2082         public Builder after(long delay) {
   2083             // setup dummy ValueAnimator just to run the clock
   2084             ValueAnimator anim = ValueAnimator.ofFloat(0f, 1f);
   2085             anim.setDuration(delay);
   2086             after(anim);
   2087             return this;
   2088         }
   2089 
   2090     }
   2091 
   2092 }
   2093