diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/layoutanimation/LayoutAnimationController.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/layoutanimation/LayoutAnimationController.kt index eff00259d5d..6e7a8088ca2 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/layoutanimation/LayoutAnimationController.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/layoutanimation/LayoutAnimationController.kt @@ -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(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, + ) } }