Merge "Long-press not required for mouse or stylus." into tm-qpr-dev

This commit is contained in:
Ale Nijamkin
2023-02-03 20:22:01 +00:00
committed by Android (Google) Code Review

View File

@@ -381,82 +381,87 @@ object KeyguardBottomAreaViewBinder {
return when (event?.actionMasked) { return when (event?.actionMasked) {
MotionEvent.ACTION_DOWN -> MotionEvent.ACTION_DOWN ->
if (viewModel.configKey != null) { if (viewModel.configKey != null) {
longPressAnimator = if (isUsingAccurateTool(event)) {
view // For accurate tool types (stylus, mouse, etc.), we don't require a
.animate() // long-press.
.scaleX(PRESSED_SCALE) } else {
.scaleY(PRESSED_SCALE) // When not using a stylus, we require a long-press to activate the
.setDuration(longPressDurationMs) // quick affordance, mostly to do "falsing" (e.g. protect from false
.withEndAction { // clicks in the pocket/bag).
view.setOnClickListener { longPressAnimator =
vibratorHelper?.vibrate( view
if (viewModel.isActivated) { .animate()
Vibrations.Activated .scaleX(PRESSED_SCALE)
} else { .scaleY(PRESSED_SCALE)
Vibrations.Deactivated .setDuration(longPressDurationMs)
} .withEndAction {
) dispatchClick(viewModel.configKey)
viewModel.onClicked( cancel()
KeyguardQuickAffordanceViewModel.OnClickedParameters(
configKey = viewModel.configKey,
expandable = Expandable.fromView(view),
)
)
} }
view.performClick() }
view.setOnClickListener(null)
cancel()
}
true true
} else { } else {
false false
} }
MotionEvent.ACTION_MOVE -> { MotionEvent.ACTION_MOVE -> {
if (event.historySize > 0) { if (!isUsingAccurateTool(event)) {
val distance = // Moving too far while performing a long-press gesture cancels that
sqrt( // gesture.
(event.y - event.getHistoricalY(0)).pow(2) + val distanceMoved = distanceMoved(event)
(event.x - event.getHistoricalX(0)).pow(2) if (distanceMoved > ViewConfiguration.getTouchSlop()) {
)
if (distance > ViewConfiguration.getTouchSlop()) {
cancel() cancel()
} }
} }
true true
} }
MotionEvent.ACTION_UP -> { MotionEvent.ACTION_UP -> {
cancel( if (isUsingAccurateTool(event)) {
onAnimationEnd = // When using an accurate tool type (stylus, mouse, etc.), we don't require
if (event.eventTime - event.downTime < longPressDurationMs) { // a long-press gesture to activate the quick affordance. Therefore, lifting
Runnable { // the pointer performs a click.
messageDisplayer.invoke( if (
R.string.keyguard_affordance_press_too_short viewModel.configKey != null &&
) distanceMoved(event) <= ViewConfiguration.getTouchSlop()
val amplitude = ) {
view.context.resources dispatchClick(viewModel.configKey)
.getDimensionPixelSize( }
R.dimen.keyguard_affordance_shake_amplitude } else {
) // When not using a stylus, lifting the finger/pointer will actually cancel
.toFloat() // the long-press gesture. Calling cancel after the quick affordance was
val shakeAnimator = // already long-press activated is a no-op, so it's safe to call from here.
ObjectAnimator.ofFloat( cancel(
view, onAnimationEnd =
"translationX", if (event.eventTime - event.downTime < longPressDurationMs) {
-amplitude / 2, Runnable {
amplitude / 2, messageDisplayer.invoke(
R.string.keyguard_affordance_press_too_short
) )
shakeAnimator.duration = val amplitude =
ShakeAnimationDuration.inWholeMilliseconds view.context.resources
shakeAnimator.interpolator = .getDimensionPixelSize(
CycleInterpolator(ShakeAnimationCycles) R.dimen.keyguard_affordance_shake_amplitude
shakeAnimator.start() )
.toFloat()
val shakeAnimator =
ObjectAnimator.ofFloat(
view,
"translationX",
-amplitude / 2,
amplitude / 2,
)
shakeAnimator.duration =
ShakeAnimationDuration.inWholeMilliseconds
shakeAnimator.interpolator =
CycleInterpolator(ShakeAnimationCycles)
shakeAnimator.start()
vibratorHelper?.vibrate(Vibrations.Shake) vibratorHelper?.vibrate(Vibrations.Shake)
}
} else {
null
} }
} else { )
null }
}
)
true true
} }
MotionEvent.ACTION_CANCEL -> { MotionEvent.ACTION_CANCEL -> {
@@ -467,6 +472,28 @@ object KeyguardBottomAreaViewBinder {
} }
} }
private fun dispatchClick(
configKey: String,
) {
view.setOnClickListener {
vibratorHelper?.vibrate(
if (viewModel.isActivated) {
Vibrations.Activated
} else {
Vibrations.Deactivated
}
)
viewModel.onClicked(
KeyguardQuickAffordanceViewModel.OnClickedParameters(
configKey = configKey,
expandable = Expandable.fromView(view),
)
)
}
view.performClick()
view.setOnClickListener(null)
}
private fun cancel(onAnimationEnd: Runnable? = null) { private fun cancel(onAnimationEnd: Runnable? = null) {
longPressAnimator?.cancel() longPressAnimator?.cancel()
longPressAnimator = null longPressAnimator = null
@@ -475,6 +502,40 @@ object KeyguardBottomAreaViewBinder {
companion object { companion object {
private const val PRESSED_SCALE = 1.5f private const val PRESSED_SCALE = 1.5f
/**
* Returns `true` if the tool type at the given pointer index is an accurate tool (like
* stylus or mouse), which means we can trust it to not be a false click; `false`
* otherwise.
*/
private fun isUsingAccurateTool(
event: MotionEvent,
pointerIndex: Int = 0,
): Boolean {
return when (event.getToolType(pointerIndex)) {
MotionEvent.TOOL_TYPE_STYLUS -> true
MotionEvent.TOOL_TYPE_MOUSE -> true
else -> false
}
}
/**
* Returns the amount of distance the pointer moved since the historical record at the
* [since] index.
*/
private fun distanceMoved(
event: MotionEvent,
since: Int = 0,
): Float {
return if (event.historySize > 0) {
sqrt(
(event.y - event.getHistoricalY(since)).pow(2) +
(event.x - event.getHistoricalX(since)).pow(2)
)
} else {
0f
}
}
} }
} }