mirror of
https://github.com/facebook/react-native.git
synced 2025-11-01 09:14:26 +00:00
Convert ReactViewManager to Kotlin (#46965)
Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/46965 Converting this class as close to 1:1 as possible. Changelog: [Internal] Reviewed By: NickGerleman, rshest Differential Revision: D64124890 fbshipit-source-id: 2034ed8ecb877bb267a953b183b77daaf883a80a
This commit is contained in:
committed by
Facebook GitHub Bot
parent
483b928224
commit
fc199bb5f3
@@ -8295,6 +8295,8 @@ public class com/facebook/react/views/view/ReactViewGroup : android/view/ViewGro
|
||||
}
|
||||
|
||||
public class com/facebook/react/views/view/ReactViewManager : com/facebook/react/views/view/ReactClippingViewManager {
|
||||
public static final field Companion Lcom/facebook/react/views/view/ReactViewManager$Companion;
|
||||
public static final field REACT_CLASS Ljava/lang/String;
|
||||
public fun <init> ()V
|
||||
public synthetic fun createViewInstance (Lcom/facebook/react/uimanager/ThemedReactContext;)Landroid/view/View;
|
||||
public fun createViewInstance (Lcom/facebook/react/uimanager/ThemedReactContext;)Lcom/facebook/react/views/view/ReactViewGroup;
|
||||
@@ -8305,7 +8307,7 @@ public class com/facebook/react/views/view/ReactViewManager : com/facebook/react
|
||||
public fun nextFocusLeft (Lcom/facebook/react/views/view/ReactViewGroup;I)V
|
||||
public fun nextFocusRight (Lcom/facebook/react/views/view/ReactViewGroup;I)V
|
||||
public fun nextFocusUp (Lcom/facebook/react/views/view/ReactViewGroup;I)V
|
||||
protected synthetic fun prepareToRecycleView (Lcom/facebook/react/uimanager/ThemedReactContext;Landroid/view/View;)Landroid/view/View;
|
||||
public synthetic fun prepareToRecycleView (Lcom/facebook/react/uimanager/ThemedReactContext;Landroid/view/View;)Landroid/view/View;
|
||||
protected fun prepareToRecycleView (Lcom/facebook/react/uimanager/ThemedReactContext;Lcom/facebook/react/views/view/ReactViewGroup;)Lcom/facebook/react/views/view/ReactViewGroup;
|
||||
public synthetic fun receiveCommand (Landroid/view/View;ILcom/facebook/react/bridge/ReadableArray;)V
|
||||
public synthetic fun receiveCommand (Landroid/view/View;Ljava/lang/String;Lcom/facebook/react/bridge/ReadableArray;)V
|
||||
@@ -8331,7 +8333,7 @@ public class com/facebook/react/views/view/ReactViewManager : com/facebook/react
|
||||
public fun setOverflow (Lcom/facebook/react/views/view/ReactViewGroup;Ljava/lang/String;)V
|
||||
public fun setPointerEvents (Lcom/facebook/react/views/view/ReactViewGroup;Ljava/lang/String;)V
|
||||
public fun setTVPreferredFocus (Lcom/facebook/react/views/view/ReactViewGroup;Z)V
|
||||
protected synthetic fun setTransformProperty (Landroid/view/View;Lcom/facebook/react/bridge/ReadableArray;Lcom/facebook/react/bridge/ReadableArray;)V
|
||||
public synthetic fun setTransformProperty (Landroid/view/View;Lcom/facebook/react/bridge/ReadableArray;Lcom/facebook/react/bridge/ReadableArray;)V
|
||||
protected fun setTransformProperty (Lcom/facebook/react/views/view/ReactViewGroup;Lcom/facebook/react/bridge/ReadableArray;Lcom/facebook/react/bridge/ReadableArray;)V
|
||||
}
|
||||
|
||||
@@ -8342,6 +8344,9 @@ public class com/facebook/react/views/view/ReactViewManager$$PropsSetter : com/f
|
||||
public fun setProperty (Lcom/facebook/react/views/view/ReactViewManager;Lcom/facebook/react/views/view/ReactViewGroup;Ljava/lang/String;Ljava/lang/Object;)V
|
||||
}
|
||||
|
||||
public final class com/facebook/react/views/view/ReactViewManager$Companion {
|
||||
}
|
||||
|
||||
public final class com/facebook/react/views/view/ViewGroupClickEvent : com/facebook/react/uimanager/events/Event {
|
||||
public fun <init> (I)V
|
||||
public fun <init> (II)V
|
||||
|
||||
-418
@@ -1,418 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* 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.views.view;
|
||||
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.view.View;
|
||||
import androidx.annotation.Nullable;
|
||||
import com.facebook.common.logging.FLog;
|
||||
import com.facebook.infer.annotation.Nullsafe;
|
||||
import com.facebook.react.bridge.Dynamic;
|
||||
import com.facebook.react.bridge.DynamicFromObject;
|
||||
import com.facebook.react.bridge.JSApplicationIllegalArgumentException;
|
||||
import com.facebook.react.bridge.ReactContext;
|
||||
import com.facebook.react.bridge.ReadableArray;
|
||||
import com.facebook.react.bridge.ReadableMap;
|
||||
import com.facebook.react.common.MapBuilder;
|
||||
import com.facebook.react.common.ReactConstants;
|
||||
import com.facebook.react.common.annotations.VisibleForTesting;
|
||||
import com.facebook.react.module.annotations.ReactModule;
|
||||
import com.facebook.react.uimanager.BackgroundStyleApplicator;
|
||||
import com.facebook.react.uimanager.LengthPercentage;
|
||||
import com.facebook.react.uimanager.LengthPercentageType;
|
||||
import com.facebook.react.uimanager.PixelUtil;
|
||||
import com.facebook.react.uimanager.PointerEvents;
|
||||
import com.facebook.react.uimanager.Spacing;
|
||||
import com.facebook.react.uimanager.ThemedReactContext;
|
||||
import com.facebook.react.uimanager.UIManagerHelper;
|
||||
import com.facebook.react.uimanager.ViewProps;
|
||||
import com.facebook.react.uimanager.annotations.ReactProp;
|
||||
import com.facebook.react.uimanager.annotations.ReactPropGroup;
|
||||
import com.facebook.react.uimanager.common.UIManagerType;
|
||||
import com.facebook.react.uimanager.common.ViewUtil;
|
||||
import com.facebook.react.uimanager.events.EventDispatcher;
|
||||
import com.facebook.react.uimanager.style.BackgroundImageLayer;
|
||||
import com.facebook.react.uimanager.style.BorderRadiusProp;
|
||||
import com.facebook.react.uimanager.style.BorderStyle;
|
||||
import com.facebook.react.uimanager.style.LogicalEdge;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/** View manager for AndroidViews (plain React Views). */
|
||||
@ReactModule(name = ReactViewManager.REACT_CLASS)
|
||||
@Nullsafe(Nullsafe.Mode.LOCAL)
|
||||
public class ReactViewManager extends ReactClippingViewManager<ReactViewGroup> {
|
||||
|
||||
@VisibleForTesting public static final String REACT_CLASS = ViewProps.VIEW_CLASS_NAME;
|
||||
|
||||
private static final int[] SPACING_TYPES = {
|
||||
Spacing.ALL,
|
||||
Spacing.LEFT,
|
||||
Spacing.RIGHT,
|
||||
Spacing.TOP,
|
||||
Spacing.BOTTOM,
|
||||
Spacing.START,
|
||||
Spacing.END,
|
||||
Spacing.BLOCK,
|
||||
Spacing.BLOCK_END,
|
||||
Spacing.BLOCK_START
|
||||
};
|
||||
private static final int CMD_HOTSPOT_UPDATE = 1;
|
||||
private static final int CMD_SET_PRESSED = 2;
|
||||
private static final String HOTSPOT_UPDATE_KEY = "hotspotUpdate";
|
||||
|
||||
public ReactViewManager() {
|
||||
super();
|
||||
|
||||
setupViewRecycling();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected @Nullable ReactViewGroup prepareToRecycleView(
|
||||
ThemedReactContext reactContext, ReactViewGroup view) {
|
||||
// BaseViewManager
|
||||
ReactViewGroup preparedView = super.prepareToRecycleView(reactContext, view);
|
||||
if (preparedView != null) {
|
||||
preparedView.recycleView();
|
||||
}
|
||||
return view;
|
||||
}
|
||||
|
||||
@ReactProp(name = "accessible")
|
||||
public void setAccessible(ReactViewGroup view, boolean accessible) {
|
||||
view.setFocusable(accessible);
|
||||
}
|
||||
|
||||
@ReactProp(name = "hasTVPreferredFocus")
|
||||
public void setTVPreferredFocus(ReactViewGroup view, boolean hasTVPreferredFocus) {
|
||||
if (hasTVPreferredFocus) {
|
||||
view.setFocusable(true);
|
||||
view.setFocusableInTouchMode(true);
|
||||
view.requestFocus();
|
||||
}
|
||||
}
|
||||
|
||||
@ReactProp(name = ViewProps.BACKGROUND_IMAGE, customType = "BackgroundImage")
|
||||
public void setBackgroundImage(ReactViewGroup view, @Nullable ReadableArray backgroundImage) {
|
||||
if (ViewUtil.getUIManagerType(view) == UIManagerType.FABRIC) {
|
||||
if (backgroundImage != null && backgroundImage.size() > 0) {
|
||||
List<BackgroundImageLayer> backgroundImageLayers = new ArrayList<>(backgroundImage.size());
|
||||
for (int i = 0; i < backgroundImage.size(); i++) {
|
||||
ReadableMap backgroundImageMap = backgroundImage.getMap(i);
|
||||
BackgroundImageLayer layer =
|
||||
new BackgroundImageLayer(backgroundImageMap, view.getContext());
|
||||
backgroundImageLayers.add(layer);
|
||||
}
|
||||
BackgroundStyleApplicator.setBackgroundImage(view, backgroundImageLayers);
|
||||
} else {
|
||||
BackgroundStyleApplicator.setBackgroundImage(view, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ReactProp(name = "nextFocusDown", defaultInt = View.NO_ID)
|
||||
public void nextFocusDown(ReactViewGroup view, int viewId) {
|
||||
view.setNextFocusDownId(viewId);
|
||||
}
|
||||
|
||||
@ReactProp(name = "nextFocusForward", defaultInt = View.NO_ID)
|
||||
public void nextFocusForward(ReactViewGroup view, int viewId) {
|
||||
view.setNextFocusForwardId(viewId);
|
||||
}
|
||||
|
||||
@ReactProp(name = "nextFocusLeft", defaultInt = View.NO_ID)
|
||||
public void nextFocusLeft(ReactViewGroup view, int viewId) {
|
||||
view.setNextFocusLeftId(viewId);
|
||||
}
|
||||
|
||||
@ReactProp(name = "nextFocusRight", defaultInt = View.NO_ID)
|
||||
public void nextFocusRight(ReactViewGroup view, int viewId) {
|
||||
view.setNextFocusRightId(viewId);
|
||||
}
|
||||
|
||||
@ReactProp(name = "nextFocusUp", defaultInt = View.NO_ID)
|
||||
public void nextFocusUp(ReactViewGroup view, int viewId) {
|
||||
view.setNextFocusUpId(viewId);
|
||||
}
|
||||
|
||||
@ReactPropGroup(
|
||||
names = {
|
||||
ViewProps.BORDER_RADIUS,
|
||||
ViewProps.BORDER_TOP_LEFT_RADIUS,
|
||||
ViewProps.BORDER_TOP_RIGHT_RADIUS,
|
||||
ViewProps.BORDER_BOTTOM_RIGHT_RADIUS,
|
||||
ViewProps.BORDER_BOTTOM_LEFT_RADIUS,
|
||||
ViewProps.BORDER_TOP_START_RADIUS,
|
||||
ViewProps.BORDER_TOP_END_RADIUS,
|
||||
ViewProps.BORDER_BOTTOM_START_RADIUS,
|
||||
ViewProps.BORDER_BOTTOM_END_RADIUS,
|
||||
ViewProps.BORDER_END_END_RADIUS,
|
||||
ViewProps.BORDER_END_START_RADIUS,
|
||||
ViewProps.BORDER_START_END_RADIUS,
|
||||
ViewProps.BORDER_START_START_RADIUS,
|
||||
})
|
||||
public void setBorderRadius(ReactViewGroup view, int index, Dynamic rawBorderRadius) {
|
||||
@Nullable LengthPercentage borderRadius = LengthPercentage.setFromDynamic(rawBorderRadius);
|
||||
|
||||
// We do not support percentage border radii on Paper in order to be consistent with iOS (to
|
||||
// avoid developer surprise if it works on one platform but not another).
|
||||
if (ViewUtil.getUIManagerType(view) != UIManagerType.FABRIC
|
||||
&& borderRadius != null
|
||||
&& borderRadius.getType() == LengthPercentageType.PERCENT) {
|
||||
borderRadius = null;
|
||||
}
|
||||
|
||||
BackgroundStyleApplicator.setBorderRadius(view, BorderRadiusProp.values()[index], borderRadius);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Use {@link #setBorderRadius(ReactViewGroup, int, Dynamic)} instead.
|
||||
*/
|
||||
@Deprecated(since = "0.75.0", forRemoval = true)
|
||||
public void setBorderRadius(ReactViewGroup view, int index, float borderRadius) {
|
||||
setBorderRadius(view, index, new DynamicFromObject(borderRadius));
|
||||
}
|
||||
|
||||
@ReactProp(name = "borderStyle")
|
||||
public void setBorderStyle(ReactViewGroup view, @Nullable String borderStyle) {
|
||||
@Nullable
|
||||
BorderStyle parsedBorderStyle =
|
||||
borderStyle == null ? null : BorderStyle.fromString(borderStyle);
|
||||
BackgroundStyleApplicator.setBorderStyle(view, parsedBorderStyle);
|
||||
}
|
||||
|
||||
@ReactProp(name = "hitSlop")
|
||||
public void setHitSlop(final ReactViewGroup view, Dynamic hitSlop) {
|
||||
switch (hitSlop.getType()) {
|
||||
case Map:
|
||||
ReadableMap hitSlopMap = hitSlop.asMap();
|
||||
view.setHitSlopRect(
|
||||
new Rect(
|
||||
hitSlopMap.hasKey("left")
|
||||
? (int) PixelUtil.toPixelFromDIP(hitSlopMap.getDouble("left"))
|
||||
: 0,
|
||||
hitSlopMap.hasKey("top")
|
||||
? (int) PixelUtil.toPixelFromDIP(hitSlopMap.getDouble("top"))
|
||||
: 0,
|
||||
hitSlopMap.hasKey("right")
|
||||
? (int) PixelUtil.toPixelFromDIP(hitSlopMap.getDouble("right"))
|
||||
: 0,
|
||||
hitSlopMap.hasKey("bottom")
|
||||
? (int) PixelUtil.toPixelFromDIP(hitSlopMap.getDouble("bottom"))
|
||||
: 0));
|
||||
break;
|
||||
case Number:
|
||||
int hitSlopValue = (int) PixelUtil.toPixelFromDIP(hitSlop.asDouble());
|
||||
view.setHitSlopRect(new Rect(hitSlopValue, hitSlopValue, hitSlopValue, hitSlopValue));
|
||||
break;
|
||||
default:
|
||||
FLog.w(ReactConstants.TAG, "Invalid type for 'hitSlop' value " + hitSlop.getType());
|
||||
/* falls through */
|
||||
case Null:
|
||||
view.setHitSlopRect(null);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@ReactProp(name = ViewProps.POINTER_EVENTS)
|
||||
public void setPointerEvents(ReactViewGroup view, @Nullable String pointerEventsStr) {
|
||||
view.setPointerEvents(PointerEvents.parsePointerEvents(pointerEventsStr));
|
||||
}
|
||||
|
||||
@ReactProp(name = "nativeBackgroundAndroid")
|
||||
public void setNativeBackground(ReactViewGroup view, @Nullable ReadableMap bg) {
|
||||
Drawable background;
|
||||
if (bg != null) {
|
||||
background = ReactDrawableHelper.createDrawableFromJSDescription(view.getContext(), bg);
|
||||
} else {
|
||||
background = null;
|
||||
}
|
||||
BackgroundStyleApplicator.setFeedbackUnderlay(view, background);
|
||||
}
|
||||
|
||||
@ReactProp(name = "nativeForegroundAndroid")
|
||||
public void setNativeForeground(ReactViewGroup view, @Nullable ReadableMap fg) {
|
||||
view.setForeground(
|
||||
fg == null
|
||||
? null
|
||||
: ReactDrawableHelper.createDrawableFromJSDescription(view.getContext(), fg));
|
||||
}
|
||||
|
||||
@ReactProp(name = ViewProps.NEEDS_OFFSCREEN_ALPHA_COMPOSITING)
|
||||
public void setNeedsOffscreenAlphaCompositing(
|
||||
ReactViewGroup view, boolean needsOffscreenAlphaCompositing) {
|
||||
view.setNeedsOffscreenAlphaCompositing(needsOffscreenAlphaCompositing);
|
||||
}
|
||||
|
||||
@ReactPropGroup(
|
||||
names = {
|
||||
ViewProps.BORDER_WIDTH,
|
||||
ViewProps.BORDER_LEFT_WIDTH,
|
||||
ViewProps.BORDER_RIGHT_WIDTH,
|
||||
ViewProps.BORDER_TOP_WIDTH,
|
||||
ViewProps.BORDER_BOTTOM_WIDTH,
|
||||
ViewProps.BORDER_START_WIDTH,
|
||||
ViewProps.BORDER_END_WIDTH,
|
||||
},
|
||||
defaultFloat = Float.NaN)
|
||||
public void setBorderWidth(ReactViewGroup view, int index, float width) {
|
||||
BackgroundStyleApplicator.setBorderWidth(view, LogicalEdge.values()[index], width);
|
||||
}
|
||||
|
||||
@ReactPropGroup(
|
||||
names = {
|
||||
ViewProps.BORDER_COLOR,
|
||||
ViewProps.BORDER_LEFT_COLOR,
|
||||
ViewProps.BORDER_RIGHT_COLOR,
|
||||
ViewProps.BORDER_TOP_COLOR,
|
||||
ViewProps.BORDER_BOTTOM_COLOR,
|
||||
ViewProps.BORDER_START_COLOR,
|
||||
ViewProps.BORDER_END_COLOR,
|
||||
ViewProps.BORDER_BLOCK_COLOR,
|
||||
ViewProps.BORDER_BLOCK_END_COLOR,
|
||||
ViewProps.BORDER_BLOCK_START_COLOR
|
||||
},
|
||||
customType = "Color")
|
||||
public void setBorderColor(ReactViewGroup view, int index, @Nullable Integer color) {
|
||||
BackgroundStyleApplicator.setBorderColor(
|
||||
view, LogicalEdge.fromSpacingType(SPACING_TYPES[index]), color);
|
||||
}
|
||||
|
||||
@ReactProp(name = ViewProps.COLLAPSABLE)
|
||||
public void setCollapsable(ReactViewGroup view, boolean collapsable) {
|
||||
// no-op: it's here only so that "collapsable" property is exported to JS. The value is actually
|
||||
// handled in NativeViewHierarchyOptimizer
|
||||
}
|
||||
|
||||
@ReactProp(name = ViewProps.COLLAPSABLE_CHILDREN)
|
||||
public void setCollapsableChildren(ReactViewGroup view, boolean collapsableChildren) {
|
||||
// no-op: it's here only so that "collapsableChildren" property is exported to JS.
|
||||
}
|
||||
|
||||
@ReactProp(name = "focusable")
|
||||
public void setFocusable(final ReactViewGroup view, boolean focusable) {
|
||||
if (focusable) {
|
||||
view.setOnClickListener(
|
||||
new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
final EventDispatcher mEventDispatcher =
|
||||
UIManagerHelper.getEventDispatcherForReactTag(
|
||||
(ReactContext) view.getContext(), view.getId());
|
||||
if (mEventDispatcher == null) {
|
||||
return;
|
||||
}
|
||||
mEventDispatcher.dispatchEvent(
|
||||
new ViewGroupClickEvent(
|
||||
UIManagerHelper.getSurfaceId(view.getContext()), view.getId()));
|
||||
}
|
||||
});
|
||||
|
||||
// Clickable elements are focusable. On API 26, this is taken care by setClickable.
|
||||
// Explicitly calling setFocusable here for backward compatibility.
|
||||
view.setFocusable(true /*isFocusable*/);
|
||||
} else {
|
||||
view.setOnClickListener(null);
|
||||
view.setClickable(false);
|
||||
// Don't set view.setFocusable(false) because we might still want it to be focusable for
|
||||
// accessibility reasons
|
||||
}
|
||||
}
|
||||
|
||||
@ReactProp(name = ViewProps.OVERFLOW)
|
||||
public void setOverflow(ReactViewGroup view, String overflow) {
|
||||
view.setOverflow(overflow);
|
||||
}
|
||||
|
||||
@ReactProp(name = "backfaceVisibility")
|
||||
public void setBackfaceVisibility(ReactViewGroup view, String backfaceVisibility) {
|
||||
view.setBackfaceVisibility(backfaceVisibility);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setOpacity(ReactViewGroup view, float opacity) {
|
||||
view.setOpacityIfPossible(opacity);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setTransformProperty(
|
||||
ReactViewGroup view,
|
||||
@Nullable ReadableArray transforms,
|
||||
@Nullable ReadableArray transformOrigin) {
|
||||
super.setTransformProperty(view, transforms, transformOrigin);
|
||||
view.setBackfaceVisibilityDependantOpacity();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return REACT_CLASS;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ReactViewGroup createViewInstance(ThemedReactContext context) {
|
||||
return new ReactViewGroup(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Integer> getCommandsMap() {
|
||||
return MapBuilder.of(HOTSPOT_UPDATE_KEY, CMD_HOTSPOT_UPDATE, "setPressed", CMD_SET_PRESSED);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void receiveCommand(ReactViewGroup root, int commandId, @Nullable ReadableArray args) {
|
||||
switch (commandId) {
|
||||
case CMD_HOTSPOT_UPDATE:
|
||||
{
|
||||
handleHotspotUpdate(root, args);
|
||||
break;
|
||||
}
|
||||
case CMD_SET_PRESSED:
|
||||
{
|
||||
handleSetPressed(root, args);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void receiveCommand(ReactViewGroup root, String commandId, @Nullable ReadableArray args) {
|
||||
switch (commandId) {
|
||||
case HOTSPOT_UPDATE_KEY:
|
||||
{
|
||||
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");
|
||||
}
|
||||
|
||||
float x = PixelUtil.toPixelFromDIP(args.getDouble(0));
|
||||
float y = PixelUtil.toPixelFromDIP(args.getDouble(1));
|
||||
root.drawableHotspotChanged(x, y);
|
||||
}
|
||||
}
|
||||
+372
@@ -0,0 +1,372 @@
|
||||
/*
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* 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.views.view
|
||||
|
||||
import android.graphics.Rect
|
||||
import android.view.View
|
||||
import com.facebook.common.logging.FLog
|
||||
import com.facebook.react.bridge.Dynamic
|
||||
import com.facebook.react.bridge.DynamicFromObject
|
||||
import com.facebook.react.bridge.JSApplicationIllegalArgumentException
|
||||
import com.facebook.react.bridge.ReactContext
|
||||
import com.facebook.react.bridge.ReadableArray
|
||||
import com.facebook.react.bridge.ReadableMap
|
||||
import com.facebook.react.bridge.ReadableType
|
||||
import com.facebook.react.common.ReactConstants
|
||||
import com.facebook.react.module.annotations.ReactModule
|
||||
import com.facebook.react.uimanager.BackgroundStyleApplicator
|
||||
import com.facebook.react.uimanager.LengthPercentage
|
||||
import com.facebook.react.uimanager.LengthPercentageType
|
||||
import com.facebook.react.uimanager.PixelUtil.dpToPx
|
||||
import com.facebook.react.uimanager.PointerEvents
|
||||
import com.facebook.react.uimanager.Spacing
|
||||
import com.facebook.react.uimanager.ThemedReactContext
|
||||
import com.facebook.react.uimanager.UIManagerHelper
|
||||
import com.facebook.react.uimanager.ViewProps
|
||||
import com.facebook.react.uimanager.annotations.ReactProp
|
||||
import com.facebook.react.uimanager.annotations.ReactPropGroup
|
||||
import com.facebook.react.uimanager.common.UIManagerType
|
||||
import com.facebook.react.uimanager.common.ViewUtil
|
||||
import com.facebook.react.uimanager.style.BackgroundImageLayer
|
||||
import com.facebook.react.uimanager.style.BorderRadiusProp
|
||||
import com.facebook.react.uimanager.style.BorderStyle
|
||||
import com.facebook.react.uimanager.style.LogicalEdge
|
||||
|
||||
/** View manager for AndroidViews (plain React Views). */
|
||||
@ReactModule(name = ReactViewManager.REACT_CLASS)
|
||||
public open class ReactViewManager : ReactClippingViewManager<ReactViewGroup>() {
|
||||
|
||||
public companion object {
|
||||
public const val REACT_CLASS: String = ViewProps.VIEW_CLASS_NAME
|
||||
|
||||
private val SPACING_TYPES =
|
||||
intArrayOf(
|
||||
Spacing.ALL,
|
||||
Spacing.LEFT,
|
||||
Spacing.RIGHT,
|
||||
Spacing.TOP,
|
||||
Spacing.BOTTOM,
|
||||
Spacing.START,
|
||||
Spacing.END,
|
||||
Spacing.BLOCK,
|
||||
Spacing.BLOCK_END,
|
||||
Spacing.BLOCK_START,
|
||||
)
|
||||
private const val CMD_HOTSPOT_UPDATE = 1
|
||||
private const val CMD_SET_PRESSED = 2
|
||||
private const val HOTSPOT_UPDATE_KEY = "hotspotUpdate"
|
||||
}
|
||||
|
||||
init {
|
||||
setupViewRecycling()
|
||||
}
|
||||
|
||||
override fun prepareToRecycleView(
|
||||
reactContext: ThemedReactContext,
|
||||
view: ReactViewGroup
|
||||
): ReactViewGroup {
|
||||
// BaseViewManager
|
||||
val preparedView = super.prepareToRecycleView(reactContext, view)
|
||||
preparedView?.recycleView()
|
||||
return view
|
||||
}
|
||||
|
||||
@ReactProp(name = "accessible")
|
||||
public open fun setAccessible(view: ReactViewGroup, accessible: Boolean) {
|
||||
view.isFocusable = accessible
|
||||
}
|
||||
|
||||
@ReactProp(name = "hasTVPreferredFocus")
|
||||
public open fun setTVPreferredFocus(view: ReactViewGroup, hasTVPreferredFocus: Boolean) {
|
||||
if (hasTVPreferredFocus) {
|
||||
view.isFocusable = true
|
||||
view.isFocusableInTouchMode = true
|
||||
view.requestFocus()
|
||||
}
|
||||
}
|
||||
|
||||
@ReactProp(name = ViewProps.BACKGROUND_IMAGE, customType = "BackgroundImage")
|
||||
public open fun setBackgroundImage(view: ReactViewGroup, backgroundImage: ReadableArray?) {
|
||||
if (ViewUtil.getUIManagerType(view) == UIManagerType.FABRIC) {
|
||||
if (backgroundImage != null && backgroundImage.size() > 0) {
|
||||
val backgroundImageLayers = ArrayList<BackgroundImageLayer>(backgroundImage.size())
|
||||
for (i in 0 until backgroundImage.size()) {
|
||||
val backgroundImageMap = backgroundImage.getMap(i)
|
||||
val layer = BackgroundImageLayer(backgroundImageMap, view.context)
|
||||
backgroundImageLayers.add(layer)
|
||||
}
|
||||
BackgroundStyleApplicator.setBackgroundImage(view, backgroundImageLayers)
|
||||
} else {
|
||||
BackgroundStyleApplicator.setBackgroundImage(view, null)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ReactProp(name = "nextFocusDown", defaultInt = View.NO_ID)
|
||||
public open fun nextFocusDown(view: ReactViewGroup, viewId: Int) {
|
||||
view.nextFocusDownId = viewId
|
||||
}
|
||||
|
||||
@ReactProp(name = "nextFocusForward", defaultInt = View.NO_ID)
|
||||
public open fun nextFocusForward(view: ReactViewGroup, viewId: Int) {
|
||||
view.nextFocusForwardId = viewId
|
||||
}
|
||||
|
||||
@ReactProp(name = "nextFocusLeft", defaultInt = View.NO_ID)
|
||||
public open fun nextFocusLeft(view: ReactViewGroup, viewId: Int) {
|
||||
view.nextFocusLeftId = viewId
|
||||
}
|
||||
|
||||
@ReactProp(name = "nextFocusRight", defaultInt = View.NO_ID)
|
||||
public open fun nextFocusRight(view: ReactViewGroup, viewId: Int) {
|
||||
view.nextFocusRightId = viewId
|
||||
}
|
||||
|
||||
@ReactProp(name = "nextFocusUp", defaultInt = View.NO_ID)
|
||||
public open fun nextFocusUp(view: ReactViewGroup, viewId: Int) {
|
||||
view.nextFocusUpId = viewId
|
||||
}
|
||||
|
||||
@ReactPropGroup(
|
||||
names =
|
||||
[
|
||||
ViewProps.BORDER_RADIUS,
|
||||
ViewProps.BORDER_TOP_LEFT_RADIUS,
|
||||
ViewProps.BORDER_TOP_RIGHT_RADIUS,
|
||||
ViewProps.BORDER_BOTTOM_RIGHT_RADIUS,
|
||||
ViewProps.BORDER_BOTTOM_LEFT_RADIUS,
|
||||
ViewProps.BORDER_TOP_START_RADIUS,
|
||||
ViewProps.BORDER_TOP_END_RADIUS,
|
||||
ViewProps.BORDER_BOTTOM_START_RADIUS,
|
||||
ViewProps.BORDER_BOTTOM_END_RADIUS,
|
||||
ViewProps.BORDER_END_END_RADIUS,
|
||||
ViewProps.BORDER_END_START_RADIUS,
|
||||
ViewProps.BORDER_START_END_RADIUS,
|
||||
ViewProps.BORDER_START_START_RADIUS,
|
||||
])
|
||||
public open fun setBorderRadius(view: ReactViewGroup, index: Int, rawBorderRadius: Dynamic) {
|
||||
var borderRadius = LengthPercentage.setFromDynamic(rawBorderRadius)
|
||||
|
||||
// We do not support percentage border radii on Paper in order to be consistent with iOS (to
|
||||
// avoid developer surprise if it works on one platform but not another).
|
||||
if (ViewUtil.getUIManagerType(view) != UIManagerType.FABRIC &&
|
||||
borderRadius != null &&
|
||||
borderRadius.type == LengthPercentageType.PERCENT) {
|
||||
borderRadius = null
|
||||
}
|
||||
|
||||
BackgroundStyleApplicator.setBorderRadius(view, BorderRadiusProp.values()[index], borderRadius)
|
||||
}
|
||||
|
||||
@Deprecated(
|
||||
"Don't use setBorderRadius(view, int, Float) as it was deprecated in React Native 0.75.0.",
|
||||
ReplaceWith("setBorderRadius(view, index, DynamicFromObject(borderRadius)"))
|
||||
public open fun setBorderRadius(view: ReactViewGroup, index: Int, borderRadius: Float) {
|
||||
setBorderRadius(view, index, DynamicFromObject(borderRadius))
|
||||
}
|
||||
|
||||
@ReactProp(name = "borderStyle")
|
||||
public open fun setBorderStyle(view: ReactViewGroup, borderStyle: String?) {
|
||||
val parsedBorderStyle = if (borderStyle == null) null else BorderStyle.fromString(borderStyle)
|
||||
BackgroundStyleApplicator.setBorderStyle(view, parsedBorderStyle)
|
||||
}
|
||||
|
||||
@ReactProp(name = "hitSlop")
|
||||
public open fun setHitSlop(view: ReactViewGroup, hitSlop: Dynamic) {
|
||||
when (hitSlop.type) {
|
||||
ReadableType.Map -> {
|
||||
val hitSlopMap = hitSlop.asMap()
|
||||
view.setHitSlopRect(
|
||||
Rect(
|
||||
hitSlopMap.px("left"),
|
||||
hitSlopMap.px("top"),
|
||||
hitSlopMap.px("right"),
|
||||
hitSlopMap.px("bottom"),
|
||||
))
|
||||
}
|
||||
|
||||
ReadableType.Number -> {
|
||||
val hitSlopValue = hitSlop.asDouble().dpToPx().toInt()
|
||||
view.setHitSlopRect(Rect(hitSlopValue, hitSlopValue, hitSlopValue, hitSlopValue))
|
||||
}
|
||||
|
||||
ReadableType.Null -> view.setHitSlopRect(null)
|
||||
else -> {
|
||||
FLog.w(ReactConstants.TAG, "Invalid type for 'hitSlop' value ${hitSlop.type}")
|
||||
view.setHitSlopRect(null)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun ReadableMap.px(key: String) = if (hasKey(key)) getDouble(key).dpToPx().toInt() else 0
|
||||
|
||||
@ReactProp(name = ViewProps.POINTER_EVENTS)
|
||||
public open fun setPointerEvents(view: ReactViewGroup, pointerEventsStr: String?) {
|
||||
view.pointerEvents = PointerEvents.parsePointerEvents(pointerEventsStr)
|
||||
}
|
||||
|
||||
@ReactProp(name = "nativeBackgroundAndroid")
|
||||
public open fun setNativeBackground(view: ReactViewGroup, background: ReadableMap?) {
|
||||
val bg =
|
||||
background?.let { ReactDrawableHelper.createDrawableFromJSDescription(view.context, it) }
|
||||
BackgroundStyleApplicator.setFeedbackUnderlay(view, bg)
|
||||
}
|
||||
|
||||
@ReactProp(name = "nativeForegroundAndroid")
|
||||
public open fun setNativeForeground(view: ReactViewGroup, foreground: ReadableMap?) {
|
||||
view.foreground =
|
||||
foreground?.let { ReactDrawableHelper.createDrawableFromJSDescription(view.context, it) }
|
||||
}
|
||||
|
||||
@ReactProp(name = ViewProps.NEEDS_OFFSCREEN_ALPHA_COMPOSITING)
|
||||
public open fun setNeedsOffscreenAlphaCompositing(
|
||||
view: ReactViewGroup,
|
||||
needsOffscreenAlphaCompositing: Boolean
|
||||
) {
|
||||
view.setNeedsOffscreenAlphaCompositing(needsOffscreenAlphaCompositing)
|
||||
}
|
||||
|
||||
@ReactPropGroup(
|
||||
names =
|
||||
[
|
||||
ViewProps.BORDER_WIDTH,
|
||||
ViewProps.BORDER_LEFT_WIDTH,
|
||||
ViewProps.BORDER_RIGHT_WIDTH,
|
||||
ViewProps.BORDER_TOP_WIDTH,
|
||||
ViewProps.BORDER_BOTTOM_WIDTH,
|
||||
ViewProps.BORDER_START_WIDTH,
|
||||
ViewProps.BORDER_END_WIDTH,
|
||||
],
|
||||
defaultFloat = Float.NaN)
|
||||
public open fun setBorderWidth(view: ReactViewGroup, index: Int, width: Float) {
|
||||
BackgroundStyleApplicator.setBorderWidth(view, LogicalEdge.values()[index], width)
|
||||
}
|
||||
|
||||
@ReactPropGroup(
|
||||
names =
|
||||
[
|
||||
ViewProps.BORDER_COLOR,
|
||||
ViewProps.BORDER_LEFT_COLOR,
|
||||
ViewProps.BORDER_RIGHT_COLOR,
|
||||
ViewProps.BORDER_TOP_COLOR,
|
||||
ViewProps.BORDER_BOTTOM_COLOR,
|
||||
ViewProps.BORDER_START_COLOR,
|
||||
ViewProps.BORDER_END_COLOR,
|
||||
ViewProps.BORDER_BLOCK_COLOR,
|
||||
ViewProps.BORDER_BLOCK_END_COLOR,
|
||||
ViewProps.BORDER_BLOCK_START_COLOR,
|
||||
],
|
||||
customType = "Color")
|
||||
public open fun setBorderColor(view: ReactViewGroup, index: Int, color: Int?) {
|
||||
BackgroundStyleApplicator.setBorderColor(
|
||||
view, LogicalEdge.fromSpacingType(SPACING_TYPES[index]), color)
|
||||
}
|
||||
|
||||
@ReactProp(name = ViewProps.COLLAPSABLE)
|
||||
@Suppress("UNUSED_PARAMETER")
|
||||
public open fun setCollapsable(view: ReactViewGroup, collapsable: Boolean) {
|
||||
// no-op: it's here only so that "collapsable" property is exported to JS. The value is actually
|
||||
// handled in NativeViewHierarchyOptimizer
|
||||
}
|
||||
|
||||
@ReactProp(name = ViewProps.COLLAPSABLE_CHILDREN)
|
||||
@Suppress("UNUSED_PARAMETER")
|
||||
public open fun setCollapsableChildren(view: ReactViewGroup, collapsableChildren: Boolean) {
|
||||
// no-op: it's here only so that "collapsableChildren" property is exported to JS.
|
||||
}
|
||||
|
||||
@ReactProp(name = "focusable")
|
||||
public open fun setFocusable(view: ReactViewGroup, focusable: Boolean) {
|
||||
if (focusable) {
|
||||
view.setOnClickListener {
|
||||
val eventDispatcher =
|
||||
UIManagerHelper.getEventDispatcherForReactTag((view.context as ReactContext), view.id)
|
||||
eventDispatcher?.dispatchEvent(
|
||||
ViewGroupClickEvent(UIManagerHelper.getSurfaceId(view.context), view.id))
|
||||
}
|
||||
|
||||
// Clickable elements are focusable. On API 26, this is taken care by setClickable.
|
||||
// Explicitly calling setFocusable here for backward compatibility.
|
||||
view.isFocusable = true
|
||||
} else {
|
||||
view.setOnClickListener(null)
|
||||
view.isClickable = false
|
||||
// Don't set view.setFocusable(false) because we might still want it to be focusable for
|
||||
// accessibility reasons
|
||||
}
|
||||
}
|
||||
|
||||
@ReactProp(name = ViewProps.OVERFLOW)
|
||||
public open fun setOverflow(view: ReactViewGroup, overflow: String?) {
|
||||
view.overflow = overflow
|
||||
}
|
||||
|
||||
@ReactProp(name = "backfaceVisibility")
|
||||
public open fun setBackfaceVisibility(view: ReactViewGroup, backfaceVisibility: String) {
|
||||
view.setBackfaceVisibility(backfaceVisibility)
|
||||
}
|
||||
|
||||
override fun setOpacity(view: ReactViewGroup, opacity: Float) {
|
||||
view.setOpacityIfPossible(opacity)
|
||||
}
|
||||
|
||||
override fun setTransformProperty(
|
||||
view: ReactViewGroup,
|
||||
transforms: ReadableArray?,
|
||||
transformOrigin: ReadableArray?
|
||||
) {
|
||||
super.setTransformProperty(view, transforms, transformOrigin)
|
||||
view.setBackfaceVisibilityDependantOpacity()
|
||||
}
|
||||
|
||||
override fun getName(): String = REACT_CLASS
|
||||
|
||||
public override fun createViewInstance(context: ThemedReactContext): ReactViewGroup =
|
||||
ReactViewGroup(context)
|
||||
|
||||
override fun getCommandsMap(): MutableMap<String, Int> =
|
||||
mutableMapOf(HOTSPOT_UPDATE_KEY to CMD_HOTSPOT_UPDATE, "setPressed" to CMD_SET_PRESSED)
|
||||
|
||||
@Deprecated(
|
||||
"Use receiveCommand(View, String, ReadableArray)",
|
||||
ReplaceWith("receiveCommand(root, commandIdString, args)"))
|
||||
override fun receiveCommand(root: ReactViewGroup, commandId: Int, args: ReadableArray?) {
|
||||
when (commandId) {
|
||||
CMD_HOTSPOT_UPDATE -> handleHotspotUpdate(root, args)
|
||||
CMD_SET_PRESSED -> handleSetPressed(root, args)
|
||||
else -> {}
|
||||
}
|
||||
}
|
||||
|
||||
override fun receiveCommand(root: ReactViewGroup, commandId: String, args: ReadableArray?) {
|
||||
when (commandId) {
|
||||
HOTSPOT_UPDATE_KEY -> handleHotspotUpdate(root, args)
|
||||
"setPressed" -> handleSetPressed(root, args)
|
||||
else -> {}
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleSetPressed(root: ReactViewGroup, args: ReadableArray?) {
|
||||
if (args == null || args.size() != 1) {
|
||||
throw JSApplicationIllegalArgumentException(
|
||||
"Illegal number of arguments for 'setPressed' command")
|
||||
}
|
||||
root.isPressed = args.getBoolean(0)
|
||||
}
|
||||
|
||||
private fun handleHotspotUpdate(root: ReactViewGroup, args: ReadableArray?) {
|
||||
if (args == null || args.size() != 2) {
|
||||
throw JSApplicationIllegalArgumentException(
|
||||
"Illegal number of arguments for 'updateHotspot' command")
|
||||
}
|
||||
|
||||
val x = args.getDouble(0).dpToPx()
|
||||
val y = args.getDouble(1).dpToPx()
|
||||
root.drawableHotspotChanged(x, y)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user