Merge "New back panel" into tm-qpr-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
e108be8501
@@ -48,7 +48,50 @@
|
|||||||
<!-- 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-->
|
||||||
<dimen name="navigation_edge_finger_offset">48dp</dimen>
|
<dimen name="navigation_edge_finger_offset">64dp</dimen>
|
||||||
|
|
||||||
|
<!-- 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 -->
|
||||||
|
<dimen name="navigation_edge_entry_margin">4dp</dimen>
|
||||||
|
<dimen name="navigation_edge_entry_background_width">8dp</dimen>
|
||||||
|
<dimen name="navigation_edge_entry_background_height">60dp</dimen>
|
||||||
|
<dimen name="navigation_edge_entry_edge_corners">6dp</dimen>
|
||||||
|
<dimen name="navigation_edge_entry_far_corners">6dp</dimen>
|
||||||
|
<dimen name="navigation_edge_entry_arrow_length">10dp</dimen>
|
||||||
|
<dimen name="navigation_edge_entry_arrow_height">7dp</dimen>
|
||||||
|
|
||||||
|
<!-- pre-threshold -->
|
||||||
|
<dimen name="navigation_edge_pre_threshold_margin">4dp</dimen>
|
||||||
|
<dimen name="navigation_edge_pre_threshold_background_width">64dp</dimen>
|
||||||
|
<dimen name="navigation_edge_pre_threshold_background_height">60dp</dimen>
|
||||||
|
<dimen name="navigation_edge_pre_threshold_edge_corners">22dp</dimen>
|
||||||
|
<dimen name="navigation_edge_pre_threshold_far_corners">26dp</dimen>
|
||||||
|
|
||||||
|
<!-- post-threshold / active -->
|
||||||
|
<dimen name="navigation_edge_active_margin">14dp</dimen>
|
||||||
|
<dimen name="navigation_edge_active_background_width">60dp</dimen>
|
||||||
|
<dimen name="navigation_edge_active_background_height">60dp</dimen>
|
||||||
|
<dimen name="navigation_edge_active_edge_corners">30dp</dimen>
|
||||||
|
<dimen name="navigation_edge_active_far_corners">30dp</dimen>
|
||||||
|
<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_stretched_arrow_length">7dp</dimen>
|
||||||
|
<dimen name="navigation_edge_stretched_arrow_height">10dp</dimen>
|
||||||
|
|
||||||
|
<dimen name="navigation_edge_cancelled_arrow_length">12dp</dimen>
|
||||||
|
<dimen name="navigation_edge_cancelled_arrow_height">0dp</dimen>
|
||||||
|
|
||||||
<!-- Height of notification icons in the status bar -->
|
<!-- Height of notification icons in the status bar -->
|
||||||
<dimen name="status_bar_icon_size">@*android:dimen/status_bar_icon_size</dimen>
|
<dimen name="status_bar_icon_size">@*android:dimen/status_bar_icon_size</dimen>
|
||||||
|
|||||||
@@ -0,0 +1,340 @@
|
|||||||
|
package com.android.systemui.navigationbar.gestural
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.graphics.Canvas
|
||||||
|
import android.graphics.Paint
|
||||||
|
import android.graphics.Path
|
||||||
|
import android.graphics.RectF
|
||||||
|
import android.view.View
|
||||||
|
import androidx.dynamicanimation.animation.FloatPropertyCompat
|
||||||
|
import androidx.dynamicanimation.animation.SpringAnimation
|
||||||
|
import androidx.dynamicanimation.animation.SpringForce
|
||||||
|
import com.android.internal.util.LatencyTracker
|
||||||
|
import com.android.settingslib.Utils
|
||||||
|
import com.android.systemui.navigationbar.gestural.BackPanelController.DelayedOnAnimationEndListener
|
||||||
|
|
||||||
|
private const val TAG = "BackPanel"
|
||||||
|
private const val DEBUG = false
|
||||||
|
|
||||||
|
class BackPanel(context: Context, private val latencyTracker: LatencyTracker) : View(context) {
|
||||||
|
|
||||||
|
var arrowsPointLeft = false
|
||||||
|
set(value) {
|
||||||
|
if (field != value) {
|
||||||
|
invalidate()
|
||||||
|
field = value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Arrow color and shape
|
||||||
|
private val arrowPath = Path()
|
||||||
|
private val arrowPaint = Paint()
|
||||||
|
|
||||||
|
// Arrow background color and shape
|
||||||
|
private var arrowBackgroundRect = RectF()
|
||||||
|
private var arrowBackgroundPaint = Paint()
|
||||||
|
|
||||||
|
// True if the panel is currently on the left of the screen
|
||||||
|
var isLeftPanel = false
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used to track back arrow latency from [android.view.MotionEvent.ACTION_DOWN] to [onDraw]
|
||||||
|
*/
|
||||||
|
private var trackingBackArrowLatency = false
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The length of the arrow measured horizontally. Used for animating [arrowPath]
|
||||||
|
*/
|
||||||
|
private var arrowLength = AnimatedFloat("arrowLength", SpringForce())
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The height of the arrow measured vertically from its center to its top (i.e. half the total
|
||||||
|
* height). Used for animating [arrowPath]
|
||||||
|
*/
|
||||||
|
private var arrowHeight = AnimatedFloat("arrowHeight", SpringForce())
|
||||||
|
|
||||||
|
private val backgroundWidth = AnimatedFloat(
|
||||||
|
name = "backgroundWidth",
|
||||||
|
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).
|
||||||
|
* Used for animating [arrowBackgroundRect]
|
||||||
|
*/
|
||||||
|
private val backgroundEdgeCornerRadius = AnimatedFloat(
|
||||||
|
name = "backgroundEdgeCornerRadius",
|
||||||
|
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(
|
||||||
|
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
|
||||||
|
* background's margin relative to the screen edge. The arrow will be centered within the
|
||||||
|
* background.
|
||||||
|
*/
|
||||||
|
private var horizontalTranslation = AnimatedFloat("horizontalTranslation", SpringForce())
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Canvas vertical translation. How far up/down the arrow and background appear relative to the
|
||||||
|
* canvas.
|
||||||
|
*/
|
||||||
|
private var verticalTranslation: AnimatedFloat =
|
||||||
|
AnimatedFloat("verticalTranslation", SpringForce().apply {
|
||||||
|
stiffness = SpringForce.STIFFNESS_MEDIUM
|
||||||
|
})
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use for drawing debug info. Can only be set if [DEBUG]=true
|
||||||
|
*/
|
||||||
|
var drawDebugInfo: ((canvas: Canvas) -> Unit)? = null
|
||||||
|
set(value) {
|
||||||
|
if (DEBUG) field = value
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun updateArrowPaint(arrowThickness: Float) {
|
||||||
|
// Arrow constants
|
||||||
|
arrowPaint.strokeWidth = arrowThickness
|
||||||
|
|
||||||
|
arrowPaint.color =
|
||||||
|
Utils.getColorAttrDefaultColor(context, com.android.internal.R.attr.colorPrimary)
|
||||||
|
arrowBackgroundPaint.color = Utils.getColorAccentDefaultColor(context)
|
||||||
|
}
|
||||||
|
|
||||||
|
private inner class AnimatedFloat(name: String, springForce: SpringForce) {
|
||||||
|
// The resting position when not stretched by a touch drag
|
||||||
|
private var restingPosition = 0f
|
||||||
|
|
||||||
|
// The current position as updated by the SpringAnimation
|
||||||
|
var pos = 0f
|
||||||
|
set(v) {
|
||||||
|
if (field != v) {
|
||||||
|
field = v
|
||||||
|
invalidate()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val animation: SpringAnimation
|
||||||
|
|
||||||
|
init {
|
||||||
|
val floatProp = object : FloatPropertyCompat<AnimatedFloat>(name) {
|
||||||
|
override fun setValue(animatedFloat: AnimatedFloat, value: Float) {
|
||||||
|
animatedFloat.pos = value
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getValue(animatedFloat: AnimatedFloat): Float = animatedFloat.pos
|
||||||
|
}
|
||||||
|
animation = SpringAnimation(this, floatProp)
|
||||||
|
animation.spring = springForce
|
||||||
|
}
|
||||||
|
|
||||||
|
fun snapTo(newPosition: Float) {
|
||||||
|
animation.cancel()
|
||||||
|
restingPosition = newPosition
|
||||||
|
animation.spring.finalPosition = newPosition
|
||||||
|
pos = newPosition
|
||||||
|
}
|
||||||
|
|
||||||
|
fun stretchTo(stretchAmount: Float) {
|
||||||
|
animation.animateToFinalPosition(restingPosition + stretchAmount)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun updateRestingPosition(pos: Float, animated: Boolean) {
|
||||||
|
restingPosition = pos
|
||||||
|
if (animated)
|
||||||
|
animation.animateToFinalPosition(restingPosition)
|
||||||
|
else
|
||||||
|
snapTo(restingPosition)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
init {
|
||||||
|
visibility = GONE
|
||||||
|
arrowPaint.apply {
|
||||||
|
style = Paint.Style.STROKE
|
||||||
|
strokeCap = Paint.Cap.SQUARE
|
||||||
|
}
|
||||||
|
arrowBackgroundPaint.apply {
|
||||||
|
style = Paint.Style.FILL
|
||||||
|
strokeJoin = Paint.Join.ROUND
|
||||||
|
strokeCap = Paint.Cap.ROUND
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun calculateArrowPath(dx: Float, dy: Float): Path {
|
||||||
|
arrowPath.reset()
|
||||||
|
arrowPath.moveTo(dx, -dy)
|
||||||
|
arrowPath.lineTo(0f, 0f)
|
||||||
|
arrowPath.lineTo(dx, dy)
|
||||||
|
arrowPath.moveTo(dx, -dy)
|
||||||
|
return arrowPath
|
||||||
|
}
|
||||||
|
|
||||||
|
fun addEndListener(endListener: DelayedOnAnimationEndListener): Boolean {
|
||||||
|
return if (horizontalTranslation.animation.isRunning) {
|
||||||
|
horizontalTranslation.animation.addEndListener(endListener)
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
endListener.runNow()
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setStretch(
|
||||||
|
arrowLengthStretch: Float,
|
||||||
|
arrowHeightStretch: Float,
|
||||||
|
backgroundWidthStretch: Float,
|
||||||
|
backgroundHeightStretch: Float,
|
||||||
|
backgroundEdgeCornerRadiusStretch: Float,
|
||||||
|
backgroundDragCornerRadiusStretch: Float,
|
||||||
|
horizontalTranslationStretch: Float
|
||||||
|
) {
|
||||||
|
arrowLength.stretchTo(arrowLengthStretch)
|
||||||
|
arrowHeight.stretchTo(arrowHeightStretch)
|
||||||
|
backgroundWidth.stretchTo(backgroundWidthStretch)
|
||||||
|
backgroundHeight.stretchTo(backgroundHeightStretch)
|
||||||
|
backgroundEdgeCornerRadius.stretchTo(backgroundEdgeCornerRadiusStretch)
|
||||||
|
backgroundDragCornerRadius.stretchTo(backgroundDragCornerRadiusStretch)
|
||||||
|
horizontalTranslation.stretchTo(horizontalTranslationStretch)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun resetStretch() {
|
||||||
|
setStretch(0f, 0f, 0f, 0f, 0f, 0f, 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,
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun animateVertically(yPos: Float) = verticalTranslation.stretchTo(yPos)
|
||||||
|
|
||||||
|
fun setArrowStiffness(arrowStiffness: Float, arrowDampingRatio: Float) {
|
||||||
|
arrowLength.animation.spring.apply {
|
||||||
|
stiffness = arrowStiffness
|
||||||
|
dampingRatio = arrowDampingRatio
|
||||||
|
}
|
||||||
|
arrowHeight.animation.spring.apply {
|
||||||
|
stiffness = arrowStiffness
|
||||||
|
dampingRatio = arrowDampingRatio
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun hasOverlappingRendering() = false
|
||||||
|
|
||||||
|
override fun onDraw(canvas: Canvas) {
|
||||||
|
var edgeCorner = backgroundEdgeCornerRadius.pos
|
||||||
|
val farCorner = backgroundDragCornerRadius.pos
|
||||||
|
val halfHeight = backgroundHeight.pos / 2
|
||||||
|
|
||||||
|
canvas.save()
|
||||||
|
|
||||||
|
if (!isLeftPanel) canvas.scale(-1f, 1f, width / 2.0f, 0f)
|
||||||
|
|
||||||
|
canvas.translate(
|
||||||
|
horizontalTranslation.pos,
|
||||||
|
height * 0.5f + verticalTranslation.pos
|
||||||
|
)
|
||||||
|
|
||||||
|
val arrowBackground = arrowBackgroundRect.apply {
|
||||||
|
left = 0f
|
||||||
|
top = -halfHeight
|
||||||
|
right = backgroundWidth.pos
|
||||||
|
bottom = halfHeight
|
||||||
|
}.toPathWithRoundCorners(
|
||||||
|
topLeft = edgeCorner,
|
||||||
|
bottomLeft = edgeCorner,
|
||||||
|
topRight = farCorner,
|
||||||
|
bottomRight = farCorner
|
||||||
|
)
|
||||||
|
canvas.drawPath(arrowBackground, arrowBackgroundPaint)
|
||||||
|
|
||||||
|
val dx = arrowLength.pos
|
||||||
|
val dy = arrowHeight.pos
|
||||||
|
|
||||||
|
// How far the arrow bounding box should be from the edge of the screen. Measured from
|
||||||
|
// either the tip or the back of the arrow, whichever is closer
|
||||||
|
var arrowOffset = (backgroundWidth.pos - dx) / 2
|
||||||
|
canvas.translate(
|
||||||
|
/* dx= */ arrowOffset,
|
||||||
|
/* dy= */ 0f /* pass 0 for the y position since the canvas was already translated */
|
||||||
|
)
|
||||||
|
|
||||||
|
val arrowPointsAwayFromEdge = !arrowsPointLeft.xor(isLeftPanel)
|
||||||
|
if (arrowPointsAwayFromEdge) {
|
||||||
|
canvas.apply {
|
||||||
|
scale(-1f, 1f, 0f, 0f)
|
||||||
|
translate(-dx, 0f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val arrowPath = calculateArrowPath(dx = dx, dy = dy)
|
||||||
|
canvas.drawPath(arrowPath, arrowPaint)
|
||||||
|
canvas.restore()
|
||||||
|
|
||||||
|
if (trackingBackArrowLatency) {
|
||||||
|
latencyTracker.onActionEnd(LatencyTracker.ACTION_SHOW_BACK_ARROW)
|
||||||
|
trackingBackArrowLatency = false
|
||||||
|
}
|
||||||
|
|
||||||
|
if (DEBUG) drawDebugInfo?.invoke(canvas)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun startTrackingShowBackArrowLatency() {
|
||||||
|
latencyTracker.onActionStart(LatencyTracker.ACTION_SHOW_BACK_ARROW)
|
||||||
|
trackingBackArrowLatency = true
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun RectF.toPathWithRoundCorners(
|
||||||
|
topLeft: Float = 0f,
|
||||||
|
topRight: Float = 0f,
|
||||||
|
bottomRight: Float = 0f,
|
||||||
|
bottomLeft: Float = 0f
|
||||||
|
): Path = Path().apply {
|
||||||
|
val corners = floatArrayOf(
|
||||||
|
topLeft, topLeft,
|
||||||
|
topRight, topRight,
|
||||||
|
bottomRight, bottomRight,
|
||||||
|
bottomLeft, bottomLeft
|
||||||
|
)
|
||||||
|
addRoundRect(this@toPathWithRoundCorners, corners, Path.Direction.CW)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,735 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2022 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package com.android.systemui.navigationbar.gestural
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.res.Configuration
|
||||||
|
import android.graphics.Color
|
||||||
|
import android.graphics.Paint
|
||||||
|
import android.graphics.Point
|
||||||
|
import android.os.Handler
|
||||||
|
import android.os.SystemClock
|
||||||
|
import android.os.VibrationEffect
|
||||||
|
import android.util.Log
|
||||||
|
import android.util.MathUtils.constrain
|
||||||
|
import android.util.MathUtils.saturate
|
||||||
|
import android.view.Gravity
|
||||||
|
import android.view.MotionEvent
|
||||||
|
import android.view.VelocityTracker
|
||||||
|
import android.view.View
|
||||||
|
import android.view.WindowManager
|
||||||
|
import android.view.animation.AccelerateInterpolator
|
||||||
|
import android.view.animation.PathInterpolator
|
||||||
|
import android.window.BackEvent
|
||||||
|
import androidx.dynamicanimation.animation.DynamicAnimation
|
||||||
|
import androidx.dynamicanimation.animation.SpringForce
|
||||||
|
import com.android.internal.util.LatencyTracker
|
||||||
|
import com.android.systemui.dagger.qualifiers.Main
|
||||||
|
import com.android.systemui.plugins.NavigationEdgeBackPlugin
|
||||||
|
import com.android.systemui.statusbar.VibratorHelper
|
||||||
|
import com.android.systemui.statusbar.policy.ConfigurationController
|
||||||
|
import com.android.systemui.util.ViewController
|
||||||
|
import com.android.wm.shell.back.BackAnimation
|
||||||
|
import java.io.PrintWriter
|
||||||
|
import javax.inject.Inject
|
||||||
|
import kotlin.math.abs
|
||||||
|
import kotlin.math.max
|
||||||
|
import kotlin.math.min
|
||||||
|
import kotlin.math.sign
|
||||||
|
|
||||||
|
private const val TAG = "BackPanelController"
|
||||||
|
private const val DEBUG = false
|
||||||
|
|
||||||
|
private const val ENABLE_FAILSAFE = true
|
||||||
|
|
||||||
|
private const val FAILSAFE_DELAY_MS: Long = 350
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The time required between the arrow-appears vibration effect and the back-committed vibration
|
||||||
|
* effect. If the arrow is flung quickly, the phone only vibrates once. However, if the arrow is
|
||||||
|
* held on the screen for a long time, it will vibrate a second time when the back gesture is
|
||||||
|
* committed.
|
||||||
|
*/
|
||||||
|
private const val GESTURE_DURATION_FOR_CLICK_MS = 400
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The min duration arrow remains on screen during a fling event.
|
||||||
|
*/
|
||||||
|
private const val FLING_PAUSE_DURATION_MS = 50L
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The min duration arrow remains on screen during a fling event.
|
||||||
|
*/
|
||||||
|
private const val MIN_FLING_VELOCITY = 3000
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The amount of rubber banding we do for the vertical translation
|
||||||
|
*/
|
||||||
|
private const val RUBBER_BAND_AMOUNT = 15
|
||||||
|
|
||||||
|
private const val ARROW_APPEAR_STIFFNESS = 600f
|
||||||
|
private const val ARROW_APPEAR_DAMPING_RATIO = 0.4f
|
||||||
|
private const val ARROW_DISAPPEAR_STIFFNESS = 1200f
|
||||||
|
private const val ARROW_DISAPPEAR_DAMPING_RATIO = SpringForce.DAMPING_RATIO_NO_BOUNCY
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The interpolator used to rubber band
|
||||||
|
*/
|
||||||
|
private val RUBBER_BAND_INTERPOLATOR = PathInterpolator(1.0f / 5.0f, 1.0f, 1.0f, 1.0f)
|
||||||
|
|
||||||
|
private val ACCELERATE_INTERPOLATOR = AccelerateInterpolator(0.7f)
|
||||||
|
|
||||||
|
class BackPanelController private constructor(
|
||||||
|
context: Context,
|
||||||
|
private var backAnimation: BackAnimation?,
|
||||||
|
private val windowManager: WindowManager,
|
||||||
|
@Main private val mainHandler: Handler,
|
||||||
|
private val vibratorHelper: VibratorHelper,
|
||||||
|
private val configurationController: ConfigurationController,
|
||||||
|
latencyTracker: LatencyTracker
|
||||||
|
) : ViewController<BackPanel>(BackPanel(context, latencyTracker)), NavigationEdgeBackPlugin {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Injectable instance to create a new BackPanelController.
|
||||||
|
*
|
||||||
|
* Necessary because EdgeBackGestureHandler sometimes needs to create new instances of
|
||||||
|
* BackPanelController, and we need to match EdgeBackGestureHandler's context.
|
||||||
|
*/
|
||||||
|
class Factory @Inject constructor(
|
||||||
|
private val windowManager: WindowManager,
|
||||||
|
@Main private val mainHandler: Handler,
|
||||||
|
private val vibratorHelper: VibratorHelper,
|
||||||
|
private val configurationController: ConfigurationController,
|
||||||
|
private val latencyTracker: LatencyTracker
|
||||||
|
) {
|
||||||
|
/** Construct a [BackPanelController]. */
|
||||||
|
fun create(context: Context, backAnimation: BackAnimation?): BackPanelController {
|
||||||
|
val backPanelController = BackPanelController(
|
||||||
|
context,
|
||||||
|
backAnimation,
|
||||||
|
windowManager,
|
||||||
|
mainHandler,
|
||||||
|
vibratorHelper,
|
||||||
|
configurationController,
|
||||||
|
latencyTracker
|
||||||
|
)
|
||||||
|
backPanelController.init()
|
||||||
|
return backPanelController
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private var params: EdgePanelParams = EdgePanelParams(resources)
|
||||||
|
private var currentState: GestureState = GestureState.GONE
|
||||||
|
private var previousState: GestureState = GestureState.GONE
|
||||||
|
|
||||||
|
// Phone should only vibrate the first time the arrow is activated
|
||||||
|
private var hasHapticPlayed = false
|
||||||
|
|
||||||
|
// Screen attributes
|
||||||
|
private lateinit var layoutParams: WindowManager.LayoutParams
|
||||||
|
private val displaySize = Point()
|
||||||
|
|
||||||
|
private lateinit var backCallback: NavigationEdgeBackPlugin.BackCallback
|
||||||
|
|
||||||
|
private var previousXTranslation = 0f
|
||||||
|
private var totalTouchDelta = 0f
|
||||||
|
private var velocityTracker: VelocityTracker? = null
|
||||||
|
set(value) {
|
||||||
|
if (field != value) field?.recycle()
|
||||||
|
field = value
|
||||||
|
}
|
||||||
|
get() {
|
||||||
|
if (field == null) field = VelocityTracker.obtain()
|
||||||
|
return field
|
||||||
|
}
|
||||||
|
|
||||||
|
// The x,y position of the first touch event
|
||||||
|
private var startX = 0f
|
||||||
|
private var startY = 0f
|
||||||
|
|
||||||
|
private val failsafeRunnable = Runnable { onFailsafe() }
|
||||||
|
|
||||||
|
private enum class GestureState {
|
||||||
|
/* Arrow is off the screen and invisible */
|
||||||
|
GONE,
|
||||||
|
|
||||||
|
/* Arrow is animating in */
|
||||||
|
ENTRY,
|
||||||
|
|
||||||
|
/* could be entry, neutral, or stretched, releasing will commit back */
|
||||||
|
ACTIVE,
|
||||||
|
|
||||||
|
/* releasing will cancel back */
|
||||||
|
INACTIVE,
|
||||||
|
|
||||||
|
/* like committed, but animation takes longer */
|
||||||
|
FLUNG,
|
||||||
|
|
||||||
|
/* back action currently occurring, arrow soon to be GONE */
|
||||||
|
COMMITTED,
|
||||||
|
|
||||||
|
/* back action currently cancelling, arrow soon to be GONE */
|
||||||
|
CANCELLED
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wrapper around OnAnimationEndListener which runs the given runnable after a delay. The
|
||||||
|
* runnable is not called if the animation is cancelled
|
||||||
|
*/
|
||||||
|
class DelayedOnAnimationEndListener(
|
||||||
|
private val handler: Handler,
|
||||||
|
private val runnable: Runnable,
|
||||||
|
private val delay: Long
|
||||||
|
) : DynamicAnimation.OnAnimationEndListener {
|
||||||
|
|
||||||
|
override fun onAnimationEnd(
|
||||||
|
animation: DynamicAnimation<*>,
|
||||||
|
canceled: Boolean,
|
||||||
|
value: Float,
|
||||||
|
velocity: Float
|
||||||
|
) {
|
||||||
|
animation.removeEndListener(this)
|
||||||
|
if (!canceled) {
|
||||||
|
handler.postDelayed(runnable, delay)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun runNow() {
|
||||||
|
runnable.run()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private val setCommittedEndListener =
|
||||||
|
DelayedOnAnimationEndListener(
|
||||||
|
mainHandler,
|
||||||
|
{ updateArrowState(GestureState.COMMITTED) },
|
||||||
|
delay = FLING_PAUSE_DURATION_MS
|
||||||
|
)
|
||||||
|
|
||||||
|
private val setGoneEndListener =
|
||||||
|
DelayedOnAnimationEndListener(
|
||||||
|
mainHandler,
|
||||||
|
{
|
||||||
|
cancelFailsafe()
|
||||||
|
updateArrowState(GestureState.GONE)
|
||||||
|
},
|
||||||
|
delay = 0
|
||||||
|
)
|
||||||
|
|
||||||
|
// Vibration
|
||||||
|
private var vibrationTime: Long = 0
|
||||||
|
|
||||||
|
// Minimum size of the screen's width or height
|
||||||
|
private var screenSize = 0
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used for initialization and configuration changes
|
||||||
|
*/
|
||||||
|
private fun updateConfiguration() {
|
||||||
|
params.update(resources)
|
||||||
|
initializeBackAnimation()
|
||||||
|
mView.updateArrowPaint(params.arrowThickness)
|
||||||
|
}
|
||||||
|
|
||||||
|
private val configurationListener = object : ConfigurationController.ConfigurationListener {
|
||||||
|
override fun onConfigChanged(newConfig: Configuration?) {
|
||||||
|
updateConfiguration()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onLayoutDirectionChanged(isLayoutRtl: Boolean) {
|
||||||
|
updateArrowDirection(isLayoutRtl)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onViewAttached() {
|
||||||
|
updateConfiguration()
|
||||||
|
updateArrowDirection(configurationController.isLayoutRtl)
|
||||||
|
updateArrowState(GestureState.GONE, force = true)
|
||||||
|
updateRestingArrowDimens(animated = false, currentState)
|
||||||
|
configurationController.addCallback(configurationListener)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Update the arrow direction. The arrow should point the same way for both panels. */
|
||||||
|
private fun updateArrowDirection(isLayoutRtl: Boolean) {
|
||||||
|
mView.arrowsPointLeft = isLayoutRtl
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onViewDetached() {
|
||||||
|
configurationController.removeCallback(configurationListener)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onMotionEvent(event: MotionEvent) {
|
||||||
|
backAnimation?.onBackMotion(
|
||||||
|
event,
|
||||||
|
event.actionMasked,
|
||||||
|
if (mView.isLeftPanel) BackEvent.EDGE_LEFT else BackEvent.EDGE_RIGHT
|
||||||
|
)
|
||||||
|
|
||||||
|
velocityTracker!!.addMovement(event)
|
||||||
|
when (event.actionMasked) {
|
||||||
|
MotionEvent.ACTION_DOWN -> {
|
||||||
|
resetOnDown()
|
||||||
|
startX = event.x
|
||||||
|
startY = event.y
|
||||||
|
|
||||||
|
// Reset the arrow to the side
|
||||||
|
updateArrowState(GestureState.ENTRY)
|
||||||
|
|
||||||
|
windowManager.updateViewLayout(mView, layoutParams)
|
||||||
|
mView.startTrackingShowBackArrowLatency()
|
||||||
|
}
|
||||||
|
MotionEvent.ACTION_MOVE -> handleMoveEvent(event)
|
||||||
|
MotionEvent.ACTION_UP -> {
|
||||||
|
if (currentState == GestureState.ACTIVE) {
|
||||||
|
updateArrowState(if (isFlung()) GestureState.FLUNG else GestureState.COMMITTED)
|
||||||
|
} else {
|
||||||
|
updateArrowState(GestureState.CANCELLED)
|
||||||
|
}
|
||||||
|
velocityTracker = null
|
||||||
|
}
|
||||||
|
MotionEvent.ACTION_CANCEL -> {
|
||||||
|
updateArrowState(GestureState.CANCELLED)
|
||||||
|
velocityTracker = null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun updateArrowStateOnMove(yTranslation: Float, xTranslation: Float) {
|
||||||
|
when (currentState) {
|
||||||
|
GestureState.GONE, GestureState.FLUNG, GestureState.COMMITTED, GestureState.CANCELLED ->
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
val x = event.x
|
||||||
|
val y = event.y
|
||||||
|
|
||||||
|
val yOffset = y - startY
|
||||||
|
|
||||||
|
// How far in the y direction we are from the original touch
|
||||||
|
val yTranslation = abs(yOffset)
|
||||||
|
|
||||||
|
// How far in the x direction we are from the original touch ignoring motion that
|
||||||
|
// occurs between the screen edge and the touch start.
|
||||||
|
val xTranslation = max(0f, if (mView.isLeftPanel) x - startX else startX - x)
|
||||||
|
|
||||||
|
// Compared to last time, how far we moved in the x direction. If <0, we are moving closer
|
||||||
|
// to the edge. If >0, we are moving further from the edge
|
||||||
|
val xDelta = xTranslation - previousXTranslation
|
||||||
|
previousXTranslation = xTranslation
|
||||||
|
|
||||||
|
if (abs(xDelta) > 0) {
|
||||||
|
if (sign(xDelta) == sign(totalTouchDelta)) {
|
||||||
|
// Direction has NOT changed, so keep counting the delta
|
||||||
|
totalTouchDelta += xDelta
|
||||||
|
} else {
|
||||||
|
// Direction has changed, so reset the delta
|
||||||
|
totalTouchDelta = xDelta
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
updateArrowStateOnMove(yTranslation, xTranslation)
|
||||||
|
when (currentState) {
|
||||||
|
GestureState.ACTIVE -> setActiveStretch(fullScreenStretchProgress(xTranslation))
|
||||||
|
GestureState.ENTRY ->
|
||||||
|
setEntryStretch(preThresholdStretchProgress(xTranslation))
|
||||||
|
GestureState.INACTIVE -> mView.resetStretch()
|
||||||
|
}
|
||||||
|
|
||||||
|
// set y translation
|
||||||
|
setVerticalTranslation(yOffset)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setVerticalTranslation(yOffset: Float) {
|
||||||
|
val yTranslation = abs(yOffset)
|
||||||
|
val maxYOffset = (mView.height / 2) - (params.entryBackgroundHeight / 2)
|
||||||
|
val yProgress = saturate(yTranslation / (maxYOffset * RUBBER_BAND_AMOUNT))
|
||||||
|
mView.animateVertically(
|
||||||
|
RUBBER_BAND_INTERPOLATOR.getInterpolation(yProgress) * maxYOffset * sign(
|
||||||
|
yOffset
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @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 {
|
||||||
|
return saturate(
|
||||||
|
(xTranslation - params.swipeTriggerThreshold) /
|
||||||
|
(min(
|
||||||
|
params.fullyStretchedThreshold,
|
||||||
|
screenSize.toFloat()
|
||||||
|
) - params.swipeTriggerThreshold)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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 {
|
||||||
|
return saturate(xTranslation / params.swipeTriggerThreshold)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setActiveStretch(progress: Float) {
|
||||||
|
val stretch = 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)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setEntryStretch(progress: Float) {
|
||||||
|
val bgStretch = ACCELERATE_INTERPOLATOR.getInterpolation(progress)
|
||||||
|
val arrowStretch = RUBBER_BAND_INTERPOLATOR.getInterpolation(progress)
|
||||||
|
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)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setBackAnimation(backAnimation: BackAnimation?) {
|
||||||
|
this.backAnimation = backAnimation
|
||||||
|
initializeBackAnimation()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun initializeBackAnimation() {
|
||||||
|
backAnimation?.setSwipeThresholds(
|
||||||
|
params.swipeTriggerThreshold,
|
||||||
|
params.swipeProgressThreshold
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onDestroy() {
|
||||||
|
cancelFailsafe()
|
||||||
|
windowManager.removeView(mView)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun setIsLeftPanel(isLeftPanel: Boolean) {
|
||||||
|
mView.isLeftPanel = isLeftPanel
|
||||||
|
layoutParams.gravity = if (isLeftPanel) {
|
||||||
|
Gravity.LEFT or Gravity.TOP
|
||||||
|
} else {
|
||||||
|
Gravity.RIGHT or Gravity.TOP
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun setInsets(insetLeft: Int, insetRight: Int) {
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun setBackCallback(callback: NavigationEdgeBackPlugin.BackCallback) {
|
||||||
|
backCallback = callback
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun setLayoutParams(layoutParams: WindowManager.LayoutParams) {
|
||||||
|
this.layoutParams = layoutParams
|
||||||
|
windowManager.addView(mView, layoutParams)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun isFlung() = velocityTracker!!.run {
|
||||||
|
computeCurrentVelocity(1000)
|
||||||
|
abs(xVelocity) > MIN_FLING_VELOCITY
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun playFlingBackAnimation() {
|
||||||
|
playAnimation(setCommittedEndListener)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun playCommitBackAnimation() {
|
||||||
|
// Check if we should vibrate again
|
||||||
|
if (previousState != GestureState.FLUNG) {
|
||||||
|
backCallback.triggerBack()
|
||||||
|
velocityTracker!!.computeCurrentVelocity(1000)
|
||||||
|
val isSlow = abs(velocityTracker!!.xVelocity) < 500
|
||||||
|
val hasNotVibratedRecently =
|
||||||
|
SystemClock.uptimeMillis() - vibrationTime >= GESTURE_DURATION_FOR_CLICK_MS
|
||||||
|
if (isSlow || hasNotVibratedRecently) {
|
||||||
|
vibratorHelper.vibrate(VibrationEffect.EFFECT_CLICK)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
playAnimation(setGoneEndListener)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun playCancelBackAnimation() {
|
||||||
|
backCallback.cancelBack()
|
||||||
|
playAnimation(setGoneEndListener)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return true if the animation is running, false otherwise. Some transitions don't animate
|
||||||
|
*/
|
||||||
|
private fun playAnimation(endListener: DelayedOnAnimationEndListener) {
|
||||||
|
updateRestingArrowDimens(animated = true, currentState)
|
||||||
|
|
||||||
|
if (!mView.addEndListener(endListener)) {
|
||||||
|
scheduleFailsafe()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun resetOnDown() {
|
||||||
|
hasHapticPlayed = false
|
||||||
|
totalTouchDelta = 0f
|
||||||
|
vibrationTime = 0
|
||||||
|
cancelFailsafe()
|
||||||
|
backAnimation?.setTriggerBack(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun updateYPosition(touchY: Float) {
|
||||||
|
var yPosition = touchY - params.fingerOffset
|
||||||
|
yPosition = Math.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)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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)
|
||||||
|
else ->
|
||||||
|
mView.setArrowStiffness(
|
||||||
|
ARROW_DISAPPEAR_STIFFNESS, ARROW_DISAPPEAR_DAMPING_RATIO)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update arrow state. If state has not changed, this is a no-op.
|
||||||
|
*
|
||||||
|
* Transitioning to active/inactive will indicate whether or not releasing touch will trigger
|
||||||
|
* the back action.
|
||||||
|
*/
|
||||||
|
private fun updateArrowState(newState: GestureState, force: Boolean = false) {
|
||||||
|
if (!force && currentState == newState) return
|
||||||
|
|
||||||
|
if (DEBUG) Log.d(TAG, "updateArrowState $currentState -> $newState")
|
||||||
|
previousState = currentState
|
||||||
|
currentState = newState
|
||||||
|
mView.visibility = if (currentState == GestureState.GONE) View.GONE else View.VISIBLE
|
||||||
|
|
||||||
|
when (currentState) {
|
||||||
|
// Transitioning to GONE never animates since the arrow is (presumably) already off the
|
||||||
|
// screen
|
||||||
|
GestureState.GONE -> updateRestingArrowDimens(animated = false, currentState)
|
||||||
|
GestureState.ENTRY -> {
|
||||||
|
updateYPosition(startY)
|
||||||
|
updateRestingArrowDimens(animated = true, currentState)
|
||||||
|
}
|
||||||
|
GestureState.ACTIVE -> {
|
||||||
|
backAnimation?.setTriggerBack(true)
|
||||||
|
updateRestingArrowDimens(animated = true, currentState)
|
||||||
|
// Vibrate the first time we transition to ACTIVE
|
||||||
|
if (!hasHapticPlayed) {
|
||||||
|
hasHapticPlayed = true
|
||||||
|
vibrationTime = SystemClock.uptimeMillis()
|
||||||
|
vibratorHelper.vibrate(VibrationEffect.EFFECT_TICK)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
GestureState.INACTIVE -> {
|
||||||
|
backAnimation?.setTriggerBack(false)
|
||||||
|
updateRestingArrowDimens(animated = true, currentState)
|
||||||
|
}
|
||||||
|
GestureState.FLUNG -> playFlingBackAnimation()
|
||||||
|
GestureState.COMMITTED -> playCommitBackAnimation()
|
||||||
|
GestureState.CANCELLED -> playCancelBackAnimation()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun scheduleFailsafe() {
|
||||||
|
if (!ENABLE_FAILSAFE) return
|
||||||
|
cancelFailsafe()
|
||||||
|
if (DEBUG) Log.d(TAG, "scheduleFailsafe")
|
||||||
|
mainHandler.postDelayed(failsafeRunnable, FAILSAFE_DELAY_MS)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun cancelFailsafe() {
|
||||||
|
if (DEBUG) Log.d(TAG, "cancelFailsafe")
|
||||||
|
mainHandler.removeCallbacks(failsafeRunnable)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun onFailsafe() {
|
||||||
|
if (DEBUG) Log.d(TAG, "onFailsafe")
|
||||||
|
updateArrowState(GestureState.GONE, force = true)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun dump(pw: PrintWriter) {
|
||||||
|
pw.println("$TAG:")
|
||||||
|
pw.println(" currentState=$currentState")
|
||||||
|
pw.println(" isLeftPanel=$mView.isLeftPanel")
|
||||||
|
}
|
||||||
|
|
||||||
|
init {
|
||||||
|
if (DEBUG) mView.drawDebugInfo = { canvas ->
|
||||||
|
val debugStrings = listOf(
|
||||||
|
"$currentState",
|
||||||
|
"startX=$startX",
|
||||||
|
"startY=$startY",
|
||||||
|
"xDelta=${"%.1f".format(totalTouchDelta)}",
|
||||||
|
"xTranslation=${"%.1f".format(previousXTranslation)}",
|
||||||
|
"pre=${"%.0f".format(preThresholdStretchProgress(previousXTranslation) * 100)}%",
|
||||||
|
"post=${"%.0f".format(fullScreenStretchProgress(previousXTranslation) * 100)}%"
|
||||||
|
)
|
||||||
|
val debugPaint = Paint().apply {
|
||||||
|
color = Color.WHITE
|
||||||
|
}
|
||||||
|
val debugInfoBottom = debugStrings.size * 32f + 4f
|
||||||
|
canvas.drawRect(
|
||||||
|
4f,
|
||||||
|
4f,
|
||||||
|
canvas.width.toFloat(),
|
||||||
|
debugStrings.size * 32f + 4f,
|
||||||
|
debugPaint
|
||||||
|
)
|
||||||
|
debugPaint.apply {
|
||||||
|
color = Color.BLACK
|
||||||
|
textSize = 32f
|
||||||
|
}
|
||||||
|
var offset = 32f
|
||||||
|
for (debugText in debugStrings) {
|
||||||
|
canvas.drawText(debugText, 10f, offset, debugPaint)
|
||||||
|
offset += 32f
|
||||||
|
}
|
||||||
|
debugPaint.apply {
|
||||||
|
color = Color.RED
|
||||||
|
style = Paint.Style.STROKE
|
||||||
|
strokeWidth = 4f
|
||||||
|
}
|
||||||
|
val canvasWidth = canvas.width.toFloat()
|
||||||
|
val canvasHeight = canvas.height.toFloat()
|
||||||
|
canvas.drawRect(0f, 0f, canvasWidth, canvasHeight, debugPaint)
|
||||||
|
|
||||||
|
fun drawVerticalLine(x: Float, color: Int) {
|
||||||
|
debugPaint.color = color
|
||||||
|
val x = if (mView.isLeftPanel) x else canvasWidth - x
|
||||||
|
canvas.drawLine(x, debugInfoBottom, x, canvas.height.toFloat(), debugPaint)
|
||||||
|
}
|
||||||
|
|
||||||
|
drawVerticalLine(x = params.swipeTriggerThreshold, color = Color.BLUE)
|
||||||
|
drawVerticalLine(x = startX, color = Color.GREEN)
|
||||||
|
drawVerticalLine(x = previousXTranslation, color = Color.DKGRAY)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -62,6 +62,8 @@ import com.android.systemui.R;
|
|||||||
import com.android.systemui.SystemUIFactory;
|
import com.android.systemui.SystemUIFactory;
|
||||||
import com.android.systemui.broadcast.BroadcastDispatcher;
|
import com.android.systemui.broadcast.BroadcastDispatcher;
|
||||||
import com.android.systemui.dagger.qualifiers.Main;
|
import com.android.systemui.dagger.qualifiers.Main;
|
||||||
|
import com.android.systemui.flags.FeatureFlags;
|
||||||
|
import com.android.systemui.flags.Flags;
|
||||||
import com.android.systemui.model.SysUiState;
|
import com.android.systemui.model.SysUiState;
|
||||||
import com.android.systemui.navigationbar.NavigationBarView;
|
import com.android.systemui.navigationbar.NavigationBarView;
|
||||||
import com.android.systemui.navigationbar.NavigationModeController;
|
import com.android.systemui.navigationbar.NavigationModeController;
|
||||||
@@ -182,6 +184,7 @@ public class EdgeBackGestureHandler extends CurrentUserTracker
|
|||||||
private final PluginManager mPluginManager;
|
private final PluginManager mPluginManager;
|
||||||
private final ProtoTracer mProtoTracer;
|
private final ProtoTracer mProtoTracer;
|
||||||
private final NavigationModeController mNavigationModeController;
|
private final NavigationModeController mNavigationModeController;
|
||||||
|
private final BackPanelController.Factory mBackPanelControllerFactory;
|
||||||
private final ViewConfiguration mViewConfiguration;
|
private final ViewConfiguration mViewConfiguration;
|
||||||
private final WindowManager mWindowManager;
|
private final WindowManager mWindowManager;
|
||||||
private final IWindowManager mWindowManagerService;
|
private final IWindowManager mWindowManagerService;
|
||||||
@@ -199,6 +202,7 @@ public class EdgeBackGestureHandler extends CurrentUserTracker
|
|||||||
private final Region mExcludeRegion = new Region();
|
private final Region mExcludeRegion = new Region();
|
||||||
private final Region mUnrestrictedExcludeRegion = new Region();
|
private final Region mUnrestrictedExcludeRegion = new Region();
|
||||||
private final LatencyTracker mLatencyTracker;
|
private final LatencyTracker mLatencyTracker;
|
||||||
|
private final FeatureFlags mFeatureFlags;
|
||||||
|
|
||||||
// The left side edge width where touch down is allowed
|
// The left side edge width where touch down is allowed
|
||||||
private int mEdgeWidthLeft;
|
private int mEdgeWidthLeft;
|
||||||
@@ -230,6 +234,7 @@ public class EdgeBackGestureHandler extends CurrentUserTracker
|
|||||||
private boolean mIsBackGestureAllowed;
|
private boolean mIsBackGestureAllowed;
|
||||||
private boolean mGestureBlockingActivityRunning;
|
private boolean mGestureBlockingActivityRunning;
|
||||||
private boolean mIsInPipMode;
|
private boolean mIsInPipMode;
|
||||||
|
private boolean mIsPredictiveBackAnimEnabled;
|
||||||
|
|
||||||
private InputMonitor mInputMonitor;
|
private InputMonitor mInputMonitor;
|
||||||
private InputChannelCompat.InputEventReceiver mInputEventReceiver;
|
private InputChannelCompat.InputEventReceiver mInputEventReceiver;
|
||||||
@@ -298,12 +303,22 @@ public class EdgeBackGestureHandler extends CurrentUserTracker
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
EdgeBackGestureHandler(Context context, OverviewProxyService overviewProxyService,
|
EdgeBackGestureHandler(
|
||||||
SysUiState sysUiState, PluginManager pluginManager, @Main Executor executor,
|
Context context,
|
||||||
BroadcastDispatcher broadcastDispatcher, ProtoTracer protoTracer,
|
OverviewProxyService overviewProxyService,
|
||||||
NavigationModeController navigationModeController, ViewConfiguration viewConfiguration,
|
SysUiState sysUiState,
|
||||||
WindowManager windowManager, IWindowManager windowManagerService,
|
PluginManager pluginManager,
|
||||||
FalsingManager falsingManager, LatencyTracker latencyTracker) {
|
@Main Executor executor,
|
||||||
|
BroadcastDispatcher broadcastDispatcher,
|
||||||
|
ProtoTracer protoTracer,
|
||||||
|
NavigationModeController navigationModeController,
|
||||||
|
BackPanelController.Factory backPanelControllerFactory,
|
||||||
|
ViewConfiguration viewConfiguration,
|
||||||
|
WindowManager windowManager,
|
||||||
|
IWindowManager windowManagerService,
|
||||||
|
FalsingManager falsingManager,
|
||||||
|
LatencyTracker latencyTracker,
|
||||||
|
FeatureFlags featureFlags) {
|
||||||
super(broadcastDispatcher);
|
super(broadcastDispatcher);
|
||||||
mContext = context;
|
mContext = context;
|
||||||
mDisplayId = context.getDisplayId();
|
mDisplayId = context.getDisplayId();
|
||||||
@@ -313,11 +328,13 @@ public class EdgeBackGestureHandler extends CurrentUserTracker
|
|||||||
mPluginManager = pluginManager;
|
mPluginManager = pluginManager;
|
||||||
mProtoTracer = protoTracer;
|
mProtoTracer = protoTracer;
|
||||||
mNavigationModeController = navigationModeController;
|
mNavigationModeController = navigationModeController;
|
||||||
|
mBackPanelControllerFactory = backPanelControllerFactory;
|
||||||
mViewConfiguration = viewConfiguration;
|
mViewConfiguration = viewConfiguration;
|
||||||
mWindowManager = windowManager;
|
mWindowManager = windowManager;
|
||||||
mWindowManagerService = windowManagerService;
|
mWindowManagerService = windowManagerService;
|
||||||
mFalsingManager = falsingManager;
|
mFalsingManager = falsingManager;
|
||||||
mLatencyTracker = latencyTracker;
|
mLatencyTracker = latencyTracker;
|
||||||
|
mFeatureFlags = featureFlags;
|
||||||
ComponentName recentsComponentName = ComponentName.unflattenFromString(
|
ComponentName recentsComponentName = ComponentName.unflattenFromString(
|
||||||
context.getString(com.android.internal.R.string.config_recentsComponentName));
|
context.getString(com.android.internal.R.string.config_recentsComponentName));
|
||||||
if (recentsComponentName != null) {
|
if (recentsComponentName != null) {
|
||||||
@@ -507,8 +524,9 @@ public class EdgeBackGestureHandler extends CurrentUserTracker
|
|||||||
Choreographer.getInstance(), this::onInputEvent);
|
Choreographer.getInstance(), this::onInputEvent);
|
||||||
|
|
||||||
// Add a nav bar panel window
|
// Add a nav bar panel window
|
||||||
setEdgeBackPlugin(
|
mIsPredictiveBackAnimEnabled =
|
||||||
new NavigationBarEdgePanel(mContext, mBackAnimation, mLatencyTracker));
|
mFeatureFlags.isEnabled(Flags.WM_ENABLE_PREDICTIVE_BACK_ANIM);
|
||||||
|
resetEdgeBackPlugin();
|
||||||
mPluginManager.addPluginListener(
|
mPluginManager.addPluginListener(
|
||||||
this, NavigationEdgeBackPlugin.class, /*allowMultiple=*/ false);
|
this, NavigationEdgeBackPlugin.class, /*allowMultiple=*/ false);
|
||||||
}
|
}
|
||||||
@@ -523,7 +541,17 @@ public class EdgeBackGestureHandler extends CurrentUserTracker
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onPluginDisconnected(NavigationEdgeBackPlugin plugin) {
|
public void onPluginDisconnected(NavigationEdgeBackPlugin plugin) {
|
||||||
setEdgeBackPlugin(new NavigationBarEdgePanel(mContext, mBackAnimation, mLatencyTracker));
|
resetEdgeBackPlugin();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void resetEdgeBackPlugin() {
|
||||||
|
if (mIsPredictiveBackAnimEnabled) {
|
||||||
|
setEdgeBackPlugin(
|
||||||
|
mBackPanelControllerFactory.create(mContext, mBackAnimation));
|
||||||
|
} else {
|
||||||
|
setEdgeBackPlugin(
|
||||||
|
new NavigationBarEdgePanel(mContext, mBackAnimation, mLatencyTracker));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setEdgeBackPlugin(NavigationEdgeBackPlugin edgeBackPlugin) {
|
private void setEdgeBackPlugin(NavigationEdgeBackPlugin edgeBackPlugin) {
|
||||||
@@ -948,8 +976,12 @@ public class EdgeBackGestureHandler extends CurrentUserTracker
|
|||||||
|
|
||||||
public void setBackAnimation(BackAnimation backAnimation) {
|
public void setBackAnimation(BackAnimation backAnimation) {
|
||||||
mBackAnimation = backAnimation;
|
mBackAnimation = backAnimation;
|
||||||
if (mEdgeBackPlugin != null && mEdgeBackPlugin instanceof NavigationBarEdgePanel) {
|
if (mEdgeBackPlugin != null) {
|
||||||
((NavigationBarEdgePanel) mEdgeBackPlugin).setBackAnimation(backAnimation);
|
if (mEdgeBackPlugin instanceof NavigationBarEdgePanel) {
|
||||||
|
((NavigationBarEdgePanel) mEdgeBackPlugin).setBackAnimation(backAnimation);
|
||||||
|
} else if (mEdgeBackPlugin instanceof BackPanelController) {
|
||||||
|
((BackPanelController) mEdgeBackPlugin).setBackAnimation(backAnimation);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -967,20 +999,29 @@ public class EdgeBackGestureHandler extends CurrentUserTracker
|
|||||||
private final BroadcastDispatcher mBroadcastDispatcher;
|
private final BroadcastDispatcher mBroadcastDispatcher;
|
||||||
private final ProtoTracer mProtoTracer;
|
private final ProtoTracer mProtoTracer;
|
||||||
private final NavigationModeController mNavigationModeController;
|
private final NavigationModeController mNavigationModeController;
|
||||||
|
private final BackPanelController.Factory mBackPanelControllerFactory;
|
||||||
private final ViewConfiguration mViewConfiguration;
|
private final ViewConfiguration mViewConfiguration;
|
||||||
private final WindowManager mWindowManager;
|
private final WindowManager mWindowManager;
|
||||||
private final IWindowManager mWindowManagerService;
|
private final IWindowManager mWindowManagerService;
|
||||||
private final FalsingManager mFalsingManager;
|
private final FalsingManager mFalsingManager;
|
||||||
private final LatencyTracker mLatencyTracker;
|
private final LatencyTracker mLatencyTracker;
|
||||||
|
private final FeatureFlags mFeatureFlags;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public Factory(OverviewProxyService overviewProxyService,
|
public Factory(OverviewProxyService overviewProxyService,
|
||||||
SysUiState sysUiState, PluginManager pluginManager, @Main Executor executor,
|
SysUiState sysUiState,
|
||||||
BroadcastDispatcher broadcastDispatcher, ProtoTracer protoTracer,
|
PluginManager pluginManager,
|
||||||
NavigationModeController navigationModeController,
|
@Main Executor executor,
|
||||||
ViewConfiguration viewConfiguration, WindowManager windowManager,
|
BroadcastDispatcher broadcastDispatcher,
|
||||||
IWindowManager windowManagerService, FalsingManager falsingManager,
|
ProtoTracer protoTracer,
|
||||||
LatencyTracker latencyTracker) {
|
NavigationModeController navigationModeController,
|
||||||
|
BackPanelController.Factory backPanelControllerFactory,
|
||||||
|
ViewConfiguration viewConfiguration,
|
||||||
|
WindowManager windowManager,
|
||||||
|
IWindowManager windowManagerService,
|
||||||
|
FalsingManager falsingManager,
|
||||||
|
LatencyTracker latencyTracker,
|
||||||
|
FeatureFlags featureFlags) {
|
||||||
mOverviewProxyService = overviewProxyService;
|
mOverviewProxyService = overviewProxyService;
|
||||||
mSysUiState = sysUiState;
|
mSysUiState = sysUiState;
|
||||||
mPluginManager = pluginManager;
|
mPluginManager = pluginManager;
|
||||||
@@ -988,19 +1029,33 @@ public class EdgeBackGestureHandler extends CurrentUserTracker
|
|||||||
mBroadcastDispatcher = broadcastDispatcher;
|
mBroadcastDispatcher = broadcastDispatcher;
|
||||||
mProtoTracer = protoTracer;
|
mProtoTracer = protoTracer;
|
||||||
mNavigationModeController = navigationModeController;
|
mNavigationModeController = navigationModeController;
|
||||||
|
mBackPanelControllerFactory = backPanelControllerFactory;
|
||||||
mViewConfiguration = viewConfiguration;
|
mViewConfiguration = viewConfiguration;
|
||||||
mWindowManager = windowManager;
|
mWindowManager = windowManager;
|
||||||
mWindowManagerService = windowManagerService;
|
mWindowManagerService = windowManagerService;
|
||||||
mFalsingManager = falsingManager;
|
mFalsingManager = falsingManager;
|
||||||
mLatencyTracker = latencyTracker;
|
mLatencyTracker = latencyTracker;
|
||||||
|
mFeatureFlags = featureFlags;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Construct a {@link EdgeBackGestureHandler}. */
|
/** Construct a {@link EdgeBackGestureHandler}. */
|
||||||
public EdgeBackGestureHandler create(Context context) {
|
public EdgeBackGestureHandler create(Context context) {
|
||||||
return new EdgeBackGestureHandler(context, mOverviewProxyService, mSysUiState,
|
return new EdgeBackGestureHandler(
|
||||||
mPluginManager, mExecutor, mBroadcastDispatcher, mProtoTracer,
|
context,
|
||||||
mNavigationModeController, mViewConfiguration, mWindowManager,
|
mOverviewProxyService,
|
||||||
mWindowManagerService, mFalsingManager, mLatencyTracker);
|
mSysUiState,
|
||||||
|
mPluginManager,
|
||||||
|
mExecutor,
|
||||||
|
mBroadcastDispatcher,
|
||||||
|
mProtoTracer,
|
||||||
|
mNavigationModeController,
|
||||||
|
mBackPanelControllerFactory,
|
||||||
|
mViewConfiguration,
|
||||||
|
mWindowManager,
|
||||||
|
mWindowManagerService,
|
||||||
|
mFalsingManager,
|
||||||
|
mLatencyTracker,
|
||||||
|
mFeatureFlags);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,139 @@
|
|||||||
|
package com.android.systemui.navigationbar.gestural
|
||||||
|
|
||||||
|
import android.content.res.Resources
|
||||||
|
import com.android.systemui.R
|
||||||
|
|
||||||
|
data class EdgePanelParams(private var resources: Resources) {
|
||||||
|
var arrowThickness: Float = 0f
|
||||||
|
private set
|
||||||
|
var entryArrowLength: Float = 0f
|
||||||
|
private set
|
||||||
|
var entryArrowHeight: Float = 0f
|
||||||
|
private set
|
||||||
|
var activeArrowLength: Float = 0f
|
||||||
|
private set
|
||||||
|
var activeArrowHeight: Float = 0f
|
||||||
|
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
|
||||||
|
|
||||||
|
// navigation bar edge constants
|
||||||
|
var arrowPaddingEnd: Int = 0
|
||||||
|
private set
|
||||||
|
|
||||||
|
// The closest to y
|
||||||
|
var minArrowYPosition: Int = 0
|
||||||
|
private set
|
||||||
|
var fingerOffset: Int = 0
|
||||||
|
private set
|
||||||
|
var swipeTriggerThreshold: Float = 0f
|
||||||
|
private set
|
||||||
|
var swipeProgressThreshold: Float = 0f
|
||||||
|
private set
|
||||||
|
|
||||||
|
// The minimum delta needed to change direction / stop triggering back
|
||||||
|
var minDeltaForSwitch: Int = 0
|
||||||
|
private set
|
||||||
|
|
||||||
|
init {
|
||||||
|
update(resources)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getDimen(id: Int): Float {
|
||||||
|
return resources.getDimension(id)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getPx(id: Int): Int {
|
||||||
|
return resources.getDimensionPixelSize(id)
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user