Remove code from public LayoutAnimationController class (#53784)

Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/53784

LayoutAnimation is part of legacy architecture, I'm deleting the code used from its non supported public APIs

changelog: [internal] internal

Reviewed By: shwanton

Differential Revision: D82235545

fbshipit-source-id: 9665c57b68eba9d958d3f8b9dd4224558cc8e9f6
This commit is contained in:
David Vacca
2025-09-22 17:45:08 -07:00
committed by Facebook GitHub Bot
parent ec5bc3b285
commit f09f4a779e
@@ -9,19 +9,12 @@
package com.facebook.react.uimanager.layoutanimation
import android.util.SparseArray
import android.view.View
import android.view.ViewGroup
import android.view.animation.Animation
import android.view.animation.Animation.AnimationListener
import com.facebook.react.bridge.Callback
import com.facebook.react.bridge.ReadableMap
import com.facebook.react.bridge.UiThreadUtil.assertOnUiThread
import com.facebook.react.bridge.UiThreadUtil.getUiThreadHandler
import com.facebook.react.common.annotations.internal.LegacyArchitecture
import com.facebook.react.common.annotations.internal.LegacyArchitectureLogLevel
import com.facebook.react.common.annotations.internal.LegacyArchitectureLogger.assertLegacyArchitecture
import com.facebook.react.uimanager.layoutanimation.LayoutAnimationType.Companion.toString
import com.facebook.react.common.annotations.internal.LegacyArchitectureLogger
import javax.annotation.concurrent.NotThreadSafe
/**
@@ -36,75 +29,27 @@ import javax.annotation.concurrent.NotThreadSafe
level = DeprecationLevel.WARNING,
)
public open class LayoutAnimationController {
private val layoutCreateAnimation: AbstractLayoutAnimation = LayoutCreateAnimation()
private val layoutUpdateAnimation: AbstractLayoutAnimation = LayoutUpdateAnimation()
private val layoutDeleteAnimation: AbstractLayoutAnimation = LayoutDeleteAnimation()
private val layoutHandlers = SparseArray<LayoutHandlingAnimation?>(0)
private var shouldAnimateLayout = false
private var maxAnimationDuration: Long = -1
private var completionRunnable: Runnable? = null
public fun initializeFromConfig(config: ReadableMap?, completionCallback: Callback?) {
if (config == null) {
reset()
return
}
shouldAnimateLayout = false
val globalDuration = if (config.hasKey("duration")) config.getInt("duration") else 0
if (config.hasKey(toString(LayoutAnimationType.CREATE))) {
layoutCreateAnimation.initializeFromConfig(
config.getMap(toString(LayoutAnimationType.CREATE))!!,
globalDuration,
)
shouldAnimateLayout = true
}
if (config.hasKey(toString(LayoutAnimationType.UPDATE))) {
layoutUpdateAnimation.initializeFromConfig(
config.getMap(toString(LayoutAnimationType.UPDATE))!!,
globalDuration,
)
shouldAnimateLayout = true
}
if (config.hasKey(toString(LayoutAnimationType.DELETE))) {
layoutDeleteAnimation.initializeFromConfig(
config.getMap(toString(LayoutAnimationType.DELETE))!!,
globalDuration,
)
shouldAnimateLayout = true
}
if (shouldAnimateLayout && completionCallback != null) {
completionRunnable = Runnable { completionCallback.invoke(java.lang.Boolean.TRUE) }
}
LegacyArchitectureLogger.assertLegacyArchitecture(
"LayoutAnimationController",
LegacyArchitectureLogLevel.ERROR,
)
}
public open fun reset() {
layoutCreateAnimation.reset()
layoutUpdateAnimation.reset()
layoutDeleteAnimation.reset()
completionRunnable = null
shouldAnimateLayout = false
maxAnimationDuration = -1
for (i in layoutHandlers.size() - 1 downTo 0) {
val animation = layoutHandlers.valueAt(i)
if (!animation!!.isValid()) {
layoutHandlers.removeAt(i)
}
}
LegacyArchitectureLogger.assertLegacyArchitecture(
"LayoutAnimationController",
LegacyArchitectureLogLevel.ERROR,
)
}
public open fun shouldAnimateLayout(viewToAnimate: View?): Boolean {
// if view parent is null, skip animation: view have been clipped, we don't want animation to
// resume when view is re-attached to parent, which is the standard android animation behavior.
// If there's a layout handling animation going on, it should be animated nonetheless since the
// ongoing animation needs to be updated.
if (viewToAnimate == null) {
return false
}
return ((shouldAnimateLayout && viewToAnimate.parent != null) ||
layoutHandlers[viewToAnimate.id] != null)
LegacyArchitectureLogger.assertLegacyArchitecture(
"LayoutAnimationController",
LegacyArchitectureLogLevel.ERROR,
)
return false
}
/**
@@ -119,57 +64,10 @@ public open class LayoutAnimationController {
* @param height the new height value for the view
*/
public open fun applyLayoutUpdate(view: View, x: Int, y: Int, width: Int, height: Int) {
assertOnUiThread()
val reactTag = view.id
// Update an ongoing animation if possible, otherwise the layout update would be ignored as
// the existing animation would still animate to the old layout.
val existingAnimation = layoutHandlers[reactTag]
if (existingAnimation != null) {
if (!existingAnimation.isValid()) {
layoutHandlers.remove(reactTag)
} else {
existingAnimation.onLayoutUpdate(x, y, width, height)
return
}
}
// Determine which animation to use : if view is initially invisible, use create animation,
// otherwise use update animation. This approach is easier than maintaining a list of tags
// for recently created views.
val layoutAnimation =
if ((view.width == 0 || view.height == 0)) layoutCreateAnimation else layoutUpdateAnimation
val animation = layoutAnimation.createAnimation(view, x, y, width, height)
if (animation is LayoutHandlingAnimation) {
animation.setAnimationListener(
object : AnimationListener {
override fun onAnimationStart(animation: Animation) {
layoutHandlers.put(reactTag, animation as LayoutHandlingAnimation)
}
override fun onAnimationEnd(animation: Animation) {
layoutHandlers.remove(reactTag)
}
override fun onAnimationRepeat(animation: Animation) = Unit
}
)
} else {
view.layout(x, y, x + width, y + height)
}
if (animation != null) {
val animationDuration = animation.duration
if (animationDuration > maxAnimationDuration) {
maxAnimationDuration = animationDuration
scheduleCompletionCallback(animationDuration)
}
view.startAnimation(animation)
}
LegacyArchitectureLogger.assertLegacyArchitecture(
"LayoutAnimationController",
LegacyArchitectureLogLevel.ERROR,
)
}
/**
@@ -181,59 +79,9 @@ public open class LayoutAnimationController {
* view.
*/
public open fun deleteView(view: View, listener: LayoutAnimationListener) {
assertOnUiThread()
val animation =
layoutDeleteAnimation.createAnimation(view, view.left, view.top, view.width, view.height)
if (animation != null) {
disableUserInteractions(view)
animation.setAnimationListener(
object : AnimationListener {
override fun onAnimationStart(anim: Animation) = Unit
override fun onAnimationRepeat(anim: Animation) = Unit
override fun onAnimationEnd(anim: Animation) {
listener.onAnimationEnd()
}
}
)
val animationDuration = animation.duration
if (animationDuration > maxAnimationDuration) {
scheduleCompletionCallback(animationDuration)
maxAnimationDuration = animationDuration
}
view.startAnimation(animation)
} else {
listener.onAnimationEnd()
}
}
/** Disables user interactions for a view and all it's subviews. */
private fun disableUserInteractions(view: View) {
view.isClickable = false
if (view is ViewGroup) {
for (i in 0 until view.childCount) {
disableUserInteractions(view.getChildAt(i))
}
}
}
private fun scheduleCompletionCallback(delayMillis: Long) {
if (completionRunnable != null) {
val completionHandler = getUiThreadHandler()
completionHandler.removeCallbacks(completionRunnable!!)
completionHandler.postDelayed(completionRunnable!!, delayMillis)
}
}
private companion object {
init {
assertLegacyArchitecture("LayoutAnimationController", LegacyArchitectureLogLevel.ERROR)
}
LegacyArchitectureLogger.assertLegacyArchitecture(
"LayoutAnimationController",
LegacyArchitectureLogLevel.ERROR,
)
}
}