diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java index c4784d6211bb0..ad8ae75edc236 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java +++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java @@ -961,6 +961,7 @@ public final class NotificationPanelViewController implements Dumpable { onTrackingStopped(false); instantCollapse(); } else { + mView.animate().cancel(); mView.animate() .alpha(0f) .setStartDelay(0) @@ -3087,7 +3088,9 @@ public final class NotificationPanelViewController implements Dumpable { */ public void startFoldToAodAnimation(Runnable startAction, Runnable endAction, Runnable cancelAction) { - mView.animate() + final ViewPropertyAnimator viewAnimator = mView.animate(); + viewAnimator.cancel(); + viewAnimator .translationX(0) .alpha(1f) .setDuration(ANIMATION_DURATION_FOLD_TO_AOD) @@ -3106,9 +3109,14 @@ public final class NotificationPanelViewController implements Dumpable { @Override public void onAnimationEnd(Animator animation) { endAction.run(); + + viewAnimator.setListener(null); + viewAnimator.setUpdateListener(null); } - }).setUpdateListener(anim -> mKeyguardStatusViewController.animateFoldToAod( - anim.getAnimatedFraction())).start(); + }) + .setUpdateListener(anim -> + mKeyguardStatusViewController.animateFoldToAod(anim.getAnimatedFraction())) + .start(); } /** Cancels fold to AOD transition and resets view state. */ @@ -3307,6 +3315,7 @@ public final class NotificationPanelViewController implements Dumpable { } public ViewPropertyAnimator fadeOut(long startDelayMs, long durationMs, Runnable endAction) { + mView.animate().cancel(); return mView.animate().alpha(0).setStartDelay(startDelayMs).setDuration( durationMs).setInterpolator(Interpolators.ALPHA_OUT).withLayer().withEndAction( endAction); diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java index 99cf8d0ebe93c..7087c01359988 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java @@ -24,7 +24,9 @@ import static com.google.common.truth.Truth.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.ArgumentMatchers.anyFloat; import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.atLeast; import static org.mockito.Mockito.doAnswer; @@ -184,6 +186,7 @@ public class NotificationPanelViewControllerBaseTest extends SysuiTestCase { @Mock protected NotificationStackScrollLayout mNotificationStackScrollLayout; @Mock protected KeyguardBottomAreaView mKeyguardBottomArea; @Mock protected KeyguardBottomAreaViewController mKeyguardBottomAreaViewController; + @Mock protected ViewPropertyAnimator mViewPropertyAnimator; @Mock protected KeyguardBottomAreaView mQsFrame; @Mock protected HeadsUpManagerPhone mHeadsUpManager; @Mock protected NotificationShelfController mNotificationShelfController; @@ -357,7 +360,14 @@ public class NotificationPanelViewControllerBaseTest extends SysuiTestCase { .thenReturn(mHeadsUpCallback); when(mKeyguardBottomAreaViewController.getView()).thenReturn(mKeyguardBottomArea); when(mView.findViewById(R.id.keyguard_bottom_area)).thenReturn(mKeyguardBottomArea); - when(mKeyguardBottomArea.animate()).thenReturn(mock(ViewPropertyAnimator.class)); + when(mKeyguardBottomArea.animate()).thenReturn(mViewPropertyAnimator); + when(mView.animate()).thenReturn(mViewPropertyAnimator); + when(mViewPropertyAnimator.translationX(anyFloat())).thenReturn(mViewPropertyAnimator); + when(mViewPropertyAnimator.alpha(anyFloat())).thenReturn(mViewPropertyAnimator); + when(mViewPropertyAnimator.setDuration(anyLong())).thenReturn(mViewPropertyAnimator); + when(mViewPropertyAnimator.setInterpolator(any())).thenReturn(mViewPropertyAnimator); + when(mViewPropertyAnimator.setListener(any())).thenReturn(mViewPropertyAnimator); + when(mViewPropertyAnimator.setUpdateListener(any())).thenReturn(mViewPropertyAnimator); when(mView.findViewById(R.id.qs_frame)).thenReturn(mQsFrame); when(mView.findViewById(R.id.keyguard_status_view)) .thenReturn(mock(KeyguardStatusView.class)); diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java index 4517596e8b024..b868018b7dda2 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java @@ -42,6 +42,8 @@ import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import android.animation.Animator; +import android.animation.ValueAnimator; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import android.view.MotionEvent; @@ -673,6 +675,24 @@ public class NotificationPanelViewControllerTest extends NotificationPanelViewCo verify(mKeyguardStatusViewController, never()).displayClock(LARGE, /* animate */ true); } + @Test + public void testFoldToAodAnimationCleansupInAnimationEnd() { + ArgumentCaptor animCaptor = + ArgumentCaptor.forClass(Animator.AnimatorListener.class); + ArgumentCaptor updateCaptor = + ArgumentCaptor.forClass(ValueAnimator.AnimatorUpdateListener.class); + + // Start fold animation & Capture Listeners + mNotificationPanelViewController.startFoldToAodAnimation(() -> {}, () -> {}, () -> {}); + verify(mViewPropertyAnimator).setListener(animCaptor.capture()); + verify(mViewPropertyAnimator).setUpdateListener(updateCaptor.capture()); + + // End animation and validate listeners were unset + animCaptor.getValue().onAnimationEnd(null); + verify(mViewPropertyAnimator).setListener(null); + verify(mViewPropertyAnimator).setUpdateListener(null); + } + @Test public void testExpandWithQsMethodIsUsingLockscreenTransitionController() { enableSplitShade(/* enabled= */ true);