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