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

Commit d2728f84 authored by Marvin Bernal's avatar Marvin Bernal
Browse files

Back arrow animation polish for CANCEL and fling from INACTIVE states

Bug: 273619032
Test: Manual
Change-Id: I2b846277776e4c441e5852878538516f95ae775c
parent 7ad77ab9
Loading
Loading
Loading
Loading
+0 −4
Original line number Diff line number Diff line
@@ -43,8 +43,6 @@
    <dimen name="navigation_edge_panel_height">268dp</dimen>
    <!-- The threshold to drag to trigger the edge action -->
    <dimen name="navigation_edge_action_drag_threshold">16dp</dimen>
    <!-- The drag distance to consider evaluating gesture -->
    <dimen name="navigation_edge_action_min_distance_to_start_animation">24dp</dimen>
    <!-- The threshold to progress back animation for edge swipe -->
    <dimen name="navigation_edge_action_progress_threshold">412dp</dimen>
    <!-- The minimum display position of the arrow on the screen -->
@@ -58,8 +56,6 @@

    <!-- The thickness of the arrow -->
    <dimen name="navigation_edge_arrow_thickness">4dp</dimen>
    <!-- The minimum delta needed to change direction / stop triggering back -->
    <dimen name="navigation_edge_minimum_x_delta_for_switch">32dp</dimen>

    <!-- entry state -->
    <item name="navigation_edge_entry_scale" format="float" type="dimen">0.98</item>
+56 −26
Original line number Diff line number Diff line
@@ -53,11 +53,14 @@ private const val ENABLE_FAILSAFE = true
private const val PX_PER_SEC = 1000
private const val PX_PER_MS = 1

internal const val MIN_DURATION_ACTIVE_ANIMATION = 300L
internal const val MIN_DURATION_ACTIVE_BEFORE_INACTIVE_ANIMATION = 300L
private const val MIN_DURATION_ACTIVE_AFTER_INACTIVE_ANIMATION = 130L
private const val MIN_DURATION_CANCELLED_ANIMATION = 200L
private const val MIN_DURATION_COMMITTED_ANIMATION = 120L
private const val MIN_DURATION_INACTIVE_BEFORE_FLUNG_ANIMATION = 50L
private const val MIN_DURATION_CONSIDERED_AS_FLING = 100L

private const val MIN_DURATION_ENTRY_TO_ACTIVE_CONSIDERED_AS_FLING = 100L
private const val MIN_DURATION_INACTIVE_TO_ACTIVE_CONSIDERED_AS_FLING = 400L

private const val FAILSAFE_DELAY_MS = 350L
private const val POP_ON_FLING_DELAY = 140L
@@ -145,12 +148,12 @@ class BackPanelController internal constructor(
    private var startY = 0f
    private var startIsLeft: Boolean? = null

    private var gestureSinceActionDown = 0L
    private var gestureEntryTime = 0L
    private var gestureInactiveTime = 0L
    private var gestureActiveTime = 0L

    private val elapsedTimeSinceActionDown
        get() = SystemClock.uptimeMillis() - gestureSinceActionDown
    private val elapsedTimeSinceInactive
        get() = SystemClock.uptimeMillis() - gestureInactiveTime
    private val elapsedTimeSinceEntry
        get() = SystemClock.uptimeMillis() - gestureEntryTime

@@ -158,6 +161,9 @@ class BackPanelController internal constructor(
    // so that we can unambiguously start showing the ENTRY animation
    private var hasPassedDragSlop = false

    // Distance in pixels a drag can be considered for a fling event
    private var minFlingDistance = 0

    private val failsafeRunnable = Runnable { onFailsafe() }

    internal enum class GestureState {
@@ -235,6 +241,7 @@ class BackPanelController internal constructor(
    private fun updateConfiguration() {
        params.update(resources)
        mView.updateArrowPaint(params.arrowThickness)
        minFlingDistance = ViewConfiguration.get(context).scaledTouchSlop * 3
    }

    private val configurationListener = object : ConfigurationController.ConfigurationListener {
@@ -268,7 +275,6 @@ class BackPanelController internal constructor(
        velocityTracker!!.addMovement(event)
        when (event.actionMasked) {
            MotionEvent.ACTION_DOWN -> {
                gestureSinceActionDown = SystemClock.uptimeMillis()
                cancelAllPendingAnimations()
                startX = event.x
                startY = event.y
@@ -307,8 +313,22 @@ class BackPanelController internal constructor(
                        }
                    }
                    GestureState.ACTIVE -> {
                        if (elapsedTimeSinceEntry < MIN_DURATION_CONSIDERED_AS_FLING) {
                        if (previousState == GestureState.ENTRY &&
                                elapsedTimeSinceEntry
                                    < MIN_DURATION_ENTRY_TO_ACTIVE_CONSIDERED_AS_FLING
                        ) {
                            updateArrowState(GestureState.FLUNG)
                        } else if (previousState == GestureState.INACTIVE &&
                                elapsedTimeSinceInactive
                                    < MIN_DURATION_INACTIVE_TO_ACTIVE_CONSIDERED_AS_FLING
                        ) {
                            // A delay is added to allow the background to transition back to ACTIVE
                            // since it was briefly in INACTIVE. Without this delay, setting it
                            // immediately to COMMITTED would result in the committed animation
                            // appearing like it was playing in INACTIVE.
                            mainHandler.postDelayed(MIN_DURATION_ACTIVE_AFTER_INACTIVE_ANIMATION) {
                                updateArrowState(GestureState.COMMITTED)
                            }
                        } else {
                            updateArrowState(GestureState.COMMITTED)
                        }
@@ -376,7 +396,7 @@ class BackPanelController internal constructor(
                val isPastDynamicDeactivationThreshold =
                        totalTouchDelta <= params.deactivationSwipeTriggerThreshold
                val isMinDurationElapsed =
                        elapsedTimeSinceActionDown > MIN_DURATION_ACTIVE_ANIMATION
                        elapsedTimeSinceEntry > MIN_DURATION_ACTIVE_BEFORE_INACTIVE_ANIMATION

                if (isMinDurationElapsed && (!isWithinYActivationThreshold ||
                                isPastDynamicDeactivationThreshold)
@@ -470,8 +490,15 @@ class BackPanelController internal constructor(
            GestureState.GONE -> 0f
        }

        val indicator = when (currentState) {
            GestureState.ENTRY -> params.entryIndicator
            GestureState.INACTIVE -> params.preThresholdIndicator
            GestureState.ACTIVE -> params.activeIndicator
            else -> params.preThresholdIndicator
        }

        strokeAlphaProgress?.let { progress ->
            params.arrowStrokeAlphaSpring.get(progress).takeIf { it.isNewState }?.let {
            indicator.arrowDimens.alphaSpring?.get(progress)?.takeIf { it.isNewState }?.let {
                mView.popArrowAlpha(0f, it.value)
            }
        }
@@ -537,7 +564,8 @@ class BackPanelController internal constructor(
                backgroundHeightStretchAmount = params.heightInterpolator
                        .getInterpolation(progress),
                backgroundAlphaStretchAmount = 1f,
                arrowAlphaStretchAmount = params.arrowStrokeAlphaInterpolator.get(progress).value,
                arrowAlphaStretchAmount = params.entryIndicator.arrowDimens
                        .alphaInterpolator?.get(progress)?.value ?: 0f,
                edgeCornerStretchAmount = params.edgeCornerInterpolator.getInterpolation(progress),
                farCornerStretchAmount = params.farCornerInterpolator.getInterpolation(progress),
                fullyStretchedDimens = params.preThresholdIndicator
@@ -567,7 +595,8 @@ class BackPanelController internal constructor(
                backgroundHeightStretchAmount = params.heightInterpolator
                        .getInterpolation(progress),
                backgroundAlphaStretchAmount = 1f,
                arrowAlphaStretchAmount = params.arrowStrokeAlphaInterpolator.get(progress).value,
                arrowAlphaStretchAmount = params.preThresholdIndicator.arrowDimens
                        .alphaInterpolator?.get(progress)?.value ?: 0f,
                edgeCornerStretchAmount = params.edgeCornerInterpolator.getInterpolation(progress),
                farCornerStretchAmount = params.farCornerInterpolator.getInterpolation(progress),
                fullyStretchedDimens = params.preThresholdIndicator
@@ -599,19 +628,15 @@ class BackPanelController internal constructor(
        windowManager.addView(mView, layoutParams)
    }

    private fun isDragAwayFromEdge(velocityPxPerSecThreshold: Int = 0) = velocityTracker!!.run {
        computeCurrentVelocity(PX_PER_SEC)
        val velocity = xVelocity.takeIf { mView.isLeftPanel } ?: (xVelocity * -1)
        velocity > velocityPxPerSecThreshold
    }

    private fun isFlungAwayFromEdge(endX: Float, startX: Float = touchDeltaStartX): Boolean {
        val minDistanceConsideredForFling = ViewConfiguration.get(context).scaledTouchSlop
        val flingDistance = if (mView.isLeftPanel) endX - startX else startX - endX
        val isPastFlingVelocity = isDragAwayFromEdge(
                velocityPxPerSecThreshold =
                ViewConfiguration.get(context).scaledMinimumFlingVelocity)
        return flingDistance > minDistanceConsideredForFling && isPastFlingVelocity
        val flingVelocity = velocityTracker?.run {
            computeCurrentVelocity(PX_PER_SEC)
            xVelocity.takeIf { mView.isLeftPanel } ?: (xVelocity * -1)
        } ?: 0f
        val isPastFlingVelocityThreshold =
                flingVelocity > ViewConfiguration.get(context).scaledMinimumFlingVelocity
        return flingDistance > minFlingDistance && isPastFlingVelocityThreshold
    }

    private fun playHorizontalAnimationThen(onEnd: DelayedOnAnimationEndListener) {
@@ -664,7 +689,6 @@ class BackPanelController internal constructor(
                mView.setSpring(
                        arrowLength = params.entryIndicator.arrowDimens.lengthSpring,
                        arrowHeight = params.entryIndicator.arrowDimens.heightSpring,
                        arrowAlpha = params.entryIndicator.arrowDimens.alphaSpring,
                        scale = params.entryIndicator.scaleSpring,
                        verticalTranslation = params.entryIndicator.verticalTranslationSpring,
                        horizontalTranslation = params.entryIndicator.horizontalTranslationSpring,
@@ -725,6 +749,7 @@ class BackPanelController internal constructor(
                        arrowLength = params.committedIndicator.arrowDimens.lengthSpring,
                        arrowHeight = params.committedIndicator.arrowDimens.heightSpring,
                        scale = params.committedIndicator.scaleSpring,
                        backgroundAlpha = params.committedIndicator.backgroundDimens.alphaSpring,
                        backgroundWidth = params.committedIndicator.backgroundDimens.widthSpring,
                        backgroundHeight = params.committedIndicator.backgroundDimens.heightSpring,
                        backgroundEdgeCornerRadius = params.committedIndicator.backgroundDimens
@@ -733,6 +758,10 @@ class BackPanelController internal constructor(
                                .farCornerRadiusSpring,
                )
            }
            GestureState.CANCELLED -> {
                mView.setSpring(
                        backgroundAlpha = params.cancelledIndicator.backgroundDimens.alphaSpring)
            }
            else -> {}
        }

@@ -864,6 +893,7 @@ class BackPanelController internal constructor(
            }

            GestureState.INACTIVE -> {
                gestureInactiveTime = SystemClock.uptimeMillis()

                // Typically entering INACTIVE means
                // totalTouchDelta <= deactivationSwipeTriggerThreshold
@@ -900,9 +930,9 @@ class BackPanelController internal constructor(
                val delay = max(0, MIN_DURATION_CANCELLED_ANIMATION - elapsedTimeSinceEntry)
                playWithBackgroundWidthAnimation(onEndSetGoneStateListener, delay)

                params.arrowStrokeAlphaSpring.get(0f).takeIf { it.isNewState }?.let {
                    mView.popArrowAlpha(0f, it.value)
                }
                val springForceOnCancelled = params.cancelledIndicator
                        .arrowDimens.alphaSpring?.get(0f)?.value
                mView.popArrowAlpha(0f, springForceOnCancelled)
                mainHandler.postDelayed(10L) { vibratorHelper.cancel() }
            }
        }
+43 −40
Original line number Diff line number Diff line
@@ -12,9 +12,10 @@ data class EdgePanelParams(private var resources: Resources) {
            val length: Float? = 0f,
            val height: Float? = 0f,
            val alpha: Float = 0f,
            var alphaSpring: SpringForce? = null,
            val heightSpring: SpringForce? = null,
            val lengthSpring: SpringForce? = null,
            var alphaSpring: Step<SpringForce>? = null,
            var alphaInterpolator: Step<Float>? = null
    )

    data class BackgroundDimens(
@@ -61,11 +62,6 @@ data class EdgePanelParams(private var resources: Resources) {
        private set
    var arrowThickness: Float = 0f
        private set
    lateinit var arrowStrokeAlphaSpring: Step<SpringForce>
        private set
    lateinit var arrowStrokeAlphaInterpolator: Step<Float>
        private set

    // The closest to y
    var minArrowYPosition: Int = 0
        private set
@@ -81,13 +77,6 @@ data class EdgePanelParams(private var resources: Resources) {
    var swipeProgressThreshold: Float = 0f
        private set

    // The minimum delta needed to change direction / stop triggering back
    var minDeltaForSwitch: Int = 0
        private set

    var minDragToStartAnimation: Float = 0f
        private set

    lateinit var entryWidthInterpolator: PathInterpolator
        private set
    lateinit var entryWidthTowardsEdgeInterpolator: PathInterpolator
@@ -133,23 +122,17 @@ data class EdgePanelParams(private var resources: Resources) {
        deactivationSwipeTriggerThreshold =
                getDimen(R.dimen.navigation_edge_action_deactivation_drag_threshold)
        swipeProgressThreshold = getDimen(R.dimen.navigation_edge_action_progress_threshold)
        minDeltaForSwitch = getPx(R.dimen.navigation_edge_minimum_x_delta_for_switch)
        minDragToStartAnimation =
                getDimen(R.dimen.navigation_edge_action_min_distance_to_start_animation)

        entryWidthInterpolator = PathInterpolator(.19f, 1.27f, .71f, .86f)
        entryWidthTowardsEdgeInterpolator = PathInterpolator(1f, -3f, 1f, 1.2f)
        activeWidthInterpolator = PathInterpolator(.32f, 0f, .16f, .94f)
        activeWidthInterpolator = PathInterpolator(.7f, .06f, .34f, .97f)
        arrowAngleInterpolator = entryWidthInterpolator
        translationInterpolator = PathInterpolator(0.2f, 1.0f, 1.0f, 1.0f)
        farCornerInterpolator = PathInterpolator(.03f, .19f, .14f, 1.09f)
        edgeCornerInterpolator = PathInterpolator(0f, 1.11f, .85f, .84f)
        heightInterpolator = PathInterpolator(1f, .05f, .9f, -0.29f)

        val showArrowOnProgressValue = .23f
        val showArrowOnProgressValueFactor = 1.05f

        val entryActiveHorizontalTranslationSpring = createSpring(800f, 0.8f)
        val entryActiveHorizontalTranslationSpring = createSpring(800f, 0.76f)
        val activeCommittedArrowLengthSpring = createSpring(1500f, 0.29f)
        val activeCommittedArrowHeightSpring = createSpring(1500f, 0.29f)
        val flungCommittedEdgeCornerSpring = createSpring(10000f, 1f)
@@ -157,6 +140,8 @@ data class EdgePanelParams(private var resources: Resources) {
        val flungCommittedWidthSpring = createSpring(10000f, 1f)
        val flungCommittedHeightSpring = createSpring(10000f, 1f)

        val entryIndicatorAlphaThreshold = .23f
        val entryIndicatorAlphaFactor = 1.05f
        entryIndicator = BackIndicatorDimens(
                horizontalTranslation = getDimen(R.dimen.navigation_edge_entry_margin),
                scale = getDimenFloat(R.dimen.navigation_edge_entry_scale),
@@ -168,9 +153,20 @@ data class EdgePanelParams(private var resources: Resources) {
                        length = getDimen(R.dimen.navigation_edge_entry_arrow_length),
                        height = getDimen(R.dimen.navigation_edge_entry_arrow_height),
                        alpha = 0f,
                        alphaSpring = createSpring(200f, 1f),
                        lengthSpring = createSpring(600f, 0.4f),
                        heightSpring = createSpring(600f, 0.4f),
                        alphaSpring = Step(
                                threshold = entryIndicatorAlphaThreshold,
                                factor = entryIndicatorAlphaFactor,
                                postThreshold = createSpring(200f, 1f),
                                preThreshold = createSpring(2000f, 0.6f)
                        ),
                        alphaInterpolator = Step(
                                threshold = entryIndicatorAlphaThreshold,
                                factor = entryIndicatorAlphaFactor,
                                postThreshold = 1f,
                                preThreshold = 0f
                        )
                ),
                backgroundDimens = BackgroundDimens(
                        alpha = 1f,
@@ -186,6 +182,20 @@ data class EdgePanelParams(private var resources: Resources) {
                )
        )

        val preThresholdAndActiveIndicatorAlphaThreshold = .355f
        val preThresholdAndActiveIndicatorAlphaFactor = 1.05f
        val preThresholdAndActiveAlphaSpring = Step(
                threshold = preThresholdAndActiveIndicatorAlphaThreshold,
                factor = preThresholdAndActiveIndicatorAlphaFactor,
                postThreshold = createSpring(180f, 0.9f),
                preThreshold = createSpring(2000f, 0.6f)
        )
        val preThresholdAndActiveAlphaSpringInterpolator = Step(
                threshold = preThresholdAndActiveIndicatorAlphaThreshold,
                factor = preThresholdAndActiveIndicatorAlphaFactor,
                postThreshold = 1f,
                preThreshold = 0f
        )
        activeIndicator = BackIndicatorDimens(
                horizontalTranslation = getDimen(R.dimen.navigation_edge_active_margin),
                scale = getDimenFloat(R.dimen.navigation_edge_active_scale),
@@ -197,6 +207,8 @@ data class EdgePanelParams(private var resources: Resources) {
                        alpha = 1f,
                        lengthSpring = activeCommittedArrowLengthSpring,
                        heightSpring = activeCommittedArrowHeightSpring,
                        alphaSpring = preThresholdAndActiveAlphaSpring,
                        alphaInterpolator = preThresholdAndActiveAlphaSpringInterpolator
                ),
                backgroundDimens = BackgroundDimens(
                        alpha = 1f,
@@ -204,7 +216,7 @@ data class EdgePanelParams(private var resources: Resources) {
                        height = getDimen(R.dimen.navigation_edge_active_background_height),
                        edgeCornerRadius = getDimen(R.dimen.navigation_edge_active_edge_corners),
                        farCornerRadius = getDimen(R.dimen.navigation_edge_active_far_corners),
                        widthSpring = createSpring(375f, 0.675f),
                        widthSpring = createSpring(650f, 0.75f),
                        heightSpring = createSpring(10000f, 1f),
                        edgeCornerRadiusSpring = createSpring(600f, 0.36f),
                        farCornerRadiusSpring = createSpring(2500f, 0.855f),
@@ -223,6 +235,8 @@ data class EdgePanelParams(private var resources: Resources) {
                        alpha = 1f,
                        lengthSpring = createSpring(100f, 0.6f),
                        heightSpring = createSpring(100f, 0.6f),
                        alphaSpring = preThresholdAndActiveAlphaSpring,
                        alphaInterpolator = preThresholdAndActiveAlphaSpringInterpolator
                ),
                backgroundDimens = BackgroundDimens(
                        alpha = 1f,
@@ -255,6 +269,7 @@ data class EdgePanelParams(private var resources: Resources) {
                        heightSpring = flungCommittedHeightSpring,
                        edgeCornerRadiusSpring = flungCommittedEdgeCornerSpring,
                        farCornerRadiusSpring = flungCommittedFarCornerSpring,
                        alphaSpring = createSpring(1100f, 1f),
                ),
                scale = 0.85f,
                scaleSpring = createSpring(1150f, 1f),
@@ -276,7 +291,11 @@ data class EdgePanelParams(private var resources: Resources) {
        )

        cancelledIndicator = entryIndicator.copy(
                backgroundDimens = entryIndicator.backgroundDimens.copy(width = 0f)
                backgroundDimens = entryIndicator.backgroundDimens.copy(
                        width = 0f,
                        alpha = 0f,
                        alphaSpring = createSpring(450f, 1f)
                )
        )

        fullyStretchedIndicator = BackIndicatorDimens(
@@ -306,22 +325,6 @@ data class EdgePanelParams(private var resources: Resources) {
                        farCornerRadiusSpring = null,
                )
        )

        arrowStrokeAlphaInterpolator = Step(
                threshold = showArrowOnProgressValue,
                factor = showArrowOnProgressValueFactor,
                postThreshold = 1f,
                preThreshold = 0f
        )

        entryIndicator.arrowDimens.alphaSpring?.let { alphaSpring ->
            arrowStrokeAlphaSpring = Step(
                    threshold = showArrowOnProgressValue,
                    factor = showArrowOnProgressValueFactor,
                    postThreshold = alphaSpring,
                    preThreshold = SpringForce().setStiffness(2000f).setDampingRatio(1f)
            )
        }
    }
}

+1 −1
Original line number Diff line number Diff line
@@ -127,7 +127,7 @@ class BackPanelControllerTest : SysuiTestCase() {
                mBackPanelController.params.deactivationSwipeTriggerThreshold
        )
        clearInvocations(backCallback)
        Thread.sleep(MIN_DURATION_ACTIVE_ANIMATION)
        Thread.sleep(MIN_DURATION_ACTIVE_BEFORE_INACTIVE_ANIMATION)
        // Move in the opposite direction to cross the deactivation threshold and cancel back
        continueTouch(START_X)