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) {
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user