Add onChildEndedNativeGesture to JSTouchDispatcher and ReactRootView to handle child gesture finishes event

Summary:
Changelog:
[Android][Added] Adding new API `onChildEndedNativeGesture` to the RootView interface to let its implementations notify the JS side that a child gesture is ended.

Reviewed By: javache

Differential Revision: D32228745

fbshipit-source-id: ad1f26546dd60f9c5a569b0bc3ad5020a01b90cc
This commit is contained in:
Xin Chen
2021-11-09 12:53:14 -08:00
committed by Facebook GitHub Bot
parent e973b3afc2
commit 9b33c31ee0
5 changed files with 58 additions and 8 deletions
@@ -185,14 +185,7 @@ public class ReactRootView extends FrameLayout implements RootView, ReactRoot {
@Override
public void onChildStartedNativeGesture(MotionEvent ev) {
if (mReactInstanceManager == null
|| !mIsAttachedToInstance
|| mReactInstanceManager.getCurrentReactContext() == null) {
FLog.w(TAG, "Unable to dispatch touch to JS as the catalyst instance has not been attached");
return;
}
if (mJSTouchDispatcher == null) {
FLog.w(TAG, "Unable to dispatch touch to JS before the dispatcher is available");
if (!isDispatcherReady()) {
return;
}
ReactContext reactContext = mReactInstanceManager.getCurrentReactContext();
@@ -209,6 +202,35 @@ public class ReactRootView extends FrameLayout implements RootView, ReactRoot {
onChildStartedNativeGesture(ev);
}
@Override
public void onChildEndedNativeGesture(View childView, MotionEvent ev) {
if (!isDispatcherReady()) {
return;
}
ReactContext reactContext = mReactInstanceManager.getCurrentReactContext();
UIManager uiManager = UIManagerHelper.getUIManager(reactContext, getUIManagerType());
if (uiManager != null) {
EventDispatcher eventDispatcher = uiManager.getEventDispatcher();
mJSTouchDispatcher.onChildEndedNativeGesture(ev, eventDispatcher);
}
}
private boolean isDispatcherReady() {
if (mReactInstanceManager == null
|| !mIsAttachedToInstance
|| mReactInstanceManager.getCurrentReactContext() == null) {
FLog.w(TAG, "Unable to dispatch touch to JS as the catalyst instance has not been attached");
return false;
}
if (mJSTouchDispatcher == null) {
FLog.w(TAG, "Unable to dispatch touch to JS before the dispatcher is available");
return false;
}
return true;
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
dispatchJSTouchEvent(ev);
@@ -50,6 +50,11 @@ public class JSTouchDispatcher {
mTargetTag = -1;
}
public void onChildEndedNativeGesture(MotionEvent androidEvent, EventDispatcher eventDispatcher) {
// There should be only one child gesture at any given time. We can safely turn off the flag.
mChildIsHandlingNativeGesture = false;
}
/**
* Main catalyst view is responsible for collecting and sending touch events to JS. This method
* reacts for an incoming android native touch events ({@link MotionEvent}) and calls into {@link
@@ -22,5 +22,11 @@ public interface RootView {
/** @deprecated */
void onChildStartedNativeGesture(MotionEvent ev);
/**
* Called when a child ends a native gesture. Should be called from the child's onTouchIntercepted
* implementation.
*/
void onChildEndedNativeGesture(View childView, MotionEvent ev);
void handleException(Throwable t);
}
@@ -25,4 +25,16 @@ public class NativeGestureUtil {
public static void notifyNativeGestureStarted(View view, MotionEvent event) {
RootViewUtil.getRootView(view).onChildStartedNativeGesture(view, event);
}
/**
* Helper method that should be called when a native view ends a native gesture (e.g. a native
* ScrollView takes control of a gesture stream and starts scrolling). This will handle
* dispatching the appropriate events to JS to make sure future gesture is not blocked.
*
* @param view the View ending the native gesture
* @param event the MotionEvent that caused the gesture to be ended
*/
public static void notifyNativeGestureEnded(View view, MotionEvent event) {
RootViewUtil.getRootView(view).onChildEndedNativeGesture(view, event);
}
}
@@ -537,6 +537,11 @@ public class ReactModalHostView extends ViewGroup
mJSTouchDispatcher.onChildStartedNativeGesture(ev, mEventDispatcher);
}
@Override
public void onChildEndedNativeGesture(View childView, MotionEvent ev) {
mJSTouchDispatcher.onChildEndedNativeGesture(ev, mEventDispatcher);
}
@Override
public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) {
// No-op - override in order to still receive events to onInterceptTouchEvent