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

Commit 3df9b8b3 authored by Juan Sebastian Martinez's avatar Juan Sebastian Martinez
Browse files

Polishing thresholds and reattaching.

We increase the magnetic detach and attach thresholds to allow more
swiping room. We also atract neighbors and restore magnetic pull forces
when the notification crosses the attach threshold. For better motion,
detachment and attachment now query the current gesture velocity.

Test: manual. Verified that attachment restores magnetic pulling and
  attracts neighbors. Tested at different swipe velocities.
Flag: com.android.systemui.magnetic_notification_swipes
Bug: 405450400

Change-Id: I70e7af5a6c42b652e3cf3107a8181f63bf9f8ae0
parent d5b8d158
Loading
Loading
Loading
Loading
+2 −2
Original line number Diff line number Diff line
@@ -410,8 +410,8 @@ class MagneticNotificationRowManagerImplTest : SysuiTestCase() {
    private fun MagneticRowListener.asTestableListener(rowIndex: Int): MagneticRowListener {
        val delegate = this
        return object : MagneticRowListener {
            override fun setMagneticTranslation(translation: Float, trackEagerly: Boolean) {
                delegate.setMagneticTranslation(translation, trackEagerly)
            override fun setMagneticTranslation(translation: Float) {
                delegate.setMagneticTranslation(translation)
            }

            override fun triggerMagneticForce(
+11 −0
Original line number Diff line number Diff line
@@ -197,6 +197,17 @@ public class SwipeHelper implements Gefingerpoken, Dumpable {
        return vt.getXVelocity();
    }

    /**
     * @return the current swipe velocity, or zero if there is no swipe in progress.
     */
    public float getCurrentVelocity() {
        if (mVelocityTracker != null) {
            mVelocityTracker.computeCurrentVelocity(1000 /* px/sec */, getMaxVelocity());
            return getVelocity(mVelocityTracker);
        } else {
            return 0f;
        }
    }

    protected Animator getViewTranslationAnimator(View view, float target,
            AnimatorUpdateListener listener) {
+1 −18
Original line number Diff line number Diff line
@@ -19,8 +19,6 @@ package com.android.systemui.statusbar.notification.row;
import static com.android.systemui.Flags.notificationColorUpdateLogger;
import static com.android.systemui.Flags.physicalNotificationMovement;

import static java.lang.Math.abs;

import android.animation.AnimatorListenerAdapter;
import android.content.Context;
import android.content.res.Configuration;
@@ -31,7 +29,6 @@ import android.util.FloatProperty;
import android.util.IndentingPrintWriter;
import android.util.Log;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewGroup;
import android.view.ViewParent;
import android.widget.FrameLayout;
@@ -113,25 +110,12 @@ public abstract class ExpandableView extends FrameLayout implements Dumpable, Ro
    protected SpringAnimation mMagneticAnimator = new SpringAnimation(
            this /* object */, DynamicAnimation.TRANSLATION_X);

    private int mTouchSlop;

    protected MagneticRowListener mMagneticRowListener = new MagneticRowListener() {

        @Override
        public void setMagneticTranslation(float translation, boolean trackEagerly) {
        public void setMagneticTranslation(float translation) {
            if (!mMagneticAnimator.isRunning()) {
                setTranslation(translation);
                return;
            }

            if (trackEagerly) {
                float delta = abs(getTranslation() - translation);
                if (delta > mTouchSlop) {
                    mMagneticAnimator.animateToFinalPosition(translation);
                } else {
                    mMagneticAnimator.cancel();
                    setTranslation(translation);
                }
            } else {
                mMagneticAnimator.animateToFinalPosition(translation);
            }
@@ -199,7 +183,6 @@ public abstract class ExpandableView extends FrameLayout implements Dumpable, Ro
    private void initDimens() {
        mContentShift = getResources().getDimensionPixelSize(
                R.dimen.shelf_transform_content_shift);
        mTouchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop();
    }

    @Override
+14 −2
Original line number Diff line number Diff line
@@ -32,6 +32,9 @@ import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
 */
interface MagneticNotificationRowManager {

    /** Set a new [SwipeInfoProvider]. */
    fun setInfoProvider(swipeInfoProvider: SwipeInfoProvider?)

    /**
     * Notifies a change in the device density. The density can be used to compute the values of
     * thresholds in pixels.
@@ -105,18 +108,27 @@ interface MagneticNotificationRowManager {
     */
    fun reset()

    /** A provider for the [MagneticNotificationRowManager] to query swipe information. */
    interface SwipeInfoProvider {

        /* Get the current velocity of a swipe */
        fun getCurrentSwipeVelocity(): Float
    }

    companion object {
        /** Detaching threshold in dp */
        const val MAGNETIC_DETACH_THRESHOLD_DP = 56
        const val MAGNETIC_DETACH_THRESHOLD_DP = 72

        /** Re-attaching threshold in dp */
        const val MAGNETIC_ATTACH_THRESHOLD_DP = 40
        const val MAGNETIC_ATTACH_THRESHOLD_DP = 56

        /* An empty implementation of a manager */
        @JvmStatic
        val Empty: MagneticNotificationRowManager
            get() =
                object : MagneticNotificationRowManager {
                    override fun setInfoProvider(swipeInfoProvider: SwipeInfoProvider?) {}

                    override fun onDensityChange(density: Float) {}

                    override fun setMagneticAndRoundableTargets(
+48 −29
Original line number Diff line number Diff line
@@ -66,17 +66,18 @@ constructor(
    val swipedRowMultiplier =
        MAGNETIC_TRANSLATION_MULTIPLIERS[MAGNETIC_TRANSLATION_MULTIPLIERS.size / 2]

    /**
     * An offset applied to input translation that increases on subsequent re-attachments of a
     * detached magnetic view. This helps keep computations consistent when the drag gesture input
     * and the swiped notification don't share the same origin point after a re-attaching animation.
     */
    private var translationOffset = 0f

    private var dismissVelocity = 0f

    private val detachDirectionEstimator = DirectionEstimator()

    private var magneticSwipeInfoProvider: MagneticNotificationRowManager.SwipeInfoProvider? = null

    override fun setInfoProvider(
        swipeInfoProvider: MagneticNotificationRowManager.SwipeInfoProvider?
    ) {
        magneticSwipeInfoProvider = swipeInfoProvider
    }

    override fun onDensityChange(density: Float) {
        magneticDetachThreshold =
            density * MagneticNotificationRowManager.MAGNETIC_DETACH_THRESHOLD_DP
@@ -91,7 +92,6 @@ constructor(
        sectionsManager: NotificationSectionsManager,
    ) {
        if (currentState == State.IDLE) {
            translationOffset = 0f
            detachDirectionEstimator.reset()
            updateMagneticAndRoundableTargets(swipingRow, stackScrollLayout, sectionsManager)
            currentState = State.TARGETS_SET
@@ -142,29 +142,28 @@ constructor(

        val canTargetBeDismissed =
            currentMagneticListeners.swipedListener()?.canRowBeDismissed() ?: false
        val correctedTranslation = translation - translationOffset
        when (currentState) {
            State.IDLE -> {
                logger.logMagneticRowTranslationNotSet(currentState, row.getLoggingKey())
                return false
            }
            State.TARGETS_SET -> {
                detachDirectionEstimator.recordTranslation(correctedTranslation)
                pullTargets(correctedTranslation, canTargetBeDismissed)
                detachDirectionEstimator.recordTranslation(translation)
                pullTargets(translation, canTargetBeDismissed)
                currentState = State.PULLING
            }
            State.PULLING -> {
                detachDirectionEstimator.recordTranslation(correctedTranslation)
                updateRoundness(correctedTranslation)
                detachDirectionEstimator.recordTranslation(translation)
                updateRoundness(translation)
                if (canTargetBeDismissed) {
                    pullDismissibleRow(correctedTranslation)
                    pullDismissibleRow(translation)
                } else {
                    pullTargets(correctedTranslation, canSwipedBeDismissed = false)
                    pullTargets(translation, canSwipedBeDismissed = false)
                }
            }
            State.DETACHED -> {
                detachDirectionEstimator.recordTranslation(correctedTranslation)
                translateDetachedRow(correctedTranslation)
                detachDirectionEstimator.recordTranslation(translation)
                translateDetachedRow(translation)
            }
        }
        return true
@@ -238,8 +237,14 @@ constructor(
    }

    private fun detach(listener: MagneticRowListener, toPosition: Float) {
        val direction = detachDirectionEstimator.direction
        val velocity = magneticSwipeInfoProvider?.getCurrentSwipeVelocity() ?: 0f
        listener.cancelMagneticAnimations()
        listener.triggerMagneticForce(toPosition, detachForce)
        listener.triggerMagneticForce(
            toPosition,
            detachForce,
            startVelocity = direction * abs(velocity),
        )
        notificationRoundnessManager.setRoundnessForAffectedViews(
            /* roundness */ 1f,
            /* animate */ true,
@@ -259,25 +264,40 @@ constructor(
    private fun translateDetachedRow(translation: Float) {
        val crossedThreshold = abs(translation) <= magneticAttachThreshold
        if (crossedThreshold) {
            translationOffset += translation
            detachDirectionEstimator.reset()
            updateRoundness(translation = 0f, animate = true)
            currentMagneticListeners.swipedListener()?.let { attach(it) }
            updateRoundness(translation, animate = true)
            attach(translation)
            currentState = State.PULLING
        } else {
            val swiped = currentMagneticListeners.swipedListener()
            swiped?.setMagneticTranslation(translation, trackEagerly = false)
            swiped?.setMagneticTranslation(translation)
        }
    }

    private fun attach(listener: MagneticRowListener) {
        listener.cancelMagneticAnimations()
        listener.triggerMagneticForce(endTranslation = 0f, attachForce)
    private fun attach(translation: Float) {
        val detachDirection = detachDirectionEstimator.direction
        val swipeVelocity = magneticSwipeInfoProvider?.getCurrentSwipeVelocity() ?: 0f
        msdlPlayer.playToken(MSDLToken.SWIPE_THRESHOLD_INDICATOR)
        currentMagneticListeners.forEachIndexed { i, listener ->
            val targetTranslation = MAGNETIC_TRANSLATION_MULTIPLIERS[i] * translation
            val attachForce =
                SpringForce().setStiffness(ATTACH_STIFFNESS).setDampingRatio(ATTACH_DAMPING_RATIO)
            val velocity =
                if (i == currentMagneticListeners.size / 2) {
                    detachDirection * abs(swipeVelocity)
                } else {
                    0f
                }
            listener?.cancelMagneticAnimations()
            listener?.triggerMagneticForce(
                endTranslation = targetTranslation,
                springForce = attachForce,
                startVelocity = velocity,
            )
        }
        detachDirectionEstimator.reset()
    }

    override fun onMagneticInteractionEnd(row: ExpandableNotificationRow, velocity: Float?) {
        translationOffset = 0f
        detachDirectionEstimator.reset()
        if (row.isSwipedTarget()) {
            when (currentState) {
@@ -321,7 +341,6 @@ constructor(
    override fun resetRoundness() = notificationRoundnessManager.clear()

    override fun reset() {
        translationOffset = 0f
        detachDirectionEstimator.reset()
        currentMagneticListeners.forEach {
            it?.cancelMagneticAnimations()
@@ -429,7 +448,7 @@ constructor(
        private const val DETACH_DAMPING_RATIO = 0.95f
        private const val SNAP_BACK_STIFFNESS = 550f
        private const val SNAP_BACK_DAMPING_RATIO = 0.6f
        private const val ATTACH_STIFFNESS = 800f
        private const val ATTACH_STIFFNESS = 850f
        private const val ATTACH_DAMPING_RATIO = 0.95f

        private const val DISMISS_VELOCITY = 500 // in dp/sec
Loading