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

Commit 21ed30cc authored by Marvin Bernal's avatar Marvin Bernal Committed by Automerger Merge Worker
Browse files

Merge "Add a stickiness factor to Back arrow on entry" into udc-dev am: 682209e2

parents e93a63f0 682209e2
Loading
Loading
Loading
Loading
+82 −72
Original line number Diff line number Diff line
@@ -60,15 +60,21 @@ private const val MIN_DURATION_CANCELLED_ANIMATION = 200L
private const val MIN_DURATION_COMMITTED_ANIMATION = 80L
private const val MIN_DURATION_COMMITTED_AFTER_FLING_ANIMATION = 120L
private const val MIN_DURATION_INACTIVE_BEFORE_FLUNG_ANIMATION = 50L
private const val MIN_DURATION_INACTIVE_BEFORE_ACTIVE_ANIMATION = 80L
private const val MIN_DURATION_INACTIVE_BEFORE_ACTIVE_ANIMATION = 160F
private const val MIN_DURATION_ENTRY_BEFORE_ACTIVE_ANIMATION = 10F
internal const val MAX_DURATION_ENTRY_BEFORE_ACTIVE_ANIMATION = 100F
private const val MIN_DURATION_FLING_ANIMATION = 160L

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 POP_ON_FLING_DELAY = 60L
private const val POP_ON_FLING_SCALE = 2f
private const val POP_ON_COMMITTED_SCALE = 3f
private const val POP_ON_FLING_VELOCITY = 2f
private const val POP_ON_COMMITTED_VELOCITY = 3f
private const val POP_ON_ACTIVE_MAX_VELOCITY = 2.5f
private const val POP_ON_ACTIVE_MIN_VELOCITY = 4.5f
private const val POP_ON_INACTIVE_MAX_VELOCITY = -1.05f
private const val POP_ON_INACTIVE_MIN_VELOCITY = -1.5f

internal val VIBRATE_ACTIVATED_EFFECT =
        VibrationEffect.createPredefined(VibrationEffect.EFFECT_CLICK)
@@ -156,13 +162,22 @@ class BackPanelController internal constructor(

    private var gestureEntryTime = 0L
    private var gestureInactiveTime = 0L
    private var gesturePastActiveThresholdWhileInactiveTime = 0L

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


    private var pastThresholdWhileEntryOrInactiveTime = 0L
    private var entryToActiveDelay = 0F
    private val entryToActiveDelayCalculation = {
        convertVelocityToAnimationFactor(
            valueOnFastVelocity = MIN_DURATION_ENTRY_BEFORE_ACTIVE_ANIMATION,
            valueOnSlowVelocity = MAX_DURATION_ENTRY_BEFORE_ACTIVE_ANIMATION,
        )
    }

    // Whether the current gesture has moved a sufficiently large amount,
    // so that we can unambiguously start showing the ENTRY animation
    private var hasPassedDragSlop = false
@@ -306,7 +321,9 @@ class BackPanelController internal constructor(
            MotionEvent.ACTION_UP -> {
                when (currentState) {
                    GestureState.ENTRY -> {
                        if (isFlungAwayFromEdge(endX = event.x)) {
                        if (isFlungAwayFromEdge(endX = event.x) ||
                            previousXTranslation > params.staticTriggerThreshold
                        ) {
                            updateArrowState(GestureState.ACTIVE)
                            updateArrowState(GestureState.FLUNG)
                        } else {
@@ -394,12 +411,29 @@ class BackPanelController internal constructor(
    }

    private fun updateArrowStateOnMove(yTranslation: Float, xTranslation: Float) {

        val isWithinYActivationThreshold = xTranslation * 2 >= yTranslation

        val isPastStaticThreshold = xTranslation > params.staticTriggerThreshold
        when (currentState) {
            GestureState.ENTRY -> {
                if (xTranslation > params.staticTriggerThreshold) {
                if (isPastThresholdToActive(
                        isPastThreshold = isPastStaticThreshold,
                        dynamicDelay = entryToActiveDelayCalculation
                    )
                ) {
                    updateArrowState(GestureState.ACTIVE)
                }
            }
            GestureState.INACTIVE -> {
                val isPastDynamicReactivationThreshold =
                    totalTouchDeltaInactive >= params.reactivationTriggerThreshold

                if (isPastThresholdToActive(
                        isPastThreshold = isPastStaticThreshold &&
                                isPastDynamicReactivationThreshold &&
                                isWithinYActivationThreshold,
                        delay = MIN_DURATION_INACTIVE_BEFORE_ACTIVE_ANIMATION
                    )
                ) {
                    updateArrowState(GestureState.ACTIVE)
                }
            }
@@ -408,43 +442,12 @@ class BackPanelController internal constructor(
                    totalTouchDeltaActive <= params.deactivationTriggerThreshold
                val isMinDurationElapsed =
                    elapsedTimeSinceEntry > MIN_DURATION_ACTIVE_BEFORE_INACTIVE_ANIMATION

                if (isMinDurationElapsed && (!isWithinYActivationThreshold ||
                            isPastDynamicDeactivationThreshold)
                ) {
                val isPastAllThresholds =
                    !isWithinYActivationThreshold || isPastDynamicDeactivationThreshold
                if (isPastAllThresholds && isMinDurationElapsed) {
                    updateArrowState(GestureState.INACTIVE)
                }
            }
            GestureState.INACTIVE -> {
                val isPastStaticThreshold =
                    xTranslation > params.staticTriggerThreshold
                val isPastDynamicReactivationThreshold =
                    totalTouchDeltaInactive >= params.reactivationTriggerThreshold
                val isPastAllThresholds = isPastStaticThreshold &&
                        isPastDynamicReactivationThreshold &&
                        isWithinYActivationThreshold
                val isPastAllThresholdsForFirstTime = isPastAllThresholds &&
                        gesturePastActiveThresholdWhileInactiveTime == 0L

                gesturePastActiveThresholdWhileInactiveTime = when {
                    isPastAllThresholdsForFirstTime -> SystemClock.uptimeMillis()
                    isPastAllThresholds -> gesturePastActiveThresholdWhileInactiveTime
                    else -> 0L
                }

                val elapsedTimePastAllThresholds =
                    SystemClock.uptimeMillis() - gesturePastActiveThresholdWhileInactiveTime

                val isPastMinimumInactiveToActiveDuration =
                    elapsedTimePastAllThresholds > MIN_DURATION_INACTIVE_BEFORE_ACTIVE_ANIMATION

                if (isPastAllThresholds && isPastMinimumInactiveToActiveDuration) {
                    // The minimum duration adds the 'edge stickiness'
                    // factor before pulling it off edge
                    updateArrowState(GestureState.ACTIVE)
                }
            }

            else -> {}
        }
    }
@@ -672,6 +675,28 @@ class BackPanelController internal constructor(
        return flingDistance > minFlingDistance && isPastFlingVelocityThreshold
    }

    private fun isPastThresholdToActive(
        isPastThreshold: Boolean,
        delay: Float? = null,
        dynamicDelay: () -> Float = { delay ?: 0F }
    ): Boolean {
        val resetValue = 0L
        val isPastThresholdForFirstTime = pastThresholdWhileEntryOrInactiveTime == resetValue

        if (!isPastThreshold) {
            pastThresholdWhileEntryOrInactiveTime = resetValue
            return false
        }

        if (isPastThresholdForFirstTime) {
            pastThresholdWhileEntryOrInactiveTime = SystemClock.uptimeMillis()
            entryToActiveDelay = dynamicDelay()
        }
        val timePastThreshold = SystemClock.uptimeMillis() - pastThresholdWhileEntryOrInactiveTime

        return timePastThreshold > entryToActiveDelay
    }

    private fun playWithBackgroundWidthAnimation(
            onEnd: DelayedOnAnimationEndListener,
            delay: Long = 0L
@@ -886,34 +911,17 @@ class BackPanelController internal constructor(
            }
            GestureState.ACTIVE -> {
                previousXTranslationOnActiveOffset = previousXTranslation

                updateRestingArrowDimens()

                vibratorHelper.cancel()
                mainHandler.postDelayed(10L) {
                    vibratorHelper.vibrate(VIBRATE_ACTIVATED_EFFECT)
                }

                val minimumPop = 2f
                val maximumPop = 4.5f

                when (previousState) {
                    GestureState.ENTRY -> {
                        val startingVelocity = convertVelocityToSpringStartingVelocity(
                            valueOnFastVelocity = minimumPop,
                            valueOnSlowVelocity = maximumPop,
                            fastVelocityBound = 1f,
                            slowVelocityBound = 0.5f,
                val startingVelocity = convertVelocityToAnimationFactor(
                    valueOnFastVelocity = POP_ON_ACTIVE_MAX_VELOCITY,
                    valueOnSlowVelocity = POP_ON_ACTIVE_MIN_VELOCITY,
                )
                mView.popOffEdge(startingVelocity)
            }
                    GestureState.INACTIVE -> {
                        mView.popOffEdge(maximumPop)
                    }

                    else -> {}
                }
            }

            GestureState.INACTIVE -> {
                gestureInactiveTime = SystemClock.uptimeMillis()
@@ -925,9 +933,9 @@ class BackPanelController internal constructor(
                // so that gesture progress in this state is consistent regardless of entry
                totalTouchDeltaInactive = params.deactivationTriggerThreshold

                val startingVelocity = convertVelocityToSpringStartingVelocity(
                        valueOnFastVelocity = -1.05f,
                        valueOnSlowVelocity = -1.50f
                val startingVelocity = convertVelocityToAnimationFactor(
                        valueOnFastVelocity = POP_ON_INACTIVE_MAX_VELOCITY,
                        valueOnSlowVelocity = POP_ON_INACTIVE_MIN_VELOCITY
                )
                mView.popOffEdge(startingVelocity)

@@ -935,7 +943,9 @@ class BackPanelController internal constructor(
                updateRestingArrowDimens()
            }
            GestureState.FLUNG -> {
                mainHandler.postDelayed(POP_ON_FLING_DELAY) { mView.popScale(POP_ON_FLING_SCALE) }
                mainHandler.postDelayed(POP_ON_FLING_DELAY) {
                    mView.popScale(POP_ON_FLING_VELOCITY)
                }
                updateRestingArrowDimens()
                mainHandler.postDelayed(onEndSetCommittedStateListener.runnable,
                        MIN_DURATION_FLING_ANIMATION)
@@ -951,7 +961,7 @@ class BackPanelController internal constructor(
                    mainHandler.postDelayed(onEndSetGoneStateListener.runnable,
                            MIN_DURATION_COMMITTED_AFTER_FLING_ANIMATION)
                } else {
                    mView.popScale(POP_ON_COMMITTED_SCALE)
                    mView.popScale(POP_ON_COMMITTED_VELOCITY)
                    mainHandler.postDelayed(onAlphaEndSetGoneStateListener.runnable,
                            MIN_DURATION_COMMITTED_ANIMATION)
                }
@@ -968,11 +978,11 @@ class BackPanelController internal constructor(
        }
    }

    private fun convertVelocityToSpringStartingVelocity(
    private fun convertVelocityToAnimationFactor(
            valueOnFastVelocity: Float,
            valueOnSlowVelocity: Float,
            fastVelocityBound: Float = 3f,
            slowVelocityBound: Float = 0f,
            fastVelocityBound: Float = 1f,
            slowVelocityBound: Float = 0.5f,
    ): Float {
        val factor = velocityTracker?.run {
            computeCurrentVelocity(PX_PER_MS)
+4 −4
Original line number Diff line number Diff line
@@ -153,7 +153,7 @@ data class EdgePanelParams(private var resources: Resources) {
                horizontalTranslation = getDimen(R.dimen.navigation_edge_entry_margin),
                scale = getDimenFloat(R.dimen.navigation_edge_entry_scale),
                scalePivotX = getDimen(R.dimen.navigation_edge_pre_threshold_background_width),
                horizontalTranslationSpring = createSpring(500f, 0.76f),
                horizontalTranslationSpring = createSpring(800f, 0.76f),
                verticalTranslationSpring = createSpring(30000f, 1f),
                scaleSpring = createSpring(120f, 0.8f),
                arrowDimens = ArrowDimens(
@@ -205,8 +205,8 @@ data class EdgePanelParams(private var resources: Resources) {
        activeIndicator = BackIndicatorDimens(
                horizontalTranslation = getDimen(R.dimen.navigation_edge_active_margin),
                scale = getDimenFloat(R.dimen.navigation_edge_active_scale),
                horizontalTranslationSpring = createSpring(1000f, 0.7f),
                scaleSpring = createSpring(450f, 0.39f),
                horizontalTranslationSpring = createSpring(1000f, 0.8f),
                scaleSpring = createSpring(325f, 0.55f),
                scalePivotX = getDimen(R.dimen.navigation_edge_active_background_width),
                arrowDimens = ArrowDimens(
                        length = getDimen(R.dimen.navigation_edge_active_arrow_length),
@@ -253,7 +253,7 @@ data class EdgePanelParams(private var resources: Resources) {
                                getDimen(R.dimen.navigation_edge_pre_threshold_edge_corners),
                        farCornerRadius =
                                getDimen(R.dimen.navigation_edge_pre_threshold_far_corners),
                        widthSpring = createSpring(400f, 0.65f),
                        widthSpring = createSpring(650f, 1f),
                        heightSpring = createSpring(1500f, 0.45f),
                        farCornerRadiusSpring = createSpring(300f, 1f),
                        edgeCornerRadiusSpring = createSpring(250f, 0.5f),
+12 −1
Original line number Diff line number Diff line
@@ -104,6 +104,9 @@ class BackPanelControllerTest : SysuiTestCase() {
        continueTouch(START_X + touchSlop.toFloat() + 1)
        // Move again to cross the back trigger threshold
        continueTouch(START_X + touchSlop + triggerThreshold + 1)
        // Wait threshold duration and hold touch past trigger threshold
        Thread.sleep((MAX_DURATION_ENTRY_BEFORE_ACTIVE_ANIMATION + 1).toLong())
        continueTouch(START_X + touchSlop + triggerThreshold + 1)

        assertThat(mBackPanelController.currentState)
            .isEqualTo(BackPanelController.GestureState.ACTIVE)
@@ -114,14 +117,22 @@ class BackPanelControllerTest : SysuiTestCase() {

        finishTouchActionUp(START_X + touchSlop + triggerThreshold + 1)
        assertThat(mBackPanelController.currentState)
            .isEqualTo(BackPanelController.GestureState.FLUNG)
            .isEqualTo(BackPanelController.GestureState.COMMITTED)
        verify(backCallback).triggerBack()
    }

    @Test
    fun handlesBackCancelled() {
        startTouch()
        // Move once to cross the touch slop
        continueTouch(START_X + touchSlop.toFloat() + 1)
        // Move again to cross the back trigger threshold
        continueTouch(
            START_X + touchSlop + triggerThreshold -
                mBackPanelController.params.deactivationTriggerThreshold
        )
        // Wait threshold duration and hold touch before trigger threshold
        Thread.sleep((MAX_DURATION_ENTRY_BEFORE_ACTIVE_ANIMATION + 1).toLong())
        continueTouch(
            START_X + touchSlop + triggerThreshold -
                mBackPanelController.params.deactivationTriggerThreshold