diff --git a/Libraries/Animated/NativeAnimatedHelper.js b/Libraries/Animated/NativeAnimatedHelper.js index 38853a32e9b..76aa0612088 100644 --- a/Libraries/Animated/NativeAnimatedHelper.js +++ b/Libraries/Animated/NativeAnimatedHelper.js @@ -90,6 +90,18 @@ const API = { NativeAnimatedModule.createAnimatedNode(tag, config), ); }, + updateAnimatedNodeConfig: function ( + tag: number, + config: AnimatedNodeConfig, + ): void { + invariant(NativeAnimatedModule, 'Native animated module is not available'); + if (typeof NativeAnimatedModule.updateAnimatedNodeConfig === 'function') { + API.queueOperation(() => + // $FlowIgnore[not-a-function] - checked above + NativeAnimatedModule.updateAnimatedNodeConfig(tag, config), + ); + } + }, startListeningToAnimatedNodeValue: function (tag: number) { invariant(NativeAnimatedModule, 'Native animated module is not available'); API.queueOperation(() => diff --git a/Libraries/Animated/NativeAnimatedModule.js b/Libraries/Animated/NativeAnimatedModule.js index 167ca596d89..39ffb002f85 100644 --- a/Libraries/Animated/NativeAnimatedModule.js +++ b/Libraries/Animated/NativeAnimatedModule.js @@ -29,6 +29,7 @@ export interface Spec extends TurboModule { +startOperationBatch: () => void; +finishOperationBatch: () => void; +createAnimatedNode: (tag: number, config: AnimatedNodeConfig) => void; + +updateAnimatedNodeConfig?: (tag: number, config: AnimatedNodeConfig) => void; +getValue: (tag: number, saveValueCallback: SaveValueCallback) => void; +startListeningToAnimatedNodeValue: (tag: number) => void; +stopListeningToAnimatedNodeValue: (tag: number) => void; diff --git a/Libraries/Animated/NativeAnimatedTurboModule.js b/Libraries/Animated/NativeAnimatedTurboModule.js index b67fe15013a..7c0fbe8f8e2 100644 --- a/Libraries/Animated/NativeAnimatedTurboModule.js +++ b/Libraries/Animated/NativeAnimatedTurboModule.js @@ -29,6 +29,7 @@ export interface Spec extends TurboModule { +startOperationBatch: () => void; +finishOperationBatch: () => void; +createAnimatedNode: (tag: number, config: AnimatedNodeConfig) => void; + +updateAnimatedNodeConfig?: (tag: number, config: AnimatedNodeConfig) => void; +getValue: (tag: number, saveValueCallback: SaveValueCallback) => void; +startListeningToAnimatedNodeValue: (tag: number) => void; +stopListeningToAnimatedNodeValue: (tag: number) => void; diff --git a/Libraries/Animated/nodes/AnimatedColor.js b/Libraries/Animated/nodes/AnimatedColor.js index 356982ef28d..603b329fa7c 100644 --- a/Libraries/Animated/nodes/AnimatedColor.js +++ b/Libraries/Animated/nodes/AnimatedColor.js @@ -14,6 +14,7 @@ import AnimatedValue from './AnimatedValue'; import AnimatedWithChildren from './AnimatedWithChildren'; import normalizeColor from '../../StyleSheet/normalizeColor'; import {processColorObject} from '../../StyleSheet/PlatformColorValueTypes'; +import NativeAnimatedHelper from '../NativeAnimatedHelper'; import type {PlatformConfig} from '../AnimatedPlatformConfig'; import type {ColorValue} from '../../StyleSheet/StyleSheet'; @@ -166,9 +167,19 @@ export default class AnimatedColor extends AnimatedWithChildren { } if (this.nativeColor) { - this.__makeNative(); - // TODO (T111170195): In order to support setValue() with a platform color, update the - // native AnimatedNode (if it exists) with a new config. + if (!this.__isNative) { + this.__makeNative(); + } + + const nativeTag = this.__getNativeTag(); + NativeAnimatedHelper.API.setWaitingForIdentifier(nativeTag.toString()); + NativeAnimatedHelper.API.updateAnimatedNodeConfig( + nativeTag, + this.__getNativeConfig(), + ); + NativeAnimatedHelper.API.unsetWaitingForIdentifier( + nativeTag.toString(), + ); } } } diff --git a/Libraries/NativeAnimation/RCTNativeAnimatedModule.mm b/Libraries/NativeAnimation/RCTNativeAnimatedModule.mm index 534587b876f..ddf060b2adc 100644 --- a/Libraries/NativeAnimation/RCTNativeAnimatedModule.mm +++ b/Libraries/NativeAnimation/RCTNativeAnimatedModule.mm @@ -96,6 +96,14 @@ RCT_EXPORT_METHOD(createAnimatedNode:(double)tag }]; } +RCT_EXPORT_METHOD(updateAnimatedNodeConfig:(double)tag + config:(NSDictionary *)config) +{ + [self addOperationBlock:^(RCTNativeAnimatedNodesManager *nodesManager) { + [nodesManager updateAnimatedNodeConfig:[NSNumber numberWithDouble:tag] config:config]; + }]; +} + RCT_EXPORT_METHOD(connectAnimatedNodes:(double)parentTag childTag:(double)childTag) { diff --git a/Libraries/NativeAnimation/RCTNativeAnimatedNodesManager.h b/Libraries/NativeAnimation/RCTNativeAnimatedNodesManager.h index fe88f55c8f2..3dd32b8828e 100644 --- a/Libraries/NativeAnimation/RCTNativeAnimatedNodesManager.h +++ b/Libraries/NativeAnimation/RCTNativeAnimatedNodesManager.h @@ -63,6 +63,9 @@ NS_ASSUME_NONNULL_BEGIN - (void)extractAnimatedNodeOffset:(NSNumber *)nodeTag; +- (void)updateAnimatedNodeConfig:(NSNumber *)tag + config:(NSDictionary *)config; + // drivers - (void)startAnimatingNode:(NSNumber *)animationId diff --git a/Libraries/NativeAnimation/RCTNativeAnimatedNodesManager.m b/Libraries/NativeAnimation/RCTNativeAnimatedNodesManager.m index 8cbf79f1483..50356c65471 100644 --- a/Libraries/NativeAnimation/RCTNativeAnimatedNodesManager.m +++ b/Libraries/NativeAnimation/RCTNativeAnimatedNodesManager.m @@ -259,6 +259,12 @@ static NSString *RCTNormalizeAnimatedEventName(NSString *eventName) saveCallback(@[@(valueNode.value)]); } +- (void)updateAnimatedNodeConfig:(NSNumber *)tag + config:(NSDictionary *)config +{ + // TODO (T111179606): Support platform colors for color animations +} + #pragma mark -- Drivers - (void)startAnimatingNode:(NSNumber *)animationId diff --git a/Libraries/NativeAnimation/RCTNativeAnimatedTurboModule.mm b/Libraries/NativeAnimation/RCTNativeAnimatedTurboModule.mm index e627475bb65..0e3be345049 100644 --- a/Libraries/NativeAnimation/RCTNativeAnimatedTurboModule.mm +++ b/Libraries/NativeAnimation/RCTNativeAnimatedTurboModule.mm @@ -102,6 +102,14 @@ RCT_EXPORT_METHOD(createAnimatedNode:(double)tag }]; } +RCT_EXPORT_METHOD(updateAnimatedNodeConfig:(double)tag + config:(NSDictionary *)config) +{ + [self addOperationBlock:^(RCTNativeAnimatedNodesManager *nodesManager) { + [nodesManager updateAnimatedNodeConfig:[NSNumber numberWithDouble:tag] config:config]; + }]; +} + RCT_EXPORT_METHOD(connectAnimatedNodes:(double)parentTag childTag:(double)childTag) { diff --git a/ReactAndroid/src/main/java/com/facebook/react/animated/AnimatedNodeWithUpdateableConfig.java b/ReactAndroid/src/main/java/com/facebook/react/animated/AnimatedNodeWithUpdateableConfig.java new file mode 100644 index 00000000000..59fb7d801cb --- /dev/null +++ b/ReactAndroid/src/main/java/com/facebook/react/animated/AnimatedNodeWithUpdateableConfig.java @@ -0,0 +1,15 @@ +/* + * 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 com.facebook.react.bridge.ReadableMap; + +/** Indicates that AnimatedNode is able to receive native config updates. */ +public interface AnimatedNodeWithUpdateableConfig { + void onUpdateConfig(ReadableMap config); +} diff --git a/ReactAndroid/src/main/java/com/facebook/react/animated/ColorAnimatedNode.java b/ReactAndroid/src/main/java/com/facebook/react/animated/ColorAnimatedNode.java index 2046458470c..7c55feb8f32 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/animated/ColorAnimatedNode.java +++ b/ReactAndroid/src/main/java/com/facebook/react/animated/ColorAnimatedNode.java @@ -16,15 +16,16 @@ import com.facebook.react.bridge.ReadableMap; import com.facebook.react.views.view.ColorUtil; /** Animated node that represents a color. */ -/*package*/ class ColorAnimatedNode extends AnimatedNode { +/*package*/ class ColorAnimatedNode extends AnimatedNode + implements AnimatedNodeWithUpdateableConfig { private final NativeAnimatedNodesManager mNativeAnimatedNodesManager; private final ReactApplicationContext mReactApplicationContext; - private final int mRNodeId; - private final int mGNodeId; - private final int mBNodeId; - private final int mANodeId; - private final ReadableMap mNativeColor; + private int mRNodeId; + private int mGNodeId; + private int mBNodeId; + private int mANodeId; + private ReadableMap mNativeColor; private boolean mNativeColorApplied; public ColorAnimatedNode( @@ -33,12 +34,7 @@ import com.facebook.react.views.view.ColorUtil; ReactApplicationContext reactApplicationContext) { mNativeAnimatedNodesManager = nativeAnimatedNodesManager; mReactApplicationContext = reactApplicationContext; - mRNodeId = config.getInt("r"); - mGNodeId = config.getInt("g"); - mBNodeId = config.getInt("b"); - mANodeId = config.getInt("a"); - mNativeColor = config.getMap("nativeColor"); - tryApplyNativeColor(); + onUpdateConfig(config); } public int getColor() { @@ -57,6 +53,16 @@ import com.facebook.react.views.view.ColorUtil; return ColorUtil.normalize(r, g, b, a); } + public void onUpdateConfig(ReadableMap config) { + mRNodeId = config.getInt("r"); + mGNodeId = config.getInt("g"); + mBNodeId = config.getInt("b"); + mANodeId = config.getInt("a"); + mNativeColor = config.getMap("nativeColor"); + mNativeColorApplied = false; + tryApplyNativeColor(); + } + @Override public String prettyPrint() { return "ColorAnimatedNode[" diff --git a/ReactAndroid/src/main/java/com/facebook/react/animated/NativeAnimatedModule.java b/ReactAndroid/src/main/java/com/facebook/react/animated/NativeAnimatedModule.java index 9c370f68a91..887440dea97 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/animated/NativeAnimatedModule.java +++ b/ReactAndroid/src/main/java/com/facebook/react/animated/NativeAnimatedModule.java @@ -460,6 +460,32 @@ public class NativeAnimatedModule extends NativeAnimatedModuleSpec }); } + @Override + public void updateAnimatedNodeConfig(final double tagDouble, final ReadableMap config) { + final int tag = (int) tagDouble; + if (ANIMATED_MODULE_DEBUG) { + FLog.d( + NAME, + "queue updateAnimatedNodeConfig: " + tag + " config: " + config.toHashMap().toString()); + } + + addOperation( + new UIThreadOperation() { + @Override + public void execute(NativeAnimatedNodesManager animatedNodesManager) { + if (ANIMATED_MODULE_DEBUG) { + FLog.d( + NAME, + "execute updateAnimatedNodeConfig: " + + tag + + " config: " + + config.toHashMap().toString()); + } + animatedNodesManager.updateAnimatedNodeConfig(tag, config); + } + }); + } + @Override public void startListeningToAnimatedNodeValue(final double tagDouble) { final int tag = (int) tagDouble; diff --git a/ReactAndroid/src/main/java/com/facebook/react/animated/NativeAnimatedNodesManager.java b/ReactAndroid/src/main/java/com/facebook/react/animated/NativeAnimatedNodesManager.java index 208272eadc1..28d611f0b10 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/animated/NativeAnimatedNodesManager.java +++ b/ReactAndroid/src/main/java/com/facebook/react/animated/NativeAnimatedNodesManager.java @@ -159,6 +159,21 @@ import java.util.Queue; mUpdatedNodes.put(tag, node); } + @UiThread + public void updateAnimatedNodeConfig(int tag, ReadableMap config) { + AnimatedNode node = mAnimatedNodes.get(tag); + if (node == null) { + throw new JSApplicationIllegalArgumentException( + "updateAnimatedNode: Animated node [" + tag + "] does not exist"); + } + + if (node instanceof AnimatedNodeWithUpdateableConfig) { + stopAnimationsForNode(node); + ((AnimatedNodeWithUpdateableConfig) node).onUpdateConfig(config); + mUpdatedNodes.put(tag, node); + } + } + @UiThread public void dropAnimatedNode(int tag) { mAnimatedNodes.remove(tag);