Skip to content

Commit f6b3f5f

Browse files
authored
[Android] Handle hover events in RNGestureHandlerRootHelper (#3462)
## Description As stated in [this comment](#3407 (comment)), `TalkBack` uses `Hover` events. The fact that they were not handled in `RNGestureHandlerRootHelper` resulted in buttons being unresponsive after other gesture had been activated. Fixes #3407 ## Test plan Tested on the reproduction code from issue
1 parent f6aec4c commit f6b3f5f

File tree

2 files changed

+21
-5
lines changed

2 files changed

+21
-5
lines changed

android/src/main/java/com/swmansion/gesturehandler/core/GestureHandlerOrchestrator.kt

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@ import android.view.MotionEvent
66
import android.view.View
77
import android.view.ViewGroup
88
import android.widget.EditText
9+
import com.swmansion.gesturehandler.react.RNGestureHandlerRootHelper
910
import java.util.*
10-
import kotlin.collections.HashSet
1111

1212
class GestureHandlerOrchestrator(
1313
private val wrapperView: ViewGroup,
@@ -470,6 +470,17 @@ class GestureHandlerOrchestrator(
470470
return found
471471
}
472472

473+
// We don't want to extract gestures other than hover when processing hover events.
474+
// There's only one exception - RootViewGestureHandler. TalkBack uses hover events,
475+
// so we need to pass them into RootViewGestureHandler, otherwise press and hold
476+
// gesture stops working correctly (see https://github.com/software-mansion/react-native-gesture-handler/issues/3407)
477+
private fun shouldHandlerSkipHoverEvents(handler: GestureHandler<*>, action: Int): Boolean {
478+
val shouldSkipHoverEvents =
479+
handler !is HoverGestureHandler && handler !is RNGestureHandlerRootHelper.RootViewGestureHandler
480+
481+
return shouldSkipHoverEvents && action in listOf(MotionEvent.ACTION_HOVER_EXIT, MotionEvent.ACTION_HOVER_ENTER, MotionEvent.ACTION_HOVER_MOVE)
482+
}
483+
473484
private fun recordViewHandlersForPointer(view: View, coords: FloatArray, pointerId: Int, event: MotionEvent): Boolean {
474485
var found = false
475486
handlerRegistry.getHandlersForView(view)?.let {
@@ -480,8 +491,7 @@ class GestureHandlerOrchestrator(
480491
continue
481492
}
482493

483-
// we don't want to extract gestures other than hover when processing hover events
484-
if (event.action in listOf(MotionEvent.ACTION_HOVER_EXIT, MotionEvent.ACTION_HOVER_ENTER, MotionEvent.ACTION_HOVER_MOVE) && handler !is HoverGestureHandler) {
494+
if (shouldHandlerSkipHoverEvents(handler, event.action)) {
485495
continue
486496
}
487497

android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerRootHelper.kt

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,19 +58,25 @@ class RNGestureHandlerRootHelper(private val context: ReactContext, wrappedView:
5858
}
5959

6060
internal inner class RootViewGestureHandler : GestureHandler<RootViewGestureHandler>() {
61-
override fun onHandle(event: MotionEvent, sourceEvent: MotionEvent) {
61+
private fun handleEvent(event: MotionEvent) {
6262
val currentState = state
63+
6364
// we shouldn't stop intercepting events when there is an active handler already, which could happen when
6465
// adding a new pointer to the screen after a handler activates
6566
if (currentState == STATE_UNDETERMINED && (!shouldIntercept || orchestrator?.isAnyHandlerActive() != true)) {
6667
begin()
6768
shouldIntercept = false
6869
}
69-
if (event.actionMasked == MotionEvent.ACTION_UP) {
70+
71+
if (event.actionMasked == MotionEvent.ACTION_UP || event.actionMasked == MotionEvent.ACTION_HOVER_EXIT) {
7072
end()
7173
}
7274
}
7375

76+
override fun onHandle(event: MotionEvent, sourceEvent: MotionEvent) = handleEvent(event)
77+
78+
override fun onHandleHover(event: MotionEvent, sourceEvent: MotionEvent) = handleEvent(event)
79+
7480
override fun onCancel() {
7581
shouldIntercept = true
7682
val time = SystemClock.uptimeMillis()

0 commit comments

Comments
 (0)