diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java index 3360511617bad..827a4c12006b7 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java +++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java @@ -979,6 +979,7 @@ public final class NotificationPanelViewController implements Dumpable { onTrackingStopped(false); instantCollapse(); } else { + mView.animate().cancel(); mView.animate() .alpha(0f) .setStartDelay(0) @@ -3149,7 +3150,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) @@ -3168,9 +3171,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. */ @@ -3369,6 +3377,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 9a2e415f952fa..d36cc7e0ddbe8 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; @@ -692,6 +694,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);