Back out "Redo "[RN][Android] Convert ReactViewManager, ReactClippingViewManager to Kotlin""

Summary: Backing out the stack since a same crash that previously effected many apps appeared again, and there are changes soon landing that will add more conflicts.

Reviewed By: Abbondanzo

Differential Revision: D63493332

fbshipit-source-id: 4423bf41c793e00a0aa22d12a77bca69d3b1ae77
This commit is contained in:
Nick Gerleman
2024-09-26 15:50:26 -07:00
committed by Facebook GitHub Bot
parent 8d22e3b249
commit 3f880991aa
5 changed files with 522 additions and 470 deletions
@@ -8363,8 +8363,6 @@ 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;
@@ -8375,7 +8373,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
public synthetic fun prepareToRecycleView (Lcom/facebook/react/uimanager/ThemedReactContext;Landroid/view/View;)Landroid/view/View;
protected 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
@@ -8404,7 +8402,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
public synthetic fun setTransformProperty (Landroid/view/View;Lcom/facebook/react/bridge/ReadableArray;Lcom/facebook/react/bridge/ReadableArray;)V
protected 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
}
@@ -8415,9 +8413,6 @@ 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
@@ -0,0 +1,95 @@
/*
* 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.view.View;
import androidx.annotation.Nullable;
import com.facebook.infer.annotation.Nullsafe;
import com.facebook.react.bridge.UiThreadUtil;
import com.facebook.react.uimanager.ViewGroupManager;
import com.facebook.react.uimanager.annotations.ReactProp;
/**
* View manager which handles clipped subviews. Useful for custom views which extends from {@link
* com.facebook.react.views.view.ReactViewGroup}
*/
@Nullsafe(Nullsafe.Mode.LOCAL)
public abstract class ReactClippingViewManager<T extends ReactViewGroup>
extends ViewGroupManager<T> {
@ReactProp(
name = com.facebook.react.uimanager.ReactClippingViewGroupHelper.PROP_REMOVE_CLIPPED_SUBVIEWS)
public void setRemoveClippedSubviews(T view, boolean removeClippedSubviews) {
UiThreadUtil.assertOnUiThread();
view.setRemoveClippedSubviews(removeClippedSubviews);
}
@Override
public void addView(T parent, View child, int index) {
UiThreadUtil.assertOnUiThread();
boolean removeClippedSubviews = parent.getRemoveClippedSubviews();
if (removeClippedSubviews) {
parent.addViewWithSubviewClippingEnabled(child, index);
} else {
parent.addView(child, index);
}
}
@Override
public int getChildCount(T parent) {
boolean removeClippedSubviews = parent.getRemoveClippedSubviews();
if (removeClippedSubviews) {
return parent.getAllChildrenCount();
} else {
return parent.getChildCount();
}
}
@Override
@Nullable
public View getChildAt(T parent, int index) {
boolean removeClippedSubviews = parent.getRemoveClippedSubviews();
if (removeClippedSubviews) {
return parent.getChildAtWithSubviewClippingEnabled(index);
} else {
return parent.getChildAt(index);
}
}
@Override
public void removeViewAt(T parent, int index) {
UiThreadUtil.assertOnUiThread();
boolean removeClippedSubviews = parent.getRemoveClippedSubviews();
if (removeClippedSubviews) {
View child = getChildAt(parent, index);
if (child != null) {
if (child.getParent() != null) {
parent.removeView(child);
}
parent.removeViewWithSubviewClippingEnabled(child);
}
} else {
parent.removeViewAt(index);
}
}
@Override
public void removeAllViews(T parent) {
UiThreadUtil.assertOnUiThread();
boolean removeClippedSubviews = parent.getRemoveClippedSubviews();
if (removeClippedSubviews) {
parent.removeAllViewsWithSubviewClippingEnabled();
} else {
parent.removeAllViews();
}
}
}
@@ -1,73 +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.view.View
import com.facebook.react.bridge.UiThreadUtil
import com.facebook.react.uimanager.ReactClippingViewGroupHelper
import com.facebook.react.uimanager.ViewGroupManager
import com.facebook.react.uimanager.annotations.ReactProp
/**
* View manager which handles clipped subviews. Useful for custom views which extends from
* [com.facebook.react.views.view.ReactViewGroup]
*/
public abstract class ReactClippingViewManager<T : ReactViewGroup> : ViewGroupManager<T>() {
@ReactProp(name = ReactClippingViewGroupHelper.PROP_REMOVE_CLIPPED_SUBVIEWS)
public open fun setRemoveClippedSubviews(view: T, removeClippedSubviews: Boolean) {
UiThreadUtil.assertOnUiThread()
view.removeClippedSubviews = removeClippedSubviews
}
override fun addView(parent: T, child: View, index: Int) {
UiThreadUtil.assertOnUiThread()
if (parent.removeClippedSubviews) {
parent.addViewWithSubviewClippingEnabled(child, index)
} else {
parent.addView(child, index)
}
}
override fun getChildCount(parent: T): Int =
if (parent.removeClippedSubviews) {
parent.allChildrenCount
} else {
parent.childCount
}
override fun getChildAt(parent: T, index: Int): View? =
if (parent.removeClippedSubviews) {
parent.getChildAtWithSubviewClippingEnabled(index)
} else {
parent.getChildAt(index)
}
override fun removeViewAt(parent: T, index: Int) {
UiThreadUtil.assertOnUiThread()
if (parent.removeClippedSubviews) {
val child = getChildAt(parent, index) ?: return
if (child.parent != null) {
parent.removeView(child)
} else {
parent.removeViewWithSubviewClippingEnabled(child)
}
} else {
parent.removeViewAt(index)
}
}
override fun removeAllViews(parent: T) {
UiThreadUtil.assertOnUiThread()
if (parent.removeClippedSubviews) {
parent.removeAllViewsWithSubviewClippingEnabled()
} else {
parent.removeAllViews()
}
}
}
@@ -0,0 +1,425 @@
/*
* 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 androidx.annotation.ColorInt;
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) {
view.setTranslucentBackgroundDrawable(
bg == null
? null
: ReactDrawableHelper.createDrawableFromJSDescription(view.getContext(), bg));
}
@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();
}
@ReactProp(name = ViewProps.BOX_SHADOW, customType = "BoxShadow")
public void setBoxShadow(ReactViewGroup view, @Nullable ReadableArray shadows) {
BackgroundStyleApplicator.setBoxShadow(view, shadows);
}
@Override
public void setBackgroundColor(ReactViewGroup view, @ColorInt int backgroundColor) {
BackgroundStyleApplicator.setBackgroundColor(view, backgroundColor);
}
@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);
}
}
@@ -1,390 +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.view.View
import androidx.annotation.ColorInt
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.common.annotations.UnstableReactNativeAPI
import com.facebook.react.module.annotations.ReactModule
import com.facebook.react.uimanager.BackgroundStyleApplicator
import com.facebook.react.uimanager.LengthPercentage.Companion.setFromDynamic
import com.facebook.react.uimanager.LengthPercentageType
import com.facebook.react.uimanager.PixelUtil.dpToPx
import com.facebook.react.uimanager.PointerEvents.Companion.parsePointerEvents
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.BorderStyle.Companion.fromString
import com.facebook.react.uimanager.style.LogicalEdge
import com.facebook.react.uimanager.style.LogicalEdge.Companion.fromSpacingType
/** View manager for AndroidViews (plain React Views). */
@ReactModule(name = ReactViewManager.REACT_CLASS)
public open class ReactViewManager : ReactClippingViewManager<ReactViewGroup>() {
init {
setupViewRecycling()
}
override fun prepareToRecycleView(
reactContext: ThemedReactContext,
view: ReactViewGroup
): ReactViewGroup {
// BaseViewManager
super.prepareToRecycleView(reactContext, view)?.recycleView()
return view
}
@ReactProp(name = "accessible")
public open fun setAccessible(view: ReactViewGroup, accessible: Boolean): Unit {
view.isFocusable = accessible
}
@ReactProp(name = "hasTVPreferredFocus")
public open fun setTVPreferredFocus(view: ReactViewGroup, hasTVPreferredFocus: Boolean): Unit {
if (hasTVPreferredFocus) {
view.isFocusable = true
view.isFocusableInTouchMode = true
view.requestFocus()
}
}
@OptIn(UnstableReactNativeAPI::class)
@ReactProp(name = ViewProps.BACKGROUND_IMAGE, customType = "BackgroundImage")
public open fun setBackgroundImage(view: ReactViewGroup, backgroundImage: ReadableArray?): Unit {
if (ViewUtil.getUIManagerType(view) == UIManagerType.FABRIC) {
val size = backgroundImage?.size()
if (size != null && size > 0) {
val backgroundImageLayers = ArrayList<BackgroundImageLayer>(size)
repeat(size) { i ->
backgroundImageLayers.add(BackgroundImageLayer(backgroundImage.getMap(i), view.context))
}
BackgroundStyleApplicator.setBackgroundImage(view, backgroundImageLayers)
} else {
BackgroundStyleApplicator.setBackgroundImage(view, null)
}
}
}
@ReactProp(name = "nextFocusDown", defaultInt = View.NO_ID)
public open fun nextFocusDown(view: ReactViewGroup, viewId: Int): Unit {
view.nextFocusDownId = viewId
}
@ReactProp(name = "nextFocusForward", defaultInt = View.NO_ID)
public open fun nextFocusForward(view: ReactViewGroup, viewId: Int): Unit {
view.nextFocusForwardId = viewId
}
@ReactProp(name = "nextFocusLeft", defaultInt = View.NO_ID)
public open fun nextFocusLeft(view: ReactViewGroup, viewId: Int): Unit {
view.nextFocusLeftId = viewId
}
@ReactProp(name = "nextFocusRight", defaultInt = View.NO_ID)
public open fun nextFocusRight(view: ReactViewGroup, viewId: Int): Unit {
view.nextFocusRightId = viewId
}
@ReactProp(name = "nextFocusUp", defaultInt = View.NO_ID)
public open fun nextFocusUp(view: ReactViewGroup, viewId: Int): Unit {
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
): Unit {
var borderRadius = 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?.type == LengthPercentageType.PERCENT) {
borderRadius = null
}
BackgroundStyleApplicator.setBorderRadius(view, BorderRadiusProp.values()[index], borderRadius)
}
@Deprecated(
"Use setBorderRadius(ReactViewGroup, int, Dynamic) instead.",
ReplaceWith(
"setBorderRadius(view, index, DynamicFromObject(borderRadius))",
"com.facebook.react.bridge.DynamicFromObject.DynamicFromObject"))
public open fun setBorderRadius(view: ReactViewGroup, index: Int, borderRadius: Float): Unit {
this.setBorderRadius(view, index, DynamicFromObject(borderRadius))
}
@ReactProp(name = "borderStyle")
public open fun setBorderStyle(view: ReactViewGroup, borderStyle: String?): Unit {
val parsedBorderStyle = borderStyle?.let { BorderStyle.fromString(it) }
BackgroundStyleApplicator.setBorderStyle(view, parsedBorderStyle)
}
@ReactProp(name = "hitSlop")
public open fun setHitSlop(view: ReactViewGroup, hitSlop: Dynamic): Unit {
when (hitSlop.type) {
ReadableType.Map -> {
val hitSlopMap = hitSlop.asMap()
view.setHitSlopRect(
Rect(
getPixels(hitSlopMap, "left"),
getPixels(hitSlopMap, "top"),
getPixels(hitSlopMap, "right"),
getPixels(hitSlopMap, "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 getPixels(map: ReadableMap, key: String): Int =
if (map.hasKey(key)) {
map.getDouble(key).dpToPx().toInt()
} else {
0
}
@ReactProp(name = ViewProps.POINTER_EVENTS)
public open fun setPointerEvents(view: ReactViewGroup, pointerEventsStr: String?): Unit {
view.setPointerEvents(parsePointerEvents(pointerEventsStr))
}
@ReactProp(name = "nativeBackgroundAndroid")
public open fun setNativeBackground(view: ReactViewGroup, background: ReadableMap?): Unit {
val translucentBg =
background?.let { ReactDrawableHelper.createDrawableFromJSDescription(view.context, it) }
BackgroundStyleApplicator.setFeedbackUnderlay(view, translucentBg)
}
@ReactProp(name = "nativeForegroundAndroid")
public open fun setNativeForeground(view: ReactViewGroup, foreground: ReadableMap?): Unit {
view.foreground =
foreground?.let { ReactDrawableHelper.createDrawableFromJSDescription(view.context, it) }
}
@ReactProp(name = ViewProps.NEEDS_OFFSCREEN_ALPHA_COMPOSITING)
public open fun setNeedsOffscreenAlphaCompositing(
view: ReactViewGroup,
needsOffscreenAlphaCompositing: Boolean
): Unit {
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): Unit {
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?): Unit {
BackgroundStyleApplicator.setBorderColor(
view, LogicalEdge.fromSpacingType(SPACING_TYPES[index]), color)
}
@ReactProp(name = ViewProps.COLLAPSABLE)
@Suppress("UNUSED_PARAMETER")
public open fun setCollapsable(view: ReactViewGroup?, collapsable: Boolean): Unit {
// 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
): Unit {
// 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): Unit {
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): Unit {
view.overflow = overflow
}
@ReactProp(name = "backfaceVisibility")
public open fun setBackfaceVisibility(view: ReactViewGroup, backfaceVisibility: String): Unit {
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()
}
@ReactProp(name = ViewProps.BOX_SHADOW, customType = "BoxShadow")
public open fun setBoxShadow(view: ReactViewGroup, shadows: ReadableArray?): Unit {
BackgroundStyleApplicator.setBoxShadow(view, shadows)
}
override fun setBackgroundColor(view: ReactViewGroup, @ColorInt backgroundColor: Int) {
BackgroundStyleApplicator.setBackgroundColor(view, backgroundColor)
}
override fun getName(): String = REACT_CLASS
public override fun createViewInstance(context: ThemedReactContext): ReactViewGroup =
ReactViewGroup(context)
override fun getCommandsMap(): Map<String, Int> =
mapOf(
HOTSPOT_UPDATE_KEY to CMD_HOTSPOT_UPDATE,
"setPressed" to CMD_SET_PRESSED,
)
@Deprecated("Deprecated in ViewManager")
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?.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?.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)
}
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"
}
}