Merge "Long-press not required for mouse or stylus." into tm-qpr-dev
This commit is contained in:
@@ -381,82 +381,87 @@ object KeyguardBottomAreaViewBinder {
|
||||
return when (event?.actionMasked) {
|
||||
MotionEvent.ACTION_DOWN ->
|
||||
if (viewModel.configKey != null) {
|
||||
longPressAnimator =
|
||||
view
|
||||
.animate()
|
||||
.scaleX(PRESSED_SCALE)
|
||||
.scaleY(PRESSED_SCALE)
|
||||
.setDuration(longPressDurationMs)
|
||||
.withEndAction {
|
||||
view.setOnClickListener {
|
||||
vibratorHelper?.vibrate(
|
||||
if (viewModel.isActivated) {
|
||||
Vibrations.Activated
|
||||
} else {
|
||||
Vibrations.Deactivated
|
||||
}
|
||||
)
|
||||
viewModel.onClicked(
|
||||
KeyguardQuickAffordanceViewModel.OnClickedParameters(
|
||||
configKey = viewModel.configKey,
|
||||
expandable = Expandable.fromView(view),
|
||||
)
|
||||
)
|
||||
if (isUsingAccurateTool(event)) {
|
||||
// For accurate tool types (stylus, mouse, etc.), we don't require a
|
||||
// long-press.
|
||||
} else {
|
||||
// When not using a stylus, we require a long-press to activate the
|
||||
// quick affordance, mostly to do "falsing" (e.g. protect from false
|
||||
// clicks in the pocket/bag).
|
||||
longPressAnimator =
|
||||
view
|
||||
.animate()
|
||||
.scaleX(PRESSED_SCALE)
|
||||
.scaleY(PRESSED_SCALE)
|
||||
.setDuration(longPressDurationMs)
|
||||
.withEndAction {
|
||||
dispatchClick(viewModel.configKey)
|
||||
cancel()
|
||||
}
|
||||
view.performClick()
|
||||
view.setOnClickListener(null)
|
||||
cancel()
|
||||
}
|
||||
}
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
MotionEvent.ACTION_MOVE -> {
|
||||
if (event.historySize > 0) {
|
||||
val distance =
|
||||
sqrt(
|
||||
(event.y - event.getHistoricalY(0)).pow(2) +
|
||||
(event.x - event.getHistoricalX(0)).pow(2)
|
||||
)
|
||||
if (distance > ViewConfiguration.getTouchSlop()) {
|
||||
if (!isUsingAccurateTool(event)) {
|
||||
// Moving too far while performing a long-press gesture cancels that
|
||||
// gesture.
|
||||
val distanceMoved = distanceMoved(event)
|
||||
if (distanceMoved > ViewConfiguration.getTouchSlop()) {
|
||||
cancel()
|
||||
}
|
||||
}
|
||||
true
|
||||
}
|
||||
MotionEvent.ACTION_UP -> {
|
||||
cancel(
|
||||
onAnimationEnd =
|
||||
if (event.eventTime - event.downTime < longPressDurationMs) {
|
||||
Runnable {
|
||||
messageDisplayer.invoke(
|
||||
R.string.keyguard_affordance_press_too_short
|
||||
)
|
||||
val amplitude =
|
||||
view.context.resources
|
||||
.getDimensionPixelSize(
|
||||
R.dimen.keyguard_affordance_shake_amplitude
|
||||
)
|
||||
.toFloat()
|
||||
val shakeAnimator =
|
||||
ObjectAnimator.ofFloat(
|
||||
view,
|
||||
"translationX",
|
||||
-amplitude / 2,
|
||||
amplitude / 2,
|
||||
if (isUsingAccurateTool(event)) {
|
||||
// When using an accurate tool type (stylus, mouse, etc.), we don't require
|
||||
// a long-press gesture to activate the quick affordance. Therefore, lifting
|
||||
// the pointer performs a click.
|
||||
if (
|
||||
viewModel.configKey != null &&
|
||||
distanceMoved(event) <= ViewConfiguration.getTouchSlop()
|
||||
) {
|
||||
dispatchClick(viewModel.configKey)
|
||||
}
|
||||
} else {
|
||||
// When not using a stylus, lifting the finger/pointer will actually cancel
|
||||
// the long-press gesture. Calling cancel after the quick affordance was
|
||||
// already long-press activated is a no-op, so it's safe to call from here.
|
||||
cancel(
|
||||
onAnimationEnd =
|
||||
if (event.eventTime - event.downTime < longPressDurationMs) {
|
||||
Runnable {
|
||||
messageDisplayer.invoke(
|
||||
R.string.keyguard_affordance_press_too_short
|
||||
)
|
||||
shakeAnimator.duration =
|
||||
ShakeAnimationDuration.inWholeMilliseconds
|
||||
shakeAnimator.interpolator =
|
||||
CycleInterpolator(ShakeAnimationCycles)
|
||||
shakeAnimator.start()
|
||||
val amplitude =
|
||||
view.context.resources
|
||||
.getDimensionPixelSize(
|
||||
R.dimen.keyguard_affordance_shake_amplitude
|
||||
)
|
||||
.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
|
||||
}
|
||||
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) {
|
||||
longPressAnimator?.cancel()
|
||||
longPressAnimator = null
|
||||
@@ -475,6 +502,40 @@ object KeyguardBottomAreaViewBinder {
|
||||
|
||||
companion object {
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user