Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 04262328 authored by András Kurucz's avatar András Kurucz Committed by Android (Google) Code Review
Browse files

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

parents 036b40a7 1d77b2c1
Loading
Loading
Loading
Loading
+69 −52
Original line number Diff line number Diff line
@@ -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;
    protected Animator getViewTranslationAnimator(View view, float target,
            AnimatorUpdateListener listener) {

        cancelSnapbackAnimation(view);

        if (view instanceof ExpandableNotificationRow) {
            return ((ExpandableNotificationRow) view).getTranslateViewAnimator(target, listener);
        }

    private float getPerpendicularVelocity(VelocityTracker vt) {
        return mSwipeDirection == X ? vt.getYVelocity() :
                vt.getXVelocity();
        return createTranslationAnimation(view, target, listener);
    }

    protected Animator getViewTranslationAnimator(View v, float target,
    protected Animator createTranslationAnimation(View view, float newPos,
            AnimatorUpdateListener listener) {
        ObjectAnimator anim = createTranslationAnimation(v, target);
        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;

            @Override
            public void onAnimationCancel(Animator animator) {
                wasCancelled = true;
            }
        cancelTranslateAnimation(animView);

            @Override
            public void onAnimationEnd(Animator animator) {
        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 (!wasCancelled) {

            if (!cancelled) {
                updateSwipeProgressFromOffset(animView, canBeDismissed);
                resetSwipeState();
            }
                onSnapChildWithAnimationFinished();
            }
            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);
    }

    /**
+10 −3
Original line number Diff line number Diff line
@@ -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();
+11 −14
Original line number Diff line number Diff line
@@ -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
+5 −5
Original line number Diff line number Diff line
@@ -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