Skip to content

Commit 3a93c6e

Browse files
authored
Refactor RNGestureHandlerModule on android (#3489)
## Description - simplifies `RNGestureHandlerRootHelper` a bit - moves logic related to sending events to a separate class - moves logic related to resolving handler factories to a separate class ## Test plan Example & FabricExample apps
1 parent ebab88e commit 3a93c6e

File tree

5 files changed

+259
-261
lines changed

5 files changed

+259
-261
lines changed

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

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -854,7 +854,11 @@ open class GestureHandler<ConcreteGestureHandlerT : GestureHandler<ConcreteGestu
854854
abstract class Factory<T : GestureHandler<T>> {
855855
abstract val type: Class<T>
856856
abstract val name: String
857-
abstract fun create(context: Context?): T
857+
858+
protected abstract fun create(context: Context?): T
859+
860+
fun create(context: Context?, handlerTag: Int): T = create(context).also { it.tag = handlerTag }
861+
858862
open fun setConfig(handler: T, config: ReadableMap) {
859863
handler.resetConfig()
860864
if (config.hasKey(KEY_SHOULD_CANCEL_WHEN_OUTSIDE)) {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,189 @@
1+
package com.swmansion.gesturehandler.react
2+
3+
import android.view.MotionEvent
4+
import com.facebook.react.bridge.ReactApplicationContext
5+
import com.facebook.react.bridge.WritableMap
6+
import com.facebook.react.uimanager.events.Event
7+
import com.swmansion.gesturehandler.BuildConfig
8+
import com.swmansion.gesturehandler.ReanimatedEventDispatcher
9+
import com.swmansion.gesturehandler.core.GestureHandler
10+
import com.swmansion.gesturehandler.core.OnTouchEventListener
11+
import com.swmansion.gesturehandler.dispatchEvent
12+
13+
class RNGestureHandlerEventDispatcher(private val reactApplicationContext: ReactApplicationContext) :
14+
OnTouchEventListener {
15+
private val reanimatedEventDispatcher = ReanimatedEventDispatcher()
16+
17+
override fun <T : GestureHandler<T>> onHandlerUpdate(handler: T, event: MotionEvent) {
18+
this.dispatchHandlerUpdateEvent(handler)
19+
}
20+
21+
override fun <T : GestureHandler<T>> onStateChange(handler: T, newState: Int, oldState: Int) {
22+
this.dispatchStateChangeEvent(handler, newState, oldState)
23+
}
24+
25+
override fun <T : GestureHandler<T>> onTouchEvent(handler: T) {
26+
this.dispatchTouchEvent(handler)
27+
}
28+
29+
private fun <T : GestureHandler<T>> dispatchHandlerUpdateEvent(handler: T) {
30+
// triggers onUpdate and onChange callbacks on the JS side
31+
32+
// root containers use negative tags, we don't need to dispatch events for them to the JS
33+
if (handler.tag < 0 || handler.state != GestureHandler.STATE_ACTIVE) {
34+
return
35+
}
36+
37+
val handlerFactory = RNGestureHandlerFactoryUtil.findFactoryForHandler(handler) ?: return
38+
when (handler.actionType) {
39+
GestureHandler.ACTION_TYPE_REANIMATED_WORKLET -> {
40+
// Reanimated worklet
41+
val event = RNGestureHandlerEvent.obtain(
42+
handler,
43+
handlerFactory.createEventBuilder(handler),
44+
)
45+
sendEventForReanimated(event)
46+
}
47+
GestureHandler.ACTION_TYPE_NATIVE_ANIMATED_EVENT -> {
48+
// Animated with useNativeDriver: true
49+
val event = RNGestureHandlerEvent.obtain(
50+
handler,
51+
handlerFactory.createEventBuilder(handler),
52+
true,
53+
)
54+
sendEventForNativeAnimatedEvent(event)
55+
}
56+
GestureHandler.ACTION_TYPE_JS_FUNCTION_OLD_API -> {
57+
// JS function, Animated.event with useNativeDriver: false using old API
58+
if (BuildConfig.IS_NEW_ARCHITECTURE_ENABLED) {
59+
val data = RNGestureHandlerEvent.createEventData(
60+
handlerFactory.createEventBuilder(handler),
61+
)
62+
sendEventForDeviceEvent(RNGestureHandlerEvent.EVENT_NAME, data)
63+
} else {
64+
val event = RNGestureHandlerEvent.obtain(
65+
handler,
66+
handlerFactory.createEventBuilder(handler),
67+
)
68+
sendEventForDirectEvent(event)
69+
}
70+
}
71+
GestureHandler.ACTION_TYPE_JS_FUNCTION_NEW_API -> {
72+
// JS function, Animated.event with useNativeDriver: false using new API
73+
val data =
74+
RNGestureHandlerEvent.createEventData(handlerFactory.createEventBuilder(handler))
75+
sendEventForDeviceEvent(RNGestureHandlerEvent.EVENT_NAME, data)
76+
}
77+
}
78+
}
79+
80+
private fun <T : GestureHandler<T>> dispatchStateChangeEvent(handler: T, newState: Int, oldState: Int) {
81+
// triggers onBegin, onStart, onEnd, onFinalize callbacks on the JS side
82+
83+
if (handler.tag < 0) {
84+
// root containers use negative tags, we don't need to dispatch events for them to the JS
85+
return
86+
}
87+
val handlerFactory = RNGestureHandlerFactoryUtil.findFactoryForHandler(handler) ?: return
88+
89+
when (handler.actionType) {
90+
GestureHandler.ACTION_TYPE_REANIMATED_WORKLET -> {
91+
// Reanimated worklet
92+
val event = RNGestureHandlerStateChangeEvent.obtain(
93+
handler,
94+
newState,
95+
oldState,
96+
handlerFactory.createEventBuilder(handler),
97+
)
98+
sendEventForReanimated(event)
99+
}
100+
GestureHandler.ACTION_TYPE_NATIVE_ANIMATED_EVENT, GestureHandler.ACTION_TYPE_JS_FUNCTION_OLD_API -> {
101+
// JS function or Animated.event with useNativeDriver: false with old API
102+
if (BuildConfig.IS_NEW_ARCHITECTURE_ENABLED) {
103+
val data = RNGestureHandlerStateChangeEvent.createEventData(
104+
handlerFactory.createEventBuilder(handler),
105+
newState,
106+
oldState,
107+
)
108+
sendEventForDeviceEvent(RNGestureHandlerStateChangeEvent.EVENT_NAME, data)
109+
} else {
110+
val event = RNGestureHandlerStateChangeEvent.obtain(
111+
handler,
112+
newState,
113+
oldState,
114+
handlerFactory.createEventBuilder(handler),
115+
)
116+
sendEventForDirectEvent(event)
117+
}
118+
}
119+
GestureHandler.ACTION_TYPE_JS_FUNCTION_NEW_API -> {
120+
// JS function or Animated.event with useNativeDriver: false with new API
121+
val data = RNGestureHandlerStateChangeEvent.createEventData(
122+
handlerFactory.createEventBuilder(handler),
123+
newState,
124+
oldState,
125+
)
126+
sendEventForDeviceEvent(RNGestureHandlerStateChangeEvent.EVENT_NAME, data)
127+
}
128+
}
129+
}
130+
131+
private fun <T : GestureHandler<T>> dispatchTouchEvent(handler: T) {
132+
// triggers onTouchesDown, onTouchesMove, onTouchesUp, onTouchesCancelled callbacks on the JS side
133+
134+
if (handler.tag < 0) {
135+
// root containers use negative tags, we don't need to dispatch events for them to the JS
136+
return
137+
}
138+
139+
if (handler.state != GestureHandler.STATE_BEGAN &&
140+
handler.state != GestureHandler.STATE_ACTIVE &&
141+
handler.state != GestureHandler.STATE_UNDETERMINED &&
142+
handler.view == null
143+
) {
144+
return
145+
}
146+
147+
when (handler.actionType) {
148+
GestureHandler.ACTION_TYPE_REANIMATED_WORKLET -> {
149+
// Reanimated worklet
150+
val event = RNGestureHandlerTouchEvent.obtain(handler)
151+
sendEventForReanimated(event)
152+
}
153+
GestureHandler.ACTION_TYPE_JS_FUNCTION_NEW_API -> {
154+
// JS function, Animated.event with useNativeDriver: false with new API
155+
val data = RNGestureHandlerTouchEvent.createEventData(handler)
156+
sendEventForDeviceEvent(RNGestureHandlerEvent.EVENT_NAME, data)
157+
}
158+
}
159+
}
160+
161+
private fun <T : Event<T>> sendEventForReanimated(event: T) {
162+
// Delivers the event to Reanimated.
163+
if (BuildConfig.IS_NEW_ARCHITECTURE_ENABLED) {
164+
// Send event directly to Reanimated
165+
reanimatedEventDispatcher.sendEvent(event, reactApplicationContext)
166+
} else {
167+
// In the old architecture, Reanimated subscribes for specific direct events.
168+
sendEventForDirectEvent(event)
169+
}
170+
}
171+
172+
private fun sendEventForNativeAnimatedEvent(event: RNGestureHandlerEvent) {
173+
// Delivers the event to NativeAnimatedModule.
174+
// TODO: send event directly to NativeAnimated[Turbo]Module
175+
// ReactContext.dispatchEvent is an extension function, depending on the architecture it will
176+
// dispatch event using UIManagerModule or FabricUIManager.
177+
reactApplicationContext.dispatchEvent(event)
178+
}
179+
180+
private fun <T : Event<T>> sendEventForDirectEvent(event: T) {
181+
// Delivers the event to JS as a direct event. This method is called only on Paper.
182+
reactApplicationContext.dispatchEvent(event)
183+
}
184+
185+
private fun sendEventForDeviceEvent(eventName: String, data: WritableMap) {
186+
// Delivers the event to JS as a device event.
187+
reactApplicationContext.deviceEventEmitter.emit(eventName, data)
188+
}
189+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
package com.swmansion.gesturehandler.react
2+
3+
import com.swmansion.gesturehandler.core.FlingGestureHandler
4+
import com.swmansion.gesturehandler.core.GestureHandler
5+
import com.swmansion.gesturehandler.core.HoverGestureHandler
6+
import com.swmansion.gesturehandler.core.LongPressGestureHandler
7+
import com.swmansion.gesturehandler.core.ManualGestureHandler
8+
import com.swmansion.gesturehandler.core.NativeViewGestureHandler
9+
import com.swmansion.gesturehandler.core.PanGestureHandler
10+
import com.swmansion.gesturehandler.core.PinchGestureHandler
11+
import com.swmansion.gesturehandler.core.RotationGestureHandler
12+
import com.swmansion.gesturehandler.core.TapGestureHandler
13+
14+
object RNGestureHandlerFactoryUtil {
15+
private val handlerFactories = arrayOf<GestureHandler.Factory<*>>(
16+
NativeViewGestureHandler.Factory(),
17+
TapGestureHandler.Factory(),
18+
LongPressGestureHandler.Factory(),
19+
PanGestureHandler.Factory(),
20+
PinchGestureHandler.Factory(),
21+
RotationGestureHandler.Factory(),
22+
FlingGestureHandler.Factory(),
23+
ManualGestureHandler.Factory(),
24+
HoverGestureHandler.Factory(),
25+
)
26+
27+
@Suppress("UNCHECKED_CAST")
28+
fun <T : GestureHandler<T>> findFactoryForHandler(handler: GestureHandler<T>): GestureHandler.Factory<T>? =
29+
handlerFactories.firstOrNull { it.type == handler.javaClass } as GestureHandler.Factory<T>?
30+
31+
@Suppress("UNCHECKED_CAST")
32+
fun <T : GestureHandler<T>> findFactoryForName(handlerName: String): GestureHandler.Factory<T>? =
33+
handlerFactories.firstOrNull { it.name == handlerName } as GestureHandler.Factory<T>?
34+
}

0 commit comments

Comments
 (0)