Merge "Back arrow animation polish for CANCEL and fling from INACTIVE states" into udc-dev am: 53d240a51b

Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/22088149

Change-Id: I219f0fb08372b451b47825a3dd7ce5bf0a85f352
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
This commit is contained in:
Marvin Bernal
2023-03-17 13:17:38 +00:00
committed by Automerger Merge Worker
4 changed files with 100 additions and 71 deletions

View File

@@ -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>

View File

@@ -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() }
}
}

View File

@@ -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)
)
}
}
}

View File

@@ -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)