RN Android: Support View Manager Commands that are strings
Summary: Right now JS triggers a view manager command with the following code: ``` UIManager.dispatchViewManagerCommand( ReactNative.findNodeHandle(this), UIManager.getViewManagerConfig('RCTView').Commands.hotspotUpdate, [destX || 0, destY || 0], ); ``` As we want to get rid of calls to UIManager, we need to stop looking for the integer defined in native from JavaScript. We will be changing methods like this to be: ``` UIManager.dispatchViewManagerCommand( ReactNative.findNodeHandle(this), 'hotspotUpdate', [destX || 0, destY || 0], ); ``` We need to support ints and Strings to be backwards compatible, but ints will be deprecated. Reviewed By: shergin Differential Revision: D15955444 fbshipit-source-id: d1c488975ae03404f8f851a7035b58a90ed34163
This commit is contained in:
Родитель
4b8d07ebf3
Коммит
3cae6fa950
|
@ -32,12 +32,24 @@ public interface UIManager extends JSIModule, PerformanceCounter {
|
|||
* Dispatches the commandId received by parameter to the view associated with the reactTag.
|
||||
* The command will be processed in the UIThread.
|
||||
*
|
||||
* Receiving commands as ints is deprecated and will be removed in a future release.
|
||||
*
|
||||
* @param reactTag {@link int} that identifies the view that will receive this command
|
||||
* @param commandId {@link int} command id
|
||||
* @param commandArgs {@link ReadableArray} parameters associated with the command
|
||||
*/
|
||||
void dispatchCommand(int reactTag, int commandId, @Nullable ReadableArray commandArgs);
|
||||
|
||||
/**
|
||||
* Dispatches the commandId received by parameter to the view associated with the reactTag.
|
||||
* The command will be processed in the UIThread.
|
||||
*
|
||||
* @param reactTag {@link int} that identifies the view that will receive this command
|
||||
* @param commandId {@link String} command id
|
||||
* @param commandArgs {@link ReadableArray} parameters associated with the command
|
||||
*/
|
||||
void dispatchCommand(int reactTag, String commandId, @Nullable ReadableArray commandArgs);
|
||||
|
||||
void setJSResponder(int reactTag, boolean blockNativeResponder);
|
||||
|
||||
void clearJSResponder();
|
||||
|
|
|
@ -18,6 +18,7 @@ import com.facebook.react.fabric.mounting.ViewPool;
|
|||
import com.facebook.react.fabric.mounting.mountitems.BatchMountItem;
|
||||
import com.facebook.react.fabric.mounting.mountitems.DeleteMountItem;
|
||||
import com.facebook.react.fabric.mounting.mountitems.DispatchCommandMountItem;
|
||||
import com.facebook.react.fabric.mounting.mountitems.DispatchStringCommandMountItem;
|
||||
import com.facebook.react.fabric.mounting.mountitems.InsertMountItem;
|
||||
import com.facebook.react.fabric.mounting.mountitems.MountItem;
|
||||
import com.facebook.react.fabric.mounting.mountitems.PreAllocateViewMountItem;
|
||||
|
@ -102,6 +103,7 @@ public class FabricJSIModuleProvider implements JSIModuleProvider<UIManager> {
|
|||
BatchMountItem.class.getClass();
|
||||
DeleteMountItem.class.getClass();
|
||||
DispatchCommandMountItem.class.getClass();
|
||||
DispatchStringCommandMountItem.class.getClass();
|
||||
InsertMountItem.class.getClass();
|
||||
MountItem.class.getClass();
|
||||
RemoveMountItem.class.getClass();
|
||||
|
|
|
@ -44,6 +44,7 @@ import com.facebook.react.fabric.mounting.mountitems.BatchMountItem;
|
|||
import com.facebook.react.fabric.mounting.mountitems.CreateMountItem;
|
||||
import com.facebook.react.fabric.mounting.mountitems.DeleteMountItem;
|
||||
import com.facebook.react.fabric.mounting.mountitems.DispatchCommandMountItem;
|
||||
import com.facebook.react.fabric.mounting.mountitems.DispatchStringCommandMountItem;
|
||||
import com.facebook.react.fabric.mounting.mountitems.InsertMountItem;
|
||||
import com.facebook.react.fabric.mounting.mountitems.MountItem;
|
||||
import com.facebook.react.fabric.mounting.mountitems.PreAllocateViewMountItem;
|
||||
|
@ -486,6 +487,14 @@ public class FabricUIManager implements UIManager, LifecycleEventListener {
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispatchCommand(
|
||||
final int reactTag, final String commandId, @Nullable final ReadableArray commandArgs) {
|
||||
synchronized (mMountItemsLock) {
|
||||
mMountItems.add(new DispatchStringCommandMountItem(reactTag, commandId, commandArgs));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setJSResponder(int reactTag, boolean blockNativeResponder) {
|
||||
// do nothing for now.
|
||||
|
|
|
@ -148,6 +148,20 @@ public class MountingManager {
|
|||
viewState.mViewManager.receiveCommand(viewState.mView, commandId, commandArgs);
|
||||
}
|
||||
|
||||
public void receiveCommand(int reactTag, String commandId, @Nullable ReadableArray commandArgs) {
|
||||
ViewState viewState = getViewState(reactTag);
|
||||
|
||||
if (viewState.mViewManager == null) {
|
||||
throw new IllegalStateException("Unable to find viewState manager for tag " + reactTag);
|
||||
}
|
||||
|
||||
if (viewState.mView == null) {
|
||||
throw new IllegalStateException("Unable to find viewState view for tag " + reactTag);
|
||||
}
|
||||
|
||||
viewState.mViewManager.receiveCommand(viewState.mView, commandId, commandArgs);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked") // prevents unchecked conversion warn of the <ViewGroup> type
|
||||
private static ViewGroupManager<ViewGroup> getViewGroupManager(ViewState viewState) {
|
||||
if (viewState.mViewManager == null) {
|
||||
|
|
|
@ -0,0 +1,36 @@
|
|||
/**
|
||||
* Copyright (c) 2014-present, Facebook, Inc.
|
||||
*
|
||||
* <p>This source code is licensed under the MIT license found in the LICENSE file in the root
|
||||
* directory of this source tree.
|
||||
*/
|
||||
package com.facebook.react.fabric.mounting.mountitems;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import com.facebook.react.fabric.mounting.MountingManager;
|
||||
import com.facebook.react.bridge.ReadableArray;
|
||||
import com.facebook.react.bridge.UiThreadUtil;
|
||||
|
||||
public class DispatchStringCommandMountItem implements MountItem {
|
||||
|
||||
private final int mReactTag;
|
||||
private final String mCommandId;
|
||||
private final @Nullable ReadableArray mCommandArgs;
|
||||
|
||||
public DispatchStringCommandMountItem(
|
||||
int reactTag, String commandId, @Nullable ReadableArray commandArgs) {
|
||||
mReactTag = reactTag;
|
||||
mCommandId = commandId;
|
||||
mCommandArgs = commandArgs;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(MountingManager mountingManager) {
|
||||
mountingManager.receiveCommand(mReactTag, mCommandId, mCommandArgs);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "DispatchStringCommandMountItem [" + mReactTag + "] " + mCommandId;
|
||||
}
|
||||
}
|
|
@ -770,6 +770,21 @@ public class NativeViewHierarchyManager {
|
|||
viewManager.receiveCommand(view, commandId, args);
|
||||
}
|
||||
|
||||
public synchronized void dispatchCommand(
|
||||
int reactTag,
|
||||
String commandId,
|
||||
@Nullable ReadableArray args) {
|
||||
UiThreadUtil.assertOnUiThread();
|
||||
View view = mTagsToViews.get(reactTag);
|
||||
if (view == null) {
|
||||
throw new IllegalViewOperationException("Trying to send command to a non-existing view " +
|
||||
"with tag " + reactTag);
|
||||
}
|
||||
|
||||
ViewManager viewManager = resolveViewManager(reactTag);
|
||||
viewManager.receiveCommand(view, commandId, args);
|
||||
}
|
||||
|
||||
/**
|
||||
* Show a {@link PopupMenu}.
|
||||
*
|
||||
|
|
|
@ -748,6 +748,11 @@ public class UIImplementation {
|
|||
mOperationsQueue.enqueueDispatchCommand(reactTag, commandId, commandArgs);
|
||||
}
|
||||
|
||||
public void dispatchViewManagerCommand(int reactTag, String commandId, @Nullable ReadableArray commandArgs) {
|
||||
assertViewExists(reactTag, "dispatchViewManagerCommand");
|
||||
mOperationsQueue.enqueueDispatchCommand(reactTag, commandId, commandArgs);
|
||||
}
|
||||
|
||||
/**
|
||||
* Show a PopupMenu.
|
||||
*
|
||||
|
|
|
@ -22,6 +22,7 @@ import com.facebook.debug.holder.PrinterHolder;
|
|||
import com.facebook.debug.tags.ReactDebugOverlayTags;
|
||||
import com.facebook.react.bridge.Arguments;
|
||||
import com.facebook.react.bridge.Callback;
|
||||
import com.facebook.react.bridge.Dynamic;
|
||||
import com.facebook.react.bridge.GuardedRunnable;
|
||||
import com.facebook.react.bridge.LifecycleEventListener;
|
||||
import com.facebook.react.bridge.OnBatchCompleteListener;
|
||||
|
@ -31,6 +32,7 @@ import com.facebook.react.bridge.ReactMarker;
|
|||
import com.facebook.react.bridge.ReactMethod;
|
||||
import com.facebook.react.bridge.ReadableArray;
|
||||
import com.facebook.react.bridge.ReadableMap;
|
||||
import com.facebook.react.bridge.ReadableType;
|
||||
import com.facebook.react.bridge.UIManager;
|
||||
import com.facebook.react.bridge.UiThreadUtil;
|
||||
import com.facebook.react.bridge.WritableMap;
|
||||
|
@ -648,11 +650,19 @@ public class UIManagerModule extends ReactContextBaseJavaModule
|
|||
|
||||
@ReactMethod
|
||||
public void dispatchViewManagerCommand(
|
||||
int reactTag, int commandId, @Nullable ReadableArray commandArgs) {
|
||||
int reactTag, Dynamic commandId, @Nullable ReadableArray commandArgs) {
|
||||
// TODO: this is a temporary approach to support ViewManagerCommands in Fabric until
|
||||
// the dispatchViewManagerCommand() method is supported by Fabric JS API.
|
||||
UIManagerHelper.getUIManager(getReactApplicationContext(), ViewUtil.getUIManagerType(reactTag))
|
||||
.dispatchCommand(reactTag, commandId, commandArgs);
|
||||
if(commandId.getType() == ReadableType.Number) {
|
||||
final int commandIdNum = commandId.asInt();
|
||||
UIManagerHelper.getUIManager(getReactApplicationContext(), ViewUtil.getUIManagerType(reactTag))
|
||||
.dispatchCommand(reactTag, commandIdNum, commandArgs);
|
||||
} else if (commandId.getType() == ReadableType.String) {
|
||||
final String commandIdStr = commandId.asString();
|
||||
UIManagerHelper.getUIManager(getReactApplicationContext(), ViewUtil.getUIManagerType(reactTag))
|
||||
.dispatchCommand(reactTag, commandIdStr, commandArgs);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -660,6 +670,11 @@ public class UIManagerModule extends ReactContextBaseJavaModule
|
|||
mUIImplementation.dispatchViewManagerCommand(reactTag, commandId, commandArgs);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispatchCommand(int reactTag, String commandId, @Nullable ReadableArray commandArgs) {
|
||||
mUIImplementation.dispatchViewManagerCommand(reactTag, commandId, commandArgs);
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void playTouchSound() {
|
||||
AudioManager audioManager =
|
||||
|
|
|
@ -312,6 +312,23 @@ public class UIViewOperationQueue {
|
|||
}
|
||||
}
|
||||
|
||||
private final class DispatchStringCommandOperation extends ViewOperation {
|
||||
|
||||
private final String mCommand;
|
||||
private final @Nullable ReadableArray mArgs;
|
||||
|
||||
public DispatchStringCommandOperation(int tag, String command, @Nullable ReadableArray args) {
|
||||
super(tag);
|
||||
mCommand = command;
|
||||
mArgs = args;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute() {
|
||||
mNativeViewHierarchyManager.dispatchCommand(mTag, mCommand, mArgs);
|
||||
}
|
||||
}
|
||||
|
||||
private final class ShowPopupMenuOperation extends ViewOperation {
|
||||
|
||||
private final ReadableArray mItems;
|
||||
|
@ -659,6 +676,13 @@ public class UIViewOperationQueue {
|
|||
mOperations.add(new DispatchCommandOperation(reactTag, commandId, commandArgs));
|
||||
}
|
||||
|
||||
public void enqueueDispatchCommand(
|
||||
int reactTag,
|
||||
String commandId,
|
||||
@Nullable ReadableArray commandArgs) {
|
||||
mOperations.add(new DispatchStringCommandOperation(reactTag, commandId, commandArgs));
|
||||
}
|
||||
|
||||
public void enqueueUpdateExtraData(int reactTag, Object extraData) {
|
||||
mOperations.add(new UpdateViewExtraData(reactTag, extraData));
|
||||
}
|
||||
|
|
|
@ -172,6 +172,18 @@ public abstract class ViewManager<T extends View, C extends ReactShadowNode>
|
|||
public void receiveCommand(@Nonnull T root, int commandId, @Nullable ReadableArray args) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Subclasses may use this method to receive events/commands directly from JS through the
|
||||
* {@link UIManager}. Good example of such a command would be {@code scrollTo} request with
|
||||
* coordinates for a {@link ScrollView} instance.
|
||||
*
|
||||
* @param root View instance that should receive the command
|
||||
* @param commandId code of the command
|
||||
* @param args optional arguments for the command
|
||||
*/
|
||||
public void receiveCommand(@Nonnull T root, String commandId, @Nullable ReadableArray args) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Subclasses of {@link ViewManager} that expect to receive commands through
|
||||
* {@link UIManagerModule#dispatchViewManagerCommand} should override this method returning the
|
||||
|
|
|
@ -289,28 +289,54 @@ public class ReactViewManager extends ViewGroupManager<ReactViewGroup> {
|
|||
public void receiveCommand(ReactViewGroup root, int commandId, @Nullable ReadableArray args) {
|
||||
switch (commandId) {
|
||||
case CMD_HOTSPOT_UPDATE: {
|
||||
if (args == null || args.size() != 2) {
|
||||
throw new JSApplicationIllegalArgumentException(
|
||||
"Illegal number of arguments for 'updateHotspot' command");
|
||||
}
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
float x = PixelUtil.toPixelFromDIP(args.getDouble(0));
|
||||
float y = PixelUtil.toPixelFromDIP(args.getDouble(1));
|
||||
root.drawableHotspotChanged(x, y);
|
||||
}
|
||||
handleHotspotUpdate(root, args);
|
||||
break;
|
||||
}
|
||||
case CMD_SET_PRESSED: {
|
||||
if (args == null || args.size() != 1) {
|
||||
throw new JSApplicationIllegalArgumentException(
|
||||
"Illegal number of arguments for 'setPressed' command");
|
||||
}
|
||||
root.setPressed(args.getBoolean(0));
|
||||
handleSetPressed(root, args);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void receiveCommand(ReactViewGroup root, String commandId, @Nullable ReadableArray args) {
|
||||
switch (commandId) {
|
||||
case "hotspotUpdate": {
|
||||
handleHotspotUpdate(root, args);
|
||||
break;
|
||||
}
|
||||
case "setPressed": {
|
||||
handleSetPressed(root, args);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void handleSetPressed(
|
||||
ReactViewGroup root,
|
||||
@Nullable ReadableArray args) {
|
||||
if (args == null || args.size() != 1) {
|
||||
throw new JSApplicationIllegalArgumentException(
|
||||
"Illegal number of arguments for 'setPressed' command");
|
||||
}
|
||||
root.setPressed(args.getBoolean(0));
|
||||
}
|
||||
|
||||
private void handleHotspotUpdate(
|
||||
ReactViewGroup root,
|
||||
@Nullable ReadableArray args) {
|
||||
if (args == null || args.size() != 2) {
|
||||
throw new JSApplicationIllegalArgumentException(
|
||||
"Illegal number of arguments for 'updateHotspot' command");
|
||||
}
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
float x = PixelUtil.toPixelFromDIP(args.getDouble(0));
|
||||
float y = PixelUtil.toPixelFromDIP(args.getDouble(1));
|
||||
root.drawableHotspotChanged(x, y);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addView(ReactViewGroup parent, View child, int index) {
|
||||
boolean removeClippedSubviews = parent.getRemoveClippedSubviews();
|
||||
|
|
Загрузка…
Ссылка в новой задаче