Loading core/java/android/animation/AnimatorSet.java +19 −10 Original line number Diff line number Diff line Loading @@ -423,8 +423,13 @@ public final class AnimatorSet extends Animator implements AnimationHandler.Anim notifyListeners(AnimatorCaller.ON_CANCEL, false); callOnPlayingSet(Animator::cancel); mPlayingSet.clear(); endAnimationAndNotifyEndListenersImmediately(); } } private void endAnimationAndNotifyEndListenersImmediately() { // If the end callback is pending, invoke the end callbacks of the animator nodes before // ending this set. Pass notifyListeners=false because this endAnimation will do that. // ending this set. Pass notifyListeners=false because endAnimation will do that. if (consumePendingEndListeners(false /* notifyListeners */)) { for (int i = mNodeMap.size() - 1; i >= 0; i--) { mNodeMap.keyAt(i).consumePendingEndListeners(true /* notifyListeners */); Loading @@ -432,7 +437,6 @@ public final class AnimatorSet extends Animator implements AnimationHandler.Anim } endAnimation(); } } /** * Calls consumer on every Animator of mPlayingSet. Loading Loading @@ -529,7 +533,7 @@ public final class AnimatorSet extends Animator implements AnimationHandler.Anim } } } endAnimation(); endAnimationAndNotifyEndListenersImmediately(); } /** Loading Loading @@ -1455,8 +1459,6 @@ public final class AnimatorSet extends Animator implements AnimationHandler.Anim private void endAnimation(boolean fromLastFrame) { final boolean postNotifyEndListener = sPostNotifyEndListenerEnabled && mListeners != null && fromLastFrame && mTotalDuration > 0; mStarted = false; mLastFrameTime = -1; mFirstFrame = -1; mLastEventId = -1; mPaused = false; Loading @@ -1466,11 +1468,18 @@ public final class AnimatorSet extends Animator implements AnimationHandler.Anim // No longer receive callbacks removeAnimationCallback(); // If postNotifyEndListener is false (most cases), then it is the same as calling // completeEndAnimation directly. notifyEndListenersFromEndAnimation(mReversing, postNotifyEndListener); } @Override void completeEndAnimation(boolean isReversing, String notifyListenerTraceName) { // The mStarted and mLastFrameTime are reset here because isStarted() and isRunning() // can be true before notifying the end listeners. When notifying the end listeners, // isStarted() and isRunning() should be false. mStarted = false; mLastFrameTime = -1; super.completeEndAnimation(isReversing, notifyListenerTraceName); removeAnimationEndListener(); mSelfPulse = true; Loading core/java/android/animation/ValueAnimator.java +11 −2 Original line number Diff line number Diff line Loading @@ -1212,6 +1212,10 @@ public class ValueAnimator extends Animator implements AnimationHandler.Animatio initAnimation(); } animateValue(shouldPlayBackward(mRepeatCount, mReversing) ? 0f : 1f); if (mAnimationEndRequested) { consumePendingEndListeners(true /* notifyListeners */); return; } endAnimation(); } Loading Loading @@ -1308,8 +1312,8 @@ public class ValueAnimator extends Animator implements AnimationHandler.Animatio mLastFrameTime = -1; mFirstFrameTime = -1; mStartTime = -1; mRunning = false; mStarted = false; // If postNotifyEndListener is false (most cases), then it is the same as calling // completeEndAnimation directly. notifyEndListenersFromEndAnimation(mReversing, postNotifyEndListener); if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) { Trace.asyncTraceEnd(Trace.TRACE_TAG_VIEW, getNameForTrace(), Loading @@ -1319,6 +1323,11 @@ public class ValueAnimator extends Animator implements AnimationHandler.Animatio @Override void completeEndAnimation(boolean isReversing, String notifyListenerTraceName) { // The mRunning and mStarted are reset here because isStarted() and isRunning() // can be true before notifying the end listeners. When notifying the end listeners, // isStarted() and isRunning() should be false. mRunning = false; mStarted = false; super.completeEndAnimation(isReversing, notifyListenerTraceName); // mReversing needs to be reset *after* notifying the listeners for the end callbacks. mReversing = false; Loading core/tests/coretests/src/android/animation/AnimatorSetCallsTest.java +22 −6 Original line number Diff line number Diff line Loading @@ -38,6 +38,7 @@ import java.util.ArrayList; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicReference; import java.util.function.Consumer; @MediumTest Loading Loading @@ -490,9 +491,24 @@ public class AnimatorSetCallsTest { @Test public void testCancelOnPendingEndListener() throws Throwable { testPendingEndListener(AnimatorSet::cancel); } @Test public void testEndOnPendingEndListener() throws Throwable { testPendingEndListener(animatorSet -> { // This verifies that isRunning() and isStarted() are true at last frame. // Then the end() should invoke the end callback immediately. if (animatorSet.isRunning() && animatorSet.isStarted()) { animatorSet.end(); } }); } private void testPendingEndListener(Consumer<AnimatorSet> finishOnLastFrame) throws Throwable { final CountDownLatch endLatch = new CountDownLatch(1); final Handler handler = new Handler(Looper.getMainLooper()); final boolean[] endCalledRightAfterCancel = new boolean[2]; final boolean[] endCalledImmediately = new boolean[2]; final AnimatorSet set = new AnimatorSet(); final ValueAnimatorTests.MyListener asListener = new ValueAnimatorTests.MyListener(); final ValueAnimatorTests.MyListener vaListener = new ValueAnimatorTests.MyListener(); Loading @@ -502,9 +518,9 @@ public class AnimatorSetCallsTest { va.addUpdateListener(animation -> { if (animation.getAnimatedFraction() == 1f) { handler.post(() -> { set.cancel(); endCalledRightAfterCancel[0] = vaListener.endCalled; endCalledRightAfterCancel[1] = asListener.endCalled; finishOnLastFrame.accept(set); endCalledImmediately[0] = vaListener.endCalled; endCalledImmediately[1] = asListener.endCalled; endLatch.countDown(); }); } Loading @@ -517,8 +533,8 @@ public class AnimatorSetCallsTest { try { handler.post(set::start); assertTrue(endLatch.await(1, TimeUnit.SECONDS)); assertTrue(endCalledRightAfterCancel[0]); assertTrue(endCalledRightAfterCancel[1]); assertTrue(endCalledImmediately[0]); assertTrue(endCalledImmediately[1]); } finally { ValueAnimator.setPostNotifyEndListenerEnabled(false); } Loading core/tests/coretests/src/android/animation/ValueAnimatorTests.java +21 −4 Original line number Diff line number Diff line Loading @@ -44,6 +44,7 @@ import org.junit.runner.RunWith; import java.util.ArrayList; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.function.Consumer; @RunWith(AndroidJUnit4.class) @MediumTest Loading Loading @@ -923,9 +924,25 @@ public class ValueAnimatorTests { @Test public void testCancelOnPendingEndListener() throws Throwable { testPendingEndListener(ValueAnimator::cancel); } @Test public void testEndOnPendingEndListener() throws Throwable { testPendingEndListener(animator -> { // This verifies that isRunning() and isStarted() are true at last frame. // Then the end() should invoke the end callback immediately. if (animator.isRunning() && animator.isStarted()) { animator.end(); } }); } private void testPendingEndListener(Consumer<ValueAnimator> finishOnLastFrame) throws Throwable { final boolean[] endCalledImmediately = new boolean[1]; final CountDownLatch endLatch = new CountDownLatch(1); final Handler handler = new Handler(Looper.getMainLooper()); final boolean[] endCalledRightAfterCancel = new boolean[1]; final MyListener listener = new MyListener(); final ValueAnimator va = new ValueAnimator(); va.setFloatValues(0f, 1f); Loading @@ -933,8 +950,8 @@ public class ValueAnimatorTests { va.addUpdateListener(animation -> { if (animation.getAnimatedFraction() == 1f) { handler.post(() -> { va.cancel(); endCalledRightAfterCancel[0] = listener.endCalled; finishOnLastFrame.accept(va); endCalledImmediately[0] = listener.endCalled; endLatch.countDown(); }); } Loading @@ -945,7 +962,7 @@ public class ValueAnimatorTests { try { handler.post(va::start); assertThat(endLatch.await(1, TimeUnit.SECONDS)).isTrue(); assertThat(endCalledRightAfterCancel[0]).isTrue(); assertThat(endCalledImmediately[0]).isTrue(); } finally { ValueAnimator.setPostNotifyEndListenerEnabled(false); } Loading Loading
core/java/android/animation/AnimatorSet.java +19 −10 Original line number Diff line number Diff line Loading @@ -423,8 +423,13 @@ public final class AnimatorSet extends Animator implements AnimationHandler.Anim notifyListeners(AnimatorCaller.ON_CANCEL, false); callOnPlayingSet(Animator::cancel); mPlayingSet.clear(); endAnimationAndNotifyEndListenersImmediately(); } } private void endAnimationAndNotifyEndListenersImmediately() { // If the end callback is pending, invoke the end callbacks of the animator nodes before // ending this set. Pass notifyListeners=false because this endAnimation will do that. // ending this set. Pass notifyListeners=false because endAnimation will do that. if (consumePendingEndListeners(false /* notifyListeners */)) { for (int i = mNodeMap.size() - 1; i >= 0; i--) { mNodeMap.keyAt(i).consumePendingEndListeners(true /* notifyListeners */); Loading @@ -432,7 +437,6 @@ public final class AnimatorSet extends Animator implements AnimationHandler.Anim } endAnimation(); } } /** * Calls consumer on every Animator of mPlayingSet. Loading Loading @@ -529,7 +533,7 @@ public final class AnimatorSet extends Animator implements AnimationHandler.Anim } } } endAnimation(); endAnimationAndNotifyEndListenersImmediately(); } /** Loading Loading @@ -1455,8 +1459,6 @@ public final class AnimatorSet extends Animator implements AnimationHandler.Anim private void endAnimation(boolean fromLastFrame) { final boolean postNotifyEndListener = sPostNotifyEndListenerEnabled && mListeners != null && fromLastFrame && mTotalDuration > 0; mStarted = false; mLastFrameTime = -1; mFirstFrame = -1; mLastEventId = -1; mPaused = false; Loading @@ -1466,11 +1468,18 @@ public final class AnimatorSet extends Animator implements AnimationHandler.Anim // No longer receive callbacks removeAnimationCallback(); // If postNotifyEndListener is false (most cases), then it is the same as calling // completeEndAnimation directly. notifyEndListenersFromEndAnimation(mReversing, postNotifyEndListener); } @Override void completeEndAnimation(boolean isReversing, String notifyListenerTraceName) { // The mStarted and mLastFrameTime are reset here because isStarted() and isRunning() // can be true before notifying the end listeners. When notifying the end listeners, // isStarted() and isRunning() should be false. mStarted = false; mLastFrameTime = -1; super.completeEndAnimation(isReversing, notifyListenerTraceName); removeAnimationEndListener(); mSelfPulse = true; Loading
core/java/android/animation/ValueAnimator.java +11 −2 Original line number Diff line number Diff line Loading @@ -1212,6 +1212,10 @@ public class ValueAnimator extends Animator implements AnimationHandler.Animatio initAnimation(); } animateValue(shouldPlayBackward(mRepeatCount, mReversing) ? 0f : 1f); if (mAnimationEndRequested) { consumePendingEndListeners(true /* notifyListeners */); return; } endAnimation(); } Loading Loading @@ -1308,8 +1312,8 @@ public class ValueAnimator extends Animator implements AnimationHandler.Animatio mLastFrameTime = -1; mFirstFrameTime = -1; mStartTime = -1; mRunning = false; mStarted = false; // If postNotifyEndListener is false (most cases), then it is the same as calling // completeEndAnimation directly. notifyEndListenersFromEndAnimation(mReversing, postNotifyEndListener); if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) { Trace.asyncTraceEnd(Trace.TRACE_TAG_VIEW, getNameForTrace(), Loading @@ -1319,6 +1323,11 @@ public class ValueAnimator extends Animator implements AnimationHandler.Animatio @Override void completeEndAnimation(boolean isReversing, String notifyListenerTraceName) { // The mRunning and mStarted are reset here because isStarted() and isRunning() // can be true before notifying the end listeners. When notifying the end listeners, // isStarted() and isRunning() should be false. mRunning = false; mStarted = false; super.completeEndAnimation(isReversing, notifyListenerTraceName); // mReversing needs to be reset *after* notifying the listeners for the end callbacks. mReversing = false; Loading
core/tests/coretests/src/android/animation/AnimatorSetCallsTest.java +22 −6 Original line number Diff line number Diff line Loading @@ -38,6 +38,7 @@ import java.util.ArrayList; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicReference; import java.util.function.Consumer; @MediumTest Loading Loading @@ -490,9 +491,24 @@ public class AnimatorSetCallsTest { @Test public void testCancelOnPendingEndListener() throws Throwable { testPendingEndListener(AnimatorSet::cancel); } @Test public void testEndOnPendingEndListener() throws Throwable { testPendingEndListener(animatorSet -> { // This verifies that isRunning() and isStarted() are true at last frame. // Then the end() should invoke the end callback immediately. if (animatorSet.isRunning() && animatorSet.isStarted()) { animatorSet.end(); } }); } private void testPendingEndListener(Consumer<AnimatorSet> finishOnLastFrame) throws Throwable { final CountDownLatch endLatch = new CountDownLatch(1); final Handler handler = new Handler(Looper.getMainLooper()); final boolean[] endCalledRightAfterCancel = new boolean[2]; final boolean[] endCalledImmediately = new boolean[2]; final AnimatorSet set = new AnimatorSet(); final ValueAnimatorTests.MyListener asListener = new ValueAnimatorTests.MyListener(); final ValueAnimatorTests.MyListener vaListener = new ValueAnimatorTests.MyListener(); Loading @@ -502,9 +518,9 @@ public class AnimatorSetCallsTest { va.addUpdateListener(animation -> { if (animation.getAnimatedFraction() == 1f) { handler.post(() -> { set.cancel(); endCalledRightAfterCancel[0] = vaListener.endCalled; endCalledRightAfterCancel[1] = asListener.endCalled; finishOnLastFrame.accept(set); endCalledImmediately[0] = vaListener.endCalled; endCalledImmediately[1] = asListener.endCalled; endLatch.countDown(); }); } Loading @@ -517,8 +533,8 @@ public class AnimatorSetCallsTest { try { handler.post(set::start); assertTrue(endLatch.await(1, TimeUnit.SECONDS)); assertTrue(endCalledRightAfterCancel[0]); assertTrue(endCalledRightAfterCancel[1]); assertTrue(endCalledImmediately[0]); assertTrue(endCalledImmediately[1]); } finally { ValueAnimator.setPostNotifyEndListenerEnabled(false); } Loading
core/tests/coretests/src/android/animation/ValueAnimatorTests.java +21 −4 Original line number Diff line number Diff line Loading @@ -44,6 +44,7 @@ import org.junit.runner.RunWith; import java.util.ArrayList; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.function.Consumer; @RunWith(AndroidJUnit4.class) @MediumTest Loading Loading @@ -923,9 +924,25 @@ public class ValueAnimatorTests { @Test public void testCancelOnPendingEndListener() throws Throwable { testPendingEndListener(ValueAnimator::cancel); } @Test public void testEndOnPendingEndListener() throws Throwable { testPendingEndListener(animator -> { // This verifies that isRunning() and isStarted() are true at last frame. // Then the end() should invoke the end callback immediately. if (animator.isRunning() && animator.isStarted()) { animator.end(); } }); } private void testPendingEndListener(Consumer<ValueAnimator> finishOnLastFrame) throws Throwable { final boolean[] endCalledImmediately = new boolean[1]; final CountDownLatch endLatch = new CountDownLatch(1); final Handler handler = new Handler(Looper.getMainLooper()); final boolean[] endCalledRightAfterCancel = new boolean[1]; final MyListener listener = new MyListener(); final ValueAnimator va = new ValueAnimator(); va.setFloatValues(0f, 1f); Loading @@ -933,8 +950,8 @@ public class ValueAnimatorTests { va.addUpdateListener(animation -> { if (animation.getAnimatedFraction() == 1f) { handler.post(() -> { va.cancel(); endCalledRightAfterCancel[0] = listener.endCalled; finishOnLastFrame.accept(va); endCalledImmediately[0] = listener.endCalled; endLatch.countDown(); }); } Loading @@ -945,7 +962,7 @@ public class ValueAnimatorTests { try { handler.post(va::start); assertThat(endLatch.await(1, TimeUnit.SECONDS)).isTrue(); assertThat(endCalledRightAfterCancel[0]).isTrue(); assertThat(endCalledImmediately[0]).isTrue(); } finally { ValueAnimator.setPostNotifyEndListenerEnabled(false); } Loading