From 0d1bb87327c43b789aa11b60416885949d103cd2 Mon Sep 17 00:00:00 2001 From: kushagra-zepto Date: Fri, 20 Jun 2025 18:30:52 +0530 Subject: [PATCH 1/6] Fixed patch --- .../hermes-engine/build.gradle.kts | 3 +- .../react/animated/AdditionAnimatedNode.kt | 6 +- .../react/animated/AnimationDriver.kt | 5 +- .../react/animated/DiffClampAnimatedNode.kt | 20 +- .../react/animated/DivisionAnimatedNode.kt | 12 +- .../react/animated/EventAnimationDriver.kt | 16 +- .../animated/InterpolationAnimatedNode.kt | 29 +- .../react/animated/ModulusAnimatedNode.kt | 5 +- .../animated/MultiplicationAnimatedNode.kt | 3 +- .../react/animated/NativeAnimatedModule.java | 304 +++++++------ .../animated/NativeAnimatedNodesManager.java | 422 +++++++++++------- .../react/animated/PropsAnimatedNode.kt | 24 +- .../react/animated/StyleAnimatedNode.kt | 10 +- .../react/animated/TransformAnimatedNode.kt | 14 +- .../modules/network/NetworkingModule.java | 187 ++++---- 15 files changed, 631 insertions(+), 429 deletions(-) diff --git a/packages/react-native/ReactAndroid/hermes-engine/build.gradle.kts b/packages/react-native/ReactAndroid/hermes-engine/build.gradle.kts index d8b0d5f382f5..24702eec38c3 100644 --- a/packages/react-native/ReactAndroid/hermes-engine/build.gradle.kts +++ b/packages/react-native/ReactAndroid/hermes-engine/build.gradle.kts @@ -329,7 +329,8 @@ afterEvaluate { tasks.withType().configureEach { options.compilerArgs.add("-Xlint:deprecation,unchecked") - options.compilerArgs.add("-Werror") + // PATCH: COMMENTED OUT TO AVOID BUILD ERROR + // options.compilerArgs.add("-Werror") } /* Publishing Configuration */ diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/animated/AdditionAnimatedNode.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/animated/AdditionAnimatedNode.kt index fd93cee90ffb..d78251cecb83 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/animated/AdditionAnimatedNode.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/animated/AdditionAnimatedNode.kt @@ -41,8 +41,10 @@ internal class AdditionAnimatedNode( if (animatedNode is ValueAnimatedNode) { acc + animatedNode.getValue() } else { - throw JSApplicationCausedNativeException( - "Illegal node ID set as an input for Animated.Add node") + acc + 0.0 + // PATCH: COMMENTED OUT EXCEPTION THROWING + // throw JSApplicationCausedNativeException( + // "Illegal node ID set as an input for Animated.Add node") } }) } diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/animated/AnimationDriver.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/animated/AnimationDriver.kt index e55fa2435d47..df931bbe1712 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/animated/AnimationDriver.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/animated/AnimationDriver.kt @@ -34,7 +34,8 @@ internal abstract class AnimationDriver { * start animating with the new properties (different destination or spring settings) */ open fun resetConfig(config: ReadableMap) { - throw JSApplicationCausedNativeException( - "Animation config for ${javaClass.simpleName} cannot be reset") + // PATCH: COMMENTED OUT EXCEPTION THROWING + // throw JSApplicationCausedNativeException( + // "Animation config for ${javaClass.simpleName} cannot be reset") } } diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/animated/DiffClampAnimatedNode.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/animated/DiffClampAnimatedNode.kt index fbe312e48593..d8c6ef7bd0f5 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/animated/DiffClampAnimatedNode.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/animated/DiffClampAnimatedNode.kt @@ -29,18 +29,22 @@ internal class DiffClampAnimatedNode( } override fun update() { - val value = inputNodeValue - val diff = value - lastValue - lastValue = value - nodeValue = min(max(nodeValue + diff, minValue), maxValue) + if (inputNodeValue != null) { + val value = inputNodeValue!! + val diff = value - lastValue + lastValue = value + nodeValue = min(max(nodeValue + diff, minValue), maxValue) + } } - private val inputNodeValue: Double + private val inputNodeValue: Double? get() { val animatedNode = nativeAnimatedNodesManager.getNodeById(inputNodeTag) if (animatedNode == null || animatedNode !is ValueAnimatedNode) { - throw JSApplicationCausedNativeException( - "Illegal node ID set as an input for Animated.DiffClamp node") + return null + // PATCH: COMMENTED OUT EXCEPTION THROWING + // throw JSApplicationCausedNativeException( + // "Illegal node ID set as an input for Animated.DiffClamp node") } return animatedNode.getValue() } @@ -48,4 +52,4 @@ internal class DiffClampAnimatedNode( override fun prettyPrint(): String = "DiffClampAnimatedNode[$tag]: InputNodeTag: $inputNodeTag min: $minValue " + "max: $maxValue lastValue: $lastValue super: ${super.prettyPrint()}" -} +} \ No newline at end of file diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/animated/DivisionAnimatedNode.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/animated/DivisionAnimatedNode.kt index b2625bf0f00e..17a0f397fec8 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/animated/DivisionAnimatedNode.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/animated/DivisionAnimatedNode.kt @@ -38,14 +38,18 @@ internal class DivisionAnimatedNode( if (i == 0) { nodeValue = v } else if (v == 0.0) { - throw JSApplicationCausedNativeException( - "Detected a division by zero in Animated.divide node with Animated ID $tag") + return + // PATCH: COMMENTED OUT EXCEPTION THROWING + // throw JSApplicationCausedNativeException( + // "Detected a division by zero in Animated.divide node with Animated ID $tag") } else { nodeValue /= v } } else { - throw JSApplicationCausedNativeException( - "Illegal node ID set as an input for Animated.divide node with Animated ID $tag") + return + // PATCH: COMMENTED OUT EXCEPTION THROWING + // throw JSApplicationCausedNativeException( + // "Illegal node ID set as an input for Animated.divide node with Animated ID $tag") } } } diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/animated/EventAnimationDriver.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/animated/EventAnimationDriver.kt index f59b730cf225..f3f9ebe46817 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/animated/EventAnimationDriver.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/animated/EventAnimationDriver.kt @@ -43,12 +43,14 @@ internal class EventAnimationDriver( touches: WritableArray, changedIndices: WritableArray ) { - throw UnsupportedOperationException("receiveTouches is not support by native animated events") + // PATCH: COMMENTED OUT EXCEPTION THROWING + // throw UnsupportedOperationException("receiveTouches is not support by native animated events") } @Deprecated("Deprecated in Java") override fun receiveTouches(event: TouchEvent) { - throw UnsupportedOperationException("receiveTouches is not support by native animated events") + // PATCH: COMMENTED OUT EXCEPTION THROWING + // throw UnsupportedOperationException("receiveTouches is not support by native animated events") } override fun receiveEvent( @@ -60,7 +62,11 @@ internal class EventAnimationDriver( params: WritableMap?, @EventCategoryDef category: Int ) { - requireNotNull(params) { "Native animated events must have event data." } + // requireNotNull(params) { "Native animated events must have event data." } + + if(params == null) { + return; + } // Get the new value for the node by looking into the event map using the provided event path. var currMap: ReadableMap? = params @@ -76,7 +82,7 @@ internal class EventAnimationDriver( currArray = currMap.getArray(key) currMap = null } else { - throw UnexpectedNativeTypeException("Unexpected type $keyType for key '$key'") + // throw UnexpectedNativeTypeException("Unexpected type $keyType for key '$key'") } } else { val index = eventPath[i].toInt() @@ -88,7 +94,7 @@ internal class EventAnimationDriver( currArray = currArray?.getArray(index) currMap = null } else { - throw UnexpectedNativeTypeException("Unexpected type $keyType for index '$index'") + // throw UnexpectedNativeTypeException("Unexpected type $keyType for index '$index'") } } } diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/animated/InterpolationAnimatedNode.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/animated/InterpolationAnimatedNode.kt index e53a4529670e..449e9449b8cd 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/animated/InterpolationAnimatedNode.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/animated/InterpolationAnimatedNode.kt @@ -51,13 +51,22 @@ public class InterpolationAnimatedNode(config: ReadableMap) : ValueAnimatedNode( } override fun onAttachedToNode(parent: AnimatedNode) { - check(this.parent == null) { "Parent already attached" } - require(parent is ValueAnimatedNode) { "Parent is of an invalid type" } + // check(this.parent == null) { "Parent already attached" } + // require(parent is ValueAnimatedNode) { "Parent is of an invalid type" } + if(this.parent != null) { + return; + } + if(parent !is ValueAnimatedNode ) { + return; + } this.parent = parent } override fun onDetachedFromNode(parent: AnimatedNode) { - require(parent === this.parent) { "Invalid parent node provided" } + // require(parent === this.parent) { "Invalid parent node provided" } + if (parent != this.parent) { + return + } this.parent = null } @@ -169,9 +178,10 @@ public class InterpolationAnimatedNode(config: ReadableMap) : ValueAnimatedNode( EXTRAPOLATE_TYPE_IDENTITY -> return result EXTRAPOLATE_TYPE_CLAMP -> result = inputMin EXTRAPOLATE_TYPE_EXTEND -> {} - else -> - throw JSApplicationIllegalArgumentException( - "Invalid extrapolation type " + extrapolateLeft + "for left extrapolation") + else -> return result + // PATCH: COMMENTED OUT EXCEPTION THROWING + // throw JSApplicationIllegalArgumentException( + // "Invalid extrapolation type " + extrapolateLeft + "for left extrapolation") } } if (result > inputMax) { @@ -179,9 +189,10 @@ public class InterpolationAnimatedNode(config: ReadableMap) : ValueAnimatedNode( EXTRAPOLATE_TYPE_IDENTITY -> return result EXTRAPOLATE_TYPE_CLAMP -> result = inputMax EXTRAPOLATE_TYPE_EXTEND -> {} - else -> - throw JSApplicationIllegalArgumentException( - "Invalid extrapolation type " + extrapolateRight + "for right extrapolation") + else -> return result + // PATCH: COMMENTED OUT EXCEPTION THROWING + // throw JSApplicationIllegalArgumentException( + // "Invalid extrapolation type " + extrapolateRight + "for right extrapolation") } } if (outputMin == outputMax) { diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/animated/ModulusAnimatedNode.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/animated/ModulusAnimatedNode.kt index b09d0f5276f1..39eb364b7bf6 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/animated/ModulusAnimatedNode.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/animated/ModulusAnimatedNode.kt @@ -24,8 +24,9 @@ internal class ModulusAnimatedNode( val animatedNodeValue = animatedNode.getValue() nodeValue = (animatedNodeValue % modulus + modulus) % modulus } else { - throw JSApplicationCausedNativeException( - "Illegal node ID set as an input for Animated.modulus node") + // PATCH: COMMENTED OUT EXCEPTION THROWING + // throw JSApplicationCausedNativeException( + // "Illegal node ID set as an input for Animated.modulus node") } } diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/animated/MultiplicationAnimatedNode.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/animated/MultiplicationAnimatedNode.kt index fa1f7d4a8e18..51e16f92ab5d 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/animated/MultiplicationAnimatedNode.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/animated/MultiplicationAnimatedNode.kt @@ -38,8 +38,7 @@ internal class MultiplicationAnimatedNode( if (animatedNode != null && animatedNode is ValueAnimatedNode) { animatedNode.getValue() } else { - throw JSApplicationCausedNativeException( - "Illegal node ID set as an input for Animated.multiply node") + return } nodeValue *= multiplier } diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/animated/NativeAnimatedModule.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/animated/NativeAnimatedModule.java index 0f5043d19063..4399fd500868 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/animated/NativeAnimatedModule.java +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/animated/NativeAnimatedModule.java @@ -44,47 +44,78 @@ import java.util.concurrent.atomic.AtomicReference; /** - * Module that exposes interface for creating and managing animated nodes on the "native" side. + * Module that exposes interface for creating and managing animated nodes on the + * "native" side. * - *

Animated.js library is based on a concept of a graph where nodes are values or transform - * operations (such as interpolation, addition, etc) and connection are used to describe how change + *

+ * Animated.js library is based on a concept of a graph where nodes are values + * or transform + * operations (such as interpolation, addition, etc) and connection are used to + * describe how change * of the value in one node can affect other nodes. * - *

Few examples of the nodes that can be created on the JS side: + *

+ * Few examples of the nodes that can be created on the JS side: * *

* - *

You can mix and chain nodes however you like and this way create nodes graph with connections + *

+ * You can mix and chain nodes however you like and this way create nodes graph + * with connections * between them. * - *

To map animated node values to view properties there is a special type of a node: - * AnimatedProps. It is created by AnimatedImplementation whenever you render Animated.View and - * stores a mapping from the view properties to the corresponding animated values (so it's actually + *

+ * To map animated node values to view properties there is a special type of a + * node: + * AnimatedProps. It is created by AnimatedImplementation whenever you render + * Animated.View and + * stores a mapping from the view properties to the corresponding animated + * values (so it's actually * also a node with connections to the value nodes). * - *

Last "special" elements of the graph are "animation drivers". Those are objects (represented - * as a graph nodes too) that based on some criteria updates attached values every frame (we have - * few types of those, e.g., spring, timing, decay). Animation objects can be "started" and - * "stopped". Those are like "pulse generators" for the rest of the nodes graph. Those pulses then - * propagate along the graph to the children nodes up to the special node type: AnimatedProps which + *

+ * Last "special" elements of the graph are "animation drivers". Those are + * objects (represented + * as a graph nodes too) that based on some criteria updates attached values + * every frame (we have + * few types of those, e.g., spring, timing, decay). Animation objects can be + * "started" and + * "stopped". Those are like "pulse generators" for the rest of the nodes graph. + * Those pulses then + * propagate along the graph to the children nodes up to the special node type: + * AnimatedProps which * then can be used to calculate property update map for a view. * - *

This class acts as a proxy between the "native" API that can be called from JS and the main - * class that coordinates all the action: {@link NativeAnimatedNodesManager}. Since all the methods - * from {@link NativeAnimatedNodesManager} need to be called from the UI thread, we we create a - * queue of animated graph operations that is then enqueued to be executed in the UI Thread at the - * end of the batch of JS->native calls (similarly to how it's handled in {@link UIManagerModule}). - * This isolates us from the problems that may be caused by concurrent updates of animated graph + *

+ * This class acts as a proxy between the "native" API that can be called from + * JS and the main + * class that coordinates all the action: {@link NativeAnimatedNodesManager}. + * Since all the methods + * from {@link NativeAnimatedNodesManager} need to be called from the UI thread, + * we we create a + * queue of animated graph operations that is then enqueued to be executed in + * the UI Thread at the + * end of the batch of JS->native calls (similarly to how it's handled in + * {@link UIManagerModule}). + * This isolates us from the problems that may be caused by concurrent updates + * of animated graph * while UI thread is "executing" the animation loop. */ @ReactModule(name = NativeAnimatedModuleSpec.NAME) @@ -153,7 +184,8 @@ public long getBatchNumber() { private class ConcurrentOperationQueue { private final Queue mQueue = new ConcurrentLinkedQueue<>(); - @Nullable private UIThreadOperation mPeekedOperation = null; + @Nullable + private UIThreadOperation mPeekedOperation = null; @AnyThread boolean isEmpty() { @@ -184,7 +216,8 @@ void executeBatch(long maxBatchNumber, NativeAnimatedNodesManager nodesManager) List operations = new ArrayList<>(); while (true) { - // Due to a race condition, we manually "carry-over" a polled item from previous batch + // Due to a race condition, we manually "carry-over" a polled item from previous + // batch // instead of peeking the queue itself for consistency. // TODO(T112522554): Clean up the queue access if (mPeekedOperation != null) { @@ -202,7 +235,8 @@ void executeBatch(long maxBatchNumber, NativeAnimatedNodesManager nodesManager) } if (polledOperation.getBatchNumber() > maxBatchNumber) { - // Because the operation is already retrieved from the queue, there's no way of placing it + // Because the operation is already retrieved from the queue, there's no way of + // placing it // back as the head element, so we remember it manually here mPeekedOperation = polledOperation; break; @@ -214,11 +248,14 @@ void executeBatch(long maxBatchNumber, NativeAnimatedNodesManager nodesManager) } } - @NonNull private final GuardedFrameCallback mAnimatedFrameCallback; + @NonNull + private final GuardedFrameCallback mAnimatedFrameCallback; private final ReactChoreographer mReactChoreographer; - @NonNull private final ConcurrentOperationQueue mOperations = new ConcurrentOperationQueue(); - @NonNull private final ConcurrentOperationQueue mPreOperations = new ConcurrentOperationQueue(); + @NonNull + private final ConcurrentOperationQueue mOperations = new ConcurrentOperationQueue(); + @NonNull + private final ConcurrentOperationQueue mPreOperations = new ConcurrentOperationQueue(); private final AtomicReference mNodesManager = new AtomicReference<>(); @@ -237,38 +274,43 @@ public NativeAnimatedModule(ReactApplicationContext reactContext) { super(reactContext); mReactChoreographer = ReactChoreographer.getInstance(); - mAnimatedFrameCallback = - new GuardedFrameCallback(reactContext) { - @Override - protected void doFrameGuarded(final long frameTimeNanos) { - try { - mEnqueuedAnimationOnFrame = false; - NativeAnimatedNodesManager nodesManager = getNodesManager(); - if (nodesManager != null && nodesManager.hasActiveAnimations()) { - nodesManager.runUpdates(frameTimeNanos); - } - // This is very unlikely to ever be hit. - if (nodesManager == null || mReactChoreographer == null) { - return; - } + mAnimatedFrameCallback = new GuardedFrameCallback(reactContext) { + @Override + protected void doFrameGuarded(final long frameTimeNanos) { + try { + mEnqueuedAnimationOnFrame = false; + NativeAnimatedNodesManager nodesManager = getNodesManager(); + if (nodesManager != null && nodesManager.hasActiveAnimations()) { + nodesManager.runUpdates(frameTimeNanos); + } + // This is very unlikely to ever be hit. + if (nodesManager == null || mReactChoreographer == null) { + return; + } - if (!ReactNativeFeatureFlags.lazyAnimationCallbacks() - || nodesManager.hasActiveAnimations()) { - enqueueFrameCallback(); - } - } catch (Exception ex) { - throw new RuntimeException(ex); - } + if (!ReactNativeFeatureFlags.lazyAnimationCallbacks() + || nodesManager.hasActiveAnimations()) { + enqueueFrameCallback(); } - }; + } catch (Exception ex) { + // PATCH: COMMENTED OUT EXCEPTION THROWING + // throw new RuntimeException(ex); + } + } + }; } /** - * This method is used to notify the JS side that the user has stopped scrolling. With natively - * driven animation, we might have to force a resync between the Shadow Tree and the Native Tree. - * This is because with natively driven animation, the Shadow Tree is bypassed and it can have - * stale information on the layout of the native views. This method takes care of verifying if - * there are some views listening to the native driven animation and it triggers the resynch. + * This method is used to notify the JS side that the user has stopped + * scrolling. With natively + * driven animation, we might have to force a resync between the Shadow Tree and + * the Native Tree. + * This is because with natively driven animation, the Shadow Tree is bypassed + * and it can have + * stale information on the layout of the native views. This method takes care + * of verifying if + * there are some views listening to the native driven animation and it triggers + * the resynch. * * @param viewTag The tag of the scroll view that has stopped scrolling */ @@ -356,8 +398,10 @@ public void didDispatchMountItems(UIManager uiManager) { // TODO T71377544: delete this when the JS method is confirmed safe if (!mBatchingControlledByJS) { // The problem we're trying to solve here: we could be in the middle of queueing - // a batch of related animation operations when Fabric flushes a batch of MountItems. - // It's visually bad if we execute half of the animation ops and then wait another frame + // a batch of related animation operations when Fabric flushes a batch of + // MountItems. + // It's visually bad if we execute half of the animation ops and then wait + // another frame // (or more) to execute the rest. // See mFrameNumber. If the dispatchedFrameNumber drifts too far - that // is, if no MountItems are scheduled for a while, which can happen if a tree @@ -388,21 +432,19 @@ public void willDispatchViewUpdates(final UIManager uiManager) { final long frameNo = mCurrentBatchNumber++; - UIBlock preOperationsUIBlock = - new UIBlock() { - @Override - public void execute(NativeViewHierarchyManager nativeViewHierarchyManager) { - mPreOperations.executeBatch(frameNo, getNodesManager()); - } - }; + UIBlock preOperationsUIBlock = new UIBlock() { + @Override + public void execute(NativeViewHierarchyManager nativeViewHierarchyManager) { + mPreOperations.executeBatch(frameNo, getNodesManager()); + } + }; - UIBlock operationsUIBlock = - new UIBlock() { - @Override - public void execute(NativeViewHierarchyManager nativeViewHierarchyManager) { - mOperations.executeBatch(frameNo, getNodesManager()); - } - }; + UIBlock operationsUIBlock = new UIBlock() { + @Override + public void execute(NativeViewHierarchyManager nativeViewHierarchyManager) { + mOperations.executeBatch(frameNo, getNodesManager()); + } + }; assert (uiManager instanceof UIManagerModule); UIManagerModule uiManagerModule = (UIManagerModule) uiManager; @@ -417,12 +459,14 @@ public void onHostPause() { @Override public void onHostDestroy() { - // Is it possible for onHostDestroy to be called without a corresponding onHostPause? + // Is it possible for onHostDestroy to be called without a corresponding + // onHostPause? clearFrameCallback(); } /** - * Returns a {@link NativeAnimatedNodesManager}, either the existing instance or a new one. Will + * Returns a {@link NativeAnimatedNodesManager}, either the existing instance or + * a new one. Will * return null if and only if the {@link ReactApplicationContext} is also null. * * @return {@link NativeAnimatedNodesManager} @@ -462,8 +506,10 @@ public void setNodesManager(NativeAnimatedNodesManager nodesManager) { } /** - * Given a viewTag, detect if we're running in Fabric or non-Fabric and attach an event listener - * to the correct UIManager, if necessary. This is expected to only be called from the native + * Given a viewTag, detect if we're running in Fabric or non-Fabric and attach + * an event listener + * to the correct UIManager, if necessary. This is expected to only be called + * from the native * module thread, and not concurrently. * * @param viewTag @@ -487,7 +533,8 @@ private void initializeLifecycleEventListenersForViewTag(final int viewTag) { + " NativeAnimatedNodesManager")); } - // Subscribe to UIManager (Fabric or non-Fabric) lifecycle events if we haven't yet + // Subscribe to UIManager (Fabric or non-Fabric) lifecycle events if we haven't + // yet if (mUIManagerType == UIManagerType.FABRIC ? mInitializedForFabric : mInitializedForNonFabric) { return; } @@ -508,14 +555,17 @@ private void initializeLifecycleEventListenersForViewTag(final int viewTag) { } /** - * Given a viewTag and the knowledge that a "disconnect" or "stop"-type imperative command is - * being executed, decrement the number of inflight animations and possibly switch UIManager + * Given a viewTag and the knowledge that a "disconnect" or "stop"-type + * imperative command is + * being executed, decrement the number of inflight animations and possibly + * switch UIManager * modes. * * @param viewTag */ private void decrementInFlightAnimationsForViewTag(final int viewTag) { - @UIManagerType int animationManagerType = ViewUtil.getUIManagerType(viewTag); + @UIManagerType + int animationManagerType = ViewUtil.getUIManagerType(viewTag); if (animationManagerType == UIManagerType.FABRIC) { mNumFabricAnimations--; } else { @@ -608,20 +658,18 @@ public void startListeningToAnimatedNodeValue(final double tagDouble) { FLog.d(NAME, "queue startListeningToAnimatedNodeValue: " + tag); } - final AnimatedNodeValueListener listener = - new AnimatedNodeValueListener() { - public void onValueUpdate(double value) { - WritableMap onAnimatedValueData = Arguments.createMap(); - onAnimatedValueData.putInt("tag", tag); - onAnimatedValueData.putDouble("value", value); - - ReactApplicationContext reactApplicationContext = - getReactApplicationContextIfActiveOrWarn(); - if (reactApplicationContext != null) { - reactApplicationContext.emitDeviceEvent("onAnimatedValueUpdate", onAnimatedValueData); - } - } - }; + final AnimatedNodeValueListener listener = new AnimatedNodeValueListener() { + public void onValueUpdate(double value) { + WritableMap onAnimatedValueData = Arguments.createMap(); + onAnimatedValueData.putInt("tag", tag); + onAnimatedValueData.putDouble("value", value); + + ReactApplicationContext reactApplicationContext = getReactApplicationContextIfActiveOrWarn(); + if (reactApplicationContext != null) { + reactApplicationContext.emitDeviceEvent("onAnimatedValueUpdate", onAnimatedValueData); + } + } + }; addOperation( new UIThreadOperation() { @@ -1035,15 +1083,22 @@ public void invalidate() { } /** - * This is a currently-experimental method that allows JS to queue and immediately execute many - * instructions at once. Since we make 1 JNI/JSI call instead of N, this should significantly + * This is a currently-experimental method that allows JS to queue and + * immediately execute many + * instructions at once. Since we make 1 JNI/JSI call instead of N, this should + * significantly * improve performance. * - *

The arguments operate as a byte buffer. All integer command IDs and any args are packed into + *

+ * The arguments operate as a byte buffer. All integer command IDs and any args + * are packed into * opsAndArgs. * - *

For the getValue callback: since this is batched, we accumulate a list of all requested - * values, in order, and call the callback once at the end (if present) with the list of requested + *

+ * For the getValue callback: since this is batched, we accumulate a list of all + * requested + * values, in order, and call the callback once at the end (if present) with the + * list of requested * values. */ @Override @@ -1055,10 +1110,12 @@ public void queueAndExecuteBatchedOperations(final ReadableArray opsAndArgs) { } // This block of code is unfortunate and should be refactored - we just want to - // extract the ViewTags in the ReadableArray to mark animations on views as being enabled. - // We only do this for initializing animations on views - disabling animations on views + // extract the ViewTags in the ReadableArray to mark animations on views as + // being enabled. + // We only do this for initializing animations on views - disabling animations + // on views // happens later, when the disconnect/stop operations are actually executed. - for (int i = 0; i < opBufferSize; ) { + for (int i = 0; i < opBufferSize;) { BatchExecutionOpCodes command = BatchExecutionOpCodes.fromId(opsAndArgs.getInt(i++)); switch (command) { case OP_CODE_GET_VALUE: @@ -1109,11 +1166,10 @@ public void queueAndExecuteBatchedOperations(final ReadableArray opsAndArgs) { new UIThreadOperation() { @Override public void execute(NativeAnimatedNodesManager animatedNodesManager) { - ReactApplicationContext reactApplicationContext = - getReactApplicationContextIfActiveOrWarn(); + ReactApplicationContext reactApplicationContext = getReactApplicationContextIfActiveOrWarn(); int viewTag = -1; - for (int i = 0; i < opBufferSize; ) { + for (int i = 0; i < opBufferSize;) { BatchExecutionOpCodes command = BatchExecutionOpCodes.fromId(opsAndArgs.getInt(i++)); switch (command) { @@ -1130,21 +1186,19 @@ public void execute(NativeAnimatedNodesManager animatedNodesManager) { break; case OP_START_LISTENING_TO_ANIMATED_NODE_VALUE: final int tag = opsAndArgs.getInt(i++); - final AnimatedNodeValueListener listener = - new AnimatedNodeValueListener() { - public void onValueUpdate(double value) { - WritableMap onAnimatedValueData = Arguments.createMap(); - onAnimatedValueData.putInt("tag", tag); - onAnimatedValueData.putDouble("value", value); - - ReactApplicationContext reactApplicationContext = - getReactApplicationContextIfActiveOrWarn(); - if (reactApplicationContext != null) { - reactApplicationContext.emitDeviceEvent( - "onAnimatedValueUpdate", onAnimatedValueData); - } - } - }; + final AnimatedNodeValueListener listener = new AnimatedNodeValueListener() { + public void onValueUpdate(double value) { + WritableMap onAnimatedValueData = Arguments.createMap(); + onAnimatedValueData.putInt("tag", tag); + onAnimatedValueData.putDouble("value", value); + + ReactApplicationContext reactApplicationContext = getReactApplicationContextIfActiveOrWarn(); + if (reactApplicationContext != null) { + reactApplicationContext.emitDeviceEvent( + "onAnimatedValueUpdate", onAnimatedValueData); + } + } + }; animatedNodesManager.startListeningToAnimatedNodeValue(tag, listener); break; case OP_STOP_LISTENING_TO_ANIMATED_NODE_VALUE: diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/animated/NativeAnimatedNodesManager.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/animated/NativeAnimatedNodesManager.java index b3f9c642f465..b0f45ab01d4e 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/animated/NativeAnimatedNodesManager.java +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/animated/NativeAnimatedNodesManager.java @@ -39,17 +39,26 @@ import java.util.Set; /** - * This is the main class that coordinates how native animated JS implementation drives UI changes. + * This is the main class that coordinates how native animated JS implementation + * drives UI changes. * - *

It implements a management interface for animated nodes graph as well as implements a graph + *

+ * It implements a management interface for animated nodes graph as well as + * implements a graph * traversal algorithm that is run for each animation frame. * - *

For each animation frame we visit animated nodes that might've been updated as well as their - * children that may use parent's values to update themselves. At the end of the traversal algorithm - * we expect to reach a special type of the node: PropsAnimatedNode that is then responsible for - * calculating property map which can be sent to native view hierarchy to update the view. + *

+ * For each animation frame we visit animated nodes that might've been updated + * as well as their + * children that may use parent's values to update themselves. At the end of the + * traversal algorithm + * we expect to reach a special type of the node: PropsAnimatedNode that is then + * responsible for + * calculating property map which can be sent to native view hierarchy to update + * the view. * - *

IMPORTANT: This class should be accessed only from the UI Thread + *

+ * IMPORTANT: This class should be accessed only from the UI Thread */ public class NativeAnimatedNodesManager implements EventDispatcherListener { @@ -63,7 +72,8 @@ public class NativeAnimatedNodesManager implements EventDispatcherListener { private final List mEventDrivers = new ArrayList<>(); private final ReactApplicationContext mReactApplicationContext; private int mAnimatedGraphBFSColor = 0; - // Used to avoid allocating a new array on every frame in `runUpdates` and `onEventDispatch`. + // Used to avoid allocating a new array on every frame in `runUpdates` and + // `onEventDispatch`. private final List mRunUpdateNodeList = new LinkedList<>(); private boolean mEventListenerInitializedForFabric = false; @@ -76,8 +86,10 @@ public NativeAnimatedNodesManager(ReactApplicationContext reactApplicationContex } /** - * Initialize event listeners for Fabric UIManager or non-Fabric UIManager, exactly once. Once - * Fabric is the only UIManager, this logic can be simplified. This is expected to only be called + * Initialize event listeners for Fabric UIManager or non-Fabric UIManager, + * exactly once. Once + * Fabric is the only UIManager, this logic can be simplified. This is expected + * to only be called * from the native module thread. * * @param uiManagerType @@ -113,8 +125,9 @@ public boolean hasActiveAnimations() { @UiThread public void createAnimatedNode(int tag, ReadableMap config) { if (mAnimatedNodes.get(tag) != null) { - throw new JSApplicationIllegalArgumentException( - "createAnimatedNode: Animated node [" + tag + "] already exists"); + // PATCH: COMMENTED OUT EXCEPTION THROWING + // throw new JSApplicationIllegalArgumentException( + // "createAnimatedNode: Animated node [" + tag + "] already exists"); } String type = config.getString("type"); final AnimatedNode node; @@ -147,7 +160,10 @@ public void createAnimatedNode(int tag, ReadableMap config) { } else if ("object".equals(type)) { node = new ObjectAnimatedNode(config, this); } else { - throw new JSApplicationIllegalArgumentException("Unsupported node type: " + type); + return; + // PATCH: COMMENTED OUT EXCEPTION THROWING + // throw new JSApplicationIllegalArgumentException("Unsupported node type: " + + // type); } node.tag = tag; mAnimatedNodes.put(tag, node); @@ -158,8 +174,9 @@ public void createAnimatedNode(int tag, ReadableMap config) { 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"); + // PATCH: COMMENTED OUT EXCEPTION THROWING + // throw new JSApplicationIllegalArgumentException( + // "updateAnimatedNode: Animated node [" + tag + "] does not exist"); } if (node instanceof AnimatedNodeWithUpdateableConfig) { @@ -179,10 +196,11 @@ public void dropAnimatedNode(int tag) { public void startListeningToAnimatedNodeValue(int tag, AnimatedNodeValueListener listener) { AnimatedNode node = mAnimatedNodes.get(tag); if (node == null || !(node instanceof ValueAnimatedNode)) { - throw new JSApplicationIllegalArgumentException( - "startListeningToAnimatedNodeValue: Animated node [" - + tag - + "] does not exist, or is not a 'value' node"); + // PATCH: COMMENTED OUT EXCEPTION THROWING + // throw new JSApplicationIllegalArgumentException( + // "startListeningToAnimatedNodeValue: Animated node [" + // + tag + // + "] does not exist, or is not a 'value' node"); } ((ValueAnimatedNode) node).setValueListener(listener); } @@ -191,10 +209,11 @@ public void startListeningToAnimatedNodeValue(int tag, AnimatedNodeValueListener public void stopListeningToAnimatedNodeValue(int tag) { AnimatedNode node = mAnimatedNodes.get(tag); if (node == null || !(node instanceof ValueAnimatedNode)) { - throw new JSApplicationIllegalArgumentException( - "startListeningToAnimatedNodeValue: Animated node [" - + tag - + "] does not exist, or is not a 'value' node"); + // PATCH: COMMENTED OUT EXCEPTION THROWING + // throw new JSApplicationIllegalArgumentException( + // "startListeningToAnimatedNodeValue: Animated node [" + // + tag + // + "] does not exist, or is not a 'value' node"); } ((ValueAnimatedNode) node).setValueListener(null); } @@ -203,10 +222,11 @@ public void stopListeningToAnimatedNodeValue(int tag) { public void setAnimatedNodeValue(int tag, double value) { AnimatedNode node = mAnimatedNodes.get(tag); if (node == null || !(node instanceof ValueAnimatedNode)) { - throw new JSApplicationIllegalArgumentException( - "setAnimatedNodeValue: Animated node [" - + tag - + "] does not exist, or is not a 'value' node"); + // PATCH: COMMENTED OUT EXCEPTION THROWING + // throw new JSApplicationIllegalArgumentException( + // "setAnimatedNodeValue: Animated node [" + // + tag + // + "] does not exist, or is not a 'value' node"); } stopAnimationsForNode(node); ((ValueAnimatedNode) node).nodeValue = value; @@ -217,10 +237,11 @@ public void setAnimatedNodeValue(int tag, double value) { public void setAnimatedNodeOffset(int tag, double offset) { AnimatedNode node = mAnimatedNodes.get(tag); if (node == null || !(node instanceof ValueAnimatedNode)) { - throw new JSApplicationIllegalArgumentException( - "setAnimatedNodeOffset: Animated node [" - + tag - + "] does not exist, or is not a 'value' node"); + // PATCH: COMMENTED OUT EXCEPTION THROWING + // throw new JSApplicationIllegalArgumentException( + // "setAnimatedNodeOffset: Animated node [" + // + tag + // + "] does not exist, or is not a 'value' node"); } ((ValueAnimatedNode) node).offset = offset; mUpdatedNodes.put(tag, node); @@ -230,10 +251,11 @@ public void setAnimatedNodeOffset(int tag, double offset) { public void flattenAnimatedNodeOffset(int tag) { AnimatedNode node = mAnimatedNodes.get(tag); if (node == null || !(node instanceof ValueAnimatedNode)) { - throw new JSApplicationIllegalArgumentException( - "flattenAnimatedNodeOffset: Animated node [" - + tag - + "] does not exist, or is not a 'value' node"); + // PATCH: COMMENTED OUT EXCEPTION THROWING + // throw new JSApplicationIllegalArgumentException( + // "flattenAnimatedNodeOffset: Animated node [" + // + tag + // + "] does not exist, or is not a 'value' node"); } ((ValueAnimatedNode) node).flattenOffset(); } @@ -242,10 +264,11 @@ public void flattenAnimatedNodeOffset(int tag) { public void extractAnimatedNodeOffset(int tag) { AnimatedNode node = mAnimatedNodes.get(tag); if (node == null || !(node instanceof ValueAnimatedNode)) { - throw new JSApplicationIllegalArgumentException( - "extractAnimatedNodeOffset: Animated node [" - + tag - + "] does not exist, or is not a 'value' node"); + // PATCH: COMMENTED OUT EXCEPTION THROWING + // throw new JSApplicationIllegalArgumentException( + // "extractAnimatedNodeOffset: Animated node [" + // + tag + // + "] does not exist, or is not a 'value' node"); } ((ValueAnimatedNode) node).extractOffset(); } @@ -255,20 +278,24 @@ public void startAnimatingNode( int animationId, int animatedNodeTag, ReadableMap animationConfig, Callback endCallback) { AnimatedNode node = mAnimatedNodes.get(animatedNodeTag); if (node == null) { - throw new JSApplicationIllegalArgumentException( - "startAnimatingNode: Animated node [" + animatedNodeTag + "] does not exist"); + // PATCH: COMMENTED OUT EXCEPTION THROWING + // throw new JSApplicationIllegalArgumentException( + // "startAnimatingNode: Animated node [" + animatedNodeTag + "] does not + // exist"); } if (!(node instanceof ValueAnimatedNode)) { - throw new JSApplicationIllegalArgumentException( - "startAnimatingNode: Animated node [" - + animatedNodeTag - + "] should be of type " - + ValueAnimatedNode.class.getName()); + // PATCH: COMMENTED OUT EXCEPTION THROWING + // throw new JSApplicationIllegalArgumentException( + // "startAnimatingNode: Animated node [" + // + animatedNodeTag + // + "] should be of type " + // + ValueAnimatedNode.class.getName()); } final AnimationDriver existingDriver = mActiveAnimations.get(animationId); if (existingDriver != null) { - // animation with the given ID is already running, we need to update its configuration instead + // animation with the given ID is already running, we need to update its + // configuration instead // of spawning a new one existingDriver.resetConfig(animationConfig); return; @@ -283,8 +310,11 @@ public void startAnimatingNode( } else if ("decay".equals(type)) { animation = new DecayAnimation(animationConfig); } else { - throw new JSApplicationIllegalArgumentException( - "startAnimatingNode: Unsupported animation type [" + animatedNodeTag + "]: " + type); + return; + // PATCH: COMMENTED OUT EXCEPTION THROWING + // throw new JSApplicationIllegalArgumentException( + // "startAnimatingNode: Unsupported animation type [" + animatedNodeTag + "]: " + // + type); } animation.id = animationId; animation.endCallback = endCallback; @@ -294,9 +324,12 @@ public void startAnimatingNode( @UiThread private void stopAnimationsForNode(AnimatedNode animatedNode) { - // in most of the cases there should never be more than a few active animations running at the - // same time. Therefore it does not make much sense to create an animationId -> animation - // object map that would require additional memory just to support the use-case of stopping + // in most of the cases there should never be more than a few active animations + // running at the + // same time. Therefore it does not make much sense to create an animationId -> + // animation + // object map that would require additional memory just to support the use-case + // of stopping // an animation WritableArray events = null; for (int i = 0; i < mActiveAnimations.size(); i++) { @@ -309,8 +342,10 @@ private void stopAnimationsForNode(AnimatedNode animatedNode) { endCallbackResponse.putDouble("value", animation.animatedValue.nodeValue); animation.endCallback.invoke(endCallbackResponse); } else if (mReactApplicationContext != null) { - // If no callback is passed in, this /may/ be an animation set up by the single-op - // instruction from JS, meaning that no jsi::functions are passed into native and + // If no callback is passed in, this /may/ be an animation set up by the + // single-op + // instruction from JS, meaning that no jsi::functions are passed into native + // and // we communicate via RCTDeviceEventEmitter instead of callbacks. WritableMap params = Arguments.createMap(); params.putInt("animationId", animation.id); @@ -332,9 +367,12 @@ private void stopAnimationsForNode(AnimatedNode animatedNode) { @UiThread public void stopAnimation(int animationId) { - // in most of the cases there should never be more than a few active animations running at the - // same time. Therefore it does not make much sense to create an animationId -> animation - // object map that would require additional memory just to support the use-case of stopping + // in most of the cases there should never be more than a few active animations + // running at the + // same time. Therefore it does not make much sense to create an animationId -> + // animation + // object map that would require additional memory just to support the use-case + // of stopping // an animation WritableArray events = null; for (int i = 0; i < mActiveAnimations.size(); i++) { @@ -347,8 +385,10 @@ public void stopAnimation(int animationId) { endCallbackResponse.putDouble("value", animation.animatedValue.nodeValue); animation.endCallback.invoke(endCallbackResponse); } else if (mReactApplicationContext != null) { - // If no callback is passed in, this /may/ be an animation set up by the single-op - // instruction from JS, meaning that no jsi::functions are passed into native and + // If no callback is passed in, this /may/ be an animation set up by the + // single-op + // instruction from JS, meaning that no jsi::functions are passed into native + // and // we communicate via RCTDeviceEventEmitter instead of callbacks. WritableMap params = Arguments.createMap(); params.putInt("animationId", animation.id); @@ -366,9 +406,12 @@ public void stopAnimation(int animationId) { if (events != null) { mReactApplicationContext.emitDeviceEvent("onNativeAnimatedModuleAnimationFinished", events); } - // Do not throw an error in the case animation could not be found. We only keep "active" - // animations in the registry and there is a chance that Animated.js will enqueue a - // stopAnimation call after the animation has ended or the call will reach native thread only + // Do not throw an error in the case animation could not be found. We only keep + // "active" + // animations in the registry and there is a chance that Animated.js will + // enqueue a + // stopAnimation call after the animation has ended or the call will reach + // native thread only // when the animation is already over. } @@ -376,17 +419,19 @@ public void stopAnimation(int animationId) { public void connectAnimatedNodes(int parentNodeTag, int childNodeTag) { AnimatedNode parentNode = mAnimatedNodes.get(parentNodeTag); if (parentNode == null) { - throw new JSApplicationIllegalArgumentException( - "connectAnimatedNodes: Animated node with tag (parent) [" - + parentNodeTag - + "] does not exist"); + // PATCH: COMMENTED OUT EXCEPTION THROWING + // throw new JSApplicationIllegalArgumentException( + // "connectAnimatedNodes: Animated node with tag (parent) [" + // + parentNodeTag + // + "] does not exist"); } AnimatedNode childNode = mAnimatedNodes.get(childNodeTag); if (childNode == null) { - throw new JSApplicationIllegalArgumentException( - "connectAnimatedNodes: Animated node with tag (child) [" - + childNodeTag - + "] does not exist"); + // PATCH: COMMENTED OUT EXCEPTION THROWING + // throw new JSApplicationIllegalArgumentException( + // "connectAnimatedNodes: Animated node with tag (child) [" + // + childNodeTag + // + "] does not exist"); } parentNode.addChild(childNode); mUpdatedNodes.put(childNodeTag, childNode); @@ -395,17 +440,19 @@ public void connectAnimatedNodes(int parentNodeTag, int childNodeTag) { public void disconnectAnimatedNodes(int parentNodeTag, int childNodeTag) { AnimatedNode parentNode = mAnimatedNodes.get(parentNodeTag); if (parentNode == null) { - throw new JSApplicationIllegalArgumentException( - "disconnectAnimatedNodes: Animated node with tag (parent) [" - + parentNodeTag - + "] does not exist"); + // PATCH: COMMENTED OUT EXCEPTION THROWING + // throw new JSApplicationIllegalArgumentException( + // "disconnectAnimatedNodes: Animated node with tag (parent) [" + // + parentNodeTag + // + "] does not exist"); } AnimatedNode childNode = mAnimatedNodes.get(childNodeTag); if (childNode == null) { - throw new JSApplicationIllegalArgumentException( - "disconnectAnimatedNodes: Animated node with tag (child) [" - + childNodeTag - + "] does not exist"); + // PATCH: COMMENTED OUT EXCEPTION THROWING + // throw new JSApplicationIllegalArgumentException( + // "disconnectAnimatedNodes: Animated node with tag (child) [" + // + childNodeTag + // + "] does not exist"); } parentNode.removeChild(childNode); mUpdatedNodes.put(childNodeTag, childNode); @@ -415,28 +462,30 @@ public void disconnectAnimatedNodes(int parentNodeTag, int childNodeTag) { public void connectAnimatedNodeToView(int animatedNodeTag, int viewTag) { AnimatedNode node = mAnimatedNodes.get(animatedNodeTag); if (node == null) { - throw new JSApplicationIllegalArgumentException( - "connectAnimatedNodeToView: Animated node with tag [" - + animatedNodeTag - + "] does not exist"); + // PATCH: COMMENTED OUT EXCEPTION THROWING + // throw new JSApplicationIllegalArgumentException( + // "connectAnimatedNodeToView: Animated node with tag [" + // + animatedNodeTag + // + "] does not exist"); } if (!(node instanceof PropsAnimatedNode)) { - throw new JSApplicationIllegalArgumentException( - "connectAnimatedNodeToView: Animated node connected to view [" - + viewTag - + "] should be of type " - + PropsAnimatedNode.class.getName()); + // PATCH: COMMENTED OUT EXCEPTION THROWING + // throw new JSApplicationIllegalArgumentException( + // "connectAnimatedNodeToView: Animated node connected to view [" + // + viewTag + // + "] should be of type " + // + PropsAnimatedNode.class.getName()); } if (mReactApplicationContext == null) { - throw new IllegalStateException( - "connectAnimatedNodeToView: Animated node could not be connected, no" - + " ReactApplicationContext: " - + viewTag); + // PATCH: COMMENTED OUT EXCEPTION THROWING + // throw new IllegalStateException( + // "connectAnimatedNodeToView: Animated node could not be connected, no" + // + " ReactApplicationContext: " + // + viewTag); } @Nullable - UIManager uiManager = - UIManagerHelper.getUIManagerForReactTag(mReactApplicationContext, viewTag); + UIManager uiManager = UIManagerHelper.getUIManagerForReactTag(mReactApplicationContext, viewTag); if (uiManager == null) { ReactSoftExceptionLogger.logSoftException( TAG, @@ -456,17 +505,19 @@ public void connectAnimatedNodeToView(int animatedNodeTag, int viewTag) { public void disconnectAnimatedNodeFromView(int animatedNodeTag, int viewTag) { AnimatedNode node = mAnimatedNodes.get(animatedNodeTag); if (node == null) { - throw new JSApplicationIllegalArgumentException( - "disconnectAnimatedNodeFromView: Animated node with tag [" - + animatedNodeTag - + "] does not exist"); + // PATCH: COMMENTED OUT EXCEPTION THROWING + // throw new JSApplicationIllegalArgumentException( + // "disconnectAnimatedNodeFromView: Animated node with tag [" + // + animatedNodeTag + // + "] does not exist"); } if (!(node instanceof PropsAnimatedNode)) { - throw new JSApplicationIllegalArgumentException( - "disconnectAnimatedNodeFromView: Animated node connected to view [" - + viewTag - + "] should be of type " - + PropsAnimatedNode.class.getName()); + // PATCH: COMMENTED OUT EXCEPTION THROWING + // throw new JSApplicationIllegalArgumentException( + // "disconnectAnimatedNodeFromView: Animated node connected to view [" + // + viewTag + // + "] should be of type " + // + PropsAnimatedNode.class.getName()); } PropsAnimatedNode propsAnimatedNode = (PropsAnimatedNode) node; propsAnimatedNode.disconnectFromView(viewTag); @@ -485,10 +536,12 @@ public void getValue(int tag, Callback callback) { return; } - // If there's no callback, that means that JS is using the single-operation mode, and not + // If there's no callback, that means that JS is using the single-operation + // mode, and not // passing any callbacks into Java. // See NativeAnimatedHelper.js for details. - // Instead, we use RCTDeviceEventEmitter to pass data back to JS and emulate callbacks. + // Instead, we use RCTDeviceEventEmitter to pass data back to JS and emulate + // callbacks. if (mReactApplicationContext == null) { return; } @@ -509,9 +562,10 @@ public void restoreDefaultValues(int animatedNodeTag) { return; } if (!(node instanceof PropsAnimatedNode)) { - throw new JSApplicationIllegalArgumentException( - "Animated node connected to view [?] should be of type " - + PropsAnimatedNode.class.getName()); + // PATCH: COMMENTED OUT EXCEPTION THROWING + // throw new JSApplicationIllegalArgumentException( + // "Animated node connected to view [?] should be of type " + // + PropsAnimatedNode.class.getName()); } PropsAnimatedNode propsAnimatedNode = (PropsAnimatedNode) node; propsAnimatedNode.restoreDefaultValues(); @@ -523,17 +577,20 @@ public void addAnimatedEventToView( int nodeTag = eventMapping.getInt("animatedValueTag"); AnimatedNode node = mAnimatedNodes.get(nodeTag); if (node == null) { - throw new JSApplicationIllegalArgumentException( - "addAnimatedEventToView: Animated node with tag [" + nodeTag + "] does not exist"); + // PATCH: COMMENTED OUT EXCEPTION THROWING + // throw new JSApplicationIllegalArgumentException( + // "addAnimatedEventToView: Animated node with tag [" + nodeTag + "] does not + // exist"); } if (!(node instanceof ValueAnimatedNode)) { - throw new JSApplicationIllegalArgumentException( - "addAnimatedEventToView: Animated node on view [" - + viewTag - + "] connected to event handler (" - + eventHandlerName - + ") should be of type " - + ValueAnimatedNode.class.getName()); + // PATCH: COMMENTED OUT EXCEPTION THROWING + // throw new JSApplicationIllegalArgumentException( + // "addAnimatedEventToView: Animated node on view [" + // + viewTag + // + "] connected to event handler (" + // + eventHandlerName + // + ") should be of type " + // + ValueAnimatedNode.class.getName()); } ReadableArray path = eventMapping.getArray("nativeEventPath"); @@ -544,12 +601,12 @@ public void addAnimatedEventToView( String eventName = normalizeEventName(eventHandlerName); - EventAnimationDriver eventDriver = - new EventAnimationDriver(eventName, viewTag, pathList, (ValueAnimatedNode) node); + EventAnimationDriver eventDriver = new EventAnimationDriver(eventName, viewTag, pathList, (ValueAnimatedNode) node); mEventDrivers.add(eventDriver); if (eventName.equals("topScroll")) { - // Handle the custom topScrollEnded event sent by the ScrollViews when the user stops dragging + // Handle the custom topScrollEnded event sent by the ScrollViews when the user + // stops dragging addAnimatedEventToView(viewTag, "topScrollEnded", eventMapping); } } @@ -571,14 +628,16 @@ public void removeAnimatedEventFromView( } if (eventName.equals("topScroll")) { - // Handle the custom topScrollEnded event sent by the ScrollViews when the user stops dragging + // Handle the custom topScrollEnded event sent by the ScrollViews when the user + // stops dragging removeAnimatedEventFromView(viewTag, "topScrollEnded", animatedValueTag); } } @Override public void onEventDispatch(final Event event) { - // Events can be dispatched from any thread so we have to make sure handleEvent is run from the + // Events can be dispatched from any thread so we have to make sure handleEvent + // is run from the // UI thread. if (UiThreadUtil.isOnUiThread()) { handleEvent(event); @@ -617,15 +676,23 @@ private void handleEvent(Event event) { } /** - * Animation loop performs two BFSes over the graph of animated nodes. We use incremented {@code - * mAnimatedGraphBFSColor} to mark nodes as visited in each of the BFSes which saves additional + * Animation loop performs two BFSes over the graph of animated nodes. We use + * incremented {@code + * mAnimatedGraphBFSColor} to mark nodes as visited in each of the BFSes which + * saves additional * loops for clearing "visited" states. * - *

First BFS starts with nodes that are in {@code mUpdatedNodes} (that is, their value have - * been modified from JS in the last batch of JS operations) or directly attached to an active - * animation (hence linked to objects from {@code mActiveAnimations}). In that step we calculate - * an attribute {@code activeIncomingNodes}. The second BFS runs in topological order over the - * sub-graph of *active* nodes. This is done by adding node to the BFS queue only if all its + *

+ * First BFS starts with nodes that are in {@code mUpdatedNodes} (that is, their + * value have + * been modified from JS in the last batch of JS operations) or directly + * attached to an active + * animation (hence linked to objects from {@code mActiveAnimations}). In that + * step we calculate + * an attribute {@code activeIncomingNodes}. The second BFS runs in topological + * order over the + * sub-graph of *active* nodes. This is done by adding node to the BFS queue + * only if all its * "predecessors" have already been visited. */ @UiThread @@ -654,7 +721,8 @@ public void runUpdates(long frameTimeNanos) { updateNodes(mRunUpdateNodeList); mRunUpdateNodeList.clear(); - // Cleanup finished animations. Iterate over the array of animations and override ones that has + // Cleanup finished animations. Iterate over the array of animations and + // override ones that has // finished, then resize `mActiveAnimations`. if (hasFinishedAnimations) { WritableArray events = null; @@ -667,8 +735,10 @@ public void runUpdates(long frameTimeNanos) { endCallbackResponse.putDouble("value", animation.animatedValue.nodeValue); animation.endCallback.invoke(endCallbackResponse); } else if (mReactApplicationContext != null) { - // If no callback is passed in, this /may/ be an animation set up by the single-op - // instruction from JS, meaning that no jsi::functions are passed into native and + // If no callback is passed in, this /may/ be an animation set up by the + // single-op + // instruction from JS, meaning that no jsi::functions are passed into native + // and // we communicate via RCTDeviceEventEmitter instead of callbacks. WritableMap params = Arguments.createMap(); params.putInt("animationId", animation.id); @@ -716,13 +786,16 @@ private void updateNodes(List nodes) { int updatedNodesCount = 0; // STEP 1. - // BFS over graph of nodes. Update `mIncomingNodes` attribute for each node during that BFS. - // Store number of visited nodes in `activeNodesCount`. We "execute" active animations as a part + // BFS over graph of nodes. Update `mIncomingNodes` attribute for each node + // during that BFS. + // Store number of visited nodes in `activeNodesCount`. We "execute" active + // animations as a part // of this step. mAnimatedGraphBFSColor++; /* use new color */ if (mAnimatedGraphBFSColor == AnimatedNode.INITIAL_BFS_COLOR) { - // value "0" is used as an initial color for a new node, using it in BFS may cause some nodes + // value "0" is used as an initial color for a new node, using it in BFS may + // cause some nodes // to be skipped. mAnimatedGraphBFSColor++; } @@ -752,11 +825,16 @@ private void updateNodes(List nodes) { } // STEP 2 - // BFS over the graph of active nodes in topological order -> visit node only when all its - // "predecessors" in the graph have already been visited. It is important to visit nodes in that - // order as they may often use values of their predecessors in order to calculate "next state" - // of their own. We start by determining the starting set of nodes by looking for nodes with - // `activeIncomingNodes = 0` (those can only be the ones that we start BFS in the previous + // BFS over the graph of active nodes in topological order -> visit node only + // when all its + // "predecessors" in the graph have already been visited. It is important to + // visit nodes in that + // order as they may often use values of their predecessors in order to + // calculate "next state" + // of their own. We start by determining the starting set of nodes by looking + // for nodes with + // `activeIncomingNodes = 0` (those can only be the ones that we start BFS in + // the previous // step). We store number of visited nodes in this step in `updatedNodesCount` mAnimatedGraphBFSColor++; @@ -765,7 +843,8 @@ private void updateNodes(List nodes) { mAnimatedGraphBFSColor++; } - // find nodes with zero "incoming nodes", those can be either nodes from `mUpdatedNodes` or + // find nodes with zero "incoming nodes", those can be either nodes from + // `mUpdatedNodes` or // ones connected to active animations for (AnimatedNode node : nodes) { if (node.activeIncomingNodes == 0 && node.BFSColor != mAnimatedGraphBFSColor) { @@ -786,12 +865,16 @@ private void updateNodes(List nodes) { ((PropsAnimatedNode) nextNode).updateView(); } } catch (JSApplicationCausedNativeException e) { - // An exception is thrown if the view hasn't been created yet. This can happen because - // views are created in batches. If this particular view didn't make it into a batch yet, - // the view won't exist and an exception will be thrown when attempting to start an + // An exception is thrown if the view hasn't been created yet. This can happen + // because + // views are created in batches. If this particular view didn't make it into a + // batch yet, + // the view won't exist and an exception will be thrown when attempting to start + // an // animation on it. // - // Eat the exception rather than crashing. The impact is that we may drop one or more + // Eat the exception rather than crashing. The impact is that we may drop one or + // more // frames of the animation. FLog.e(TAG, "Native animation workaround, frame lost as result of race condition", e); } @@ -814,12 +897,17 @@ private void updateNodes(List nodes) { } } - // Verify that we've visited *all* active nodes. Throw otherwise as this could mean there is a - // cycle in animated node graph, or that the graph is only partially set up. We also take - // advantage of the fact that all active nodes are visited in the step above so that all the + // Verify that we've visited *all* active nodes. Throw otherwise as this could + // mean there is a + // cycle in animated node graph, or that the graph is only partially set up. We + // also take + // advantage of the fact that all active nodes are visited in the step above so + // that all the // nodes properties `activeIncomingNodes` are set to zero. - // In Fabric there can be race conditions between the JS thread setting up or tearing down - // animated nodes, and Fabric executing them on the UI thread, leading to temporary inconsistent + // In Fabric there can be race conditions between the JS thread setting up or + // tearing down + // animated nodes, and Fabric executing them on the UI thread, leading to + // temporary inconsistent // states. if (activeNodesCount != updatedNodesCount) { if (mWarnedAboutGraphTraversal) { @@ -827,7 +915,8 @@ private void updateNodes(List nodes) { } mWarnedAboutGraphTraversal = true; - // Before crashing or logging soft exception, log details about current graph setup + // Before crashing or logging soft exception, log details about current graph + // setup FLog.e(TAG, "Detected animation cycle or disconnected graph. "); for (AnimatedNode node : nodes) { FLog.e(TAG, node.prettyPrintWithChildren()); @@ -835,29 +924,32 @@ private void updateNodes(List nodes) { // If we're running only in non-Fabric, we still throw an exception. // In Fabric, it seems that animations enter an inconsistent state fairly often. - // We detect if the inconsistency is due to a cycle (a fatal error for which we must crash) - // or disconnected regions, indicating a partially-set-up animation graph, which is not + // We detect if the inconsistency is due to a cycle (a fatal error for which we + // must crash) + // or disconnected regions, indicating a partially-set-up animation graph, which + // is not // fatal and can stay a warning. - String reason = - cyclesDetected > 0 ? "cycles (" + cyclesDetected + ")" : "disconnected regions"; - IllegalStateException ex = - new IllegalStateException( - "Looks like animated nodes graph has " - + reason - + ", there are " - + activeNodesCount - + " but toposort visited only " - + updatedNodesCount); + String reason = cyclesDetected > 0 ? "cycles (" + cyclesDetected + ")" : "disconnected regions"; + IllegalStateException ex = new IllegalStateException( + "Looks like animated nodes graph has " + + reason + + ", there are " + + activeNodesCount + + " but toposort visited only " + + updatedNodesCount); if (mEventListenerInitializedForFabric && cyclesDetected == 0) { - // TODO T71377544: investigate these SoftExceptions and see if we can remove entirely + // TODO T71377544: investigate these SoftExceptions and see if we can remove + // entirely // or fix the root cause ReactSoftExceptionLogger.logSoftException(TAG, new ReactNoCrashSoftException(ex)); } else if (mEventListenerInitializedForFabric) { - // TODO T71377544: investigate these SoftExceptions and see if we can remove entirely + // TODO T71377544: investigate these SoftExceptions and see if we can remove + // entirely // or fix the root cause ReactSoftExceptionLogger.logSoftException(TAG, new ReactNoCrashSoftException(ex)); } else { - throw ex; + // PATCH: COMMENTED OUT EXCEPTION THROWING + // throw ex; } } else { mWarnedAboutGraphTraversal = false; 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 index 9c66900aaf50..cb365d0d8d40 100644 --- 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 @@ -42,8 +42,9 @@ internal class PropsAnimatedNode( public fun connectToView(viewTag: Int, uiManager: UIManager?) { if (connectedViewTag != -1) { - throw JSApplicationIllegalArgumentException( - "Animated node $tag is already attached to a view: $connectedViewTag") + // PATCH: COMMENTED OUT EXCEPTION THROWING + // throw JSApplicationIllegalArgumentException( + // "Animated node $tag is already attached to a view: $connectedViewTag") } connectedViewTag = viewTag connectedViewUIManager = uiManager @@ -51,10 +52,11 @@ internal class PropsAnimatedNode( 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") + // PATCH: COMMENTED OUT EXCEPTION THROWINGs + // 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 } @@ -85,7 +87,10 @@ internal class PropsAnimatedNode( } for ((key, value) in propNodeMapping) { val node = nativeAnimatedNodesManager.getNodeById(value) - requireNotNull(node) { "Mapped property node does not exist" } + // requireNotNull(node) { "Mapped property node does not exist" } + if (node == null) { + return + } if (node is StyleAnimatedNode) { node.collectViewUpdates(propMap) } else if (node is ValueAnimatedNode) { @@ -102,8 +107,9 @@ internal class PropsAnimatedNode( } else if (node is ObjectAnimatedNode) { node.collectViewUpdates(key, propMap) } else { - throw IllegalArgumentException( - "Unsupported type of node used in property node ${node.javaClass}") + // PATCH: COMMENTED OUT EXCEPTION THROWING + // throw IllegalArgumentException( + // "Unsupported type of node used in property node ${node.javaClass}") } } connectedViewUIManager?.synchronouslyUpdateViewOnUIThread(connectedViewTag, propMap) diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/animated/StyleAnimatedNode.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/animated/StyleAnimatedNode.kt index bc76ca3a99ed..4765aeb0592a 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/animated/StyleAnimatedNode.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/animated/StyleAnimatedNode.kt @@ -34,7 +34,10 @@ internal class StyleAnimatedNode( public fun collectViewUpdates(propsMap: JavaOnlyMap) { for ((key, value) in propMapping) { val node = nativeAnimatedNodesManager.getNodeById(value) - requireNotNull(node) { "Mapped style node does not exist" } + // requireNotNull(node) { "Mapped style node does not exist" } + if (node == null) { + return + } if (node is TransformAnimatedNode) { node.collectViewUpdates(propsMap) } else if (node is ValueAnimatedNode) { @@ -51,8 +54,9 @@ internal class StyleAnimatedNode( } else if (node is ObjectAnimatedNode) { node.collectViewUpdates(key, propsMap) } else { - throw IllegalArgumentException( - "Unsupported type of node used in property node ${node.javaClass}") + // PATCH: COMMENTED OUT EXCEPTION THROWING + // throw IllegalArgumentException( + // "Unsupported type of node used in property node ${node.javaClass}") } } } diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/animated/TransformAnimatedNode.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/animated/TransformAnimatedNode.kt index 2f76add6b5b3..22683cb9f73b 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/animated/TransformAnimatedNode.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/animated/TransformAnimatedNode.kt @@ -53,14 +53,18 @@ internal class TransformAnimatedNode( val nodeTag = transformConfig.nodeTag val node = nativeAnimatedNodesManager.getNodeById(nodeTag) if (node == null) { - throw IllegalArgumentException("Mapped style node does not exist") + // PATCH: COMMENTED OUT EXCEPTION THROWING + // throw IllegalArgumentException("Mapped style node does not exist") + return } else if (node is ValueAnimatedNode) { node.getValue() } else { - throw IllegalArgumentException( - "Unsupported type of node used as a transform child " + - "node " + - node.javaClass) + return + // PATCH: COMMENTED OUT EXCEPTION THROWING + // throw IllegalArgumentException( + // "Unsupported type of node used as a transform child " + + // "node " + + // node.javaClass) } } else { (transformConfig as StaticTransformConfig).value diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/modules/network/NetworkingModule.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/modules/network/NetworkingModule.java index 5b24caff523e..e0b9e0a247e6 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/modules/network/NetworkingModule.java +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/modules/network/NetworkingModule.java @@ -52,7 +52,8 @@ public final class NetworkingModule extends NativeNetworkingAndroidSpec { /** - * Allows to implement a custom fetching process for specific URIs. It is the handler's job to + * Allows to implement a custom fetching process for specific URIs. It is the + * handler's job to * fetch the URI and return the JS body payload. */ public interface UriHandler { @@ -63,7 +64,10 @@ public interface UriHandler { WritableMap fetch(Uri uri) throws IOException; } - /** Allows adding custom handling to build the {@link RequestBody} from the JS body payload. */ + /** + * Allows adding custom handling to build the {@link RequestBody} from the JS + * body payload. + */ public interface RequestBodyHandler { /** Returns if the handler should be used for a JS body payload. */ boolean supports(ReadableMap map); @@ -72,7 +76,10 @@ public interface RequestBodyHandler { RequestBody toRequestBody(ReadableMap map, String contentType); } - /** Allows adding custom handling to build the JS body payload from the {@link ResponseBody}. */ + /** + * Allows adding custom handling to build the JS body payload from the + * {@link ResponseBody}. + */ public interface ResponseHandler { /** Returns if the handler should be used for a response type. */ boolean supports(String responseType); @@ -92,8 +99,7 @@ public interface ResponseHandler { private static final int CHUNK_TIMEOUT_NS = 100 * 1000000; // 100ms private static final int MAX_CHUNK_SIZE_BETWEEN_FLUSHES = 8 * 1024; // 8K - private static @Nullable com.facebook.react.modules.network.CustomClientBuilder - customClientBuilder = null; + private static @Nullable com.facebook.react.modules.network.CustomClientBuilder customClientBuilder = null; private final OkHttpClient mClient; private final ForwardingCookieHandler mCookieHandler = new ForwardingCookieHandler(); @@ -131,10 +137,11 @@ public NetworkingModule( } /** - * @param context the ReactContext of the application - * @param defaultUserAgent the User-Agent header that will be set for all requests where the - * caller does not provide one explicitly - * @param client the {@link OkHttpClient} to be used for networking + * @param context the ReactContext of the application + * @param defaultUserAgent the User-Agent header that will be set for all + * requests where the + * caller does not provide one explicitly + * @param client the {@link OkHttpClient} to be used for networking */ /* package */ NetworkingModule( ReactApplicationContext context, @Nullable String defaultUserAgent, OkHttpClient client) { @@ -145,26 +152,29 @@ public NetworkingModule( * @param context the ReactContext of the application */ public NetworkingModule(final ReactApplicationContext context) { - this(context, null, OkHttpClientProvider.createClient(context), null); + this(context, null, OkHttpClientProvider.getOkHttpClient(), null); } /** - * @param context the ReactContext of the application - * @param networkInterceptorCreators list of {@link NetworkInterceptorCreator}'s whose create() - * methods would be called to attach the interceptors to the client. + * @param context the ReactContext of the application + * @param networkInterceptorCreators list of {@link NetworkInterceptorCreator}'s + * whose create() + * methods would be called to attach the + * interceptors to the client. */ public NetworkingModule( ReactApplicationContext context, List networkInterceptorCreators) { - this(context, null, OkHttpClientProvider.createClient(context), networkInterceptorCreators); + this(context, null, OkHttpClientProvider.getOkHttpClient(), networkInterceptorCreators); } /** - * @param context the ReactContext of the application - * @param defaultUserAgent the User-Agent header that will be set for all requests where the - * caller does not provide one explicitly + * @param context the ReactContext of the application + * @param defaultUserAgent the User-Agent header that will be set for all + * requests where the + * caller does not provide one explicitly */ public NetworkingModule(ReactApplicationContext context, String defaultUserAgent) { - this(context, defaultUserAgent, OkHttpClientProvider.createClient(context), null); + this(context, defaultUserAgent, OkHttpClientProvider.getOkHttpClient(), null); } public static void setCustomClientBuilder( @@ -174,11 +184,12 @@ public static void setCustomClientBuilder( /** * @deprecated To be removed in a future release. See - * https://github.com/facebook/react-native/pull/37798#pullrequestreview-1518338914 + * https://github.com/facebook/react-native/pull/37798#pullrequestreview-1518338914 */ @Deprecated public interface CustomClientBuilder - extends com.facebook.react.modules.network.CustomClientBuilder {} + extends com.facebook.react.modules.network.CustomClientBuilder { + } private static void applyCustomBuilder(OkHttpClient.Builder builder) { if (customClientBuilder != null) { @@ -277,8 +288,7 @@ public void sendRequestInternal( final boolean useIncrementalUpdates, int timeout, boolean withCredentials) { - final ReactApplicationContext reactApplicationContext = - getReactApplicationContextIfActiveOrWarn(); + final ReactApplicationContext reactApplicationContext = getReactApplicationContextIfActiveOrWarn(); try { Uri uri = Uri.parse(url); @@ -316,43 +326,46 @@ public void sendRequestInternal( clientBuilder.cookieJar(CookieJar.NO_COOKIES); } - // If JS is listening for progress updates, install a ProgressResponseBody that intercepts the + // If JS is listening for progress updates, install a ProgressResponseBody that + // intercepts the // response and counts bytes received. if (useIncrementalUpdates) { clientBuilder.addNetworkInterceptor( chain -> { Response originalResponse = chain.proceed(chain.request()); - ProgressResponseBody responseBody = - new ProgressResponseBody( - originalResponse.body(), - new ProgressListener() { - long last = System.nanoTime(); - - @Override - public void onProgress(long bytesWritten, long contentLength, boolean done) { - long now = System.nanoTime(); - if (!done && !shouldDispatch(now, last)) { - return; - } - if (responseType.equals("text")) { - // For 'text' responses we continuously send response data with progress - // info to - // JS below, so no need to do anything here. - return; - } - ResponseUtil.onDataReceivedProgress( - reactApplicationContext, requestId, bytesWritten, contentLength); - last = now; - } - }); + ProgressResponseBody responseBody = new ProgressResponseBody( + originalResponse.body(), + new ProgressListener() { + long last = System.nanoTime(); + + @Override + public void onProgress(long bytesWritten, long contentLength, boolean done) { + long now = System.nanoTime(); + if (!done && !shouldDispatch(now, last)) { + return; + } + if (responseType.equals("text")) { + // For 'text' responses we continuously send response data with progress + // info to + // JS below, so no need to do anything here. + return; + } + ResponseUtil.onDataReceivedProgress( + reactApplicationContext, requestId, bytesWritten, contentLength); + last = now; + } + }); return originalResponse.newBuilder().body(responseBody).build(); }); } - // If the current timeout does not equal the passed in timeout, we need to clone the existing - // client and set the timeout explicitly on the clone. This is cheap as everything else is + // If the current timeout does not equal the passed in timeout, we need to clone + // the existing + // client and set the timeout explicitly on the clone. This is cheap as + // everything else is // shared under the hood. - // See https://github.com/square/okhttp/wiki/Recipes#per-call-configuration for more information + // See https://github.com/square/okhttp/wiki/Recipes#per-call-configuration for + // more information if (timeout != mClient.callTimeoutMillis()) { clientBuilder.callTimeout(timeout, TimeUnit.MILLISECONDS); } @@ -406,12 +419,12 @@ public void onProgress(long bytesWritten, long contentLength, boolean done) { } } else { // Use getBytes() to convert the body into a byte[], preventing okhttp from - // appending the character set to the Content-Type header when otherwise unspecified + // appending the character set to the Content-Type header when otherwise + // unspecified // https://github.com/facebook/react-native/issues/8237 - Charset charset = - contentMediaType == null - ? StandardCharsets.UTF_8 - : contentMediaType.charset(StandardCharsets.UTF_8); + Charset charset = contentMediaType == null + ? StandardCharsets.UTF_8 + : contentMediaType.charset(StandardCharsets.UTF_8); requestBody = RequestBody.create(contentMediaType, body.getBytes(charset)); } } else if (data.hasKey(REQUEST_BODY_KEY_BASE64)) { @@ -436,8 +449,7 @@ public void onProgress(long bytesWritten, long contentLength, boolean done) { return; } String uri = data.getString(REQUEST_BODY_KEY_URI); - InputStream fileInputStream = - RequestBodyUtil.getFileInputStream(getReactApplicationContext(), uri); + InputStream fileInputStream = RequestBodyUtil.getFileInputStream(getReactApplicationContext(), uri); if (fileInputStream == null) { ResponseUtil.onRequestError( reactApplicationContext, requestId, "Could not retrieve file for uri " + uri, null); @@ -449,8 +461,7 @@ public void onProgress(long bytesWritten, long contentLength, boolean done) { contentType = "multipart/form-data"; } ReadableArray parts = data.getArray(REQUEST_BODY_KEY_FORMDATA); - MultipartBody.Builder multipartBuilder = - constructMultipartBody(parts, contentType, requestId); + MultipartBody.Builder multipartBuilder = constructMultipartBody(parts, contentType, requestId); if (multipartBuilder == null) { return; } @@ -473,10 +484,9 @@ public void onFailure(Call call, IOException e) { return; } removeRequest(requestId); - String errorMessage = - e.getMessage() != null - ? e.getMessage() - : "Error while executing request: " + e.getClass().getSimpleName(); + String errorMessage = e.getMessage() != null + ? e.getMessage() + : "Error while executing request: " + e.getClass().getSimpleName(); ResponseUtil.onRequestError(reactApplicationContext, requestId, errorMessage, e); } @@ -500,11 +510,14 @@ public void onResponse(Call call, Response response) throws IOException { // internally. // The issue is that it won't handle decoding if the user provides a // Accept-Encoding - // header. This is also undesirable considering that iOS does handle the decoding + // header. This is also undesirable considering that iOS does handle the + // decoding // even - // when the header is provided. To make sure this works in all cases, handle gzip + // when the header is provided. To make sure this works in all cases, handle + // gzip // body - // here also. This works fine since OKHttp will remove the Content-Encoding header + // here also. This works fine since OKHttp will remove the Content-Encoding + // header // if // it used transparent gzip. // See @@ -514,11 +527,10 @@ public void onResponse(Call call, Response response) throws IOException { && responseBody != null) { GzipSource gzipSource = new GzipSource(responseBody.source()); String contentType = response.header("Content-Type"); - responseBody = - ResponseBody.create( - contentType != null ? MediaType.parse(contentType) : null, - -1L, - Okio.buffer(gzipSource)); + responseBody = ResponseBody.create( + contentType != null ? MediaType.parse(contentType) : null, + -1L, + Okio.buffer(gzipSource)); } // Check if a handler is registered @@ -575,8 +587,7 @@ private RequestBody wrapRequestBodyWithProgressEmitter( if (requestBody == null) { return null; } - final ReactApplicationContext reactApplicationContext = - getReactApplicationContextIfActiveOrWarn(); + final ReactApplicationContext reactApplicationContext = getReactApplicationContextIfActiveOrWarn(); return RequestBodyUtil.createProgressRequest( requestBody, new ProgressListener() { @@ -605,18 +616,16 @@ private void readWithProgress(int requestId, ResponseBody responseBody) throws I // Ignore } - Charset charset = - responseBody.contentType() == null - ? StandardCharsets.UTF_8 - : responseBody.contentType().charset(StandardCharsets.UTF_8); + Charset charset = responseBody.contentType() == null + ? StandardCharsets.UTF_8 + : responseBody.contentType().charset(StandardCharsets.UTF_8); ProgressiveStringDecoder streamDecoder = new ProgressiveStringDecoder(charset); InputStream inputStream = responseBody.byteStream(); try { byte[] buffer = new byte[MAX_CHUNK_SIZE_BETWEEN_FLUSHES]; int read; - final ReactApplicationContext reactApplicationContext = - getReactApplicationContextIfActiveOrWarn(); + final ReactApplicationContext reactApplicationContext = getReactApplicationContextIfActiveOrWarn(); while ((read = inputStream.read(buffer)) != -1) { ResponseUtil.onIncrementalDataReceived( reactApplicationContext, @@ -681,18 +690,19 @@ public void clearCookies(com.facebook.react.bridge.Callback callback) { } @Override - public void addListener(String eventName) {} + public void addListener(String eventName) { + } @Override - public void removeListeners(double count) {} + public void removeListeners(double count) { + } private @Nullable MultipartBody.Builder constructMultipartBody( ReadableArray body, String contentType, int requestId) { MultipartBody.Builder multipartBuilder = new MultipartBody.Builder(); multipartBuilder.setType(MediaType.parse(contentType)); - final ReactApplicationContext reactApplicationContext = - getReactApplicationContextIfActiveOrWarn(); + final ReactApplicationContext reactApplicationContext = getReactApplicationContextIfActiveOrWarn(); for (int i = 0, size = body.size(); i < size; i++) { ReadableMap bodyPart = body.getMap(i); @@ -712,7 +722,8 @@ public void removeListeners(double count) {} String partContentTypeStr = headers.get(CONTENT_TYPE_HEADER_NAME); if (partContentTypeStr != null) { partContentType = MediaType.parse(partContentTypeStr); - // Remove the content-type header because MultipartBuilder gets it explicitly as an + // Remove the content-type header because MultipartBuilder gets it explicitly as + // an // argument and doesn't expect it in the headers array. headers = headers.newBuilder().removeAll(CONTENT_TYPE_HEADER_NAME).build(); } @@ -730,8 +741,8 @@ public void removeListeners(double count) {} return null; } String fileContentUriStr = bodyPart.getString(REQUEST_BODY_KEY_URI); - InputStream fileInputStream = - RequestBodyUtil.getFileInputStream(getReactApplicationContext(), fileContentUriStr); + InputStream fileInputStream = RequestBodyUtil.getFileInputStream(getReactApplicationContext(), + fileContentUriStr); if (fileInputStream == null) { ResponseUtil.onRequestError( reactApplicationContext, @@ -750,7 +761,8 @@ public void removeListeners(double count) {} } /** - * Extracts the headers from the Array. If the format is invalid, this method will return null. + * Extracts the headers from the Array. If the format is invalid, this method + * will return null. */ private @Nullable Headers extractHeaders( @Nullable ReadableArray headersArray, @Nullable ReadableMap requestData) { @@ -774,7 +786,8 @@ public void removeListeners(double count) {} headersBuilder.add(USER_AGENT_HEADER_NAME, mDefaultUserAgent); } - // Sanitize content encoding header, supported only when request specify payload as string + // Sanitize content encoding header, supported only when request specify payload + // as string boolean isGzipSupported = requestData != null && requestData.hasKey(REQUEST_BODY_KEY_STRING); if (!isGzipSupported) { headersBuilder.removeAll(CONTENT_ENCODING_HEADER_NAME); From 878204f7a9ed7a5d082e8c684373559e25ee439d Mon Sep 17 00:00:00 2001 From: Amit Mundra Date: Tue, 22 Jul 2025 14:47:49 +0530 Subject: [PATCH 2/6] changes for fixing patch for animation crash --- .../com/facebook/react/animated/NativeAnimatedNodesManager.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/animated/NativeAnimatedNodesManager.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/animated/NativeAnimatedNodesManager.java index b0f45ab01d4e..8bd71508bb00 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/animated/NativeAnimatedNodesManager.java +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/animated/NativeAnimatedNodesManager.java @@ -445,6 +445,7 @@ public void disconnectAnimatedNodes(int parentNodeTag, int childNodeTag) { // "disconnectAnimatedNodes: Animated node with tag (parent) [" // + parentNodeTag // + "] does not exist"); + return; } AnimatedNode childNode = mAnimatedNodes.get(childNodeTag); if (childNode == null) { @@ -453,6 +454,7 @@ public void disconnectAnimatedNodes(int parentNodeTag, int childNodeTag) { // "disconnectAnimatedNodes: Animated node with tag (child) [" // + childNodeTag // + "] does not exist"); + return; } parentNode.removeChild(childNode); mUpdatedNodes.put(childNodeTag, childNode); From 8b619f58c3a1934b0ebd12d85f8f471fdd919811 Mon Sep 17 00:00:00 2001 From: amitmundraz08 <85013143+amitmundraz08@users.noreply.github.com> Date: Thu, 24 Jul 2025 10:40:16 +0530 Subject: [PATCH 3/6] changes for connect animatedNodes (#14) * changes for connect animatedNodes * returning null values --- .../react/animated/NativeAnimatedNodesManager.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/animated/NativeAnimatedNodesManager.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/animated/NativeAnimatedNodesManager.java index 8bd71508bb00..b5f96266ba6c 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/animated/NativeAnimatedNodesManager.java +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/animated/NativeAnimatedNodesManager.java @@ -201,6 +201,7 @@ public void startListeningToAnimatedNodeValue(int tag, AnimatedNodeValueListener // "startListeningToAnimatedNodeValue: Animated node [" // + tag // + "] does not exist, or is not a 'value' node"); + return; } ((ValueAnimatedNode) node).setValueListener(listener); } @@ -424,6 +425,7 @@ public void connectAnimatedNodes(int parentNodeTag, int childNodeTag) { // "connectAnimatedNodes: Animated node with tag (parent) [" // + parentNodeTag // + "] does not exist"); + return; } AnimatedNode childNode = mAnimatedNodes.get(childNodeTag); if (childNode == null) { @@ -432,6 +434,7 @@ public void connectAnimatedNodes(int parentNodeTag, int childNodeTag) { // "connectAnimatedNodes: Animated node with tag (child) [" // + childNodeTag // + "] does not exist"); + return; } parentNode.addChild(childNode); mUpdatedNodes.put(childNodeTag, childNode); @@ -469,6 +472,7 @@ public void connectAnimatedNodeToView(int animatedNodeTag, int viewTag) { // "connectAnimatedNodeToView: Animated node with tag [" // + animatedNodeTag // + "] does not exist"); + return; } if (!(node instanceof PropsAnimatedNode)) { // PATCH: COMMENTED OUT EXCEPTION THROWING @@ -477,6 +481,7 @@ public void connectAnimatedNodeToView(int animatedNodeTag, int viewTag) { // + viewTag // + "] should be of type " // + PropsAnimatedNode.class.getName()); + return; } if (mReactApplicationContext == null) { // PATCH: COMMENTED OUT EXCEPTION THROWING @@ -484,6 +489,7 @@ public void connectAnimatedNodeToView(int animatedNodeTag, int viewTag) { // "connectAnimatedNodeToView: Animated node could not be connected, no" // + " ReactApplicationContext: " // + viewTag); + return; } @Nullable @@ -512,6 +518,7 @@ public void disconnectAnimatedNodeFromView(int animatedNodeTag, int viewTag) { // "disconnectAnimatedNodeFromView: Animated node with tag [" // + animatedNodeTag // + "] does not exist"); + return; } if (!(node instanceof PropsAnimatedNode)) { // PATCH: COMMENTED OUT EXCEPTION THROWING @@ -520,6 +527,7 @@ public void disconnectAnimatedNodeFromView(int animatedNodeTag, int viewTag) { // + viewTag // + "] should be of type " // + PropsAnimatedNode.class.getName()); + return; } PropsAnimatedNode propsAnimatedNode = (PropsAnimatedNode) node; propsAnimatedNode.disconnectFromView(viewTag); From 2d7381dccd6e605b2d15ebb26055d50cabe3b846 Mon Sep 17 00:00:00 2001 From: amitmundraz08 <85013143+amitmundraz08@users.noreply.github.com> Date: Thu, 24 Jul 2025 11:11:51 +0530 Subject: [PATCH 4/6] changes for fixing patch (#15) --- .../react/animated/NativeAnimatedNodesManager.java | 11 +++++++++++ .../com/facebook/react/animated/StyleAnimatedNode.kt | 1 + 2 files changed, 12 insertions(+) diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/animated/NativeAnimatedNodesManager.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/animated/NativeAnimatedNodesManager.java index b5f96266ba6c..edbb4bbce5ea 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/animated/NativeAnimatedNodesManager.java +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/animated/NativeAnimatedNodesManager.java @@ -128,6 +128,7 @@ public void createAnimatedNode(int tag, ReadableMap config) { // PATCH: COMMENTED OUT EXCEPTION THROWING // throw new JSApplicationIllegalArgumentException( // "createAnimatedNode: Animated node [" + tag + "] already exists"); + return; } String type = config.getString("type"); final AnimatedNode node; @@ -177,6 +178,7 @@ public void updateAnimatedNodeConfig(int tag, ReadableMap config) { // PATCH: COMMENTED OUT EXCEPTION THROWING // throw new JSApplicationIllegalArgumentException( // "updateAnimatedNode: Animated node [" + tag + "] does not exist"); + return; } if (node instanceof AnimatedNodeWithUpdateableConfig) { @@ -228,6 +230,7 @@ public void setAnimatedNodeValue(int tag, double value) { // "setAnimatedNodeValue: Animated node [" // + tag // + "] does not exist, or is not a 'value' node"); + return; } stopAnimationsForNode(node); ((ValueAnimatedNode) node).nodeValue = value; @@ -243,6 +246,7 @@ public void setAnimatedNodeOffset(int tag, double offset) { // "setAnimatedNodeOffset: Animated node [" // + tag // + "] does not exist, or is not a 'value' node"); + return; } ((ValueAnimatedNode) node).offset = offset; mUpdatedNodes.put(tag, node); @@ -257,6 +261,7 @@ public void flattenAnimatedNodeOffset(int tag) { // "flattenAnimatedNodeOffset: Animated node [" // + tag // + "] does not exist, or is not a 'value' node"); + return; } ((ValueAnimatedNode) node).flattenOffset(); } @@ -270,6 +275,7 @@ public void extractAnimatedNodeOffset(int tag) { // "extractAnimatedNodeOffset: Animated node [" // + tag // + "] does not exist, or is not a 'value' node"); + return; } ((ValueAnimatedNode) node).extractOffset(); } @@ -283,6 +289,7 @@ public void startAnimatingNode( // throw new JSApplicationIllegalArgumentException( // "startAnimatingNode: Animated node [" + animatedNodeTag + "] does not // exist"); + return; } if (!(node instanceof ValueAnimatedNode)) { // PATCH: COMMENTED OUT EXCEPTION THROWING @@ -291,6 +298,7 @@ public void startAnimatingNode( // + animatedNodeTag // + "] should be of type " // + ValueAnimatedNode.class.getName()); + return; } final AnimationDriver existingDriver = mActiveAnimations.get(animationId); @@ -576,6 +584,7 @@ public void restoreDefaultValues(int animatedNodeTag) { // throw new JSApplicationIllegalArgumentException( // "Animated node connected to view [?] should be of type " // + PropsAnimatedNode.class.getName()); + return; } PropsAnimatedNode propsAnimatedNode = (PropsAnimatedNode) node; propsAnimatedNode.restoreDefaultValues(); @@ -591,6 +600,7 @@ public void addAnimatedEventToView( // throw new JSApplicationIllegalArgumentException( // "addAnimatedEventToView: Animated node with tag [" + nodeTag + "] does not // exist"); + return; } if (!(node instanceof ValueAnimatedNode)) { // PATCH: COMMENTED OUT EXCEPTION THROWING @@ -601,6 +611,7 @@ public void addAnimatedEventToView( // + eventHandlerName // + ") should be of type " // + ValueAnimatedNode.class.getName()); + return; } ReadableArray path = eventMapping.getArray("nativeEventPath"); diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/animated/StyleAnimatedNode.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/animated/StyleAnimatedNode.kt index 4765aeb0592a..baa213ea28af 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/animated/StyleAnimatedNode.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/animated/StyleAnimatedNode.kt @@ -54,6 +54,7 @@ internal class StyleAnimatedNode( } else if (node is ObjectAnimatedNode) { node.collectViewUpdates(key, propsMap) } else { + return; // PATCH: COMMENTED OUT EXCEPTION THROWING // throw IllegalArgumentException( // "Unsupported type of node used in property node ${node.javaClass}") From 720a772d558030d0a606bb19582edffefa1d72a5 Mon Sep 17 00:00:00 2001 From: Amit Mundra Date: Mon, 28 Jul 2025 23:04:53 +0530 Subject: [PATCH 5/6] changes for patch fix --- .../com/facebook/react/animated/NativeAnimatedNodesManager.java | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/animated/NativeAnimatedNodesManager.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/animated/NativeAnimatedNodesManager.java index edbb4bbce5ea..780ce3cbcb3b 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/animated/NativeAnimatedNodesManager.java +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/animated/NativeAnimatedNodesManager.java @@ -217,6 +217,7 @@ public void stopListeningToAnimatedNodeValue(int tag) { // "startListeningToAnimatedNodeValue: Animated node [" // + tag // + "] does not exist, or is not a 'value' node"); + return; } ((ValueAnimatedNode) node).setValueListener(null); } From b8bbb6f4179f50f7004dfe1289e657104f8326a7 Mon Sep 17 00:00:00 2001 From: Amit Mundra Date: Mon, 28 Jul 2025 23:13:40 +0530 Subject: [PATCH 6/6] added another missing return --- .../facebook/react/animated/NativeAnimatedNodesManager.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/animated/NativeAnimatedNodesManager.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/animated/NativeAnimatedNodesManager.java index 780ce3cbcb3b..4ab4c69d515a 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/animated/NativeAnimatedNodesManager.java +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/animated/NativeAnimatedNodesManager.java @@ -546,8 +546,10 @@ public void disconnectAnimatedNodeFromView(int animatedNodeTag, int viewTag) { public void getValue(int tag, Callback callback) { AnimatedNode node = mAnimatedNodes.get(tag); if (node == null || !(node instanceof ValueAnimatedNode)) { - throw new JSApplicationIllegalArgumentException( - "getValue: Animated node with tag [" + tag + "] does not exist or is not a 'value' node"); + // throw new JSApplicationIllegalArgumentException( + // "getValue: Animated node with tag [" + tag + "] does not exist or is not a + // 'value' node"); + return; } double value = ((ValueAnimatedNode) node).getValue(); if (callback != null) {