From 3b60d55621bbd46e896884324eea9530759faba9 Mon Sep 17 00:00:00 2001 From: Ruslan Shestopalyuk Date: Sun, 28 Jul 2024 04:04:14 -0700 Subject: [PATCH] PropsAnimatedNode.java->.kt (#45757) Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/45757 # Changelog: [Internal] - As in the title. Differential Revision: D60341400 --- .../react/animated/PropsAnimatedNode.java | 145 ------------------ .../react/animated/PropsAnimatedNode.kt | 127 +++++++++++++++ 2 files changed, 127 insertions(+), 145 deletions(-) delete mode 100644 packages/react-native/ReactAndroid/src/main/java/com/facebook/react/animated/PropsAnimatedNode.java create mode 100644 packages/react-native/ReactAndroid/src/main/java/com/facebook/react/animated/PropsAnimatedNode.kt diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/animated/PropsAnimatedNode.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/animated/PropsAnimatedNode.java deleted file mode 100644 index 312e9218ba98a6..00000000000000 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/animated/PropsAnimatedNode.java +++ /dev/null @@ -1,145 +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.animated; - -import android.view.View; -import androidx.annotation.Nullable; -import com.facebook.react.bridge.JSApplicationIllegalArgumentException; -import com.facebook.react.bridge.JavaOnlyMap; -import com.facebook.react.bridge.ReadableMap; -import com.facebook.react.bridge.ReadableMapKeySetIterator; -import com.facebook.react.bridge.UIManager; -import com.facebook.react.uimanager.IllegalViewOperationException; -import com.facebook.react.uimanager.common.UIManagerType; -import com.facebook.react.uimanager.common.ViewUtil; -import java.util.HashMap; -import java.util.Map; - -/** - * Animated node that represents view properties. There is a special handling logic implemented for - * the nodes of this type in {@link NativeAnimatedNodesManager} that is responsible for extracting a - * map of updated properties, which can be then passed down to the view. - */ -/*package*/ class PropsAnimatedNode extends AnimatedNode { - - private int mConnectedViewTag = -1; - private final NativeAnimatedNodesManager mNativeAnimatedNodesManager; - private final Map mPropNodeMapping; - private final JavaOnlyMap mPropMap; - @Nullable private UIManager mUIManager; - - PropsAnimatedNode(ReadableMap config, NativeAnimatedNodesManager nativeAnimatedNodesManager) { - ReadableMap props = config.getMap("props"); - ReadableMapKeySetIterator iter = props.keySetIterator(); - mPropNodeMapping = new HashMap<>(); - while (iter.hasNextKey()) { - String propKey = iter.nextKey(); - int nodeIndex = props.getInt(propKey); - mPropNodeMapping.put(propKey, nodeIndex); - } - mPropMap = new JavaOnlyMap(); - mNativeAnimatedNodesManager = nativeAnimatedNodesManager; - } - - public void connectToView(int viewTag, UIManager uiManager) { - if (mConnectedViewTag != -1) { - throw new JSApplicationIllegalArgumentException( - "Animated node " + tag + " is " + "already attached to a view: " + mConnectedViewTag); - } - mConnectedViewTag = viewTag; - mUIManager = uiManager; - } - - public void disconnectFromView(int viewTag) { - if (mConnectedViewTag != viewTag && mConnectedViewTag != -1) { - throw new JSApplicationIllegalArgumentException( - "Attempting to disconnect view that has " - + "not been connected with the given animated node: " - + viewTag - + " but is connected to view " - + mConnectedViewTag); - } - - mConnectedViewTag = -1; - } - - public void restoreDefaultValues() { - // Cannot restore default values if this view has already been disconnected. - if (mConnectedViewTag == -1) { - return; - } - // Don't restore default values in Fabric. - // In Non-Fabric this had the effect of "restore the value to whatever the value was on the - // ShadowNode instead of in the View hierarchy". However, "synchronouslyUpdateViewOnUIThread" - // will not have that impact on Fabric, because the FabricUIManager doesn't have access to the - // ShadowNode layer. - if (ViewUtil.getUIManagerType(mConnectedViewTag) == UIManagerType.FABRIC) { - return; - } - - ReadableMapKeySetIterator it = mPropMap.keySetIterator(); - while (it.hasNextKey()) { - mPropMap.putNull(it.nextKey()); - } - - mUIManager.synchronouslyUpdateViewOnUIThread(mConnectedViewTag, mPropMap); - } - - public final void updateView() { - if (mConnectedViewTag == -1) { - return; - } - for (Map.Entry entry : mPropNodeMapping.entrySet()) { - @Nullable AnimatedNode node = mNativeAnimatedNodesManager.getNodeById(entry.getValue()); - if (node == null) { - throw new IllegalArgumentException("Mapped property node does not exist"); - } else if (node instanceof StyleAnimatedNode) { - ((StyleAnimatedNode) node).collectViewUpdates(mPropMap); - } else if (node instanceof ValueAnimatedNode) { - Object animatedObject = ((ValueAnimatedNode) node).getAnimatedObject(); - if (animatedObject instanceof Integer) { - mPropMap.putInt(entry.getKey(), (Integer) animatedObject); - } else if (animatedObject instanceof String) { - mPropMap.putString(entry.getKey(), (String) animatedObject); - } else { - mPropMap.putDouble(entry.getKey(), ((ValueAnimatedNode) node).getValue()); - } - } else if (node instanceof ColorAnimatedNode) { - mPropMap.putInt(entry.getKey(), ((ColorAnimatedNode) node).getColor()); - } else if (node instanceof ObjectAnimatedNode) { - ((ObjectAnimatedNode) node).collectViewUpdates(entry.getKey(), mPropMap); - } else { - throw new IllegalArgumentException( - "Unsupported type of node used in property node " + node.getClass()); - } - } - - mUIManager.synchronouslyUpdateViewOnUIThread(mConnectedViewTag, mPropMap); - } - - public View getConnectedView() { - try { - return mUIManager.resolveView(mConnectedViewTag); - } catch (IllegalViewOperationException ex) { - // resolveView throws an {@link IllegalViewOperationException} when the view doesn't exist - // (this can happen if the surface is being deallocated). - return null; - } - } - - public String prettyPrint() { - return "PropsAnimatedNode[" - + tag - + "] connectedViewTag: " - + mConnectedViewTag - + " mPropNodeMapping: " - + (mPropNodeMapping != null ? mPropNodeMapping.toString() : "null") - + " mPropMap: " - + (mPropMap != null ? mPropMap.toString() : "null"); - } -} diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/animated/PropsAnimatedNode.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/animated/PropsAnimatedNode.kt new file mode 100644 index 00000000000000..14a5810e6943f8 --- /dev/null +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/animated/PropsAnimatedNode.kt @@ -0,0 +1,127 @@ +/* + * 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.animated + +import android.view.View +import com.facebook.react.bridge.JSApplicationIllegalArgumentException +import com.facebook.react.bridge.JavaOnlyMap +import com.facebook.react.bridge.ReadableMap +import com.facebook.react.bridge.UIManager +import com.facebook.react.uimanager.IllegalViewOperationException +import com.facebook.react.uimanager.common.UIManagerType +import com.facebook.react.uimanager.common.ViewUtil.getUIManagerType + +/** + * Animated node that represents view properties. There is a special handling logic implemented for + * the nodes of this type in [NativeAnimatedNodesManager] that is responsible for extracting a map + * of updated properties, which can be then passed down to the view. + */ +internal class PropsAnimatedNode( + config: ReadableMap, + private val nativeAnimatedNodesManager: NativeAnimatedNodesManager +) : AnimatedNode() { + private var connectedViewTag = -1 + private val propNodeMapping: MutableMap + private val propMap: JavaOnlyMap + private var connectedViewUIManager: UIManager? = null + + init { + val props = config.getMap("props") + val iter = props?.keySetIterator() + propNodeMapping = HashMap() + while (iter != null && iter.hasNextKey()) { + val propKey = iter.nextKey() + val nodeIndex = props.getInt(propKey) + propNodeMapping[propKey] = nodeIndex + } + propMap = JavaOnlyMap() + } + + public fun connectToView(viewTag: Int, uiManager: UIManager?) { + if (connectedViewTag != -1) { + throw JSApplicationIllegalArgumentException( + "Animated node $tag is already attached to a view: $connectedViewTag") + } + connectedViewTag = viewTag + connectedViewUIManager = uiManager + } + + public fun disconnectFromView(viewTag: Int) { + if (connectedViewTag != viewTag && connectedViewTag != -1) { + throw JSApplicationIllegalArgumentException( + "Attempting to disconnect view that has " + + "not been connected with the given animated node: $viewTag " + + "but is connected to view $connectedViewTag") + } + connectedViewTag = -1 + } + + public fun restoreDefaultValues() { + // Cannot restore default values if this view has already been disconnected. + if (connectedViewTag == -1) { + return + } + // Don't restore default values in Fabric. + // In Non-Fabric this had the effect of "restore the value to whatever the value was on the + // ShadowNode instead of in the View hierarchy". However, "synchronouslyUpdateViewOnUIThread" + // will not have that impact on Fabric, because the FabricUIManager doesn't have access to the + // ShadowNode layer. + if (getUIManagerType(connectedViewTag) == UIManagerType.FABRIC) { + return + } + val it = propMap.keySetIterator() + while (it.hasNextKey()) { + propMap.putNull(it.nextKey()) + } + connectedViewUIManager?.synchronouslyUpdateViewOnUIThread(connectedViewTag, propMap) + } + + public fun updateView() { + if (connectedViewTag == -1) { + return + } + for ((key, value) in propNodeMapping) { + val node = nativeAnimatedNodesManager.getNodeById(value) + requireNotNull(node) { "Mapped property node does not exist" } + if (node is StyleAnimatedNode) { + node.collectViewUpdates(propMap) + } else if (node is ValueAnimatedNode) { + val animatedObject = node.getAnimatedObject() + if (animatedObject is Int) { + propMap.putInt(key, animatedObject) + } else if (animatedObject is String) { + propMap.putString(key, animatedObject) + } else { + propMap.putDouble(key, node.getValue()) + } + } else if (node is ColorAnimatedNode) { + propMap.putInt(key, node.color) + } else if (node is ObjectAnimatedNode) { + node.collectViewUpdates(key, propMap) + } else { + throw IllegalArgumentException( + "Unsupported type of node used in property node ${node.javaClass}") + } + } + connectedViewUIManager?.synchronouslyUpdateViewOnUIThread(connectedViewTag, propMap) + } + + public val connectedView: View? + get() = + try { + connectedViewUIManager?.resolveView(connectedViewTag) + } catch (ex: IllegalViewOperationException) { + // resolveView throws an {@link IllegalViewOperationException} when the view doesn't exist + // (this can happen if the surface is being deallocated). + null + } + + override fun prettyPrint(): String = + "PropsAnimatedNode[$tag] connectedViewTag: $connectedViewTag " + + "propNodeMapping: $propNodeMapping propMap: $propMap" +}