diff --git a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java index 873a695ecd931..64a9cc9952482 100644 --- a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java +++ b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java @@ -16,6 +16,10 @@ package com.android.systemui; +import static androidx.dynamicanimation.animation.DynamicAnimation.TRANSLATION_X; +import static androidx.dynamicanimation.animation.DynamicAnimation.TRANSLATION_Y; +import static androidx.dynamicanimation.animation.FloatPropertyCompat.createFloatPropertyCompat; + import static com.android.systemui.classifier.Classifier.NOTIFICATION_DISMISS; import android.animation.Animator; @@ -40,6 +44,7 @@ import android.view.accessibility.AccessibilityEvent; import androidx.annotation.VisibleForTesting; +import com.android.internal.dynamicanimation.animation.SpringForce; import com.android.systemui.animation.Interpolators; import com.android.systemui.flags.FeatureFlags; import com.android.systemui.flags.Flags; @@ -47,14 +52,14 @@ import com.android.systemui.plugins.FalsingManager; import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.wm.shell.animation.FlingAnimationUtils; +import com.android.wm.shell.animation.PhysicsAnimator; +import com.android.wm.shell.animation.PhysicsAnimator.SpringConfig; import java.util.function.Consumer; public class SwipeHelper implements Gefingerpoken { static final String TAG = "com.android.systemui.SwipeHelper"; - private static final boolean DEBUG = false; private static final boolean DEBUG_INVALIDATE = false; - private static final boolean SLOW_ANIMATIONS = false; // DEBUG; private static final boolean CONSTRAIN_SWIPE = true; private static final boolean FADE_OUT_DURING_SWIPE = true; private static final boolean DISMISS_IF_SWIPED_FAR_ENOUGH = true; @@ -66,7 +71,6 @@ public class SwipeHelper implements Gefingerpoken { private static final int DEFAULT_ESCAPE_ANIMATION_DURATION = 200; // ms private static final int MAX_ESCAPE_ANIMATION_DURATION = 400; // ms private static final int MAX_DISMISS_VELOCITY = 4000; // dp/sec - private static final int SNAP_ANIM_LEN = SLOW_ANIMATIONS ? 1000 : 150; // ms public static final float SWIPE_PROGRESS_FADE_END = 0.6f; // fraction of thumbnail width // beyond which swipe progress->0 @@ -78,6 +82,9 @@ public class SwipeHelper implements Gefingerpoken { private float mMinSwipeProgress = 0f; private float mMaxSwipeProgress = 1f; + private final SpringConfig mSnapBackSpringConfig = + new SpringConfig(SpringForce.STIFFNESS_LOW, SpringForce.DAMPING_RATIO_LOW_BOUNCY); + private final FlingAnimationUtils mFlingAnimationUtils; private float mPagingTouchSlop; private final float mSlopMultiplier; @@ -188,23 +195,27 @@ public class SwipeHelper implements Gefingerpoken { vt.getYVelocity(); } - protected ObjectAnimator createTranslationAnimation(View v, float newPos) { - ObjectAnimator anim = ObjectAnimator.ofFloat(v, - mSwipeDirection == X ? View.TRANSLATION_X : View.TRANSLATION_Y, newPos); - return anim; - } - - private float getPerpendicularVelocity(VelocityTracker vt) { - return mSwipeDirection == X ? vt.getYVelocity() : - vt.getXVelocity(); - } - - protected Animator getViewTranslationAnimator(View v, float target, + protected Animator getViewTranslationAnimator(View view, float target, AnimatorUpdateListener listener) { - ObjectAnimator anim = createTranslationAnimation(v, target); + + cancelSnapbackAnimation(view); + + if (view instanceof ExpandableNotificationRow) { + return ((ExpandableNotificationRow) view).getTranslateViewAnimator(target, listener); + } + + return createTranslationAnimation(view, target, listener); + } + + protected Animator createTranslationAnimation(View view, float newPos, + AnimatorUpdateListener listener) { + ObjectAnimator anim = ObjectAnimator.ofFloat(view, + mSwipeDirection == X ? View.TRANSLATION_X : View.TRANSLATION_Y, newPos); + if (listener != null) { anim.addUpdateListener(listener); } + return anim; } @@ -327,6 +338,7 @@ public class SwipeHelper implements Gefingerpoken { mTouchedView = mCallback.getChildAtPosition(ev); if (mTouchedView != null) { + cancelSnapbackAnimation(mTouchedView); onDownUpdate(mTouchedView, ev); mCanCurrViewBeDimissed = mCallback.canChildBeDismissed(mTouchedView); mVelocityTracker.addMovement(ev); @@ -526,47 +538,59 @@ public class SwipeHelper implements Gefingerpoken { } /** - * After snapChild() and related animation finished, this function will be called. + * Starts a snapback animation and cancels any previous translate animations on the given view. + * + * @param animView view to animate + * @param targetLeft the end position of the translation + * @param velocity the initial velocity of the animation */ - protected void onSnapChildWithAnimationFinished() {} - - public void snapChild(final View animView, final float targetLeft, float velocity) { + protected void snapChild(final View animView, final float targetLeft, float velocity) { final boolean canBeDismissed = mCallback.canChildBeDismissed(animView); - AnimatorUpdateListener updateListener = animation -> onTranslationUpdate(animView, - (float) animation.getAnimatedValue(), canBeDismissed); - Animator anim = getViewTranslationAnimator(animView, targetLeft, updateListener); - if (anim == null) { - onSnapChildWithAnimationFinished(); - return; - } - anim.addListener(new AnimatorListenerAdapter() { - boolean wasCancelled = false; + cancelTranslateAnimation(animView); - @Override - public void onAnimationCancel(Animator animator) { - wasCancelled = true; - } - - @Override - public void onAnimationEnd(Animator animator) { - mSnappingChild = false; - if (!wasCancelled) { - updateSwipeProgressFromOffset(animView, canBeDismissed); - resetSwipeState(); - } - onSnapChildWithAnimationFinished(); - } + PhysicsAnimator anim = + createSnapBackAnimation(animView, targetLeft, velocity); + anim.addUpdateListener((target, values) -> { + onTranslationUpdate(target, getTranslation(target), canBeDismissed); + }); + anim.addEndListener((t, p, wasFling, cancelled, finalValue, finalVelocity, allEnded) -> { + mSnappingChild = false; + + if (!cancelled) { + updateSwipeProgressFromOffset(animView, canBeDismissed); + resetSwipeState(); + } + onChildSnappedBack(animView, targetLeft); }); - prepareSnapBackAnimation(animView, anim); mSnappingChild = true; - float maxDistance = Math.abs(targetLeft - getTranslation(animView)); - mFlingAnimationUtils.apply(anim, getTranslation(animView), targetLeft, velocity, - maxDistance); anim.start(); - mCallback.onChildSnappedBack(animView, targetLeft); } + private PhysicsAnimator createSnapBackAnimation(View target, float toPosition, + float startVelocity) { + if (target instanceof ExpandableNotificationRow) { + return PhysicsAnimator.getInstance((ExpandableNotificationRow) target).spring( + createFloatPropertyCompat(ExpandableNotificationRow.TRANSLATE_CONTENT), + toPosition, + startVelocity, + mSnapBackSpringConfig); + } + return PhysicsAnimator.getInstance(target).spring( + mSwipeDirection == X ? TRANSLATION_X : TRANSLATION_Y, toPosition, startVelocity, + mSnapBackSpringConfig); + } + + private void cancelTranslateAnimation(View animView) { + if (animView instanceof ExpandableNotificationRow) { + ((ExpandableNotificationRow) animView).cancelTranslateAnimation(); + } + cancelSnapbackAnimation(animView); + } + + private void cancelSnapbackAnimation(View target) { + PhysicsAnimator.getInstance(target).cancel(); + } /** * Called to update the content alpha while the view is swiped @@ -576,17 +600,10 @@ public class SwipeHelper implements Gefingerpoken { } /** - * Give the swipe helper itself a chance to do something on snap back so NSSL doesn't have - * to tell us what to do + * Called after {@link #snapChild(View, float, float)} and its related animation has finished. */ protected void onChildSnappedBack(View animView, float targetLeft) { - } - - /** - * Called to update the snap back animation. - */ - protected void prepareSnapBackAnimation(View view, Animator anim) { - // Do nothing + mCallback.onChildSnappedBack(animView, targetLeft); } /** diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java index a529da54fc4ef..a38f52727c265 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java @@ -47,7 +47,6 @@ import android.util.FloatProperty; import android.util.IndentingPrintWriter; import android.util.Log; import android.util.MathUtils; -import android.util.Property; import android.view.KeyEvent; import android.view.LayoutInflater; import android.view.MotionEvent; @@ -336,8 +335,8 @@ public class ExpandableNotificationRow extends ActivatableNotificationView }; private boolean mKeepInParentForDismissAnimation; private boolean mRemoved; - private static final Property TRANSLATE_CONTENT = - new FloatProperty("translate") { + public static final FloatProperty TRANSLATE_CONTENT = + new FloatProperty<>("translate") { @Override public void setValue(ExpandableNotificationRow object, float value) { object.setTranslation(value); @@ -348,6 +347,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView return object.getTranslation(); } }; + private OnClickListener mOnClickListener; private OnDragSuccessListener mOnDragSuccessListener; private boolean mHeadsupDisappearRunning; @@ -2177,6 +2177,13 @@ public class ExpandableNotificationRow extends ActivatableNotificationView return translateAnim; } + /** Cancels the ongoing translate animation if there is any. */ + public void cancelTranslateAnimation() { + if (mTranslateAnim != null) { + mTranslateAnim.cancel(); + } + } + void ensureGutsInflated() { if (mGuts == null) { mGutsStub.inflate(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelper.java index c6f56d482d43c..b476b683463ff 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelper.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelper.java @@ -135,11 +135,15 @@ class NotificationSwipeHelper extends SwipeHelper implements NotificationSwipeAc @Override protected void onChildSnappedBack(View animView, float targetLeft) { + super.onChildSnappedBack(animView, targetLeft); + final NotificationMenuRowPlugin menuRow = getCurrentMenuRow(); if (menuRow != null && targetLeft == 0) { menuRow.resetMenu(); clearCurrentMenuRow(); } + + InteractionJankMonitor.getInstance().end(CUJ_NOTIFICATION_SHADE_ROW_SWIPE); } @Override @@ -348,18 +352,13 @@ class NotificationSwipeHelper extends SwipeHelper implements NotificationSwipeAc super.dismissChild(view, velocity, useAccelerateInterpolator); } - @Override - protected void onSnapChildWithAnimationFinished() { - InteractionJankMonitor.getInstance().end(CUJ_NOTIFICATION_SHADE_ROW_SWIPE); - } - @VisibleForTesting protected void superSnapChild(final View animView, final float targetLeft, float velocity) { super.snapChild(animView, targetLeft, velocity); } @Override - public void snapChild(final View animView, final float targetLeft, float velocity) { + protected void snapChild(final View animView, final float targetLeft, float velocity) { superSnapChild(animView, targetLeft, velocity); mCallback.onDragCancelled(animView); if (targetLeft == 0) { @@ -380,20 +379,18 @@ class NotificationSwipeHelper extends SwipeHelper implements NotificationSwipeAc } } + @Override @VisibleForTesting - protected Animator superGetViewTranslationAnimator(View v, float target, + protected Animator getViewTranslationAnimator(View view, float target, ValueAnimator.AnimatorUpdateListener listener) { - return super.getViewTranslationAnimator(v, target, listener); + return super.getViewTranslationAnimator(view, target, listener); } @Override - public Animator getViewTranslationAnimator(View v, float target, + @VisibleForTesting + protected Animator createTranslationAnimation(View view, float newPos, ValueAnimator.AnimatorUpdateListener listener) { - if (v instanceof ExpandableNotificationRow) { - return ((ExpandableNotificationRow) v).getTranslateViewAnimator(target, listener); - } else { - return superGetViewTranslationAnimator(v, target, listener); - } + return super.createTranslationAnimation(view, newPos, listener); } @Override diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelperTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelperTest.java index 78da78269ac48..824eb4aa25c26 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelperTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelperTest.java @@ -425,12 +425,12 @@ public class NotificationSwipeHelperTest extends SysuiTestCase { public void testGetViewTranslationAnimator_notExpandableNotificationRow() { Animator animator = mock(Animator.class); AnimatorUpdateListener listener = mock(AnimatorUpdateListener.class); - doReturn(animator).when(mSwipeHelper).superGetViewTranslationAnimator(mView, 0, listener); + doReturn(animator).when(mSwipeHelper).createTranslationAnimation(mView, 0, listener); - assertEquals("returns the correct animator from super", animator, + assertEquals("Should create a new animator", animator, mSwipeHelper.getViewTranslationAnimator(mView, 0, listener)); - verify(mSwipeHelper, times(1)).superGetViewTranslationAnimator(mView, 0, listener); + verify(mSwipeHelper).createTranslationAnimation(mView, 0, listener); } @Test @@ -439,10 +439,10 @@ public class NotificationSwipeHelperTest extends SysuiTestCase { AnimatorUpdateListener listener = mock(AnimatorUpdateListener.class); doReturn(animator).when(mNotificationRow).getTranslateViewAnimator(0, listener); - assertEquals("returns the correct animator from super when view is an ENR", animator, + assertEquals("Should return the animator from ExpandableNotificationRow", animator, mSwipeHelper.getViewTranslationAnimator(mNotificationRow, 0, listener)); - verify(mNotificationRow, times(1)).getTranslateViewAnimator(0, listener); + verify(mNotificationRow).getTranslateViewAnimator(0, listener); } @Test