From 00105b805469c97eaa8da6646bdb7bd147e4ef9d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateo=20Guzm=C3=A1n?= Date: Tue, 18 Mar 2025 04:22:48 -0700 Subject: [PATCH] Migrate `OnLayoutEvent` to Kotlin (#50082) Summary: Migrate com.facebook.react.uimanager.OnLayoutEvent to Kotlin. ## Changelog: [INTERNAL] - Migrate com.facebook.react.uimanager.OnLayoutEvent to Kotlin Pull Request resolved: https://github.com/facebook/react-native/pull/50082 Test Plan: ```bash yarn test-android yarn android ``` Reviewed By: cortinico Differential Revision: D71385027 Pulled By: javache fbshipit-source-id: 19985a494a68e63a2e3404ce2972c03161d61527 --- .../ReactAndroid/api/ReactAndroid.api | 15 +-- .../react/uimanager/OnLayoutEvent.java | 89 ------------------ .../facebook/react/uimanager/OnLayoutEvent.kt | 93 +++++++++++++++++++ .../react/uimanager/OnLayoutEventTest.kt | 73 +++++++++++++++ 4 files changed, 175 insertions(+), 95 deletions(-) delete mode 100644 packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/OnLayoutEvent.java create mode 100644 packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/OnLayoutEvent.kt create mode 100644 packages/react-native/ReactAndroid/src/test/java/com/facebook/react/uimanager/OnLayoutEventTest.kt diff --git a/packages/react-native/ReactAndroid/api/ReactAndroid.api b/packages/react-native/ReactAndroid/api/ReactAndroid.api index f39f6aeb696..5ea6cb79104 100644 --- a/packages/react-native/ReactAndroid/api/ReactAndroid.api +++ b/packages/react-native/ReactAndroid/api/ReactAndroid.api @@ -3970,16 +3970,19 @@ public class com/facebook/react/uimanager/NativeViewHierarchyOptimizer { public fun onBatchComplete ()V } -public class com/facebook/react/uimanager/OnLayoutEvent : com/facebook/react/uimanager/events/Event { - protected fun getEventData ()Lcom/facebook/react/bridge/WritableMap; +public final class com/facebook/react/uimanager/OnLayoutEvent : com/facebook/react/uimanager/events/Event { + public static final field Companion Lcom/facebook/react/uimanager/OnLayoutEvent$Companion; public fun getEventName ()Ljava/lang/String; - protected fun init (IIIII)V - protected fun init (IIIIII)V - public static fun obtain (IIIII)Lcom/facebook/react/uimanager/OnLayoutEvent; - public static fun obtain (IIIIII)Lcom/facebook/react/uimanager/OnLayoutEvent; + public static final fun obtain (IIIII)Lcom/facebook/react/uimanager/OnLayoutEvent; + public static final fun obtain (IIIIII)Lcom/facebook/react/uimanager/OnLayoutEvent; public fun onDispose ()V } +public final class com/facebook/react/uimanager/OnLayoutEvent$Companion { + public final fun obtain (IIIII)Lcom/facebook/react/uimanager/OnLayoutEvent; + public final fun obtain (IIIIII)Lcom/facebook/react/uimanager/OnLayoutEvent; +} + public final class com/facebook/react/uimanager/PixelUtil { public static final field INSTANCE Lcom/facebook/react/uimanager/PixelUtil; public final fun dpToPx (D)F diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/OnLayoutEvent.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/OnLayoutEvent.java deleted file mode 100644 index 23b2af1d0d5..00000000000 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/OnLayoutEvent.java +++ /dev/null @@ -1,89 +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.uimanager; - -import androidx.annotation.Nullable; -import androidx.core.util.Pools; -import com.facebook.infer.annotation.Nullsafe; -import com.facebook.react.bridge.Arguments; -import com.facebook.react.bridge.WritableMap; -import com.facebook.react.common.annotations.internal.LegacyArchitecture; -import com.facebook.react.common.annotations.internal.LegacyArchitectureLogLevel; -import com.facebook.react.common.annotations.internal.LegacyArchitectureLogger; -import com.facebook.react.uimanager.events.Event; - -/** Event used to notify JS component about changes of its position or dimensions */ -@Nullsafe(Nullsafe.Mode.LOCAL) -@LegacyArchitecture -public class OnLayoutEvent extends Event { - - static { - LegacyArchitectureLogger.assertWhenLegacyArchitectureMinifyingEnabled( - "OnLayoutEvent", LegacyArchitectureLogLevel.WARNING); - } - - private static final Pools.SynchronizedPool EVENTS_POOL = - new Pools.SynchronizedPool<>(20); - - private int mX, mY, mWidth, mHeight; - - @Deprecated - public static OnLayoutEvent obtain(int viewTag, int x, int y, int width, int height) { - return obtain(-1, viewTag, x, y, width, height); - } - - public static OnLayoutEvent obtain( - int surfaceId, int viewTag, int x, int y, int width, int height) { - OnLayoutEvent event = EVENTS_POOL.acquire(); - if (event == null) { - event = new OnLayoutEvent(); - } - event.init(surfaceId, viewTag, x, y, width, height); - return event; - } - - @Override - public void onDispose() { - EVENTS_POOL.release(this); - } - - private OnLayoutEvent() {} - - @Deprecated - protected void init(int viewTag, int x, int y, int width, int height) { - init(-1, viewTag, x, y, width, height); - } - - protected void init(int surfaceId, int viewTag, int x, int y, int width, int height) { - super.init(surfaceId, viewTag); - mX = x; - mY = y; - mWidth = width; - mHeight = height; - } - - @Override - public String getEventName() { - return "topLayout"; - } - - @Nullable - @Override - protected WritableMap getEventData() { - WritableMap layout = Arguments.createMap(); - layout.putDouble("x", PixelUtil.toDIPFromPixel(mX)); - layout.putDouble("y", PixelUtil.toDIPFromPixel(mY)); - layout.putDouble("width", PixelUtil.toDIPFromPixel(mWidth)); - layout.putDouble("height", PixelUtil.toDIPFromPixel(mHeight)); - - WritableMap event = Arguments.createMap(); - event.putMap("layout", layout); - event.putInt("target", getViewTag()); - return event; - } -} diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/OnLayoutEvent.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/OnLayoutEvent.kt new file mode 100644 index 00000000000..45745037136 --- /dev/null +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/OnLayoutEvent.kt @@ -0,0 +1,93 @@ +/* + * 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.uimanager + +import androidx.core.util.Pools.SynchronizedPool +import com.facebook.react.bridge.Arguments +import com.facebook.react.bridge.WritableMap +import com.facebook.react.common.annotations.VisibleForTesting +import com.facebook.react.common.annotations.internal.LegacyArchitecture +import com.facebook.react.common.annotations.internal.LegacyArchitectureLogLevel +import com.facebook.react.common.annotations.internal.LegacyArchitectureLogger +import com.facebook.react.uimanager.PixelUtil.toDIPFromPixel +import com.facebook.react.uimanager.events.Event + +/** Event used to notify JS component about changes of its position or dimensions. */ +@LegacyArchitecture +public class OnLayoutEvent private constructor() : Event() { + @VisibleForTesting internal var x: Int = 0 + @VisibleForTesting internal var y: Int = 0 + @VisibleForTesting internal var width: Int = 0 + @VisibleForTesting internal var height: Int = 0 + + override fun onDispose() { + EVENTS_POOL.release(this) + } + + protected fun init(surfaceId: Int, viewTag: Int, x: Int, y: Int, width: Int, height: Int) { + super.init(surfaceId, viewTag) + this.x = x + this.y = y + this.width = width + this.height = height + } + + override fun getEventName(): String = "topLayout" + + override fun getEventData(): WritableMap? { + val layout = + Arguments.createMap().apply { + putDouble("x", toDIPFromPixel(x.toFloat()).toDouble()) + putDouble("y", toDIPFromPixel(y.toFloat()).toDouble()) + putDouble("width", toDIPFromPixel(width.toFloat()).toDouble()) + putDouble("height", toDIPFromPixel(height.toFloat()).toDouble()) + } + + val event = + Arguments.createMap().apply { + putMap("layout", layout) + putInt("target", viewTag) + } + + return event + } + + public companion object { + init { + LegacyArchitectureLogger.assertWhenLegacyArchitectureMinifyingEnabled( + "OnLayoutEvent", LegacyArchitectureLogLevel.WARNING) + } + + private val EVENTS_POOL: SynchronizedPool = SynchronizedPool(20) + + @Deprecated( + "Use `obtain(surfaceId, viewTag, x, y, width, height)` instead.", + ReplaceWith("obtain(surfaceId, viewTag, x, y, width, height)")) + @JvmStatic + public fun obtain(viewTag: Int, x: Int, y: Int, width: Int, height: Int): OnLayoutEvent { + return obtain(-1, viewTag, x, y, width, height) + } + + @JvmStatic + public fun obtain( + surfaceId: Int, + viewTag: Int, + x: Int, + y: Int, + width: Int, + height: Int + ): OnLayoutEvent { + var event: OnLayoutEvent? = EVENTS_POOL.acquire() + if (event == null) { + event = OnLayoutEvent() + } + event.init(surfaceId, viewTag, x, y, width, height) + return event + } + } +} diff --git a/packages/react-native/ReactAndroid/src/test/java/com/facebook/react/uimanager/OnLayoutEventTest.kt b/packages/react-native/ReactAndroid/src/test/java/com/facebook/react/uimanager/OnLayoutEventTest.kt new file mode 100644 index 00000000000..fb02e505cb6 --- /dev/null +++ b/packages/react-native/ReactAndroid/src/test/java/com/facebook/react/uimanager/OnLayoutEventTest.kt @@ -0,0 +1,73 @@ +/* + * 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.uimanager + +import com.facebook.react.common.SystemClock +import org.assertj.core.api.Assertions.assertThat +import org.junit.After +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.MockedStatic +import org.mockito.Mockito.mockStatic +import org.robolectric.RobolectricTestRunner + +@RunWith(RobolectricTestRunner::class) +class OnLayoutEventTest { + private lateinit var systemClock: MockedStatic + + @Before + fun setup() { + val ts = SystemClock.uptimeMillis() + systemClock = mockStatic(SystemClock::class.java) + systemClock.`when` { SystemClock.uptimeMillis() }.thenReturn(ts) + } + + @After + fun tearDown() { + systemClock.close() + } + + @Test + fun testObtain_shouldReturnEventWithCorrectValues() { + val surfaceId = 1 + val viewTag = 2 + val x = 10 + val y = 20 + val width = 100 + val height = 200 + + val event = OnLayoutEvent.obtain(surfaceId, viewTag, x, y, width, height) + + assertThat(event).isNotNull + assertThat(event.viewTag).isEqualTo(viewTag) + assertThat(event.x).isEqualTo(x) + assertThat(event.y).isEqualTo(y) + assertThat(event.width).isEqualTo(width) + assertThat(event.height).isEqualTo(height) + } + + @Test + fun testGetEventName_shouldReturnCorrectEventName() { + val event = OnLayoutEvent.obtain(1, 1, 10, 20, 100, 200) + + assertThat(event.eventName).isEqualTo("topLayout") + } + + @Test + fun testInit_shouldCorrectlyInitializeValues() { + val event = OnLayoutEvent.obtain(1, 1, 10, 20, 100, 200) + + assertThat(event.surfaceId).isEqualTo(1) + assertThat(event.viewTag).isEqualTo(1) + assertThat(event.x).isEqualTo(10) + assertThat(event.y).isEqualTo(20) + assertThat(event.width).isEqualTo(100) + assertThat(event.height).isEqualTo(200) + } +}