summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanelController.kt154
-rw-r--r--packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgePanelParams.kt8
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/navigationbar/gestural/BackPanelControllerTest.kt13
3 files changed, 98 insertions, 77 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 96e97565d585..91bb7898494f 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanelController.kt
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanelController.kt
@@ -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,33 +911,16 @@ 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,
- )
- mView.popOffEdge(startingVelocity)
- }
- GestureState.INACTIVE -> {
- mView.popOffEdge(maximumPop)
- }
-
- else -> {}
- }
+ val startingVelocity = convertVelocityToAnimationFactor(
+ valueOnFastVelocity = POP_ON_ACTIVE_MAX_VELOCITY,
+ valueOnSlowVelocity = POP_ON_ACTIVE_MIN_VELOCITY,
+ )
+ mView.popOffEdge(startingVelocity)
}
GestureState.INACTIVE -> {
@@ -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)
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 876c74a9f3e3..182ece7cd328 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgePanelParams.kt
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgePanelParams.kt
@@ -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),
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 d9428f897f0d..611c5b987d84 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
@@ -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