Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Migrate DecayAnimation to Kotlin #45719

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions packages/react-native/ReactAndroid/api/ReactAndroid.api
Original file line number Diff line number Diff line change
Expand Up @@ -439,6 +439,23 @@ public abstract interface class com/facebook/react/ViewManagerOnDemandReactPacka
public abstract fun getViewManagerNames (Lcom/facebook/react/bridge/ReactApplicationContext;)Ljava/util/Collection;
}

public abstract class com/facebook/react/animated/AnimatedNode {
public static final field Companion Lcom/facebook/react/animated/AnimatedNode$Companion;
public static final field DEFAULT_ANIMATED_NODE_CHILD_COUNT I
public static final field INITIAL_BFS_COLOR I
public fun <init> ()V
public final fun addChild (Lcom/facebook/react/animated/AnimatedNode;)V
public fun onAttachedToNode (Lcom/facebook/react/animated/AnimatedNode;)V
public fun onDetachedFromNode (Lcom/facebook/react/animated/AnimatedNode;)V
public abstract fun prettyPrint ()Ljava/lang/String;
public final fun prettyPrintWithChildren ()Ljava/lang/String;
public final fun removeChild (Lcom/facebook/react/animated/AnimatedNode;)V
public fun update ()V
}

public final class com/facebook/react/animated/AnimatedNode$Companion {
}

public class com/facebook/react/animated/NativeAnimatedModule : com/facebook/fbreact/specs/NativeAnimatedModuleSpec, com/facebook/react/bridge/LifecycleEventListener, com/facebook/react/bridge/UIManagerListener {
public static final field ANIMATED_MODULE_DEBUG Z
public fun <init> (Lcom/facebook/react/bridge/ReactApplicationContext;)V
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,11 @@ public AdditionAnimatedNode(

@Override
public void update() {
mValue = 0;
nodeValue = 0;
for (int i = 0; i < mInputNodes.length; i++) {
AnimatedNode animatedNode = mNativeAnimatedNodesManager.getNodeById(mInputNodes[i]);
if (animatedNode != null && animatedNode instanceof ValueAnimatedNode) {
mValue += ((ValueAnimatedNode) animatedNode).getValue();
nodeValue += ((ValueAnimatedNode) animatedNode).getValue();
} else {
throw new JSApplicationCausedNativeException(
"Illegal node ID set as an input for Animated.Add node");
Expand All @@ -47,7 +47,7 @@ public void update() {
@Override
public String prettyPrint() {
return "AdditionAnimatedNode["
+ mTag
+ tag
+ "]: input nodes: "
+ (mInputNodes != null ? mInputNodes.toString() : "null")
+ " - super: "
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

package com.facebook.react.animated

import java.util.ArrayList

/** Base class for all Animated.js library node types that can be created on the "native" side. */
public abstract class AnimatedNode {

public companion object {
public const val INITIAL_BFS_COLOR: Int = 0
public const val DEFAULT_ANIMATED_NODE_CHILD_COUNT: Int = 1
}

// TODO: T196787278 Reduce the visibility of these fields to package once we have
// converted the whole module to Kotlin

@JvmField
internal var children: MutableList<AnimatedNode>? =
null /* lazy-initialized when a child is added */
@JvmField internal var activeIncomingNodes: Int = 0
@JvmField internal var BFSColor: Int = INITIAL_BFS_COLOR
@JvmField internal var tag: Int = -1

public fun addChild(child: AnimatedNode): Unit {
val currentChildren =
children
?: ArrayList<AnimatedNode>(DEFAULT_ANIMATED_NODE_CHILD_COUNT).also { children = it }

currentChildren.add(child)
child.onAttachedToNode(this)
}

public fun removeChild(child: AnimatedNode): Unit {
val currentChildren = children ?: return
child.onDetachedFromNode(this)
currentChildren.remove(child)
}

/**
* Subclasses may want to override this method in order to store a reference to the parent of a
* given node that can then be used to calculate current node's value in {@link #update}. In that
* case it is important to also override {@link #onDetachedFromNode} to clear that reference once
* current node gets detached.
*/
public open fun onAttachedToNode(parent: AnimatedNode): Unit = Unit

/** See {@link #onAttachedToNode} */
public open fun onDetachedFromNode(parent: AnimatedNode): Unit = Unit

/**
* This method will be run on each node at most once every repetition of the animation loop. It
* will be executed on a node only when all the node's parent has already been updated. Therefore
* it can be used to calculate node's value.
*/
public open fun update(): Unit = Unit

/**
* Pretty-printer for the AnimatedNode. Only called in production pre-crash for debug diagnostics.
*/
public abstract fun prettyPrint(): String

public fun prettyPrintWithChildren(): String {

val currentChildren = children?.joinToString(" ")
return prettyPrint() +
if (!currentChildren.isNullOrBlank()) " children: $currentChildren" else ""
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ public void onUpdateConfig(ReadableMap config) {
@Override
public String prettyPrint() {
return "ColorAnimatedNode["
+ mTag
+ tag
+ "]: r: "
+ mRNodeId
+ " g: "
Expand Down Expand Up @@ -94,10 +94,10 @@ private void tryApplyNativeColor() {
ValueAnimatedNode bNode = (ValueAnimatedNode) mNativeAnimatedNodesManager.getNodeById(mBNodeId);
ValueAnimatedNode aNode = (ValueAnimatedNode) mNativeAnimatedNodesManager.getNodeById(mANodeId);

rNode.mValue = Color.red(color);
gNode.mValue = Color.green(color);
bNode.mValue = Color.blue(color);
aNode.mValue = Color.alpha(color) / 255.0;
rNode.nodeValue = Color.red(color);
gNode.nodeValue = Color.green(color);
bNode.nodeValue = Color.blue(color);
aNode.nodeValue = Color.alpha(color) / 255.0;

mNativeColorApplied = true;
}
Expand All @@ -116,8 +116,8 @@ private Context getContext() {
private static Context getContextHelper(AnimatedNode node) {
// Search children depth-first until we get to a PropsAnimatedNode, from which we can
// get the view and its context
if (node.mChildren != null) {
for (AnimatedNode child : node.mChildren) {
if (node.children != null) {
for (AnimatedNode child : node.children) {
if (child instanceof PropsAnimatedNode) {
View view = ((PropsAnimatedNode) child).getConnectedView();
return view != null ? view.getContext() : null;
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

package com.facebook.react.animated

import com.facebook.react.bridge.ReadableMap
import kotlin.math.abs
import kotlin.math.exp

/**
* Implementation of [AnimationDriver] providing support for decay animations. The implementation is
* copied from the JS version in `AnimatedImplementation.js`.
*/
internal class DecayAnimation(config: ReadableMap) : AnimationDriver() {
private var velocity: Double = 0.0
private var deceleration: Double = 0.0
private var startFrameTimeMillis: Long = -1
private var fromValue: Double = 0.0
private var lastValue: Double = 0.0
private var iterations: Int = 1
private var currentLoop: Int = 1

init {
resetConfig(config)
}

public override fun resetConfig(config: ReadableMap): Unit {
velocity = config.getDouble("velocity")
deceleration = config.getDouble("deceleration")
startFrameTimeMillis = -1
fromValue = 0.0
lastValue = 0.0
iterations = if (config.hasKey("iterations")) config.getInt("iterations") else 1
currentLoop = 1
mHasFinished = iterations == 0
}

public override fun runAnimationStep(frameTimeNanos: Long) {
val frameTimeMillis = frameTimeNanos / 1000000
if (startFrameTimeMillis == -1L) {
// since this is the first animation step, consider the start to be on the previous frame
startFrameTimeMillis = frameTimeMillis - 16
if (fromValue == lastValue) { // first iteration, assign mFromValue based on mAnimatedValue
fromValue = mAnimatedValue.nodeValue
} else { // not the first iteration, reset mAnimatedValue based on mFromValue
mAnimatedValue.nodeValue = fromValue
}
lastValue = mAnimatedValue.nodeValue
}
val value =
(fromValue +
velocity / (1 - deceleration) *
(1 - exp(-(1 - deceleration) * (frameTimeMillis - startFrameTimeMillis))))
if (abs(lastValue - value) < 0.1) {
if (iterations == -1 || currentLoop < iterations) { // looping animation, return to start
// set mStartFrameTimeMillis to -1 to reset instance variables on the next runAnimationStep
startFrameTimeMillis = -1
currentLoop++
} else { // animation has completed
mHasFinished = true
return
}
}
lastValue = value
mAnimatedValue.nodeValue = value
}
}
Loading
Loading