Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for sending device events #1561

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.swmansion.gesturehandler

import com.facebook.react.bridge.ReactContext
import com.facebook.react.modules.core.DeviceEventManagerModule
import com.facebook.react.uimanager.UIManagerModule

val ReactContext.deviceEventEmitter: DeviceEventManagerModule.RCTDeviceEventEmitter
get() = this.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java)

val ReactContext.UIManager: UIManagerModule
get() = this.getNativeModule(UIManagerModule::class.java)!!
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ open class GestureHandler<ConcreteGestureHandlerT : GestureHandler<ConcreteGestu
private set
var isEnabled = true
private set
var usesDeviceEvents = false

private var mHitSlop: FloatArray? = null
var eventCoalescingKey: Short = 0
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package com.swmansion.gesturehandler
import android.view.MotionEvent

object GestureUtils {
@JvmStatic
fun getLastPointerX(event: MotionEvent, averageTouches: Boolean): Float {
val offset = event.rawX - event.x
val excludeIndex = if (event.actionMasked == MotionEvent.ACTION_POINTER_UP) event.actionIndex else -1
Expand All @@ -26,7 +25,6 @@ object GestureUtils {
}
}

@JvmStatic
fun getLastPointerY(event: MotionEvent, averageTouches: Boolean): Float {
val offset = event.rawY - event.y
val excludeIndex = if (event.actionMasked == MotionEvent.ACTION_POINTER_UP) event.actionIndex else -1
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,7 @@ class RNGestureHandlerEvent private constructor() : Event<RNGestureHandlerEvent>
dataExtractor: RNGestureHandlerEventDataExtractor<T>?,
) {
super.init(handler.view!!.id)
extraData = Arguments.createMap().apply {
dataExtractor?.extractEventData(handler, this)
putInt("handlerTag", handler.tag)
putInt("state", handler.state)
}
extraData = createEventData(handler, dataExtractor)
coalescingKey = handler.eventCoalescingKey
}

Expand All @@ -43,13 +39,21 @@ class RNGestureHandlerEvent private constructor() : Event<RNGestureHandlerEvent>
private const val TOUCH_EVENTS_POOL_SIZE = 7 // magic
private val EVENTS_POOL = Pools.SynchronizedPool<RNGestureHandlerEvent>(TOUCH_EVENTS_POOL_SIZE)

@JvmStatic
fun <T : GestureHandler<T>> obtain(
handler: T,
dataExtractor: RNGestureHandlerEventDataExtractor<T>?,
): RNGestureHandlerEvent =
(EVENTS_POOL.acquire() ?: RNGestureHandlerEvent()).apply {
init(handler, dataExtractor)
}

fun <T: GestureHandler<T>> createEventData(
handler: T,
dataExtractor: RNGestureHandlerEventDataExtractor<T>?
): WritableMap = Arguments.createMap().apply {
dataExtractor?.extractEventData(handler, this)
putInt("handlerTag", handler.tag)
putInt("state", handler.state)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import com.facebook.react.bridge.*
import com.facebook.react.module.annotations.ReactModule
import com.facebook.react.uimanager.PixelUtil
import com.facebook.react.uimanager.UIBlock
import com.facebook.react.uimanager.UIManagerModule
import com.swmansion.gesturehandler.*
import java.util.*

Expand Down Expand Up @@ -338,9 +337,10 @@ class RNGestureHandlerModule(reactContext: ReactApplicationContext?) : ReactCont
}

@ReactMethod
fun attachGestureHandler(handlerTag: Int, viewTag: Int) {
fun attachGestureHandler(handlerTag: Int, viewTag: Int, useDeviceEvents: Boolean) {
tryInitializeHandlerForReactRootView(viewTag)
if (!registry.attachHandlerToView(handlerTag, viewTag)) {

if (!registry.attachHandlerToView(handlerTag, viewTag, useDeviceEvents)) {
throw JSApplicationIllegalArgumentException("Handler with tag $handlerTag does not exists")
}
}
Expand Down Expand Up @@ -419,8 +419,8 @@ class RNGestureHandlerModule(reactContext: ReactApplicationContext?) : ReactCont
}

private fun tryInitializeHandlerForReactRootView(ancestorViewTag: Int) {
val uiManager = reactApplicationContext.getNativeModule(UIManagerModule::class.java)
val rootViewTag = uiManager!!.resolveRootTagFromReactTag(ancestorViewTag)
val uiManager = reactApplicationContext.UIManager
val rootViewTag = uiManager.resolveRootTagFromReactTag(ancestorViewTag)
jakub-gonet marked this conversation as resolved.
Show resolved Hide resolved
if (rootViewTag < 1) {
throw JSApplicationIllegalArgumentException("Could find root view for a given ancestor with tag $ancestorViewTag")
}
Expand Down Expand Up @@ -470,7 +470,7 @@ class RNGestureHandlerModule(reactContext: ReactApplicationContext?) : ReactCont
}

private fun findRootHelperForViewAncestor(viewTag: Int): RNGestureHandlerRootHelper? {
val uiManager = reactApplicationContext.getNativeModule(UIManagerModule::class.java)!!
val uiManager = reactApplicationContext.UIManager
val rootViewTag = uiManager.resolveRootTagFromReactTag(viewTag)
if (rootViewTag < 1) {
return null
Expand All @@ -492,13 +492,22 @@ class RNGestureHandlerModule(reactContext: ReactApplicationContext?) : ReactCont
return
}
if (handler.state == GestureHandler.STATE_ACTIVE) {
reactApplicationContext
.getNativeModule(UIManagerModule::class.java)!!
.eventDispatcher.let {
val handlerFactory = findFactoryForHandler(handler)
val event = RNGestureHandlerEvent.obtain(handler, handlerFactory)
it.dispatchEvent(event)
}
val handlerFactory = findFactoryForHandler(handler)

if (handler.usesDeviceEvents) {
val data = RNGestureHandlerEvent.createEventData(handler, handlerFactory)

reactApplicationContext
.deviceEventEmitter
.emit(RNGestureHandlerEvent.EVENT_NAME, data)
} else {
reactApplicationContext
.UIManager
.eventDispatcher.let {
val event = RNGestureHandlerEvent.obtain(handler, handlerFactory)
it.dispatchEvent(event)
}
}
}
}

Expand All @@ -507,13 +516,28 @@ class RNGestureHandlerModule(reactContext: ReactApplicationContext?) : ReactCont
// root containers use negative tags, we don't need to dispatch events for them to the JS
return
}
reactApplicationContext
.getNativeModule(UIManagerModule::class.java)!!
.eventDispatcher.let {
val handlerFactory = findFactoryForHandler(handler)
val event = RNGestureHandlerStateChangeEvent.obtain(handler, newState, oldState, handlerFactory)
it.dispatchEvent(event)
}
val handlerFactory = findFactoryForHandler(handler)

if (handler.usesDeviceEvents) {
val data = RNGestureHandlerStateChangeEvent.createEventData(
handler,
handlerFactory,
newState,
oldState,
)

reactApplicationContext
.deviceEventEmitter
.emit(RNGestureHandlerStateChangeEvent.EVENT_NAME, data)
} else {
reactApplicationContext
.UIManager
.eventDispatcher.let {
val event =
RNGestureHandlerStateChangeEvent.obtain(handler, newState, oldState, handlerFactory)
it.dispatchEvent(event)
}
}
}

companion object {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,11 @@ class RNGestureHandlerRegistry : GestureHandlerRegistry {
}

@Synchronized
fun attachHandlerToView(handlerTag: Int, viewTag: Int): Boolean {
fun attachHandlerToView(handlerTag: Int, viewTag: Int, useDeviceEvents: Boolean = false): Boolean {
val handler = mHandlers[handlerTag]
return handler?.let {
detachHandler(handler)
handler.usesDeviceEvents = useDeviceEvents
registerHandlerForViewWithTag(viewTag, handler)
true
} ?: false
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,7 @@ class RNGestureHandlerStateChangeEvent private constructor() : Event<RNGestureHa
dataExtractor: RNGestureHandlerEventDataExtractor<T>?,
) {
super.init(handler.view!!.id)
extraData = Arguments.createMap().apply {
dataExtractor?.extractEventData(handler, this)
putInt("handlerTag", handler.tag)
putInt("state", newState)
putInt("oldState", oldState)
}
extraData = createEventData(handler, dataExtractor, newState, oldState)
}

override fun onDispose() {
Expand All @@ -46,7 +41,6 @@ class RNGestureHandlerStateChangeEvent private constructor() : Event<RNGestureHa
private const val TOUCH_EVENTS_POOL_SIZE = 7 // magic
private val EVENTS_POOL = Pools.SynchronizedPool<RNGestureHandlerStateChangeEvent>(TOUCH_EVENTS_POOL_SIZE)

@JvmStatic
fun <T : GestureHandler<T>> obtain(
handler: T,
newState: Int,
Expand All @@ -56,5 +50,17 @@ class RNGestureHandlerStateChangeEvent private constructor() : Event<RNGestureHa
(EVENTS_POOL.acquire() ?: RNGestureHandlerStateChangeEvent()).apply {
init(handler, newState, oldState, dataExtractor)
}

fun <T: GestureHandler<T>> createEventData(
handler: T,
dataExtractor: RNGestureHandlerEventDataExtractor<T>?,
newState: Int,
oldState: Int,
): WritableMap = Arguments.createMap().apply {
dataExtractor?.extractEventData(handler, this)
putInt("handlerTag", handler.tag)
putInt("state", newState)
putInt("oldState", oldState)
}
}
}
6 changes: 6 additions & 0 deletions ios/RNGestureHandler.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@ if (value != nil) { recognizer.prop = [RCTConvert type:value]; }\

- (void)sendStateChangeEvent:(nonnull RNGestureHandlerStateChange *)event;

- (void)sendTouchDeviceEvent:(nonnull RNGestureHandlerEvent *)event;

- (void)sendStateChangeDeviceEvent:(nonnull RNGestureHandlerStateChange *)event;

@end


Expand All @@ -54,6 +58,7 @@ if (value != nil) { recognizer.prop = [RCTConvert type:value]; }\
@property (nonatomic, weak, nullable) id<RNGestureHandlerEventEmitter> emitter;
@property (nonatomic, readonly, nullable) UIGestureRecognizer *recognizer;
@property (nonatomic) BOOL enabled;
@property (nonatomic) BOOL usesDeviceEvents;
@property(nonatomic) BOOL shouldCancelWhenOutside;

- (void)bindToView:(nonnull UIView *)view;
Expand All @@ -69,6 +74,7 @@ if (value != nil) { recognizer.prop = [RCTConvert type:value]; }\
- (void)sendEventsInState:(RNGestureHandlerState)state
forViewWithTag:(nonnull NSNumber *)reactTag
withExtraData:(nonnull RNGestureHandlerEventExtraData *)extraData;
- (void)sendStateChangeEvent:(nonnull RNGestureHandlerStateChange *)event;

@end

24 changes: 17 additions & 7 deletions ios/RNGestureHandler.m
Original file line number Diff line number Diff line change
Expand Up @@ -182,19 +182,20 @@ - (void)sendEventsInState:(RNGestureHandlerState)state
self->_eventCoalescingKey = nextEventCoalescingKey++;

} else if (state == RNGestureHandlerStateEnd && _lastState != RNGestureHandlerStateActive) {
[self.emitter sendStateChangeEvent:[[RNGestureHandlerStateChange alloc] initWithReactTag:reactTag
handlerTag:_tag
state:RNGestureHandlerStateActive
prevState:_lastState
extraData:extraData]];
id event = [[RNGestureHandlerStateChange alloc] initWithReactTag:reactTag
handlerTag:_tag
state:RNGestureHandlerStateActive
prevState:_lastState
extraData:extraData];
[self sendStateChangeEvent:event];
_lastState = RNGestureHandlerStateActive;
}
id stateEvent = [[RNGestureHandlerStateChange alloc] initWithReactTag:reactTag
handlerTag:_tag
state:state
prevState:_lastState
extraData:extraData];
[self.emitter sendStateChangeEvent:stateEvent];
[self sendStateChangeEvent:stateEvent];
_lastState = state;
}

Expand All @@ -204,7 +205,16 @@ - (void)sendEventsInState:(RNGestureHandlerState)state
state:state
extraData:extraData
coalescingKey:self->_eventCoalescingKey];
[self.emitter sendTouchEvent:touchEvent];
[self sendStateChangeEvent:touchEvent];
}
}

- (void)sendStateChangeEvent:(RNGestureHandlerStateChange *)event
{
if (self.usesDeviceEvents) {
[self.emitter sendStateChangeDeviceEvent:event];
} else {
[self.emitter sendStateChangeEvent:event];
}
}

Expand Down
3 changes: 3 additions & 0 deletions ios/RNGestureHandlerManager.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@
- (void)attachGestureHandler:(nonnull NSNumber *)handlerTag
toViewWithTag:(nonnull NSNumber *)viewTag;

- (void)attachGestureHandlerForDeviceEvents:(nonnull NSNumber *)handlerTag
toViewWithTag:(nonnull NSNumber *)viewTag;

- (void)updateGestureHandler:(nonnull NSNumber *)handlerTag config:(nonnull NSDictionary *)config;

- (void)dropGestureHandler:(nonnull NSNumber *)handlerTag;
Expand Down
23 changes: 23 additions & 0 deletions ios/RNGestureHandlerManager.m
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,17 @@ - (void)attachGestureHandler:(nonnull NSNumber *)handlerTag
[self registerViewWithGestureRecognizerAttachedIfNeeded:view];
}

- (void)attachGestureHandlerForDeviceEvents:(nonnull NSNumber *)handlerTag
toViewWithTag:(nonnull NSNumber *)viewTag
{
UIView *view = [_uiManager viewForReactTag:viewTag];

[_registry attachHandlerWithTagForDeviceEvents:handlerTag toView:view];

// register view if not already there
[self registerViewWithGestureRecognizerAttachedIfNeeded:view];
}

- (void)updateGestureHandler:(NSNumber *)handlerTag config:(NSDictionary *)config
{
RNGestureHandler *handler = [_registry handlerWithTag:handlerTag];
Expand Down Expand Up @@ -187,4 +198,16 @@ - (void)sendStateChangeEvent:(RNGestureHandlerStateChange *)event
[_eventDispatcher sendEvent:event];
}

- (void)sendTouchDeviceEvent:(RNGestureHandlerEvent *)event
{
NSMutableDictionary *body = [[event arguments] objectAtIndex:2];
[_eventDispatcher sendDeviceEventWithName:@"onGestureHandlerEvent" body:body];
}

- (void)sendStateChangeDeviceEvent:(RNGestureHandlerStateChange *)event
{
NSMutableDictionary *body = [[event arguments] objectAtIndex:2];
[_eventDispatcher sendDeviceEventWithName:@"onGestureHandlerStateChange" body:body];
}

@end
8 changes: 6 additions & 2 deletions ios/RNGestureHandlerModule.m
Original file line number Diff line number Diff line change
Expand Up @@ -102,10 +102,14 @@ - (void)setBridge:(RCTBridge *)bridge
}];
}

RCT_EXPORT_METHOD(attachGestureHandler:(nonnull NSNumber *)handlerTag toViewWithTag:(nonnull NSNumber *)viewTag)
RCT_EXPORT_METHOD(attachGestureHandler:(nonnull NSNumber *)handlerTag toViewWithTag:(nonnull NSNumber *)viewTag useDeviceEvents: (BOOL)useDeviceEvents)
{
[self addOperationBlock:^(RNGestureHandlerManager *manager) {
[manager attachGestureHandler:handlerTag toViewWithTag:viewTag];
if (useDeviceEvents) {
[manager attachGestureHandlerForDeviceEvents:handlerTag toViewWithTag:viewTag];
} else {
[manager attachGestureHandler:handlerTag toViewWithTag:viewTag];
}
}];
}

Expand Down
1 change: 1 addition & 0 deletions ios/RNGestureHandlerRegistry.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
- (nullable RNGestureHandler *)handlerWithTag:(nonnull NSNumber *)handlerTag;
- (void)registerGestureHandler:(nonnull RNGestureHandler *)gestureHandler;
- (void)attachHandlerWithTag:(nonnull NSNumber *)handlerTag toView:(nonnull UIView *)view;
- (void)attachHandlerWithTagForDeviceEvents:(nonnull NSNumber *)handlerTag toView:(nonnull UIView *)view;
- (void)dropHandlerWithTag:(nonnull NSNumber *)handlerTag;

@end
10 changes: 10 additions & 0 deletions ios/RNGestureHandlerRegistry.m
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,16 @@ - (void)attachHandlerWithTag:(NSNumber *)handlerTag toView:(UIView *)view
RNGestureHandler *handler = _handlers[handlerTag];
RCTAssert(handler != nil, @"Handler for tag %@ does not exists", handlerTag);
[handler unbindFromView];
handler.usesDeviceEvents = NO;
[handler bindToView:view];
}

- (void)attachHandlerWithTagForDeviceEvents:(NSNumber *)handlerTag toView:(UIView *)view
{
RNGestureHandler *handler = _handlers[handlerTag];
RCTAssert(handler != nil, @"Handler for tag %@ does not exists", handlerTag);
[handler unbindFromView];
handler.usesDeviceEvents = YES;
[handler bindToView:view];
}

Expand Down
Loading