Merge "Updates to new back indicator" into tm-qpr-dev

This commit is contained in:
Peter Kalauskas
2022-06-03 15:44:07 +00:00
committed by Android (Google) Code Review
4 changed files with 320 additions and 273 deletions

View File

@@ -44,7 +44,7 @@
<!-- The threshold to drag to trigger the edge action --> <!-- The threshold to drag to trigger the edge action -->
<dimen name="navigation_edge_action_drag_threshold">16dp</dimen> <dimen name="navigation_edge_action_drag_threshold">16dp</dimen>
<!-- The threshold to progress back animation for edge swipe --> <!-- The threshold to progress back animation for edge swipe -->
<dimen name="navigation_edge_action_progress_threshold">400dp</dimen> <dimen name="navigation_edge_action_progress_threshold">412dp</dimen>
<!-- The minimum display position of the arrow on the screen --> <!-- The minimum display position of the arrow on the screen -->
<dimen name="navigation_edge_arrow_min_y">64dp</dimen> <dimen name="navigation_edge_arrow_min_y">64dp</dimen>
<!-- The amount by which the arrow is shifted to avoid the finger--> <!-- The amount by which the arrow is shifted to avoid the finger-->
@@ -80,13 +80,11 @@
<dimen name="navigation_edge_active_arrow_length">8dp</dimen> <dimen name="navigation_edge_active_arrow_length">8dp</dimen>
<dimen name="navigation_edge_active_arrow_height">9dp</dimen> <dimen name="navigation_edge_active_arrow_height">9dp</dimen>
<!-- stretch @412 dp -->
<dimen name="navigation_edge_stretch_threshold">412dp</dimen>
<dimen name="navigation_edge_stretch_margin">18dp</dimen> <dimen name="navigation_edge_stretch_margin">18dp</dimen>
<dimen name="navigation_edge_stretch_background_width">74dp</dimen> <dimen name="navigation_edge_stretch_background_width">74dp</dimen>
<dimen name="navigation_edge_stretch_background_height">60dp</dimen> <dimen name="navigation_edge_stretch_background_height">60dp</dimen>
<dimen name="navigation_edge_stretch_left_corners">30dp</dimen> <dimen name="navigation_edge_stretch_edge_corners">30dp</dimen>
<dimen name="navigation_edge_stretch_right_corners">30dp</dimen> <dimen name="navigation_edge_stretch_far_corners">30dp</dimen>
<dimen name="navigation_edge_stretched_arrow_length">7dp</dimen> <dimen name="navigation_edge_stretched_arrow_length">7dp</dimen>
<dimen name="navigation_edge_stretched_arrow_height">10dp</dimen> <dimen name="navigation_edge_stretched_arrow_height">10dp</dimen>

View File

@@ -58,14 +58,16 @@ class BackPanel(context: Context, private val latencyTracker: LatencyTracker) :
SpringForce().apply { SpringForce().apply {
stiffness = 600f stiffness = 600f
dampingRatio = 0.65f dampingRatio = 0.65f
}) }
)
private val backgroundHeight = AnimatedFloat( private val backgroundHeight = AnimatedFloat(
name = "backgroundHeight", name = "backgroundHeight",
SpringForce().apply { SpringForce().apply {
stiffness = 600f stiffness = 600f
dampingRatio = 0.65f dampingRatio = 0.65f
}) }
)
/** /**
* Corners of the background closer to the edge of the screen (where the arrow appeared from). * Corners of the background closer to the edge of the screen (where the arrow appeared from).
@@ -76,18 +78,20 @@ class BackPanel(context: Context, private val latencyTracker: LatencyTracker) :
SpringForce().apply { SpringForce().apply {
stiffness = 400f stiffness = 400f
dampingRatio = SpringForce.DAMPING_RATIO_MEDIUM_BOUNCY dampingRatio = SpringForce.DAMPING_RATIO_MEDIUM_BOUNCY
}) }
)
/** /**
* Corners of the background further from the edge of the screens (toward the direction the * Corners of the background further from the edge of the screens (toward the direction the
* arrow is being dragged). Used for animating [arrowBackgroundRect] * arrow is being dragged). Used for animating [arrowBackgroundRect]
*/ */
private val backgroundDragCornerRadius = AnimatedFloat( private val backgroundFarCornerRadius = AnimatedFloat(
name = "backgroundDragCornerRadius", name = "backgroundDragCornerRadius",
SpringForce().apply { SpringForce().apply {
stiffness = 2200f stiffness = 2200f
dampingRatio = SpringForce.DAMPING_RATIO_NO_BOUNCY dampingRatio = SpringForce.DAMPING_RATIO_NO_BOUNCY
}) }
)
/** /**
* Left/right position of the background relative to the canvas. Also corresponds with the * Left/right position of the background relative to the canvas. Also corresponds with the
@@ -96,14 +100,32 @@ class BackPanel(context: Context, private val latencyTracker: LatencyTracker) :
*/ */
private var horizontalTranslation = AnimatedFloat("horizontalTranslation", SpringForce()) private var horizontalTranslation = AnimatedFloat("horizontalTranslation", SpringForce())
private val currentAlpha: FloatPropertyCompat<BackPanel> =
object : FloatPropertyCompat<BackPanel>("currentAlpha") {
override fun setValue(panel: BackPanel, value: Float) {
panel.alpha = value
}
override fun getValue(panel: BackPanel): Float = panel.alpha
}
private val alphaAnimation = SpringAnimation(this, currentAlpha)
.setSpring(
SpringForce()
.setStiffness(60f)
.setDampingRatio(SpringForce.DAMPING_RATIO_NO_BOUNCY)
)
/** /**
* Canvas vertical translation. How far up/down the arrow and background appear relative to the * Canvas vertical translation. How far up/down the arrow and background appear relative to the
* canvas. * canvas.
*/ */
private var verticalTranslation: AnimatedFloat = private var verticalTranslation: AnimatedFloat = AnimatedFloat(
AnimatedFloat("verticalTranslation", SpringForce().apply { name = "verticalTranslation",
SpringForce().apply {
stiffness = SpringForce.STIFFNESS_MEDIUM stiffness = SpringForce.STIFFNESS_MEDIUM
}) }
)
/** /**
* Use for drawing debug info. Can only be set if [DEBUG]=true * Use for drawing debug info. Can only be set if [DEBUG]=true
@@ -160,6 +182,17 @@ class BackPanel(context: Context, private val latencyTracker: LatencyTracker) :
animation.animateToFinalPosition(restingPosition + stretchAmount) animation.animateToFinalPosition(restingPosition + stretchAmount)
} }
/**
* Animates to a new position ([finalPosition]) that is the given fraction ([amount])
* between the existing [restingPosition] and the new [finalPosition].
*
* The [restingPosition] will remain unchanged. Only the animation is updated.
*/
fun stretchBy(finalPosition: Float, amount: Float) {
val stretchedAmount = amount * (finalPosition - restingPosition)
animation.animateToFinalPosition(restingPosition + stretchedAmount)
}
fun updateRestingPosition(pos: Float, animated: Boolean) { fun updateRestingPosition(pos: Float, animated: Boolean) {
restingPosition = pos restingPosition = pos
if (animated) if (animated)
@@ -192,7 +225,10 @@ class BackPanel(context: Context, private val latencyTracker: LatencyTracker) :
} }
fun addEndListener(endListener: DelayedOnAnimationEndListener): Boolean { fun addEndListener(endListener: DelayedOnAnimationEndListener): Boolean {
return if (horizontalTranslation.animation.isRunning) { return if (alphaAnimation.isRunning) {
alphaAnimation.addEndListener(endListener)
true
} else if (horizontalTranslation.animation.isRunning) {
horizontalTranslation.animation.addEndListener(endListener) horizontalTranslation.animation.addEndListener(endListener)
true true
} else { } else {
@@ -202,47 +238,59 @@ class BackPanel(context: Context, private val latencyTracker: LatencyTracker) :
} }
fun setStretch( fun setStretch(
arrowLengthStretch: Float, horizontalTranslationStretchAmount: Float,
arrowHeightStretch: Float, arrowStretchAmount: Float,
backgroundWidthStretch: Float, backgroundWidthStretchAmount: Float,
backgroundHeightStretch: Float, fullyStretchedDimens: EdgePanelParams.BackIndicatorDimens
backgroundEdgeCornerRadiusStretch: Float,
backgroundDragCornerRadiusStretch: Float,
horizontalTranslationStretch: Float
) { ) {
arrowLength.stretchTo(arrowLengthStretch) horizontalTranslation.stretchBy(
arrowHeight.stretchTo(arrowHeightStretch) finalPosition = fullyStretchedDimens.horizontalTranslation,
backgroundWidth.stretchTo(backgroundWidthStretch) amount = horizontalTranslationStretchAmount
backgroundHeight.stretchTo(backgroundHeightStretch) )
backgroundEdgeCornerRadius.stretchTo(backgroundEdgeCornerRadiusStretch) arrowLength.stretchBy(
backgroundDragCornerRadius.stretchTo(backgroundDragCornerRadiusStretch) finalPosition = fullyStretchedDimens.arrowDimens.length,
horizontalTranslation.stretchTo(horizontalTranslationStretch) amount = arrowStretchAmount
)
arrowHeight.stretchBy(
finalPosition = fullyStretchedDimens.arrowDimens.height,
amount = arrowStretchAmount
)
backgroundWidth.stretchBy(
finalPosition = fullyStretchedDimens.backgroundDimens.width,
amount = backgroundWidthStretchAmount
)
} }
fun resetStretch() { fun resetStretch() {
setStretch(0f, 0f, 0f, 0f, 0f, 0f, 0f) horizontalTranslation.stretchTo(0f)
arrowLength.stretchTo(0f)
arrowHeight.stretchTo(0f)
backgroundWidth.stretchTo(0f)
backgroundHeight.stretchTo(0f)
backgroundEdgeCornerRadius.stretchTo(0f)
backgroundFarCornerRadius.stretchTo(0f)
} }
/** /**
* Updates resting arrow and background size not accounting for stretch * Updates resting arrow and background size not accounting for stretch
*/ */
internal fun updateRestingArrowDimens( internal fun setRestingDimens(
backgroundWidth: Float, restingParams: EdgePanelParams.BackIndicatorDimens,
backgroundHeight: Float,
backgroundEdgeCornerRadius: Float,
backgroundDragCornerRadius: Float,
arrowLength: Float,
arrowHeight: Float,
horizontalTranslation: Float,
animate: Boolean animate: Boolean
) { ) {
this.arrowLength.updateRestingPosition(arrowLength, animate) horizontalTranslation.updateRestingPosition(restingParams.horizontalTranslation, animate)
this.arrowHeight.updateRestingPosition(arrowHeight, animate) arrowLength.updateRestingPosition(restingParams.arrowDimens.length, animate)
this.backgroundWidth.updateRestingPosition(backgroundWidth, animate) arrowHeight.updateRestingPosition(restingParams.arrowDimens.height, animate)
this.backgroundHeight.updateRestingPosition(backgroundHeight, animate) backgroundWidth.updateRestingPosition(restingParams.backgroundDimens.width, animate)
this.backgroundEdgeCornerRadius.updateRestingPosition(backgroundEdgeCornerRadius, animate) backgroundHeight.updateRestingPosition(restingParams.backgroundDimens.height, animate)
this.backgroundDragCornerRadius.updateRestingPosition(backgroundDragCornerRadius, animate) backgroundEdgeCornerRadius.updateRestingPosition(
this.horizontalTranslation.updateRestingPosition(horizontalTranslation, animate) restingParams.backgroundDimens.edgeCornerRadius,
animate
)
backgroundFarCornerRadius.updateRestingPosition(
restingParams.backgroundDimens.farCornerRadius,
animate
)
} }
fun animateVertically(yPos: Float) = verticalTranslation.stretchTo(yPos) fun animateVertically(yPos: Float) = verticalTranslation.stretchTo(yPos)
@@ -262,7 +310,7 @@ class BackPanel(context: Context, private val latencyTracker: LatencyTracker) :
override fun onDraw(canvas: Canvas) { override fun onDraw(canvas: Canvas) {
var edgeCorner = backgroundEdgeCornerRadius.pos var edgeCorner = backgroundEdgeCornerRadius.pos
val farCorner = backgroundDragCornerRadius.pos val farCorner = backgroundFarCornerRadius.pos
val halfHeight = backgroundHeight.pos / 2 val halfHeight = backgroundHeight.pos / 2
canvas.save() canvas.save()
@@ -337,4 +385,13 @@ class BackPanel(context: Context, private val latencyTracker: LatencyTracker) :
) )
addRoundRect(this@toPathWithRoundCorners, corners, Path.Direction.CW) addRoundRect(this@toPathWithRoundCorners, corners, Path.Direction.CW)
} }
fun cancelAlphaAnimations() {
alphaAnimation.cancel()
alpha = 1f
}
fun fadeOut() {
alphaAnimation.animateToFinalPosition(0f)
}
} }

View File

@@ -31,7 +31,7 @@ import android.view.MotionEvent
import android.view.VelocityTracker import android.view.VelocityTracker
import android.view.View import android.view.View
import android.view.WindowManager import android.view.WindowManager
import android.view.animation.AccelerateInterpolator import android.view.animation.DecelerateInterpolator
import android.view.animation.PathInterpolator import android.view.animation.PathInterpolator
import android.window.BackEvent import android.window.BackEvent
import androidx.dynamicanimation.animation.DynamicAnimation import androidx.dynamicanimation.animation.DynamicAnimation
@@ -68,7 +68,7 @@ private const val GESTURE_DURATION_FOR_CLICK_MS = 400
/** /**
* The min duration arrow remains on screen during a fling event. * The min duration arrow remains on screen during a fling event.
*/ */
private const val FLING_PAUSE_DURATION_MS = 50L private const val FLING_MIN_APPEARANCE_DURATION = 235L
/** /**
* The min duration arrow remains on screen during a fling event. * The min duration arrow remains on screen during a fling event.
@@ -90,7 +90,9 @@ private const val ARROW_DISAPPEAR_DAMPING_RATIO = SpringForce.DAMPING_RATIO_NO_B
*/ */
private val RUBBER_BAND_INTERPOLATOR = PathInterpolator(1.0f / 5.0f, 1.0f, 1.0f, 1.0f) private val RUBBER_BAND_INTERPOLATOR = PathInterpolator(1.0f / 5.0f, 1.0f, 1.0f, 1.0f)
private val ACCELERATE_INTERPOLATOR = AccelerateInterpolator(0.7f) private val DECELERATE_INTERPOLATOR = DecelerateInterpolator()
private val DECELERATE_INTERPOLATOR_SLOW = DecelerateInterpolator(0.7f)
class BackPanelController private constructor( class BackPanelController private constructor(
context: Context, context: Context,
@@ -160,6 +162,8 @@ class BackPanelController private constructor(
private var startX = 0f private var startX = 0f
private var startY = 0f private var startY = 0f
private var gestureStartTime = 0L
private val failsafeRunnable = Runnable { onFailsafe() } private val failsafeRunnable = Runnable { onFailsafe() }
private enum class GestureState { private enum class GestureState {
@@ -182,19 +186,29 @@ class BackPanelController private constructor(
COMMITTED, COMMITTED,
/* back action currently cancelling, arrow soon to be GONE */ /* back action currently cancelling, arrow soon to be GONE */
CANCELLED CANCELLED;
/**
* @return true if the current state responds to touch move events in some way (e.g. by
* stretching the back indicator)
*/
fun isInteractive(): Boolean {
return when (this) {
ENTRY, ACTIVE, INACTIVE -> true
GONE, FLUNG, COMMITTED, CANCELLED -> false
}
}
} }
/** /**
* Wrapper around OnAnimationEndListener which runs the given runnable after a delay. The * Wrapper around OnAnimationEndListener which runs the given runnable after a delay. The
* runnable is not called if the animation is cancelled * runnable is not called if the animation is cancelled
*/ */
class DelayedOnAnimationEndListener( inner class DelayedOnAnimationEndListener internal constructor(
private val handler: Handler, private val handler: Handler,
private val runnable: Runnable, private val runnable: Runnable,
private val delay: Long private val minDuration: Long
) : DynamicAnimation.OnAnimationEndListener { ) : DynamicAnimation.OnAnimationEndListener {
override fun onAnimationEnd( override fun onAnimationEnd(
animation: DynamicAnimation<*>, animation: DynamicAnimation<*>,
canceled: Boolean, canceled: Boolean,
@@ -203,11 +217,15 @@ class BackPanelController private constructor(
) { ) {
animation.removeEndListener(this) animation.removeEndListener(this)
if (!canceled) { if (!canceled) {
// Total elapsed time of the gesture and the animation
val totalElapsedTime = SystemClock.uptimeMillis() - gestureStartTime
// The delay between finishing this animation and starting the runnable
val delay = max(0, minDuration - totalElapsedTime)
handler.postDelayed(runnable, delay) handler.postDelayed(runnable, delay)
} }
} }
fun runNow() { internal fun runNow() {
runnable.run() runnable.run()
} }
} }
@@ -216,7 +234,7 @@ class BackPanelController private constructor(
DelayedOnAnimationEndListener( DelayedOnAnimationEndListener(
mainHandler, mainHandler,
{ updateArrowState(GestureState.COMMITTED) }, { updateArrowState(GestureState.COMMITTED) },
delay = FLING_PAUSE_DURATION_MS minDuration = FLING_MIN_APPEARANCE_DURATION
) )
private val setGoneEndListener = private val setGoneEndListener =
@@ -226,21 +244,21 @@ class BackPanelController private constructor(
cancelFailsafe() cancelFailsafe()
updateArrowState(GestureState.GONE) updateArrowState(GestureState.GONE)
}, },
delay = 0 minDuration = 0
) )
// Vibration // Vibration
private var vibrationTime: Long = 0 private var vibrationTime: Long = 0
// Minimum size of the screen's width or height // Minimum of the screen's width or the predefined threshold
private var screenSize = 0 private var fullyStretchedThreshold = 0f
/** /**
* Used for initialization and configuration changes * Used for initialization and configuration changes
*/ */
private fun updateConfiguration() { private fun updateConfiguration() {
params.update(resources) params.update(resources)
initializeBackAnimation() updateBackAnimationSwipeThresholds()
mView.updateArrowPaint(params.arrowThickness) mView.updateArrowPaint(params.arrowThickness)
} }
@@ -284,6 +302,7 @@ class BackPanelController private constructor(
resetOnDown() resetOnDown()
startX = event.x startX = event.x
startY = event.y startY = event.y
gestureStartTime = SystemClock.uptimeMillis()
// Reset the arrow to the side // Reset the arrow to the side
updateArrowState(GestureState.ENTRY) updateArrowState(GestureState.ENTRY)
@@ -308,40 +327,39 @@ class BackPanelController private constructor(
} }
private fun updateArrowStateOnMove(yTranslation: Float, xTranslation: Float) { private fun updateArrowStateOnMove(yTranslation: Float, xTranslation: Float) {
if (!currentState.isInteractive())
return
when (currentState) { when (currentState) {
GestureState.GONE, GestureState.FLUNG, GestureState.COMMITTED, GestureState.CANCELLED -> // Check if we should transition from ENTRY to ACTIVE
return GestureState.ENTRY ->
if (xTranslation > params.swipeTriggerThreshold) {
updateArrowState(GestureState.ACTIVE)
}
// Abort if we had continuous motion toward the edge for a while, OR the direction
// in Y is bigger than X * 2
GestureState.ACTIVE ->
if ((totalTouchDelta < 0 && -totalTouchDelta > params.minDeltaForSwitch) ||
(yTranslation > xTranslation * 2)
) {
updateArrowState(GestureState.INACTIVE)
}
// Re-activate if we had continuous motion away from the edge for a while
GestureState.INACTIVE ->
if (totalTouchDelta > 0 && totalTouchDelta > params.minDeltaForSwitch) {
updateArrowState(GestureState.ACTIVE)
}
// By default assume the current direction is kept
else -> {}
} }
updateArrowState(
when {
// Check if we should transition from ENTRY to ACTIVE
currentState == GestureState.ENTRY && xTranslation > params.swipeTriggerThreshold ->
GestureState.ACTIVE
// Abort if we had continuous motion toward the edge for a while, OR the direction
// in Y is bigger than X * 2
currentState == GestureState.ACTIVE &&
((totalTouchDelta < 0 && -totalTouchDelta > params.minDeltaForSwitch) ||
(yTranslation > xTranslation * 2)) ->
GestureState.INACTIVE
// Re-activate if we had continuous motion away from the edge for a while
currentState == GestureState.INACTIVE &&
(totalTouchDelta > 0 && totalTouchDelta > params.minDeltaForSwitch) ->
GestureState.ACTIVE
// By default assume the current direction is kept
else -> currentState
}
)
} }
private fun handleMoveEvent(event: MotionEvent) { private fun handleMoveEvent(event: MotionEvent) {
when (currentState) { if (!currentState.isInteractive())
GestureState.GONE, GestureState.FLUNG, GestureState.COMMITTED, return
GestureState.CANCELLED -> return
}
val x = event.x val x = event.x
val y = event.y val y = event.y
@@ -372,24 +390,25 @@ class BackPanelController private constructor(
updateArrowStateOnMove(yTranslation, xTranslation) updateArrowStateOnMove(yTranslation, xTranslation)
when (currentState) { when (currentState) {
GestureState.ACTIVE -> setActiveStretch(fullScreenStretchProgress(xTranslation)) GestureState.ACTIVE ->
stretchActiveBackIndicator(fullScreenStretchProgress(xTranslation))
GestureState.ENTRY -> GestureState.ENTRY ->
setEntryStretch(preThresholdStretchProgress(xTranslation)) stretchEntryBackIndicator(preThresholdStretchProgress(xTranslation))
GestureState.INACTIVE -> mView.resetStretch() GestureState.INACTIVE ->
mView.resetStretch()
} }
// set y translation // set y translation
setVerticalTranslation(yOffset) setVerticalTranslation(yOffset)
} }
fun setVerticalTranslation(yOffset: Float) { private fun setVerticalTranslation(yOffset: Float) {
val yTranslation = abs(yOffset) val yTranslation = abs(yOffset)
val maxYOffset = (mView.height / 2) - (params.entryBackgroundHeight / 2) val maxYOffset = (mView.height - params.entryIndicator.backgroundDimens.height) / 2f
val yProgress = saturate(yTranslation / (maxYOffset * RUBBER_BAND_AMOUNT)) val yProgress = saturate(yTranslation / (maxYOffset * RUBBER_BAND_AMOUNT))
mView.animateVertically( mView.animateVertically(
RUBBER_BAND_INTERPOLATOR.getInterpolation(yProgress) * maxYOffset * sign( RUBBER_BAND_INTERPOLATOR.getInterpolation(yProgress) * maxYOffset *
yOffset sign(yOffset)
)
) )
} }
@@ -397,13 +416,10 @@ class BackPanelController private constructor(
* @return the relative position of the drag from the time after the arrow is activated until * @return the relative position of the drag from the time after the arrow is activated until
* the arrow is fully stretched (between 0.0 - 1.0f) * the arrow is fully stretched (between 0.0 - 1.0f)
*/ */
fun fullScreenStretchProgress(xTranslation: Float): Float { private fun fullScreenStretchProgress(xTranslation: Float): Float {
return saturate( return saturate(
(xTranslation - params.swipeTriggerThreshold) / (xTranslation - params.swipeTriggerThreshold) /
(min( (fullyStretchedThreshold - params.swipeTriggerThreshold)
params.fullyStretchedThreshold,
screenSize.toFloat()
) - params.swipeTriggerThreshold)
) )
} }
@@ -411,57 +427,38 @@ class BackPanelController private constructor(
* Tracks the relative position of the drag from the entry until the threshold where the arrow * Tracks the relative position of the drag from the entry until the threshold where the arrow
* activates (between 0.0 - 1.0f) * activates (between 0.0 - 1.0f)
*/ */
fun preThresholdStretchProgress(xTranslation: Float): Float { private fun preThresholdStretchProgress(xTranslation: Float): Float {
return saturate(xTranslation / params.swipeTriggerThreshold) return saturate(xTranslation / params.swipeTriggerThreshold)
} }
fun setActiveStretch(progress: Float) { private fun stretchActiveBackIndicator(progress: Float) {
val stretch = RUBBER_BAND_INTERPOLATOR.getInterpolation(progress) val rubberBandIterpolation = RUBBER_BAND_INTERPOLATOR.getInterpolation(progress)
mView.setStretch( mView.setStretch(
arrowLengthStretch = stretch * (params.stretchedArrowLength - params.activeArrowLength), horizontalTranslationStretchAmount = rubberBandIterpolation,
arrowHeightStretch = stretch * (params.stretchedArrowHeight - params.activeArrowHeight), arrowStretchAmount = rubberBandIterpolation,
backgroundWidthStretch = backgroundWidthStretchAmount = DECELERATE_INTERPOLATOR_SLOW.getInterpolation(progress),
stretch * (params.stretchBackgroundWidth - params.activeBackgroundWidth), params.fullyStretchedIndicator
backgroundHeightStretch =
stretch * (params.stretchBackgroundHeight - params.activeBackgroundHeight),
backgroundEdgeCornerRadiusStretch =
stretch * (params.stretchEdgeCorners - params.activeEdgeCorners),
backgroundDragCornerRadiusStretch =
stretch * (params.stretchFarCorners - params.activeFarCorners),
horizontalTranslationStretch = stretch * (params.stretchMargin - params.activeMargin)
) )
} }
fun setEntryStretch(progress: Float) { private fun stretchEntryBackIndicator(progress: Float) {
val bgStretch = ACCELERATE_INTERPOLATOR.getInterpolation(progress)
val arrowStretch = RUBBER_BAND_INTERPOLATOR.getInterpolation(progress)
mView.setStretch( mView.setStretch(
arrowLengthStretch = horizontalTranslationStretchAmount = 0f,
arrowStretch * (params.activeArrowLength - params.entryArrowLength), arrowStretchAmount = RUBBER_BAND_INTERPOLATOR.getInterpolation(progress),
arrowHeightStretch = backgroundWidthStretchAmount = DECELERATE_INTERPOLATOR.getInterpolation(progress),
arrowStretch * (params.activeArrowHeight - params.entryArrowHeight), params.preThresholdIndicator
backgroundWidthStretch =
bgStretch * (params.preThresholdBackgroundWidth - params.entryBackgroundWidth),
backgroundHeightStretch =
bgStretch * (params.preThresholdBackgroundHeight - params.entryBackgroundHeight),
backgroundEdgeCornerRadiusStretch =
bgStretch * (params.preThresholdEdgeCorners - params.entryEdgeCorners),
backgroundDragCornerRadiusStretch =
bgStretch * (params.preThresholdFarCorners - params.entryFarCorners),
horizontalTranslationStretch =
bgStretch * (params.preThresholdMargin - params.entryMargin)
) )
} }
fun setBackAnimation(backAnimation: BackAnimation?) { fun setBackAnimation(backAnimation: BackAnimation?) {
this.backAnimation = backAnimation this.backAnimation = backAnimation
initializeBackAnimation() updateBackAnimationSwipeThresholds()
} }
private fun initializeBackAnimation() { private fun updateBackAnimationSwipeThresholds() {
backAnimation?.setSwipeThresholds( backAnimation?.setSwipeThresholds(
params.swipeTriggerThreshold, params.swipeTriggerThreshold,
params.swipeProgressThreshold fullyStretchedThreshold
) )
} }
@@ -541,79 +538,68 @@ class BackPanelController private constructor(
private fun updateYPosition(touchY: Float) { private fun updateYPosition(touchY: Float) {
var yPosition = touchY - params.fingerOffset var yPosition = touchY - params.fingerOffset
yPosition = Math.max(yPosition, params.minArrowYPosition.toFloat()) yPosition = max(yPosition, params.minArrowYPosition.toFloat())
yPosition -= layoutParams.height / 2.0f yPosition -= layoutParams.height / 2.0f
layoutParams.y = constrain(yPosition.toInt(), 0, displaySize.y) layoutParams.y = constrain(yPosition.toInt(), 0, displaySize.y)
} }
override fun setDisplaySize(displaySize: Point) { override fun setDisplaySize(displaySize: Point) {
this.displaySize.set(displaySize.x, displaySize.y) this.displaySize.set(displaySize.x, displaySize.y)
screenSize = Math.min(displaySize.x, displaySize.y) fullyStretchedThreshold = min(displaySize.x.toFloat(), params.swipeProgressThreshold)
updateBackAnimationSwipeThresholds()
} }
/** /**
* Updates resting arrow and background size not accounting for stretch * Updates resting arrow and background size not accounting for stretch
*/ */
private fun updateRestingArrowDimens(animated: Boolean, currentState: GestureState) { private fun updateRestingArrowDimens(animated: Boolean, currentState: GestureState) {
mView.updateRestingArrowDimens(
backgroundWidth =
when (currentState) {
GestureState.GONE, GestureState.ENTRY -> params.entryBackgroundWidth
else -> params.activeBackgroundWidth
},
backgroundHeight =
when (currentState) {
GestureState.GONE, GestureState.ENTRY -> params.entryBackgroundHeight
else -> params.activeBackgroundHeight
},
backgroundEdgeCornerRadius =
when (currentState) {
GestureState.GONE, GestureState.ENTRY, GestureState.INACTIVE ->
params.entryEdgeCorners
else ->
params.activeEdgeCorners
},
backgroundDragCornerRadius =
when (currentState) {
GestureState.GONE, GestureState.ENTRY -> params.entryFarCorners
else -> params.activeFarCorners
},
arrowLength =
when (currentState) {
GestureState.ACTIVE, GestureState.INACTIVE, GestureState.COMMITTED,
GestureState.FLUNG -> params.activeArrowLength
GestureState.CANCELLED -> params.cancelledArrowLength
GestureState.GONE, GestureState.ENTRY -> params.entryArrowLength
},
arrowHeight =
when (currentState) {
GestureState.ACTIVE, GestureState.INACTIVE, GestureState.COMMITTED,
GestureState.FLUNG -> params.activeArrowHeight
GestureState.CANCELLED -> params.cancelledArrowHeight
GestureState.GONE, GestureState.ENTRY -> params.entryArrowHeight
},
horizontalTranslation =
when (currentState) {
GestureState.GONE -> -params.activeBackgroundWidth
// Position the cancelled/committed arrow slightly further off the screen so we
// do not see part of it bouncing
GestureState.CANCELLED, GestureState.COMMITTED ->
-params.activeBackgroundWidth * 1.5f
GestureState.FLUNG -> params.stretchMargin
GestureState.ACTIVE -> params.activeMargin
GestureState.ENTRY, GestureState.INACTIVE -> params.entryMargin
},
animate = animated
)
if (animated) { if (animated) {
when (currentState) { when (currentState) {
GestureState.ENTRY, GestureState.ACTIVE, GestureState.FLUNG -> GestureState.ENTRY, GestureState.ACTIVE, GestureState.FLUNG ->
mView.setArrowStiffness(ARROW_APPEAR_STIFFNESS, ARROW_APPEAR_DAMPING_RATIO) mView.setArrowStiffness(ARROW_APPEAR_STIFFNESS, ARROW_APPEAR_DAMPING_RATIO)
GestureState.CANCELLED -> mView.fadeOut()
else -> else ->
mView.setArrowStiffness( mView.setArrowStiffness(
ARROW_DISAPPEAR_STIFFNESS, ARROW_DISAPPEAR_DAMPING_RATIO) ARROW_DISAPPEAR_STIFFNESS,
ARROW_DISAPPEAR_DAMPING_RATIO
)
} }
} }
mView.setRestingDimens(
restingParams = EdgePanelParams.BackIndicatorDimens(
horizontalTranslation = when (currentState) {
GestureState.GONE -> -params.activeIndicator.backgroundDimens.width
// Position the committed arrow slightly further off the screen so we do not
// see part of it bouncing
GestureState.COMMITTED ->
-params.activeIndicator.backgroundDimens.width * 1.5f
GestureState.FLUNG -> params.fullyStretchedIndicator.horizontalTranslation
GestureState.ACTIVE -> params.activeIndicator.horizontalTranslation
GestureState.ENTRY, GestureState.INACTIVE, GestureState.CANCELLED ->
params.entryIndicator.horizontalTranslation
},
arrowDimens = when (currentState) {
GestureState.ACTIVE, GestureState.INACTIVE,
GestureState.COMMITTED, GestureState.FLUNG -> params.activeIndicator.arrowDimens
GestureState.CANCELLED -> params.cancelledArrowDimens
GestureState.GONE, GestureState.ENTRY -> params.entryIndicator.arrowDimens
},
backgroundDimens = when (currentState) {
GestureState.GONE, GestureState.ENTRY -> params.entryIndicator.backgroundDimens
else ->
params.activeIndicator.backgroundDimens.copy(
edgeCornerRadius =
if (currentState == GestureState.INACTIVE ||
currentState == GestureState.CANCELLED
)
params.entryIndicator.backgroundDimens.edgeCornerRadius
else
params.activeIndicator.backgroundDimens.edgeCornerRadius
)
}
),
animate = animated
)
} }
/** /**
@@ -628,7 +614,12 @@ class BackPanelController private constructor(
if (DEBUG) Log.d(TAG, "updateArrowState $currentState -> $newState") if (DEBUG) Log.d(TAG, "updateArrowState $currentState -> $newState")
previousState = currentState previousState = currentState
currentState = newState currentState = newState
mView.visibility = if (currentState == GestureState.GONE) View.GONE else View.VISIBLE if (currentState == GestureState.GONE) {
mView.cancelAlphaAnimations()
mView.visibility = View.GONE
} else {
mView.visibility = View.VISIBLE
}
when (currentState) { when (currentState) {
// Transitioning to GONE never animates since the arrow is (presumably) already off the // Transitioning to GONE never animates since the arrow is (presumably) already off the

View File

@@ -4,66 +4,36 @@ import android.content.res.Resources
import com.android.systemui.R import com.android.systemui.R
data class EdgePanelParams(private var resources: Resources) { data class EdgePanelParams(private var resources: Resources) {
data class ArrowDimens(
val length: Float = 0f,
val height: Float = 0f
)
data class BackgroundDimens(
val width: Float = 0f,
val height: Float = 0f,
val edgeCornerRadius: Float = 0f,
val farCornerRadius: Float = 0f
)
data class BackIndicatorDimens(
val horizontalTranslation: Float = 0f,
val arrowDimens: ArrowDimens = ArrowDimens(),
val backgroundDimens: BackgroundDimens = BackgroundDimens()
)
var arrowThickness: Float = 0f var arrowThickness: Float = 0f
private set private set
var entryArrowLength: Float = 0f var entryIndicator = BackIndicatorDimens()
private set private set
var entryArrowHeight: Float = 0f var activeIndicator = BackIndicatorDimens()
private set private set
var activeArrowLength: Float = 0f var preThresholdIndicator = BackIndicatorDimens()
private set private set
var activeArrowHeight: Float = 0f var fullyStretchedIndicator = BackIndicatorDimens()
private set
var stretchedArrowLength: Float = 0f
private set
var stretchedArrowHeight: Float = 0f
private set
var cancelledArrowLength: Float = 0f
private set
var cancelledArrowHeight: Float = 0f
private set
var entryMargin: Float = 0f
private set
var entryBackgroundWidth: Float = 0f
private set
var entryBackgroundHeight: Float = 0f
private set
var entryEdgeCorners: Float = 0f
private set
var entryFarCorners: Float = 0f
private set
var preThresholdMargin: Float = 0f
private set
var preThresholdBackgroundWidth: Float = 0f
private set
var preThresholdBackgroundHeight: Float = 0f
private set
var preThresholdEdgeCorners: Float = 0f
private set
var preThresholdFarCorners: Float = 0f
private set
var activeMargin: Float = 0f
private set
var activeBackgroundWidth: Float = 0f
private set
var activeBackgroundHeight: Float = 0f
private set
var activeEdgeCorners: Float = 0f
private set
var activeFarCorners: Float = 0f
private set
var fullyStretchedThreshold: Float = 0f
private set
var stretchMargin: Float = 0f
private set
var stretchBackgroundWidth: Float = 0f
private set
var stretchBackgroundHeight: Float = 0f
private set
var stretchEdgeCorners: Float = 0f
private set
var stretchFarCorners: Float = 0f
private set private set
var cancelledArrowDimens = ArrowDimens()
// navigation bar edge constants // navigation bar edge constants
var arrowPaddingEnd: Int = 0 var arrowPaddingEnd: Int = 0
@@ -98,42 +68,73 @@ data class EdgePanelParams(private var resources: Resources) {
fun update(resources: Resources) { fun update(resources: Resources) {
this.resources = resources this.resources = resources
arrowThickness = getDimen(R.dimen.navigation_edge_arrow_thickness) arrowThickness = getDimen(R.dimen.navigation_edge_arrow_thickness)
entryArrowLength = getDimen(R.dimen.navigation_edge_entry_arrow_length)
entryArrowHeight = getDimen(R.dimen.navigation_edge_entry_arrow_height)
activeArrowLength = getDimen(R.dimen.navigation_edge_active_arrow_length)
activeArrowHeight = getDimen(R.dimen.navigation_edge_active_arrow_height)
stretchedArrowLength = getDimen(R.dimen.navigation_edge_stretched_arrow_length)
stretchedArrowHeight = getDimen(R.dimen.navigation_edge_stretched_arrow_height)
cancelledArrowLength = getDimen(R.dimen.navigation_edge_cancelled_arrow_length)
cancelledArrowHeight = getDimen(R.dimen.navigation_edge_cancelled_arrow_height)
entryMargin = getDimen(R.dimen.navigation_edge_entry_margin)
entryBackgroundWidth = getDimen(R.dimen.navigation_edge_entry_background_width)
entryBackgroundHeight = getDimen(R.dimen.navigation_edge_entry_background_height)
entryEdgeCorners = getDimen(R.dimen.navigation_edge_entry_edge_corners)
entryFarCorners = getDimen(R.dimen.navigation_edge_entry_far_corners)
preThresholdMargin = getDimen(R.dimen.navigation_edge_pre_threshold_margin)
preThresholdBackgroundWidth =
getDimen(R.dimen.navigation_edge_pre_threshold_background_width)
preThresholdBackgroundHeight =
getDimen(R.dimen.navigation_edge_pre_threshold_background_height)
preThresholdEdgeCorners = getDimen(R.dimen.navigation_edge_pre_threshold_edge_corners)
preThresholdFarCorners = getDimen(R.dimen.navigation_edge_pre_threshold_far_corners)
activeMargin = getDimen(R.dimen.navigation_edge_active_margin)
activeBackgroundWidth = getDimen(R.dimen.navigation_edge_active_background_width)
activeBackgroundHeight = getDimen(R.dimen.navigation_edge_active_background_height)
activeEdgeCorners = getDimen(R.dimen.navigation_edge_active_edge_corners)
activeFarCorners = getDimen(R.dimen.navigation_edge_active_far_corners)
fullyStretchedThreshold = getDimen(R.dimen.navigation_edge_stretch_threshold)
stretchMargin = getDimen(R.dimen.navigation_edge_stretch_margin)
stretchBackgroundWidth = getDimen(R.dimen.navigation_edge_stretch_background_width)
stretchBackgroundHeight = getDimen(R.dimen.navigation_edge_stretch_background_height)
stretchEdgeCorners = getDimen(R.dimen.navigation_edge_stretch_left_corners)
stretchFarCorners = getDimen(R.dimen.navigation_edge_stretch_right_corners)
arrowPaddingEnd = getPx(R.dimen.navigation_edge_panel_padding) arrowPaddingEnd = getPx(R.dimen.navigation_edge_panel_padding)
minArrowYPosition = getPx(R.dimen.navigation_edge_arrow_min_y) minArrowYPosition = getPx(R.dimen.navigation_edge_arrow_min_y)
fingerOffset = getPx(R.dimen.navigation_edge_finger_offset) fingerOffset = getPx(R.dimen.navigation_edge_finger_offset)
swipeTriggerThreshold = getDimen(R.dimen.navigation_edge_action_drag_threshold) swipeTriggerThreshold = getDimen(R.dimen.navigation_edge_action_drag_threshold)
swipeProgressThreshold = getDimen(R.dimen.navigation_edge_action_progress_threshold) swipeProgressThreshold = getDimen(R.dimen.navigation_edge_action_progress_threshold)
minDeltaForSwitch = getPx(R.dimen.navigation_edge_minimum_x_delta_for_switch) minDeltaForSwitch = getPx(R.dimen.navigation_edge_minimum_x_delta_for_switch)
entryIndicator = BackIndicatorDimens(
horizontalTranslation = getDimen(R.dimen.navigation_edge_entry_margin),
arrowDimens = ArrowDimens(
length = getDimen(R.dimen.navigation_edge_entry_arrow_length),
height = getDimen(R.dimen.navigation_edge_entry_arrow_height),
),
backgroundDimens = BackgroundDimens(
width = getDimen(R.dimen.navigation_edge_entry_background_width),
height = getDimen(R.dimen.navigation_edge_entry_background_height),
edgeCornerRadius = getDimen(R.dimen.navigation_edge_entry_edge_corners),
farCornerRadius = getDimen(R.dimen.navigation_edge_entry_far_corners)
)
)
activeIndicator = BackIndicatorDimens(
horizontalTranslation = getDimen(R.dimen.navigation_edge_active_margin),
arrowDimens = ArrowDimens(
length = getDimen(R.dimen.navigation_edge_active_arrow_length),
height = getDimen(R.dimen.navigation_edge_active_arrow_height),
),
backgroundDimens = BackgroundDimens(
width = getDimen(R.dimen.navigation_edge_active_background_width),
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)
)
)
preThresholdIndicator = BackIndicatorDimens(
horizontalTranslation = getDimen(R.dimen.navigation_edge_pre_threshold_margin),
arrowDimens = ArrowDimens(
length = entryIndicator.arrowDimens.length,
height = entryIndicator.arrowDimens.height,
),
backgroundDimens = BackgroundDimens(
width = getDimen(R.dimen.navigation_edge_pre_threshold_background_width),
height = getDimen(R.dimen.navigation_edge_pre_threshold_background_height),
edgeCornerRadius = getDimen(R.dimen.navigation_edge_pre_threshold_edge_corners),
farCornerRadius = getDimen(R.dimen.navigation_edge_pre_threshold_far_corners)
)
)
fullyStretchedIndicator = BackIndicatorDimens(
horizontalTranslation = getDimen(R.dimen.navigation_edge_stretch_margin),
arrowDimens = ArrowDimens(
length = getDimen(R.dimen.navigation_edge_stretched_arrow_length),
height = getDimen(R.dimen.navigation_edge_stretched_arrow_height),
),
backgroundDimens = BackgroundDimens(
width = getDimen(R.dimen.navigation_edge_stretch_background_width),
height = getDimen(R.dimen.navigation_edge_stretch_background_height),
edgeCornerRadius = getDimen(R.dimen.navigation_edge_stretch_edge_corners),
farCornerRadius = getDimen(R.dimen.navigation_edge_stretch_far_corners)
)
)
cancelledArrowDimens = ArrowDimens(
length = getDimen(R.dimen.navigation_edge_cancelled_arrow_length),
height = getDimen(R.dimen.navigation_edge_cancelled_arrow_height)
)
} }
} }