Implement JS Responder Handler in Fabric Android

Summary: This diff implements the JSResponderHandler in Fabric Android code

Reviewed By: JoshuaGross

Differential Revision: D16543438

fbshipit-source-id: 13680f77a5368e8ba1180383a5f9fb7d7330b90a
This commit is contained in:
David Vacca
2019-08-02 16:38:45 -07:00
committed by Facebook Github Bot
parent 08e4537680
commit 0e067fd23b
2 changed files with 82 additions and 3 deletions
@@ -569,11 +569,32 @@ public class FabricUIManager implements UIManager, LifecycleEventListener {
@DoNotStrip
public void setJSResponder(
final int reactTag, final int initialReactTag, final boolean blockNativeResponder) {
// do nothing for now.
synchronized (mMountItemsLock) {
mMountItems.add(
new MountItem() {
@Override
public void execute(MountingManager mountingManager) {
mountingManager.setJSResponder(reactTag, initialReactTag, blockNativeResponder);
}
});
}
}
/**
* Clears the JS Responder specified by {@link #setJSResponder(int, int, boolean)}. After this
* method is called, all the touch events are going to be handled by JS.
*/
@DoNotStrip
public void clearJSResponder() {
// do nothing for now.
synchronized (mMountItemsLock) {
mMountItems.add(
new MountItem() {
@Override
public void execute(MountingManager mountingManager) {
mountingManager.clearJSResponder();
}
});
}
}
@Override
@@ -17,10 +17,12 @@ import com.facebook.infer.annotation.Assertions;
import com.facebook.react.bridge.ReadableArray;
import com.facebook.react.bridge.ReadableMap;
import com.facebook.react.bridge.ReadableNativeMap;
import com.facebook.react.bridge.SoftAssertions;
import com.facebook.react.bridge.UiThreadUtil;
import com.facebook.react.fabric.FabricUIManager;
import com.facebook.react.fabric.events.EventEmitterWrapper;
import com.facebook.react.fabric.mounting.mountitems.MountItem;
import com.facebook.react.touch.JSResponderHandler;
import com.facebook.react.uimanager.IllegalViewOperationException;
import com.facebook.react.uimanager.ReactStylesDiffMap;
import com.facebook.react.uimanager.RootView;
@@ -40,6 +42,7 @@ import java.util.concurrent.ConcurrentHashMap;
public class MountingManager {
private final ConcurrentHashMap<Integer, ViewState> mTagToViewState;
private final JSResponderHandler mJSResponderHandler = new JSResponderHandler();
private final ViewManagerRegistry mViewManagerRegistry;
private final RootViewManager mRootViewManager = new RootViewManager();
@@ -183,7 +186,9 @@ public class MountingManager {
if (isLayoutable) {
viewManager = mViewManagerRegistry.get(componentName);
// View Managers are responsible for dealing with initial state and props.
view = viewManager.createView(themedReactContext, propsDiffMap, stateWrapper, null);
view =
viewManager.createView(
themedReactContext, propsDiffMap, stateWrapper, mJSResponderHandler);
view.setId(reactTag);
}
@@ -329,6 +334,59 @@ public class MountingManager {
viewState.mEventEmitter = eventEmitter;
}
/**
* Set the JS responder for the view associated with the tags received as a parameter.
*
* <p>The JSResponder coordinates the return values of the onInterceptTouch method in Android
* Views. This allows JS to coordinate when a touch should be handled by JS or by the Android
* native views. See {@link JSResponderHandler} for more details.
*
* <p>This method is going to be executed on the UIThread as soon as it is delivered from JS to
* RN.
*
* <p>Currently, there is no warranty that the view associated with the react tag exists, because
* this method is not handled by the react commit process.
*
* @param reactTag React tag of the first parent of the view that is NOT virtual
* @param initialReactTag React tag of the JS view that initiated the touch operation
* @param blockNativeResponder If native responder should be blocked or not
*/
@UiThread
public synchronized void setJSResponder(
int reactTag, int initialReactTag, boolean blockNativeResponder) {
if (!blockNativeResponder) {
mJSResponderHandler.setJSResponder(initialReactTag, null);
return;
}
ViewState viewState = getViewState(reactTag);
View view = viewState.mView;
if (initialReactTag != reactTag && view instanceof ViewParent) {
// In this case, initialReactTag corresponds to a virtual/layout-only View, and we already
// have a parent of that View in reactTag, so we can use it.
mJSResponderHandler.setJSResponder(initialReactTag, (ViewParent) view);
return;
} else if (view == null) {
SoftAssertions.assertUnreachable("Cannot find view for tag " + reactTag + ".");
return;
}
if (viewState.mIsRoot) {
SoftAssertions.assertUnreachable(
"Cannot block native responder on " + reactTag + " that is a root view");
}
mJSResponderHandler.setJSResponder(initialReactTag, view.getParent());
}
/**
* Clears the JS Responder specified by {@link #setJSResponder(int, int, boolean)}. After this
* method is called, all the touch events are going to be handled by JS.
*/
@UiThread
public void clearJSResponder() {
mJSResponderHandler.clearJSResponder();
}
@AnyThread
public long measure(
Context context,