package com.swmansion.gesturehandler.react.events import android.view.MotionEvent import com.facebook.react.bridge.ReactApplicationContext import com.facebook.react.bridge.WritableMap import com.facebook.react.uimanager.events.Event import com.swmansion.gesturehandler.ReanimatedProxy import com.swmansion.gesturehandler.core.GestureHandler import com.swmansion.gesturehandler.core.OnTouchEventListener import com.swmansion.gesturehandler.dispatchEvent import com.swmansion.gesturehandler.react.RNGestureHandlerFactoryUtil import com.swmansion.gesturehandler.react.deviceEventEmitter class RNGestureHandlerEventDispatcher(private val reactApplicationContext: ReactApplicationContext) : OnTouchEventListener { private val reanimatedProxy = ReanimatedProxy() override fun onHandlerUpdate(handler: T, event: MotionEvent) { this.dispatchHandlerUpdateEvent(handler) } override fun onStateChange(handler: T, newState: Int, oldState: Int) { this.dispatchStateChangeEvent(handler, newState, oldState) } override fun onTouchEvent(handler: T) { this.dispatchTouchEvent(handler) } private fun dispatchHandlerUpdateEvent(handler: T) { // triggers onUpdate and onChange callbacks on the JS side // root containers use negative tags, we don't need to dispatch events for them to the JS if (handler.tag < 0 || handler.state != GestureHandler.STATE_ACTIVE) { return } val handlerFactory = RNGestureHandlerFactoryUtil.findFactoryForHandler(handler) ?: return when (handler.actionType) { GestureHandler.ACTION_TYPE_REANIMATED_WORKLET -> { // Reanimated worklet val event = RNGestureHandlerEvent.obtain( handler, handler.actionType, handlerFactory.createEventBuilder(handler), EventHandlerType.ForJS, // For API v2 compatibility ) sendEventForReanimated(event) } GestureHandler.ACTION_TYPE_NATIVE_ANIMATED_EVENT -> { // Animated with useNativeDriver: true val event = RNGestureHandlerEvent.obtain( handler, handler.actionType, handlerFactory.createEventBuilder(handler), EventHandlerType.ForAnimated, ) sendEventForNativeAnimatedEvent(event) } GestureHandler.ACTION_TYPE_JS_FUNCTION_OLD_API -> { // JS function, Animated.event with useNativeDriver: false using old API val data = RNGestureHandlerEvent.createEventData( handlerFactory.createEventBuilder(handler), ) sendEventForDeviceEvent(RNGestureHandlerEvent.EVENT_NAME, data) } GestureHandler.ACTION_TYPE_JS_FUNCTION_NEW_API -> { // JS function, Animated.event with useNativeDriver: false using new API val data = RNGestureHandlerEvent.createEventData(handlerFactory.createEventBuilder(handler)) sendEventForDeviceEvent(RNGestureHandlerEvent.EVENT_NAME, data) } GestureHandler.ACTION_TYPE_NATIVE_DETECTOR, GestureHandler.ACTION_TYPE_VIRTUAL_DETECTOR -> { val eventHandlerType = if (handler.dispatchesAnimatedEvents) { EventHandlerType.ForAnimated } else if (handler.dispatchesReanimatedEvents) { EventHandlerType.ForReanimated } else { EventHandlerType.ForJS } val event = RNGestureHandlerEvent.obtain( handler, handler.actionType, handlerFactory.createEventBuilder(handler), eventHandlerType, ) handler.hostDetectorView?.dispatchEvent(event) } } } private fun dispatchStateChangeEvent(handler: T, newState: Int, oldState: Int) { // triggers onBegin, onStart, onEnd, onFinalize callbacks on the JS side if (handler.tag < 0) { // root containers use negative tags, we don't need to dispatch events for them to the JS return } val handlerFactory = RNGestureHandlerFactoryUtil.findFactoryForHandler(handler) ?: return when (handler.actionType) { GestureHandler.ACTION_TYPE_REANIMATED_WORKLET -> { // Reanimated worklet val event = RNGestureHandlerStateChangeEvent.obtain( handler, newState, oldState, handler.actionType, handlerFactory.createEventBuilder(handler), EventHandlerType.ForJS, // For API v2 compatibility ) sendEventForReanimated(event) } GestureHandler.ACTION_TYPE_NATIVE_ANIMATED_EVENT, GestureHandler.ACTION_TYPE_JS_FUNCTION_OLD_API -> { // JS function or Animated.event with useNativeDriver: false with old API val data = RNGestureHandlerStateChangeEvent.createEventData( handlerFactory.createEventBuilder(handler), newState, oldState, ) sendEventForDeviceEvent(RNGestureHandlerStateChangeEvent.EVENT_NAME, data) } GestureHandler.ACTION_TYPE_JS_FUNCTION_NEW_API -> { // JS function or Animated.event with useNativeDriver: false with new API val data = RNGestureHandlerStateChangeEvent.createEventData( handlerFactory.createEventBuilder(handler), newState, oldState, ) sendEventForDeviceEvent(RNGestureHandlerStateChangeEvent.EVENT_NAME, data) } GestureHandler.ACTION_TYPE_NATIVE_DETECTOR, GestureHandler.ACTION_TYPE_VIRTUAL_DETECTOR -> { val eventHandlerType = if (handler.dispatchesReanimatedEvents) { EventHandlerType.ForReanimated } else { EventHandlerType.ForJS } val event = RNGestureHandlerStateChangeEvent.obtain( handler, newState, oldState, handler.actionType, handlerFactory.createEventBuilder(handler), eventHandlerType, ) handler.hostDetectorView?.dispatchEvent(event) } } } private fun dispatchTouchEvent(handler: T) { // triggers onTouchesDown, onTouchesMove, onTouchesUp, onTouchesCancelled callbacks on the JS side if (handler.tag < 0) { // root containers use negative tags, we don't need to dispatch events for them to the JS return } if (handler.state != GestureHandler.STATE_BEGAN && handler.state != GestureHandler.STATE_ACTIVE && handler.state != GestureHandler.STATE_UNDETERMINED && handler.view == null ) { return } when (handler.actionType) { GestureHandler.ACTION_TYPE_REANIMATED_WORKLET -> { // Reanimated worklet val event = RNGestureHandlerTouchEvent.obtain( handler, handler.actionType, EventHandlerType.ForJS, // For API v2 compatibility ) sendEventForReanimated(event) } GestureHandler.ACTION_TYPE_JS_FUNCTION_NEW_API -> { // JS function, Animated.event with useNativeDriver: false with new API val data = RNGestureHandlerTouchEvent.createEventData(handler) sendEventForDeviceEvent(RNGestureHandlerEvent.EVENT_NAME, data) } GestureHandler.ACTION_TYPE_NATIVE_DETECTOR, GestureHandler.ACTION_TYPE_VIRTUAL_DETECTOR -> { val eventHandlerType = if (handler.dispatchesReanimatedEvents) { EventHandlerType.ForReanimated } else { EventHandlerType.ForJS } val event = RNGestureHandlerTouchEvent.obtain(handler, handler.actionType, eventHandlerType) handler.hostDetectorView?.dispatchEvent(event) } } } private fun > sendEventForReanimated(event: T) { // Delivers the event to Reanimated. reanimatedProxy.sendEvent(event, reactApplicationContext) } private fun sendEventForNativeAnimatedEvent(event: RNGestureHandlerEvent) { // Delivers the event to NativeAnimatedModule. // TODO: send event directly to NativeAnimated[Turbo]Module // ReactContext.dispatchEvent is an extension function, depending on the architecture it will // dispatch event using UIManagerModule or FabricUIManager. reactApplicationContext.dispatchEvent(event) } private fun sendEventForDeviceEvent(eventName: String, data: WritableMap) { // Delivers the event to JS as a device event. reactApplicationContext.deviceEventEmitter.emit(eventName, data) } }