diff options
| author | 2023-04-19 18:22:14 +0000 | |
|---|---|---|
| committer | 2023-04-19 22:03:42 +0000 | |
| commit | a63e464ae76d2c62d1e684b8986683af9c90ed2d (patch) | |
| tree | 1f65741acf1235a01268ac66ea461b7eb3bb781b | |
| parent | 08787ffbaa95d577d6b4ae59afdcb30174a5e3d2 (diff) | |
Simplify arrow stretch width interpolator and edge stickiness
Previously the arrow width was based on the difference from
the reactivation point and fully stretched width point.
This caused some discrepancies when these two values were approaching
equality. In this scenario, the width animation would differ
based on the reactivation point which is undesirable.
This change removes this invisible threshold and uses a displacement
based method to calculate width.
Bug: 278083360
Test: Manual
Change-Id: If992c829dc285b883951ee4852cec89e1d707d9f
3 files changed, 85 insertions, 53 deletions
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanelController.kt b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanelController.kt index 3770b2885b18..96e97565d585 100644 --- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanelController.kt +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanelController.kt @@ -60,6 +60,7 @@ 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_FLING_ANIMATION = 160L private const val MIN_DURATION_ENTRY_TO_ACTIVE_CONSIDERED_AS_FLING = 100L @@ -135,7 +136,8 @@ class BackPanelController internal constructor( private lateinit var backCallback: NavigationEdgeBackPlugin.BackCallback private var previousXTranslationOnActiveOffset = 0f private var previousXTranslation = 0f - private var totalTouchDelta = 0f + private var totalTouchDeltaActive = 0f + private var totalTouchDeltaInactive = 0f private var touchDeltaStartX = 0f private var velocityTracker: VelocityTracker? = null set(value) { @@ -154,7 +156,7 @@ class BackPanelController internal constructor( private var gestureEntryTime = 0L private var gestureInactiveTime = 0L - private var gestureActiveTime = 0L + private var gesturePastActiveThresholdWhileInactiveTime = 0L private val elapsedTimeSinceInactive get() = SystemClock.uptimeMillis() - gestureInactiveTime @@ -250,7 +252,7 @@ class BackPanelController internal constructor( private fun updateConfiguration() { params.update(resources) mView.updateArrowPaint(params.arrowThickness) - minFlingDistance = ViewConfiguration.get(context).scaledTouchSlop * 3 + minFlingDistance = viewConfiguration.scaledTouchSlop * 3 } private val configurationListener = object : ConfigurationController.ConfigurationListener { @@ -403,30 +405,46 @@ class BackPanelController internal constructor( } GestureState.ACTIVE -> { val isPastDynamicDeactivationThreshold = - totalTouchDelta <= params.deactivationSwipeTriggerThreshold + totalTouchDeltaActive <= params.deactivationTriggerThreshold val isMinDurationElapsed = - elapsedTimeSinceEntry > MIN_DURATION_ACTIVE_BEFORE_INACTIVE_ANIMATION + elapsedTimeSinceEntry > MIN_DURATION_ACTIVE_BEFORE_INACTIVE_ANIMATION if (isMinDurationElapsed && (!isWithinYActivationThreshold || - isPastDynamicDeactivationThreshold) + isPastDynamicDeactivationThreshold) ) { updateArrowState(GestureState.INACTIVE) } } GestureState.INACTIVE -> { val isPastStaticThreshold = - xTranslation > params.staticTriggerThreshold - val isPastDynamicReactivationThreshold = totalTouchDelta > 0 && - abs(totalTouchDelta) >= - params.reactivationTriggerThreshold - - if (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 -> {} } } @@ -451,19 +469,25 @@ class BackPanelController internal constructor( previousXTranslation = xTranslation if (abs(xDelta) > 0) { - val range = - params.run { deactivationSwipeTriggerThreshold..reactivationTriggerThreshold } - val isTouchInContinuousDirection = - sign(xDelta) == sign(totalTouchDelta) || totalTouchDelta in range + val isInSameDirection = sign(xDelta) == sign(totalTouchDeltaActive) + val isInDynamicRange = totalTouchDeltaActive in params.dynamicTriggerThresholdRange + val isTouchInContinuousDirection = isInSameDirection || isInDynamicRange if (isTouchInContinuousDirection) { // Direction has NOT changed, so keep counting the delta - totalTouchDelta += xDelta + totalTouchDeltaActive += xDelta } else { // Direction has changed, so reset the delta - totalTouchDelta = xDelta + totalTouchDeltaActive = xDelta touchDeltaStartX = x } + + // Add a slop to to prevent small jitters when arrow is at edge in + // emitting small values that cause the arrow to poke out slightly + val minimumDelta = -viewConfiguration.scaledTouchSlop.toFloat() + totalTouchDeltaInactive = totalTouchDeltaInactive + .plus(xDelta) + .coerceAtLeast(minimumDelta) } updateArrowStateOnMove(yTranslation, xTranslation) @@ -471,7 +495,7 @@ class BackPanelController internal constructor( val gestureProgress = when (currentState) { GestureState.ACTIVE -> fullScreenProgress(xTranslation) GestureState.ENTRY -> staticThresholdProgress(xTranslation) - GestureState.INACTIVE -> reactivationThresholdProgress(totalTouchDelta) + GestureState.INACTIVE -> reactivationThresholdProgress(totalTouchDeltaInactive) else -> null } @@ -529,8 +553,7 @@ class BackPanelController internal constructor( * the arrow is fully stretched (between 0.0 - 1.0f) */ private fun fullScreenProgress(xTranslation: Float): Float { - val progress = abs((xTranslation - previousXTranslationOnActiveOffset) / - (fullyStretchedThreshold - previousXTranslationOnActiveOffset)) + val progress = (xTranslation - previousXTranslationOnActiveOffset) / fullyStretchedThreshold return MathUtils.saturate(progress) } @@ -581,13 +604,15 @@ class BackPanelController internal constructor( } private var previousPreThresholdWidthInterpolator = params.entryWidthTowardsEdgeInterpolator - fun preThresholdWidthStretchAmount(progress: Float): Float { + private fun preThresholdWidthStretchAmount(progress: Float): Float { val interpolator = run { - val isPastSlop = abs(totalTouchDelta) > ViewConfiguration.get(context).scaledTouchSlop + val isPastSlop = totalTouchDeltaInactive > viewConfiguration.scaledTouchSlop if (isPastSlop) { - if (totalTouchDelta > 0) { + if (totalTouchDeltaInactive > 0) { params.entryWidthInterpolator - } else params.entryWidthTowardsEdgeInterpolator + } else { + params.entryWidthTowardsEdgeInterpolator + } } else { previousPreThresholdWidthInterpolator }.also { previousPreThresholdWidthInterpolator = it } @@ -643,7 +668,7 @@ class BackPanelController internal constructor( xVelocity.takeIf { mView.isLeftPanel } ?: (xVelocity * -1) } ?: 0f val isPastFlingVelocityThreshold = - flingVelocity > ViewConfiguration.get(context).scaledMinimumFlingVelocity + flingVelocity > viewConfiguration.scaledMinimumFlingVelocity return flingDistance > minFlingDistance && isPastFlingVelocityThreshold } @@ -861,7 +886,6 @@ class BackPanelController internal constructor( } GestureState.ACTIVE -> { previousXTranslationOnActiveOffset = previousXTranslation - gestureActiveTime = SystemClock.uptimeMillis() updateRestingArrowDimens() @@ -870,21 +894,24 @@ class BackPanelController internal constructor( vibratorHelper.vibrate(VIBRATE_ACTIVATED_EFFECT) } - val startingVelocity = convertVelocityToSpringStartingVelocity( - valueOnFastVelocity = 0f, - valueOnSlowVelocity = if (previousState == GestureState.ENTRY) 2f else 4.5f - ) + val minimumPop = 2f + val maximumPop = 4.5f when (previousState) { - GestureState.ENTRY, - GestureState.INACTIVE -> { + GestureState.ENTRY -> { + val startingVelocity = convertVelocityToSpringStartingVelocity( + valueOnFastVelocity = minimumPop, + valueOnSlowVelocity = maximumPop, + fastVelocityBound = 1f, + slowVelocityBound = 0.5f, + ) mView.popOffEdge(startingVelocity) } - GestureState.COMMITTED -> { - // if previous state was committed then this activation - // was due to a quick second swipe. Don't pop the arrow this time + GestureState.INACTIVE -> { + mView.popOffEdge(maximumPop) } - else -> { } + + else -> {} } } @@ -896,7 +923,7 @@ class BackPanelController internal constructor( // but because we can also independently enter this state // if touch Y >> touch X, we force it to deactivationSwipeTriggerThreshold // so that gesture progress in this state is consistent regardless of entry - totalTouchDelta = params.deactivationSwipeTriggerThreshold + totalTouchDeltaInactive = params.deactivationTriggerThreshold val startingVelocity = convertVelocityToSpringStartingVelocity( valueOnFastVelocity = -1.05f, @@ -944,10 +971,12 @@ class BackPanelController internal constructor( private fun convertVelocityToSpringStartingVelocity( valueOnFastVelocity: Float, valueOnSlowVelocity: Float, + fastVelocityBound: Float = 3f, + slowVelocityBound: Float = 0f, ): Float { val factor = velocityTracker?.run { computeCurrentVelocity(PX_PER_MS) - MathUtils.smoothStep(0f, 3f, abs(xVelocity)) + MathUtils.smoothStep(slowVelocityBound, fastVelocityBound, abs(xVelocity)) } ?: valueOnFastVelocity return MathUtils.lerp(valueOnFastVelocity, valueOnSlowVelocity, 1 - factor) @@ -982,7 +1011,7 @@ class BackPanelController internal constructor( "$currentState", "startX=$startX", "startY=$startY", - "xDelta=${"%.1f".format(totalTouchDelta)}", + "xDelta=${"%.1f".format(totalTouchDeltaActive)}", "xTranslation=${"%.1f".format(previousXTranslation)}", "pre=${"%.0f".format(staticThresholdProgress(previousXTranslation) * 100)}%", "post=${"%.0f".format(fullScreenProgress(previousXTranslation) * 100)}%" @@ -1023,7 +1052,7 @@ class BackPanelController internal constructor( } drawVerticalLine(x = params.staticTriggerThreshold, color = Color.BLUE) - drawVerticalLine(x = params.deactivationSwipeTriggerThreshold, color = Color.BLUE) + drawVerticalLine(x = params.deactivationTriggerThreshold, color = Color.BLUE) drawVerticalLine(x = startX, color = Color.GREEN) drawVerticalLine(x = previousXTranslation, color = Color.DKGRAY) } diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgePanelParams.kt b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgePanelParams.kt index c9d8c8495dcc..876c74a9f3e3 100644 --- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgePanelParams.kt +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgePanelParams.kt @@ -72,9 +72,11 @@ data class EdgePanelParams(private var resources: Resources) { private set var reactivationTriggerThreshold: Float = 0f private set - var deactivationSwipeTriggerThreshold: Float = 0f + var deactivationTriggerThreshold: Float = 0f get() = -field private set + lateinit var dynamicTriggerThresholdRange: ClosedRange<Float> + private set var swipeProgressThreshold: Float = 0f private set @@ -122,8 +124,10 @@ data class EdgePanelParams(private var resources: Resources) { staticTriggerThreshold = getDimen(R.dimen.navigation_edge_action_drag_threshold) reactivationTriggerThreshold = getDimen(R.dimen.navigation_edge_action_reactivation_drag_threshold) - deactivationSwipeTriggerThreshold = + deactivationTriggerThreshold = getDimen(R.dimen.navigation_edge_action_deactivation_drag_threshold) + dynamicTriggerThresholdRange = + reactivationTriggerThreshold..deactivationTriggerThreshold swipeProgressThreshold = getDimen(R.dimen.navigation_edge_action_progress_threshold) entryWidthInterpolator = PathInterpolator(.19f, 1.27f, .71f, .86f) @@ -136,7 +140,6 @@ data class EdgePanelParams(private var resources: Resources) { edgeCornerInterpolator = PathInterpolator(0f, 1.11f, .85f, .84f) heightInterpolator = PathInterpolator(1f, .05f, .9f, -0.29f) - val entryActiveHorizontalTranslationSpring = createSpring(800f, 0.76f) val activeCommittedArrowLengthSpring = createSpring(1500f, 0.29f) val activeCommittedArrowHeightSpring = createSpring(1500f, 0.29f) val flungCommittedEdgeCornerSpring = createSpring(10000f, 1f) @@ -150,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 = entryActiveHorizontalTranslationSpring, + horizontalTranslationSpring = createSpring(500f, 0.76f), verticalTranslationSpring = createSpring(30000f, 1f), scaleSpring = createSpring(120f, 0.8f), arrowDimens = ArrowDimens( @@ -202,7 +205,7 @@ 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 = entryActiveHorizontalTranslationSpring, + horizontalTranslationSpring = createSpring(1000f, 0.7f), scaleSpring = createSpring(450f, 0.39f), scalePivotX = getDimen(R.dimen.navigation_edge_active_background_width), arrowDimens = ArrowDimens( @@ -222,8 +225,8 @@ data class EdgePanelParams(private var resources: Resources) { farCornerRadius = getDimen(R.dimen.navigation_edge_active_far_corners), widthSpring = createSpring(850f, 0.75f), heightSpring = createSpring(10000f, 1f), - edgeCornerRadiusSpring = createSpring(600f, 0.36f), - farCornerRadiusSpring = createSpring(2500f, 0.855f), + edgeCornerRadiusSpring = createSpring(2600f, 0.855f), + farCornerRadiusSpring = createSpring(1200f, 0.30f), ) ) @@ -250,10 +253,10 @@ 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(250f, 0.65f), + widthSpring = createSpring(400f, 0.65f), heightSpring = createSpring(1500f, 0.45f), - farCornerRadiusSpring = createSpring(200f, 1f), - edgeCornerRadiusSpring = createSpring(150f, 0.5f), + farCornerRadiusSpring = createSpring(300f, 1f), + edgeCornerRadiusSpring = createSpring(250f, 0.5f), ) ) diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/gestural/BackPanelControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/gestural/BackPanelControllerTest.kt index 8e32f81b193f..d9428f897f0d 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/gestural/BackPanelControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/gestural/BackPanelControllerTest.kt @@ -124,7 +124,7 @@ class BackPanelControllerTest : SysuiTestCase() { continueTouch(START_X + touchSlop.toFloat() + 1) continueTouch( START_X + touchSlop + triggerThreshold - - mBackPanelController.params.deactivationSwipeTriggerThreshold + mBackPanelController.params.deactivationTriggerThreshold ) clearInvocations(backCallback) Thread.sleep(MIN_DURATION_ACTIVE_BEFORE_INACTIVE_ANIMATION) |