Merge "Updates to new back indicator" into tm-qpr-dev am: 0e0bc67476 am: 3d0fccd6b2

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

Change-Id: I5b67b685bbc56d95e081aa149cefe86a8e6199d0
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index ca6fb36..9637112 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -44,7 +44,7 @@
     <!-- The threshold to drag to trigger the edge action -->
     <dimen name="navigation_edge_action_drag_threshold">16dp</dimen>
     <!-- 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 -->
     <dimen name="navigation_edge_arrow_min_y">64dp</dimen>
     <!-- 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_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_background_width">74dp</dimen>
     <dimen name="navigation_edge_stretch_background_height">60dp</dimen>
-    <dimen name="navigation_edge_stretch_left_corners">30dp</dimen>
-    <dimen name="navigation_edge_stretch_right_corners">30dp</dimen>
+    <dimen name="navigation_edge_stretch_edge_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_height">10dp</dimen>
 
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanel.kt b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanel.kt
index 56ad19a..2822435 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanel.kt
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanel.kt
@@ -58,14 +58,16 @@
         SpringForce().apply {
             stiffness = 600f
             dampingRatio = 0.65f
-        })
+        }
+    )
 
     private val backgroundHeight = AnimatedFloat(
         name = "backgroundHeight",
         SpringForce().apply {
             stiffness = 600f
             dampingRatio = 0.65f
-        })
+        }
+    )
 
     /**
      * Corners of the background closer to the edge of the screen (where the arrow appeared from).
@@ -76,18 +78,20 @@
         SpringForce().apply {
             stiffness = 400f
             dampingRatio = SpringForce.DAMPING_RATIO_MEDIUM_BOUNCY
-        })
+        }
+    )
 
     /**
      * Corners of the background further from the edge of the screens (toward the direction the
      * arrow is being dragged). Used for animating [arrowBackgroundRect]
      */
-    private val backgroundDragCornerRadius = AnimatedFloat(
+    private val backgroundFarCornerRadius = AnimatedFloat(
         name = "backgroundDragCornerRadius",
         SpringForce().apply {
             stiffness = 2200f
             dampingRatio = SpringForce.DAMPING_RATIO_NO_BOUNCY
-        })
+        }
+    )
 
     /**
      * Left/right position of the background relative to the canvas. Also corresponds with the
@@ -96,14 +100,32 @@
      */
     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.
      */
-    private var verticalTranslation: AnimatedFloat =
-        AnimatedFloat("verticalTranslation", SpringForce().apply {
+    private var verticalTranslation: AnimatedFloat = AnimatedFloat(
+        name = "verticalTranslation",
+        SpringForce().apply {
             stiffness = SpringForce.STIFFNESS_MEDIUM
-        })
+        }
+    )
 
     /**
      * Use for drawing debug info. Can only be set if [DEBUG]=true
@@ -160,6 +182,17 @@
             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) {
             restingPosition = pos
             if (animated)
@@ -192,7 +225,10 @@
     }
 
     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)
             true
         } else {
@@ -202,47 +238,59 @@
     }
 
     fun setStretch(
-        arrowLengthStretch: Float,
-        arrowHeightStretch: Float,
-        backgroundWidthStretch: Float,
-        backgroundHeightStretch: Float,
-        backgroundEdgeCornerRadiusStretch: Float,
-        backgroundDragCornerRadiusStretch: Float,
-        horizontalTranslationStretch: Float
+        horizontalTranslationStretchAmount: Float,
+        arrowStretchAmount: Float,
+        backgroundWidthStretchAmount: Float,
+        fullyStretchedDimens: EdgePanelParams.BackIndicatorDimens
     ) {
-        arrowLength.stretchTo(arrowLengthStretch)
-        arrowHeight.stretchTo(arrowHeightStretch)
-        backgroundWidth.stretchTo(backgroundWidthStretch)
-        backgroundHeight.stretchTo(backgroundHeightStretch)
-        backgroundEdgeCornerRadius.stretchTo(backgroundEdgeCornerRadiusStretch)
-        backgroundDragCornerRadius.stretchTo(backgroundDragCornerRadiusStretch)
-        horizontalTranslation.stretchTo(horizontalTranslationStretch)
+        horizontalTranslation.stretchBy(
+            finalPosition = fullyStretchedDimens.horizontalTranslation,
+            amount = horizontalTranslationStretchAmount
+        )
+        arrowLength.stretchBy(
+            finalPosition = fullyStretchedDimens.arrowDimens.length,
+            amount = arrowStretchAmount
+        )
+        arrowHeight.stretchBy(
+            finalPosition = fullyStretchedDimens.arrowDimens.height,
+            amount = arrowStretchAmount
+        )
+        backgroundWidth.stretchBy(
+            finalPosition = fullyStretchedDimens.backgroundDimens.width,
+            amount = backgroundWidthStretchAmount
+        )
     }
 
     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
      */
-    internal fun updateRestingArrowDimens(
-        backgroundWidth: Float,
-        backgroundHeight: Float,
-        backgroundEdgeCornerRadius: Float,
-        backgroundDragCornerRadius: Float,
-        arrowLength: Float,
-        arrowHeight: Float,
-        horizontalTranslation: Float,
+    internal fun setRestingDimens(
+        restingParams: EdgePanelParams.BackIndicatorDimens,
         animate: Boolean
     ) {
-        this.arrowLength.updateRestingPosition(arrowLength, animate)
-        this.arrowHeight.updateRestingPosition(arrowHeight, animate)
-        this.backgroundWidth.updateRestingPosition(backgroundWidth, animate)
-        this.backgroundHeight.updateRestingPosition(backgroundHeight, animate)
-        this.backgroundEdgeCornerRadius.updateRestingPosition(backgroundEdgeCornerRadius, animate)
-        this.backgroundDragCornerRadius.updateRestingPosition(backgroundDragCornerRadius, animate)
-        this.horizontalTranslation.updateRestingPosition(horizontalTranslation, animate)
+        horizontalTranslation.updateRestingPosition(restingParams.horizontalTranslation, animate)
+        arrowLength.updateRestingPosition(restingParams.arrowDimens.length, animate)
+        arrowHeight.updateRestingPosition(restingParams.arrowDimens.height, animate)
+        backgroundWidth.updateRestingPosition(restingParams.backgroundDimens.width, animate)
+        backgroundHeight.updateRestingPosition(restingParams.backgroundDimens.height, animate)
+        backgroundEdgeCornerRadius.updateRestingPosition(
+            restingParams.backgroundDimens.edgeCornerRadius,
+            animate
+        )
+        backgroundFarCornerRadius.updateRestingPosition(
+            restingParams.backgroundDimens.farCornerRadius,
+            animate
+        )
     }
 
     fun animateVertically(yPos: Float) = verticalTranslation.stretchTo(yPos)
@@ -262,7 +310,7 @@
 
     override fun onDraw(canvas: Canvas) {
         var edgeCorner = backgroundEdgeCornerRadius.pos
-        val farCorner = backgroundDragCornerRadius.pos
+        val farCorner = backgroundFarCornerRadius.pos
         val halfHeight = backgroundHeight.pos / 2
 
         canvas.save()
@@ -337,4 +385,13 @@
         )
         addRoundRect(this@toPathWithRoundCorners, corners, Path.Direction.CW)
     }
+
+    fun cancelAlphaAnimations() {
+        alphaAnimation.cancel()
+        alpha = 1f
+    }
+
+    fun fadeOut() {
+        alphaAnimation.animateToFinalPosition(0f)
+    }
 }
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 100411b..f1dd5ff 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanelController.kt
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanelController.kt
@@ -31,7 +31,7 @@
 import android.view.VelocityTracker
 import android.view.View
 import android.view.WindowManager
-import android.view.animation.AccelerateInterpolator
+import android.view.animation.DecelerateInterpolator
 import android.view.animation.PathInterpolator
 import android.window.BackEvent
 import androidx.dynamicanimation.animation.DynamicAnimation
@@ -68,7 +68,7 @@
 /**
  * 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.
@@ -90,7 +90,9 @@
  */
 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(
     context: Context,
@@ -160,6 +162,8 @@
     private var startX = 0f
     private var startY = 0f
 
+    private var gestureStartTime = 0L
+
     private val failsafeRunnable = Runnable { onFailsafe() }
 
     private enum class GestureState {
@@ -182,19 +186,29 @@
         COMMITTED,
 
         /* 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
      * runnable is not called if the animation is cancelled
      */
-    class DelayedOnAnimationEndListener(
+    inner class DelayedOnAnimationEndListener internal constructor(
         private val handler: Handler,
         private val runnable: Runnable,
-        private val delay: Long
+        private val minDuration: Long
     ) : DynamicAnimation.OnAnimationEndListener {
-
         override fun onAnimationEnd(
             animation: DynamicAnimation<*>,
             canceled: Boolean,
@@ -203,11 +217,15 @@
         ) {
             animation.removeEndListener(this)
             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)
             }
         }
 
-        fun runNow() {
+        internal fun runNow() {
             runnable.run()
         }
     }
@@ -216,7 +234,7 @@
         DelayedOnAnimationEndListener(
             mainHandler,
             { updateArrowState(GestureState.COMMITTED) },
-            delay = FLING_PAUSE_DURATION_MS
+            minDuration = FLING_MIN_APPEARANCE_DURATION
         )
 
     private val setGoneEndListener =
@@ -226,21 +244,21 @@
                 cancelFailsafe()
                 updateArrowState(GestureState.GONE)
             },
-            delay = 0
+            minDuration = 0
         )
 
     // Vibration
     private var vibrationTime: Long = 0
 
-    // Minimum size of the screen's width or height
-    private var screenSize = 0
+    // Minimum of the screen's width or the predefined threshold
+    private var fullyStretchedThreshold = 0f
 
     /**
      * Used for initialization and configuration changes
      */
     private fun updateConfiguration() {
         params.update(resources)
-        initializeBackAnimation()
+        updateBackAnimationSwipeThresholds()
         mView.updateArrowPaint(params.arrowThickness)
     }
 
@@ -284,6 +302,7 @@
                 resetOnDown()
                 startX = event.x
                 startY = event.y
+                gestureStartTime = SystemClock.uptimeMillis()
 
                 // Reset the arrow to the side
                 updateArrowState(GestureState.ENTRY)
@@ -308,40 +327,39 @@
     }
 
     private fun updateArrowStateOnMove(yTranslation: Float, xTranslation: Float) {
+        if (!currentState.isInteractive())
+            return
+
         when (currentState) {
-            GestureState.GONE, GestureState.FLUNG, GestureState.COMMITTED, GestureState.CANCELLED ->
-                return
+            // Check if we should transition from ENTRY to ACTIVE
+            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) {
-        when (currentState) {
-            GestureState.GONE, GestureState.FLUNG, GestureState.COMMITTED,
-            GestureState.CANCELLED -> return
-        }
+        if (!currentState.isInteractive())
+            return
 
         val x = event.x
         val y = event.y
@@ -372,24 +390,25 @@
 
         updateArrowStateOnMove(yTranslation, xTranslation)
         when (currentState) {
-            GestureState.ACTIVE -> setActiveStretch(fullScreenStretchProgress(xTranslation))
+            GestureState.ACTIVE ->
+                stretchActiveBackIndicator(fullScreenStretchProgress(xTranslation))
             GestureState.ENTRY ->
-                setEntryStretch(preThresholdStretchProgress(xTranslation))
-            GestureState.INACTIVE -> mView.resetStretch()
+                stretchEntryBackIndicator(preThresholdStretchProgress(xTranslation))
+            GestureState.INACTIVE ->
+                mView.resetStretch()
         }
 
         // set y translation
         setVerticalTranslation(yOffset)
     }
 
-    fun setVerticalTranslation(yOffset: Float) {
+    private fun setVerticalTranslation(yOffset: Float) {
         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))
         mView.animateVertically(
-            RUBBER_BAND_INTERPOLATOR.getInterpolation(yProgress) * maxYOffset * sign(
-                yOffset
-            )
+            RUBBER_BAND_INTERPOLATOR.getInterpolation(yProgress) * maxYOffset *
+                sign(yOffset)
         )
     }
 
@@ -397,13 +416,10 @@
      * @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)
      */
-    fun fullScreenStretchProgress(xTranslation: Float): Float {
+    private fun fullScreenStretchProgress(xTranslation: Float): Float {
         return saturate(
             (xTranslation - params.swipeTriggerThreshold) /
-                    (min(
-                        params.fullyStretchedThreshold,
-                        screenSize.toFloat()
-                    ) - params.swipeTriggerThreshold)
+                (fullyStretchedThreshold - params.swipeTriggerThreshold)
         )
     }
 
@@ -411,57 +427,38 @@
      * Tracks the relative position of the drag from the entry until the threshold where the arrow
      * activates (between 0.0 - 1.0f)
      */
-    fun preThresholdStretchProgress(xTranslation: Float): Float {
+    private fun preThresholdStretchProgress(xTranslation: Float): Float {
         return saturate(xTranslation / params.swipeTriggerThreshold)
     }
 
-    fun setActiveStretch(progress: Float) {
-        val stretch = RUBBER_BAND_INTERPOLATOR.getInterpolation(progress)
+    private fun stretchActiveBackIndicator(progress: Float) {
+        val rubberBandIterpolation = RUBBER_BAND_INTERPOLATOR.getInterpolation(progress)
         mView.setStretch(
-            arrowLengthStretch = stretch * (params.stretchedArrowLength - params.activeArrowLength),
-            arrowHeightStretch = stretch * (params.stretchedArrowHeight - params.activeArrowHeight),
-            backgroundWidthStretch =
-                stretch * (params.stretchBackgroundWidth - params.activeBackgroundWidth),
-            backgroundHeightStretch =
-                stretch * (params.stretchBackgroundHeight - params.activeBackgroundHeight),
-            backgroundEdgeCornerRadiusStretch =
-                stretch * (params.stretchEdgeCorners - params.activeEdgeCorners),
-            backgroundDragCornerRadiusStretch =
-                stretch * (params.stretchFarCorners - params.activeFarCorners),
-            horizontalTranslationStretch = stretch * (params.stretchMargin - params.activeMargin)
+            horizontalTranslationStretchAmount = rubberBandIterpolation,
+            arrowStretchAmount = rubberBandIterpolation,
+            backgroundWidthStretchAmount = DECELERATE_INTERPOLATOR_SLOW.getInterpolation(progress),
+            params.fullyStretchedIndicator
         )
     }
 
-    fun setEntryStretch(progress: Float) {
-        val bgStretch = ACCELERATE_INTERPOLATOR.getInterpolation(progress)
-        val arrowStretch = RUBBER_BAND_INTERPOLATOR.getInterpolation(progress)
+    private fun stretchEntryBackIndicator(progress: Float) {
         mView.setStretch(
-            arrowLengthStretch =
-                arrowStretch * (params.activeArrowLength - params.entryArrowLength),
-            arrowHeightStretch =
-                arrowStretch * (params.activeArrowHeight - params.entryArrowHeight),
-            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)
+            horizontalTranslationStretchAmount = 0f,
+            arrowStretchAmount = RUBBER_BAND_INTERPOLATOR.getInterpolation(progress),
+            backgroundWidthStretchAmount = DECELERATE_INTERPOLATOR.getInterpolation(progress),
+            params.preThresholdIndicator
         )
     }
 
     fun setBackAnimation(backAnimation: BackAnimation?) {
         this.backAnimation = backAnimation
-        initializeBackAnimation()
+        updateBackAnimationSwipeThresholds()
     }
 
-    private fun initializeBackAnimation() {
+    private fun updateBackAnimationSwipeThresholds() {
         backAnimation?.setSwipeThresholds(
             params.swipeTriggerThreshold,
-            params.swipeProgressThreshold
+            fullyStretchedThreshold
         )
     }
 
@@ -541,79 +538,68 @@
 
     private fun updateYPosition(touchY: Float) {
         var yPosition = touchY - params.fingerOffset
-        yPosition = Math.max(yPosition, params.minArrowYPosition.toFloat())
+        yPosition = max(yPosition, params.minArrowYPosition.toFloat())
         yPosition -= layoutParams.height / 2.0f
         layoutParams.y = constrain(yPosition.toInt(), 0, displaySize.y)
     }
 
     override fun setDisplaySize(displaySize: Point) {
         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
      */
     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) {
             when (currentState) {
                 GestureState.ENTRY, GestureState.ACTIVE, GestureState.FLUNG ->
                     mView.setArrowStiffness(ARROW_APPEAR_STIFFNESS, ARROW_APPEAR_DAMPING_RATIO)
+                GestureState.CANCELLED -> mView.fadeOut()
                 else ->
                     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 @@
         if (DEBUG) Log.d(TAG, "updateArrowState $currentState -> $newState")
         previousState = currentState
         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) {
             // Transitioning to GONE never animates since the arrow is (presumably) already off the
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 51566b0..a3fb58d 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgePanelParams.kt
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgePanelParams.kt
@@ -4,66 +4,36 @@
 import com.android.systemui.R
 
 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
         private set
-    var entryArrowLength: Float = 0f
+    var entryIndicator = BackIndicatorDimens()
         private set
-    var entryArrowHeight: Float = 0f
+    var activeIndicator = BackIndicatorDimens()
         private set
-    var activeArrowLength: Float = 0f
+    var preThresholdIndicator = BackIndicatorDimens()
         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
+    var cancelledArrowDimens = ArrowDimens()
 
     // navigation bar edge constants
     var arrowPaddingEnd: Int = 0
@@ -98,42 +68,73 @@
     fun update(resources: Resources) {
         this.resources = resources
         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)
         minArrowYPosition = getPx(R.dimen.navigation_edge_arrow_min_y)
         fingerOffset = getPx(R.dimen.navigation_edge_finger_offset)
         swipeTriggerThreshold = getDimen(R.dimen.navigation_edge_action_drag_threshold)
         swipeProgressThreshold = getDimen(R.dimen.navigation_edge_action_progress_threshold)
         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)
+        )
     }
 }