Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 4295ca00 authored by Chet Haase's avatar Chet Haase Committed by Android (Google) Code Review
Browse files

Merge "Fix cancellation of AnimatorSet when child animation has delay"

parents 06db72d5 8b699792
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -2179,6 +2179,7 @@ package android.animation {
    method public java.util.ArrayList<android.animation.Animator.AnimatorListener> getListeners();
    method public abstract long getStartDelay();
    method public abstract boolean isRunning();
    method public boolean isStarted();
    method public void removeAllListeners();
    method public void removeListener(android.animation.Animator.AnimatorListener);
    method public abstract android.animation.Animator setDuration(long);
+18 −1
Original line number Diff line number Diff line
@@ -111,11 +111,28 @@ public abstract class Animator implements Cloneable {
    public abstract void setInterpolator(TimeInterpolator value);

    /**
     * Returns whether this Animator is currently running (having been started and not yet ended).
     * Returns whether this Animator is currently running (having been started and gone past any
     * initial startDelay period and not yet ended).
     *
     * @return Whether the Animator is running.
     */
    public abstract boolean isRunning();

    /**
     * Returns whether this Animator has been started and not yet ended. This state is a superset
     * of the state of {@link #isRunning()}, because an Animator with a nonzero
     * {@link #getStartDelay() startDelay} will return true for {@link #isStarted()} during the
     * delay phase, whereas {@link #isRunning()} will return true only after the delay phase
     * is complete.
     *
     * @return Whether the Animator has been started and not yet ended.
     */
    public boolean isStarted() {
        // Default method returns value for isRunning(). Subclasses should override to return a
        // real value.
        return isRunning();
    }

    /**
     * Adds a listener to the set of listeners that are sent events through the life of an
     * animation, such as start, repeat, and end.
+37 −14
Original line number Diff line number Diff line
@@ -95,6 +95,12 @@ public final class AnimatorSet extends Animator {
     */
    boolean mTerminated = false;

    /**
     * Indicates whether an AnimatorSet has been start()'d, whether or
     * not there is a nonzero startDelay.
     */
    private boolean mStarted = false;

    // The amount of time in ms to delay starting the animation after start() is called
    private long mStartDelay = 0;

@@ -267,14 +273,14 @@ public final class AnimatorSet extends Animator {
    /**
     * {@inheritDoc}
     *
     * <p>Note that canceling a <code>AnimatorSet</code> also cancels all of the animations that it is
     * responsible for.</p>
     * <p>Note that canceling a <code>AnimatorSet</code> also cancels all of the animations that it
     * is responsible for.</p>
     */
    @SuppressWarnings("unchecked")
    @Override
    public void cancel() {
        mTerminated = true;
        if (isRunning()) {
        if (isStarted()) {
            ArrayList<AnimatorListener> tmpListeners = null;
            if (mListeners != null) {
                tmpListeners = (ArrayList<AnimatorListener>) mListeners.clone();
@@ -296,6 +302,7 @@ public final class AnimatorSet extends Animator {
                    listener.onAnimationEnd(this);
                }
            }
            mStarted = false;
        }
    }

@@ -308,7 +315,7 @@ public final class AnimatorSet extends Animator {
    @Override
    public void end() {
        mTerminated = true;
        if (isRunning()) {
        if (isStarted()) {
            if (mSortedNodes.size() != mNodes.size()) {
                // hasn't been started yet - sort the nodes now, then end them
                sortNodes();
@@ -334,12 +341,13 @@ public final class AnimatorSet extends Animator {
                    listener.onAnimationEnd(this);
                }
            }
            mStarted = false;
        }
    }

    /**
     * Returns true if any of the child animations of this AnimatorSet have been started and have not
     * yet ended.
     * Returns true if any of the child animations of this AnimatorSet have been started and have
     * not yet ended.
     * @return Whether this AnimatorSet has been started and has not yet ended.
     */
    @Override
@@ -349,8 +357,12 @@ public final class AnimatorSet extends Animator {
                return true;
            }
        }
        // Also return true if we're currently running the startDelay animator
        return (mDelayAnim != null && mDelayAnim.isRunning());
        return false;
    }

    @Override
    public boolean isStarted() {
        return mStarted;
    }

    /**
@@ -435,6 +447,7 @@ public final class AnimatorSet extends Animator {
    @Override
    public void start() {
        mTerminated = false;
        mStarted = true;

        // First, sort the nodes (if necessary). This will ensure that sortedNodes
        // contains the animation nodes in the correct order.
@@ -514,9 +527,17 @@ public final class AnimatorSet extends Animator {
            int numListeners = tmpListeners.size();
            for (int i = 0; i < numListeners; ++i) {
                tmpListeners.get(i).onAnimationStart(this);
                if (mNodes.size() == 0) {
            }
        }
        if (mNodes.size() == 0 && mStartDelay == 0) {
            // Handle unusual case where empty AnimatorSet is started - should send out
            // end event immediately since the event will not be sent out at all otherwise
            mStarted = false;
            if (mListeners != null) {
                ArrayList<AnimatorListener> tmpListeners =
                        (ArrayList<AnimatorListener>) mListeners.clone();
                int numListeners = tmpListeners.size();
                for (int i = 0; i < numListeners; ++i) {
                    tmpListeners.get(i).onAnimationEnd(this);
                }
            }
@@ -536,6 +557,7 @@ public final class AnimatorSet extends Animator {
         */
        anim.mNeedsSort = true;
        anim.mTerminated = false;
        anim.mStarted = false;
        anim.mPlayingSet = new ArrayList<Animator>();
        anim.mNodeMap = new HashMap<Animator, Node>();
        anim.mNodes = new ArrayList<Node>();
@@ -732,6 +754,7 @@ public final class AnimatorSet extends Animator {
                            tmpListeners.get(i).onAnimationEnd(mAnimatorSet);
                        }
                    }
                    mAnimatorSet.mStarted = false;
                }
            }
        }
@@ -936,9 +959,9 @@ public final class AnimatorSet extends Animator {
     * The <code>Builder</code> object is a utility class to facilitate adding animations to a
     * <code>AnimatorSet</code> along with the relationships between the various animations. The
     * intention of the <code>Builder</code> methods, along with the {@link
     * AnimatorSet#play(Animator) play()} method of <code>AnimatorSet</code> is to make it possible to
     * express the dependency relationships of animations in a natural way. Developers can also use
     * the {@link AnimatorSet#playTogether(Animator[]) playTogether()} and {@link
     * AnimatorSet#play(Animator) play()} method of <code>AnimatorSet</code> is to make it possible
     * to express the dependency relationships of animations in a natural way. Developers can also
     * use the {@link AnimatorSet#playTogether(Animator[]) playTogether()} and {@link
     * AnimatorSet#playSequentially(Animator[]) playSequentially()} methods if these suit the need,
     * but it might be easier in some situations to express the AnimatorSet of animations in pairs.
     * <p/>
+18 −5
Original line number Diff line number Diff line
@@ -193,6 +193,12 @@ public class ValueAnimator extends Animator {
     * Note that delayed animations are different: they are not started until their first
     * animation frame, which occurs after their delay elapses.
     */
    private boolean mRunning = false;

    /**
     * Additional playing state to indicate whether an animator has been start()'d, whether or
     * not there is a nonzero startDelay.
     */
    private boolean mStarted = false;

    /**
@@ -628,7 +634,7 @@ public class ValueAnimator extends Animator {
                        for (int i = 0; i < numReadyAnims; ++i) {
                            ValueAnimator anim = readyAnims.get(i);
                            anim.startAnimation();
                            anim.mStarted = true;
                            anim.mRunning = true;
                            delayedAnims.remove(anim);
                        }
                        readyAnims.clear();
@@ -913,13 +919,14 @@ public class ValueAnimator extends Animator {
        mPlayingBackwards = playBackwards;
        mCurrentIteration = 0;
        mPlayingState = STOPPED;
        mStarted = true;
        mStartedDelay = false;
        sPendingAnimations.get().add(this);
        if (mStartDelay == 0) {
            // This sets the initial value of the animation, prior to actually starting it running
            setCurrentPlayTime(getCurrentPlayTime());
            mPlayingState = STOPPED;
            mStarted = true;
            mRunning = true;

            if (mListeners != null) {
                ArrayList<AnimatorListener> tmpListeners =
@@ -950,7 +957,7 @@ public class ValueAnimator extends Animator {
        if (mPlayingState != STOPPED || sPendingAnimations.get().contains(this) ||
                sDelayedAnims.get().contains(this)) {
            // Only notify listeners if the animator has actually started
            if (mStarted && mListeners != null) {
            if (mRunning && mListeners != null) {
                ArrayList<AnimatorListener> tmpListeners =
                        (ArrayList<AnimatorListener>) mListeners.clone();
                for (AnimatorListener listener : tmpListeners) {
@@ -982,7 +989,12 @@ public class ValueAnimator extends Animator {

    @Override
    public boolean isRunning() {
        return (mPlayingState == RUNNING || mStarted);
        return (mPlayingState == RUNNING || mRunning);
    }

    @Override
    public boolean isStarted() {
        return mStarted;
    }

    /**
@@ -1013,7 +1025,7 @@ public class ValueAnimator extends Animator {
        sPendingAnimations.get().remove(this);
        sDelayedAnims.get().remove(this);
        mPlayingState = STOPPED;
        if (mStarted && mListeners != null) {
        if (mRunning && mListeners != null) {
            ArrayList<AnimatorListener> tmpListeners =
                    (ArrayList<AnimatorListener>) mListeners.clone();
            int numListeners = tmpListeners.size();
@@ -1021,6 +1033,7 @@ public class ValueAnimator extends Animator {
                tmpListeners.get(i).onAnimationEnd(this);
            }
        }
        mRunning = false;
        mStarted = false;
    }

+54 −5
Original line number Diff line number Diff line
@@ -15,25 +15,74 @@
*/
package android.animation;

import android.os.Handler;
import android.test.suitebuilder.annotation.MediumTest;
import android.test.suitebuilder.annotation.SmallTest;
import android.widget.Button;
import com.android.frameworks.coretests.R;

import java.util.concurrent.TimeUnit;

/**
 * Listener tests for AnimatorSet.
 */
public class AnimatorSetEventsTest extends EventsTest {

    Button button;
    ObjectAnimator xAnim = ObjectAnimator.ofFloat(this, "translationX", 0, 100);
    ObjectAnimator yAnim = ObjectAnimator.ofFloat(this, "translationY", 0, 100);

    @Override
    public void setUp() throws Exception {
        final BasicAnimatorActivity activity = getActivity();
        Button button = (Button) activity.findViewById(R.id.animatingButton);

        ObjectAnimator xAnim = ObjectAnimator.ofFloat(button, "translationX", 0, 100);
        ObjectAnimator yAnim = ObjectAnimator.ofFloat(button, "translationX", 0, 100);
        button = (Button) getActivity().findViewById(R.id.animatingButton);
        mAnimator = new AnimatorSet();
        ((AnimatorSet)mAnimator).playSequentially(xAnim, yAnim);

        super.setUp();
    }

    @Override
    protected long getTimeout() {
        return (xAnim.getDuration() + yAnim.getDuration()) +
                (xAnim.getStartDelay() + yAnim.getStartDelay()) +
                ANIM_DELAY + FUTURE_RELEASE_DELAY;
    }

    /**
     * Tests that an AnimatorSet can be correctly canceled during the delay of one of
     * its children
     */
    @MediumTest
    public void testPlayingCancelDuringChildDelay() throws Exception {
        yAnim.setStartDelay(500);
        final AnimatorSet animSet = new AnimatorSet();
        animSet.playSequentially(xAnim, yAnim);
        mFutureListener = new FutureReleaseListener(mFuture);
        getActivity().runOnUiThread(new Runnable() {
            @Override
            public void run() {
                try {
                    Handler handler = new Handler();
                    animSet.addListener(mFutureListener);
                    mRunning = true;
                    animSet.start();
                    handler.postDelayed(new Canceler(animSet, mFuture), ANIM_DURATION + 250);
                } catch (junit.framework.AssertionFailedError e) {
                    mFuture.setException(new RuntimeException(e));
                }
            }
        });
        mFuture.get(getTimeout(), TimeUnit.MILLISECONDS);
    }

    public void setTranslationX(float value) {
        button.setTranslationX(value);
    }


    public void setTranslationY(float value) {
        button.setTranslationY(value);
    }


}
Loading