Skip to content

Commit f748e81

Browse files
authored
[Android] Fix mouse interactions (#4265)
## Description #3850 restricted dispatching of generic motion events to only hover events. This caused a regression in which mouse events stopped being dispatched to handlers. This PR allows mouse events to be dispatched to handlers and adds check for mouse button press in Long Press so it can activate timeout. It also removes check for obsolete SDK version and inverts logic of skipping events - `shouldActivateWithMouse` wasn't very descriptive (especially in `onHandle`), so I renamed it to `shouldSkipEvent` instead. Fixes #3889 ## Test plan Tested on mouse buttons and Pressable examples
1 parent 1473f39 commit f748e81

7 files changed

Lines changed: 41 additions & 42 deletions

File tree

packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/core/FlingGestureHandler.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ class FlingGestureHandler : GestureHandler() {
8888
}
8989

9090
override fun onHandle(event: MotionEvent, sourceEvent: MotionEvent) {
91-
if (!shouldActivateWithMouse(sourceEvent)) {
91+
if (shouldSkipEvent(sourceEvent)) {
9292
return
9393
}
9494

packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/core/GestureHandler.kt

Lines changed: 24 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import android.app.Activity
44
import android.content.Context
55
import android.content.ContextWrapper
66
import android.graphics.PointF
7-
import android.os.Build
87
import android.view.MotionEvent
98
import android.view.MotionEvent.PointerCoords
109
import android.view.MotionEvent.PointerProperties
@@ -823,44 +822,38 @@ open class GestureHandler {
823822
return clickedButton and mouseButton != 0
824823
}
825824

826-
protected fun shouldActivateWithMouse(sourceEvent: MotionEvent): Boolean {
827-
// While using mouse, we get both sets of events, for example ACTION_DOWN and ACTION_BUTTON_PRESS. That's why we want to take actions to only one of them.
828-
// On API >= 23, we will use events with infix BUTTON, otherwise we use standard action events (like ACTION_DOWN).
825+
// Decides whether the gesture should ignore this event. While using a mouse we receive both the
826+
// touch-compatible stream (ACTION_DOWN/UP/...) and the BUTTON_* events, so we act on only the
827+
// latter, and we drop events coming from a button other than the configured `mouseButton`.
828+
// Non-mouse input is never skipped here.
829+
protected fun shouldSkipEvent(sourceEvent: MotionEvent): Boolean {
830+
if (sourceEvent.getToolType(0) != MotionEvent.TOOL_TYPE_MOUSE) {
831+
return false
832+
}
829833

830834
with(sourceEvent) {
831-
// To use actionButton, we need API >= 23.
832-
if (getToolType(0) == MotionEvent.TOOL_TYPE_MOUSE &&
833-
Build.VERSION.SDK_INT >= Build.VERSION_CODES.M
835+
// While using mouse, we want to ignore default events for touch.
836+
if (actionMasked == MotionEvent.ACTION_DOWN ||
837+
actionMasked == MotionEvent.ACTION_UP ||
838+
actionMasked == MotionEvent.ACTION_POINTER_UP ||
839+
actionMasked == MotionEvent.ACTION_POINTER_DOWN
834840
) {
835-
// While using mouse, we want to ignore default events for touch.
836-
if (action == MotionEvent.ACTION_DOWN ||
837-
action == MotionEvent.ACTION_UP ||
838-
action == MotionEvent.ACTION_POINTER_UP ||
839-
action == MotionEvent.ACTION_POINTER_DOWN
840-
) {
841-
return@shouldActivateWithMouse false
842-
}
841+
return@shouldSkipEvent true
842+
}
843843

844-
// We don't want to do anything if wrong button was clicked. If we received event for BUTTON, we have to use actionButton to get which one was clicked.
845-
if (action != MotionEvent.ACTION_MOVE && !isButtonInConfig(actionButton)) {
846-
return@shouldActivateWithMouse false
847-
}
844+
// Skip events from a button other than the configured one. For BUTTON_* events the clicked
845+
// button is read from `actionButton`.
846+
if (actionMasked != MotionEvent.ACTION_MOVE && !isButtonInConfig(actionButton)) {
847+
return@shouldSkipEvent true
848+
}
848849

849-
// When we receive ACTION_MOVE, we have to check buttonState field.
850-
if (action == MotionEvent.ACTION_MOVE && !isButtonInConfig(buttonState)) {
851-
return@shouldActivateWithMouse false
852-
}
853-
} else if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
854-
// We do not fully support mouse below API 23, so we will ignore BUTTON events.
855-
if (action == MotionEvent.ACTION_BUTTON_PRESS ||
856-
action == MotionEvent.ACTION_BUTTON_RELEASE
857-
) {
858-
return@shouldActivateWithMouse false
859-
}
850+
// For ACTION_MOVE the pressed button is read from `buttonState`.
851+
if (actionMasked == MotionEvent.ACTION_MOVE && !isButtonInConfig(buttonState)) {
852+
return@shouldSkipEvent true
860853
}
861854
}
862855

863-
return true
856+
return false
864857
}
865858

866859
/**

packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/core/LongPressGestureHandler.kt

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ class LongPressGestureHandler(context: Context) : GestureHandler() {
7171
}
7272

7373
override fun onHandle(event: MotionEvent, sourceEvent: MotionEvent) {
74-
if (!shouldActivateWithMouse(sourceEvent)) {
74+
if (shouldSkipEvent(sourceEvent)) {
7575
return
7676
}
7777

@@ -108,7 +108,8 @@ class LongPressGestureHandler(context: Context) : GestureHandler() {
108108
currentPointers == numberOfPointersRequired &&
109109
(
110110
sourceEvent.actionMasked == MotionEvent.ACTION_DOWN ||
111-
sourceEvent.actionMasked == MotionEvent.ACTION_POINTER_DOWN
111+
sourceEvent.actionMasked == MotionEvent.ACTION_POINTER_DOWN ||
112+
sourceEvent.actionMasked == MotionEvent.ACTION_BUTTON_PRESS
112113
)
113114
) {
114115
handler = Handler(Looper.getMainLooper())

packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/core/PanGestureHandler.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,7 @@ class PanGestureHandler(context: Context?) : GestureHandler() {
160160
}
161161

162162
override fun onHandle(event: MotionEvent, sourceEvent: MotionEvent) {
163-
if (!shouldActivateWithMouse(sourceEvent)) {
163+
if (shouldSkipEvent(sourceEvent)) {
164164
return
165165
}
166166

packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/core/TapGestureHandler.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ class TapGestureHandler : GestureHandler() {
8989
}
9090

9191
override fun onHandle(event: MotionEvent, sourceEvent: MotionEvent) {
92-
if (!shouldActivateWithMouse(sourceEvent)) {
92+
if (shouldSkipEvent(sourceEvent)) {
9393
return
9494
}
9595

packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/Extensions.kt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@ fun MotionEvent.isHoverAction(): Boolean = action == MotionEvent.ACTION_HOVER_MO
1717
action == MotionEvent.ACTION_HOVER_ENTER ||
1818
action == MotionEvent.ACTION_HOVER_EXIT
1919

20+
fun MotionEvent.isButtonAction(): Boolean = actionMasked == MotionEvent.ACTION_BUTTON_PRESS ||
21+
actionMasked == MotionEvent.ACTION_BUTTON_RELEASE
22+
2023
val Display.minimumFrameTime: Float
2124
get() {
2225
val supportedModes = this.supportedModes

packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerRootView.kt

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -62,12 +62,14 @@ class RNGestureHandlerRootView(context: Context?) : ReactViewGroup(context) {
6262
}
6363
}
6464

65-
override fun dispatchGenericMotionEvent(ev: MotionEvent) =
66-
if (rootViewEnabled && ev.isHoverAction() && rootHelper!!.dispatchTouchEvent(ev)) {
67-
true
68-
} else {
69-
super.dispatchGenericMotionEvent(ev)
70-
}
65+
override fun dispatchGenericMotionEvent(ev: MotionEvent) = if (rootViewEnabled &&
66+
(ev.isHoverAction() || ev.isButtonAction()) &&
67+
rootHelper!!.dispatchTouchEvent(ev)
68+
) {
69+
true
70+
} else {
71+
super.dispatchGenericMotionEvent(ev)
72+
}
7173

7274
override fun requestDisallowInterceptTouchEvent(disallowIntercept: Boolean) {
7375
if (rootViewEnabled) {

0 commit comments

Comments
 (0)