diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/animated/InterpolationAnimatedNode.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/animated/InterpolationAnimatedNode.java
deleted file mode 100644
index 4307de260166ef..00000000000000
--- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/animated/InterpolationAnimatedNode.java
+++ /dev/null
@@ -1,311 +0,0 @@
-/*
- * Copyright (c) Meta Platforms, Inc. and affiliates.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-package com.facebook.react.animated;
-
-import androidx.annotation.Nullable;
-import androidx.core.graphics.ColorUtils;
-import com.facebook.react.bridge.JSApplicationIllegalArgumentException;
-import com.facebook.react.bridge.ReadableArray;
-import com.facebook.react.bridge.ReadableMap;
-import com.facebook.react.bridge.ReadableType;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-/**
- * Animated node that corresponds to {@code AnimatedInterpolation} from AnimatedImplementation.js.
- *
- *
Currently only a linear interpolation is supported on an input range of an arbitrary size.
- */
-/*package*/ class InterpolationAnimatedNode extends ValueAnimatedNode {
-
- public static final String EXTRAPOLATE_TYPE_IDENTITY = "identity";
- public static final String EXTRAPOLATE_TYPE_CLAMP = "clamp";
- public static final String EXTRAPOLATE_TYPE_EXTEND = "extend";
-
- private static final Pattern sNumericPattern =
- Pattern.compile("[+-]?(\\d+\\.?\\d*|\\.\\d+)([eE][+-]?\\d+)?");
-
- private static final String COLOR_OUTPUT_TYPE = "color";
-
- private static double[] fromDoubleArray(ReadableArray ary) {
- double[] res = new double[ary.size()];
- for (int i = 0; i < res.length; i++) {
- res[i] = ary.getDouble(i);
- }
- return res;
- }
-
- private static int[] fromIntArray(ReadableArray ary) {
- int[] res = new int[ary.size()];
- for (int i = 0; i < res.length; i++) {
- res[i] = ary.getInt(i);
- }
- return res;
- }
-
- private static double[][] fromStringPattern(ReadableArray array) {
- int size = array.size();
- double[][] outputRange = new double[size][];
-
- // Match the first pattern into a List, since we don't know its length yet
- Matcher m = sNumericPattern.matcher(array.getString(0));
- List firstOutputRange = new ArrayList<>();
- while (m.find()) {
- firstOutputRange.add(Double.parseDouble(m.group()));
- }
- double[] firstOutputRangeArr = new double[firstOutputRange.size()];
- for (int i = 0; i < firstOutputRange.size(); i++) {
- firstOutputRangeArr[i] = firstOutputRange.get(i).doubleValue();
- }
- outputRange[0] = firstOutputRangeArr;
-
- for (int i = 1; i < size; i++) {
- double[] outputArr = new double[firstOutputRangeArr.length];
-
- int j = 0;
- m = sNumericPattern.matcher(array.getString(i));
- while (m.find() && j < firstOutputRangeArr.length) {
- outputArr[j++] = Double.parseDouble(m.group());
- }
- outputRange[i] = outputArr;
- }
-
- return outputRange;
- }
-
- private static double interpolate(
- double value,
- double inputMin,
- double inputMax,
- double outputMin,
- double outputMax,
- String extrapolateLeft,
- String extrapolateRight) {
- double result = value;
-
- // Extrapolate
- if (result < inputMin) {
- switch (extrapolateLeft) {
- case EXTRAPOLATE_TYPE_IDENTITY:
- return result;
- case EXTRAPOLATE_TYPE_CLAMP:
- result = inputMin;
- break;
- case EXTRAPOLATE_TYPE_EXTEND:
- break;
- default:
- throw new JSApplicationIllegalArgumentException(
- "Invalid extrapolation type " + extrapolateLeft + "for left extrapolation");
- }
- }
-
- if (result > inputMax) {
- switch (extrapolateRight) {
- case EXTRAPOLATE_TYPE_IDENTITY:
- return result;
- case EXTRAPOLATE_TYPE_CLAMP:
- result = inputMax;
- break;
- case EXTRAPOLATE_TYPE_EXTEND:
- break;
- default:
- throw new JSApplicationIllegalArgumentException(
- "Invalid extrapolation type " + extrapolateRight + "for right extrapolation");
- }
- }
-
- if (outputMin == outputMax) {
- return outputMin;
- }
-
- if (inputMin == inputMax) {
- if (value <= inputMin) {
- return outputMin;
- }
- return outputMax;
- }
-
- return outputMin + (outputMax - outputMin) * (result - inputMin) / (inputMax - inputMin);
- }
-
- /*package*/ static double interpolate(
- double value,
- double[] inputRange,
- double[] outputRange,
- String extrapolateLeft,
- String extrapolateRight) {
- int rangeIndex = findRangeIndex(value, inputRange);
- return interpolate(
- value,
- inputRange[rangeIndex],
- inputRange[rangeIndex + 1],
- outputRange[rangeIndex],
- outputRange[rangeIndex + 1],
- extrapolateLeft,
- extrapolateRight);
- }
-
- /*package*/ static int interpolateColor(double value, double[] inputRange, int[] outputRange) {
- int rangeIndex = findRangeIndex(value, inputRange);
- int outputMin = outputRange[rangeIndex];
- int outputMax = outputRange[rangeIndex + 1];
- if (outputMin == outputMax) {
- return outputMin;
- }
-
- double inputMin = inputRange[rangeIndex];
- double inputMax = inputRange[rangeIndex + 1];
- if (inputMin == inputMax) {
- if (value <= inputMin) {
- return outputMin;
- }
- return outputMax;
- }
-
- double ratio = (value - inputMin) / (inputMax - inputMin);
- return ColorUtils.blendARGB(outputMin, outputMax, (float) ratio);
- }
-
- /*package*/ static String interpolateString(
- String pattern,
- double value,
- double[] inputRange,
- double[][] outputRange,
- String extrapolateLeft,
- String extrapolateRight) {
- int rangeIndex = findRangeIndex(value, inputRange);
- StringBuffer sb = new StringBuffer(pattern.length());
-
- Matcher m = sNumericPattern.matcher(pattern);
- int i = 0;
- while (m.find() && i < outputRange[rangeIndex].length) {
- double val =
- interpolate(
- value,
- inputRange[rangeIndex],
- inputRange[rangeIndex + 1],
- outputRange[rangeIndex][i],
- outputRange[rangeIndex + 1][i],
- extrapolateLeft,
- extrapolateRight);
- int intVal = (int) val;
- m.appendReplacement(sb, intVal != val ? Double.toString(val) : Integer.toString(intVal));
- i++;
- }
- m.appendTail(sb);
- return sb.toString();
- }
-
- private static int findRangeIndex(double value, double[] ranges) {
- int index;
- for (index = 1; index < ranges.length - 1; index++) {
- if (ranges[index] >= value) {
- break;
- }
- }
- return index - 1;
- }
-
- private enum OutputType {
- Number,
- Color,
- String,
- }
-
- private final double mInputRange[];
- private final Object mOutputRange;
- private final OutputType mOutputType;
- private final @Nullable String mPattern;
- private final String mExtrapolateLeft;
- private final String mExtrapolateRight;
- private @Nullable ValueAnimatedNode mParent;
- private Object mObjectValue;
-
- public InterpolationAnimatedNode(ReadableMap config) {
- mInputRange = fromDoubleArray(config.getArray("inputRange"));
- ReadableArray output = config.getArray("outputRange");
-
- if (COLOR_OUTPUT_TYPE.equals(config.getString("outputType"))) {
- mOutputType = OutputType.Color;
- mOutputRange = fromIntArray(output);
- mPattern = null;
- } else if (output.getType(0) == ReadableType.String) {
- mOutputType = OutputType.String;
- mOutputRange = fromStringPattern(output);
- mPattern = output.getString(0);
- } else {
- mOutputType = OutputType.Number;
- mOutputRange = fromDoubleArray(output);
- mPattern = null;
- }
-
- mExtrapolateLeft = config.getString("extrapolateLeft");
- mExtrapolateRight = config.getString("extrapolateRight");
- }
-
- @Override
- public void onAttachedToNode(AnimatedNode parent) {
- if (mParent != null) {
- throw new IllegalStateException("Parent already attached");
- }
- if (!(parent instanceof ValueAnimatedNode)) {
- throw new IllegalArgumentException("Parent is of an invalid type");
- }
- mParent = (ValueAnimatedNode) parent;
- }
-
- @Override
- public void onDetachedFromNode(AnimatedNode parent) {
- if (parent != mParent) {
- throw new IllegalArgumentException("Invalid parent node provided");
- }
- mParent = null;
- }
-
- @Override
- public void update() {
- if (mParent == null) {
- // The graph is in the middle of being created, just skip this unattached node.
- return;
- }
-
- double value = mParent.getValue();
- switch (mOutputType) {
- case Number:
- nodeValue =
- interpolate(
- value, mInputRange, (double[]) mOutputRange, mExtrapolateLeft, mExtrapolateRight);
- break;
- case Color:
- mObjectValue = Integer.valueOf(interpolateColor(value, mInputRange, (int[]) mOutputRange));
- break;
- case String:
- mObjectValue =
- interpolateString(
- mPattern,
- value,
- mInputRange,
- (double[][]) mOutputRange,
- mExtrapolateLeft,
- mExtrapolateRight);
- break;
- }
- }
-
- @Override
- public Object getAnimatedObject() {
- return mObjectValue;
- }
-
- @Override
- public String prettyPrint() {
- return "InterpolationAnimatedNode[" + tag + "] super: " + super.prettyPrint();
- }
-}
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
new file mode 100644
index 00000000000000..6e633977ecd2bd
--- /dev/null
+++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/animated/InterpolationAnimatedNode.kt
@@ -0,0 +1,283 @@
+/*
+ * 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 androidx.core.graphics.ColorUtils
+import com.facebook.react.bridge.JSApplicationIllegalArgumentException
+import com.facebook.react.bridge.ReadableArray
+import com.facebook.react.bridge.ReadableMap
+import com.facebook.react.bridge.ReadableType
+import java.util.regex.Pattern
+
+/**
+ * Animated node that corresponds to `AnimatedInterpolation` from AnimatedImplementation.js.
+ *
+ * Currently only a linear interpolation is supported on an input range of an arbitrary size.
+ */
+internal class InterpolationAnimatedNode(config: ReadableMap) : ValueAnimatedNode() {
+ private enum class OutputType {
+ Number,
+ Color,
+ String
+ }
+
+ private val inputRange: DoubleArray
+ private var outputRange: Any? = null
+ private var outputType: OutputType? = null
+ private var pattern: String? = null
+ private val extrapolateLeft: String?
+ private val extrapolateRight: String?
+ private var parent: ValueAnimatedNode? = null
+ private var objectValue: Any? = null
+
+ init {
+ inputRange = fromDoubleArray(config.getArray("inputRange"))
+ val output = config.getArray("outputRange")
+ if (COLOR_OUTPUT_TYPE == config.getString("outputType")) {
+ outputType = OutputType.Color
+ outputRange = fromIntArray(output)
+ pattern = null
+ } else if (output?.getType(0) == ReadableType.String) {
+ outputType = OutputType.String
+ outputRange = fromStringPattern(output)
+ pattern = output.getString(0)
+ } else {
+ outputType = OutputType.Number
+ outputRange = fromDoubleArray(output)
+ pattern = null
+ }
+ extrapolateLeft = config.getString("extrapolateLeft")
+ extrapolateRight = config.getString("extrapolateRight")
+ }
+
+ override fun onAttachedToNode(parent: AnimatedNode) {
+ check(this.parent == null) { "Parent already attached" }
+ require(parent is ValueAnimatedNode) { "Parent is of an invalid type" }
+ this.parent = parent
+ }
+
+ override fun onDetachedFromNode(parent: AnimatedNode) {
+ require(parent === this.parent) { "Invalid parent node provided" }
+ this.parent = null
+ }
+
+ override fun update() {
+ // If the graph is in the middle of being created, just skip this unattached node.
+ val parentValue = parent?.getValue() ?: return
+ when (outputType) {
+ OutputType.Number ->
+ nodeValue =
+ interpolate(
+ parentValue,
+ inputRange,
+ outputRange as DoubleArray,
+ extrapolateLeft,
+ extrapolateRight)
+ OutputType.Color ->
+ objectValue =
+ Integer.valueOf(interpolateColor(parentValue, inputRange, outputRange as IntArray))
+ OutputType.String ->
+ pattern?.let {
+ @Suppress("UNCHECKED_CAST")
+ objectValue =
+ interpolateString(
+ it,
+ parentValue,
+ inputRange,
+ outputRange as Array,
+ extrapolateLeft,
+ extrapolateRight)
+ }
+
+ else -> {}
+ }
+ }
+
+ override fun getAnimatedObject(): Any? = objectValue
+
+ override fun prettyPrint(): String =
+ "InterpolationAnimatedNode[$tag] super: {super.prettyPrint()}"
+
+ public companion object {
+ public const val EXTRAPOLATE_TYPE_IDENTITY: String = "identity"
+ public const val EXTRAPOLATE_TYPE_CLAMP: String = "clamp"
+ public const val EXTRAPOLATE_TYPE_EXTEND: String = "extend"
+
+ private val numericPattern: Pattern =
+ Pattern.compile("[+-]?(\\d+\\.?\\d*|\\.\\d+)([eE][+-]?\\d+)?")
+ private const val COLOR_OUTPUT_TYPE: String = "color"
+
+ private fun fromDoubleArray(array: ReadableArray?): DoubleArray {
+ val size = array?.size() ?: return DoubleArray(0)
+ val res = DoubleArray(size)
+ for (i in res.indices) {
+ res[i] = array.getDouble(i)
+ }
+ return res
+ }
+
+ private fun fromIntArray(array: ReadableArray?): IntArray {
+ val size = array?.size() ?: return IntArray(0)
+ val res = IntArray(size)
+ for (i in res.indices) {
+ res[i] = array.getInt(i)
+ }
+ return res
+ }
+
+ private fun fromStringPattern(array: ReadableArray): Array {
+ val size = array.size()
+ val outputRange = arrayOfNulls(size)
+
+ // Match the first pattern into a List, since we don't know its length yet
+ var m = numericPattern.matcher(array.getString(0))
+ val firstOutputRange: MutableList = ArrayList()
+ while (m.find()) {
+ firstOutputRange.add(m.group().toDouble())
+ }
+ val firstOutputRangeArr = DoubleArray(firstOutputRange.size)
+ for (i in firstOutputRange.indices) {
+ firstOutputRangeArr[i] = firstOutputRange[i]
+ }
+ outputRange[0] = firstOutputRangeArr
+ for (i in 1 until size) {
+ val outputArr = DoubleArray(firstOutputRangeArr.size)
+ var j = 0
+ m = numericPattern.matcher(array.getString(i))
+ while (m.find() && j < firstOutputRangeArr.size) {
+ outputArr[j++] = m.group().toDouble()
+ }
+ outputRange[i] = outputArr
+ }
+ return outputRange
+ }
+
+ private fun interpolate(
+ value: Double,
+ inputMin: Double,
+ inputMax: Double,
+ outputMin: Double,
+ outputMax: Double,
+ extrapolateLeft: String?,
+ extrapolateRight: String?
+ ): Double {
+ var result = value
+
+ // Extrapolate
+ if (result < inputMin) {
+ when (extrapolateLeft) {
+ EXTRAPOLATE_TYPE_IDENTITY -> return result
+ EXTRAPOLATE_TYPE_CLAMP -> result = inputMin
+ EXTRAPOLATE_TYPE_EXTEND -> {}
+ else ->
+ throw JSApplicationIllegalArgumentException(
+ "Invalid extrapolation type " + extrapolateLeft + "for left extrapolation")
+ }
+ }
+ if (result > inputMax) {
+ when (extrapolateRight) {
+ EXTRAPOLATE_TYPE_IDENTITY -> return result
+ EXTRAPOLATE_TYPE_CLAMP -> result = inputMax
+ EXTRAPOLATE_TYPE_EXTEND -> {}
+ else ->
+ throw JSApplicationIllegalArgumentException(
+ "Invalid extrapolation type " + extrapolateRight + "for right extrapolation")
+ }
+ }
+ if (outputMin == outputMax) {
+ return outputMin
+ }
+ return if (inputMin == inputMax) {
+ if (value <= inputMin) {
+ outputMin
+ } else outputMax
+ } else outputMin + (outputMax - outputMin) * (result - inputMin) / (inputMax - inputMin)
+ }
+
+ internal fun interpolate(
+ value: Double,
+ inputRange: DoubleArray,
+ outputRange: DoubleArray,
+ extrapolateLeft: String?,
+ extrapolateRight: String?
+ ): Double {
+ val rangeIndex = findRangeIndex(value, inputRange)
+ return interpolate(
+ value,
+ inputRange[rangeIndex],
+ inputRange[rangeIndex + 1],
+ outputRange[rangeIndex],
+ outputRange[rangeIndex + 1],
+ extrapolateLeft,
+ extrapolateRight)
+ }
+
+ internal fun interpolateColor(
+ value: Double,
+ inputRange: DoubleArray,
+ outputRange: IntArray
+ ): Int {
+ val rangeIndex = findRangeIndex(value, inputRange)
+ val outputMin = outputRange[rangeIndex]
+ val outputMax = outputRange[rangeIndex + 1]
+ if (outputMin == outputMax) {
+ return outputMin
+ }
+ val inputMin = inputRange[rangeIndex]
+ val inputMax = inputRange[rangeIndex + 1]
+ if (inputMin == inputMax) {
+ return if (value <= inputMin) {
+ outputMin
+ } else outputMax
+ }
+ val ratio = (value - inputMin) / (inputMax - inputMin)
+ return ColorUtils.blendARGB(outputMin, outputMax, ratio.toFloat())
+ }
+
+ internal fun interpolateString(
+ pattern: String,
+ value: Double,
+ inputRange: DoubleArray,
+ outputRange: Array,
+ extrapolateLeft: String?,
+ extrapolateRight: String?
+ ): String {
+ val rangeIndex = findRangeIndex(value, inputRange)
+ val sb = StringBuffer(pattern.length)
+ val m = numericPattern.matcher(pattern)
+ var i = 0
+ while (m.find() && i < outputRange[rangeIndex].size) {
+ val v =
+ interpolate(
+ value,
+ inputRange[rangeIndex],
+ inputRange[rangeIndex + 1],
+ outputRange[rangeIndex][i],
+ outputRange[rangeIndex + 1][i],
+ extrapolateLeft,
+ extrapolateRight)
+ val intVal = v.toInt()
+ m.appendReplacement(sb, if (intVal.toDouble() != v) v.toString() else intVal.toString())
+ i++
+ }
+ m.appendTail(sb)
+ return sb.toString()
+ }
+
+ private fun findRangeIndex(value: Double, ranges: DoubleArray): Int {
+ var index: Int = 1
+ while (index < ranges.size - 1) {
+ if (ranges[index] >= value) {
+ break
+ }
+ index++
+ }
+ return index - 1
+ }
+ }
+}