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

Commit 7dfacdb1 authored by Chet Haase's avatar Chet Haase
Browse files

Fix Animator cancel() behavior

Previously, calling cancel() on an Animator would cause onAnimationCancel
events to be sent to all listeners. This was confusing for listeners
that were keying off this event for performing other actions, when the original
animator wasn't truly canceled (because it wasn't even running, or had already
been canceled earlier). This change hinges listener notification on
the animator actually running; no events are sent otherwise.

Also added the first set of Animator tests to verify that this behavior
is correct.

Change-Id: I81ab56559b5c0343c8dc7880e1c8235f3020975b
parent 221bfab8
Loading
Loading
Loading
Loading
+84 −64
Original line number Diff line number Diff line
@@ -87,11 +87,13 @@ public final class AnimatorSet extends Animator {
    private AnimatorSetListener mSetListener = null;

    /**
     * Flag indicating that the AnimatorSet has been canceled (by calling cancel() or end()).
     * Flag indicating that the AnimatorSet has been manually
     * terminated (by calling cancel() or end()).
     * This flag is used to avoid starting other animations when currently-playing
     * child animations of this AnimatorSet end.
     * child animations of this AnimatorSet end. It also determines whether cancel/end
     * notifications are sent out via the normal AnimatorSetListener mechanism.
     */
    boolean mCanceled = false;
    boolean mTerminated = false;

    // The amount of time in ms to delay starting the animation after start() is called
    private long mStartDelay = 0;
@@ -271,10 +273,11 @@ public final class AnimatorSet extends Animator {
    @SuppressWarnings("unchecked")
    @Override
    public void cancel() {
        mCanceled = true;
        mTerminated = true;
        if (isRunning()) {
            ArrayList<AnimatorListener> tmpListeners = null;
            if (mListeners != null) {
            ArrayList<AnimatorListener> tmpListeners =
                    (ArrayList<AnimatorListener>) mListeners.clone();
                tmpListeners = (ArrayList<AnimatorListener>) mListeners.clone();
                for (AnimatorListener listener : tmpListeners) {
                    listener.onAnimationCancel(this);
                }
@@ -283,19 +286,16 @@ public final class AnimatorSet extends Animator {
                // If we're currently in the startDelay period, just cancel that animator and
                // send out the end event to all listeners
                mDelayAnim.cancel();
            if (mListeners != null) {
                ArrayList<AnimatorListener> tmpListeners =
                        (ArrayList<AnimatorListener>) mListeners.clone();
            } else  if (mSortedNodes.size() > 0) {
                for (Node node : mSortedNodes) {
                    node.animation.cancel();
                }
            }
            if (tmpListeners != null) {
                for (AnimatorListener listener : tmpListeners) {
                    listener.onAnimationEnd(this);
                }
            }
            return;
        }
        if (mSortedNodes.size() > 0) {
            for (Node node : mSortedNodes) {
                node.animation.cancel();
            }
        }
    }

@@ -307,7 +307,8 @@ public final class AnimatorSet extends Animator {
     */
    @Override
    public void end() {
        mCanceled = true;
        mTerminated = true;
        if (isRunning()) {
            if (mSortedNodes.size() != mNodes.size()) {
                // hasn't been started yet - sort the nodes now, then end them
                sortNodes();
@@ -326,6 +327,14 @@ public final class AnimatorSet extends Animator {
                    node.animation.end();
                }
            }
            if (mListeners != null) {
                ArrayList<AnimatorListener> tmpListeners =
                        (ArrayList<AnimatorListener>) mListeners.clone();
                for (AnimatorListener listener : tmpListeners) {
                    listener.onAnimationEnd(this);
                }
            }
        }
    }

    /**
@@ -424,7 +433,7 @@ public final class AnimatorSet extends Animator {
    @SuppressWarnings("unchecked")
    @Override
    public void start() {
        mCanceled = false;
        mTerminated = false;

        // First, sort the nodes (if necessary). This will ensure that sortedNodes
        // contains the animation nodes in the correct order.
@@ -437,7 +446,8 @@ public final class AnimatorSet extends Animator {
            ArrayList<AnimatorListener> oldListeners = node.animation.getListeners();
            if (oldListeners != null && oldListeners.size() > 0) {
                for (AnimatorListener listener : oldListeners) {
                    if (listener instanceof DependencyListener) {
                    if (listener instanceof DependencyListener ||
                            listener instanceof AnimatorSetListener) {
                        node.animation.removeListener(listener);
                    }
                }
@@ -522,7 +532,7 @@ public final class AnimatorSet extends Animator {
         * and will populate any appropriate lists, when it is started.
         */
        anim.mNeedsSort = true;
        anim.mCanceled = false;
        anim.mTerminated = false;
        anim.mPlayingSet = new ArrayList<Animator>();
        anim.mNodeMap = new HashMap<Animator, Node>();
        anim.mNodes = new ArrayList<Node>();
@@ -640,7 +650,7 @@ public final class AnimatorSet extends Animator {
         * @param dependencyAnimation the animation that sent the event.
         */
        private void startIfReady(Animator dependencyAnimation) {
            if (mAnimatorSet.mCanceled) {
            if (mAnimatorSet.mTerminated) {
                // if the parent AnimatorSet was canceled, then don't start any dependent anims
                return;
            }
@@ -676,6 +686,9 @@ public final class AnimatorSet extends Animator {
        }

        public void onAnimationCancel(Animator animation) {
            if (!mTerminated) {
                // Listeners are already notified of the AnimatorSet canceling in cancel().
                // The logic below only kicks in when animations end normally
                if (mPlayingSet.size() == 0) {
                    if (mListeners != null) {
                        int numListeners = mListeners.size();
@@ -685,6 +698,7 @@ public final class AnimatorSet extends Animator {
                    }
                }
            }
        }

        @SuppressWarnings("unchecked")
        public void onAnimationEnd(Animator animation) {
@@ -692,6 +706,9 @@ public final class AnimatorSet extends Animator {
            mPlayingSet.remove(animation);
            Node animNode = mAnimatorSet.mNodeMap.get(animation);
            animNode.done = true;
            if (!mTerminated) {
                // Listeners are already notified of the AnimatorSet ending in cancel() or
                // end(); the logic below only kicks in when animations end normally
                ArrayList<Node> sortedNodes = mAnimatorSet.mSortedNodes;
                boolean allDone = true;
                int numSortedNodes = sortedNodes.size();
@@ -714,6 +731,7 @@ public final class AnimatorSet extends Animator {
                    }
                }
            }
        }

        // Nothing to do
        public void onAnimationRepeat(Animator animation) {
@@ -791,6 +809,8 @@ public final class AnimatorSet extends Animator {
                        }
                    }
                }
                // nodes are 'done' by default; they become un-done when started, and done
                // again when ended
                node.done = false;
            }
        }
+7 −7
Original line number Diff line number Diff line
@@ -933,6 +933,10 @@ public class ValueAnimator extends Animator {

    @Override
    public void cancel() {
        // Only cancel if the animation is actually running or has been started and is about
        // to run
        if (mPlayingState != STOPPED || sPendingAnimations.get().contains(this) ||
                sDelayedAnims.get().contains(this)) {
            if (mListeners != null) {
                ArrayList<AnimatorListener> tmpListeners =
                        (ArrayList<AnimatorListener>) mListeners.clone();
@@ -940,10 +944,6 @@ public class ValueAnimator extends Animator {
                    listener.onAnimationCancel(this);
                }
            }
        // Only cancel if the animation is actually running or has been started and is about
        // to run
        if (mPlayingState != STOPPED || sPendingAnimations.get().contains(this) ||
                sDelayedAnims.get().contains(this)) {
            endAnimation();
        }
    }
+1 −1
Original line number Diff line number Diff line
@@ -12,7 +12,7 @@ LOCAL_SRC_FILES := \
	$(call all-java-files-under, EnabledTestApp/src)

LOCAL_DX_FLAGS := --core-library
LOCAL_STATIC_JAVA_LIBRARIES := core-tests android-common frameworks-core-util-lib mockwebserver
LOCAL_STATIC_JAVA_LIBRARIES := core-tests android-common frameworks-core-util-lib mockwebserver guava
LOCAL_JAVA_LIBRARIES := android.test.runner
LOCAL_PACKAGE_NAME := FrameworksCoreTests

+7 −0
Original line number Diff line number Diff line
@@ -1235,6 +1235,13 @@
            </intent-filter>
        </activity>

        <activity android:name="android.animation.BasicAnimatorActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.FRAMEWORK_INSTRUMENTATION_TEST" />
            </intent-filter>
        </activity>

    </application>

    <instrumentation android:name="android.test.InstrumentationTestRunner"
+11 −0
Original line number Diff line number Diff line
<?xml version="1.0" encoding="utf-8"?>

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical">
    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/animatingButton"/>
</LinearLayout>
 No newline at end of file
Loading