Merge "Use spring animation for Notification SnapBack" into udc-dev

This commit is contained in:
András Kurucz
2023-03-22 17:34:43 +00:00
committed by Android (Google) Code Review
4 changed files with 100 additions and 79 deletions

View File

@@ -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<? extends View> 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<? extends View> 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);
}
/**

View File

@@ -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<ExpandableNotificationRow, Float> TRANSLATE_CONTENT =
new FloatProperty<ExpandableNotificationRow>("translate") {
public static final FloatProperty<ExpandableNotificationRow> 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();

View File

@@ -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

View File

@@ -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