From bb435a2b1176a335b2eb924102e60990fa278b8f Mon Sep 17 00:00:00 2001 From: Genki Kondo Date: Tue, 8 Feb 2022 16:27:49 -0800 Subject: [PATCH] Support AnimatedColor.setValue for platform colors Summary: In order to support AnimatedColor.setValue for platform colors, we need to pass the platform color object to the native animated node which will then resolve and apply the color. Thus, the approach is: - Add a new API updateAnimatedNodeConfig to NativeAnimatedModule - [JS] On AnimatedColor.setValue, if the value is a platform color, then we call updateAnimatedNodeConfig - [Android] We introduce AnimatedNodeWithUpdateableConfig interface with a method updateConfig. On ColorAnimatedNode.java, we use updateConfig to resolve and apply the color Changelog: [Internal][Fixed] - Use context from view when resolving platform color Reviewed By: javache, mdvacca Differential Revision: D34025193 fbshipit-source-id: 8b368f6b7cb2cf7cebe8b66461cd4185cbadd44c --- Libraries/Animated/NativeAnimatedHelper.js | 12 ++++++++ Libraries/Animated/NativeAnimatedModule.js | 1 + .../Animated/NativeAnimatedTurboModule.js | 1 + Libraries/Animated/nodes/AnimatedColor.js | 17 +++++++++-- .../RCTNativeAnimatedModule.mm | 8 +++++ .../RCTNativeAnimatedNodesManager.h | 3 ++ .../RCTNativeAnimatedNodesManager.m | 6 ++++ .../RCTNativeAnimatedTurboModule.mm | 8 +++++ .../AnimatedNodeWithUpdateableConfig.java | 15 ++++++++++ .../react/animated/ColorAnimatedNode.java | 30 +++++++++++-------- .../react/animated/NativeAnimatedModule.java | 26 ++++++++++++++++ .../animated/NativeAnimatedNodesManager.java | 15 ++++++++++ 12 files changed, 127 insertions(+), 15 deletions(-) create mode 100644 ReactAndroid/src/main/java/com/facebook/react/animated/AnimatedNodeWithUpdateableConfig.java 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);