From d02da992a4c0893313b9f059a8be228f43fffa7a Mon Sep 17 00:00:00 2001 From: Thomas Nardone Date: Thu, 7 Nov 2024 11:39:35 -0800 Subject: [PATCH] Convert + Internalize SwipeRefreshLayoutManager (#47470) Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/47470 Convert to Kotlin and formalize it being an internal class (some methods were already `protected` in Java) Changelog: [Android] [Breaking] - Stable API - Make SwipeRefreshLayoutManager internal Reviewed By: cortinico Differential Revision: D65481861 fbshipit-source-id: afc5c624373fbcd3ca2d28b2834d2682de672997 --- .../ReactAndroid/api/ReactAndroid.api | 31 --- .../react/views/swiperefresh/RefreshEvent.kt | 4 +- .../SwipeRefreshLayoutManager.java | 194 ------------------ .../swiperefresh/SwipeRefreshLayoutManager.kt | 153 ++++++++++++++ 4 files changed, 155 insertions(+), 227 deletions(-) delete mode 100644 packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/swiperefresh/SwipeRefreshLayoutManager.java create mode 100644 packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/swiperefresh/SwipeRefreshLayoutManager.kt diff --git a/packages/react-native/ReactAndroid/api/ReactAndroid.api b/packages/react-native/ReactAndroid/api/ReactAndroid.api index 061741e9d4df15..a9fb19df350f87 100644 --- a/packages/react-native/ReactAndroid/api/ReactAndroid.api +++ b/packages/react-native/ReactAndroid/api/ReactAndroid.api @@ -7155,37 +7155,6 @@ public class com/facebook/react/views/swiperefresh/ReactSwipeRefreshLayout : and public fun setRefreshing (Z)V } -public class com/facebook/react/views/swiperefresh/SwipeRefreshLayoutManager : com/facebook/react/uimanager/ViewGroupManager, com/facebook/react/viewmanagers/AndroidSwipeRefreshLayoutManagerInterface { - public static final field REACT_CLASS Ljava/lang/String; - public fun ()V - protected synthetic fun addEventEmitters (Lcom/facebook/react/uimanager/ThemedReactContext;Landroid/view/View;)V - protected fun addEventEmitters (Lcom/facebook/react/uimanager/ThemedReactContext;Lcom/facebook/react/views/swiperefresh/ReactSwipeRefreshLayout;)V - protected synthetic fun createViewInstance (Lcom/facebook/react/uimanager/ThemedReactContext;)Landroid/view/View; - protected fun createViewInstance (Lcom/facebook/react/uimanager/ThemedReactContext;)Lcom/facebook/react/views/swiperefresh/ReactSwipeRefreshLayout; - protected fun getDelegate ()Lcom/facebook/react/uimanager/ViewManagerDelegate; - public fun getExportedCustomDirectEventTypeConstants ()Ljava/util/Map; - public fun getExportedViewConstants ()Ljava/util/Map; - public fun getName ()Ljava/lang/String; - public synthetic fun receiveCommand (Landroid/view/View;Ljava/lang/String;Lcom/facebook/react/bridge/ReadableArray;)V - public fun receiveCommand (Lcom/facebook/react/views/swiperefresh/ReactSwipeRefreshLayout;Ljava/lang/String;Lcom/facebook/react/bridge/ReadableArray;)V - public synthetic fun setColors (Landroid/view/View;Lcom/facebook/react/bridge/ReadableArray;)V - public fun setColors (Lcom/facebook/react/views/swiperefresh/ReactSwipeRefreshLayout;Lcom/facebook/react/bridge/ReadableArray;)V - public synthetic fun setEnabled (Landroid/view/View;Z)V - public fun setEnabled (Lcom/facebook/react/views/swiperefresh/ReactSwipeRefreshLayout;Z)V - public synthetic fun setNativeRefreshing (Landroid/view/View;Z)V - public fun setNativeRefreshing (Lcom/facebook/react/views/swiperefresh/ReactSwipeRefreshLayout;Z)V - public synthetic fun setProgressBackgroundColor (Landroid/view/View;Ljava/lang/Integer;)V - public fun setProgressBackgroundColor (Lcom/facebook/react/views/swiperefresh/ReactSwipeRefreshLayout;Ljava/lang/Integer;)V - public synthetic fun setProgressViewOffset (Landroid/view/View;F)V - public fun setProgressViewOffset (Lcom/facebook/react/views/swiperefresh/ReactSwipeRefreshLayout;F)V - public synthetic fun setRefreshing (Landroid/view/View;Z)V - public fun setRefreshing (Lcom/facebook/react/views/swiperefresh/ReactSwipeRefreshLayout;Z)V - public synthetic fun setSize (Landroid/view/View;Ljava/lang/String;)V - public fun setSize (Lcom/facebook/react/views/swiperefresh/ReactSwipeRefreshLayout;I)V - public fun setSize (Lcom/facebook/react/views/swiperefresh/ReactSwipeRefreshLayout;Lcom/facebook/react/bridge/Dynamic;)V - public fun setSize (Lcom/facebook/react/views/swiperefresh/ReactSwipeRefreshLayout;Ljava/lang/String;)V -} - public class com/facebook/react/views/switchview/ReactSwitchManager : com/facebook/react/uimanager/SimpleViewManager, com/facebook/react/viewmanagers/AndroidSwitchManagerInterface { public static final field REACT_CLASS Ljava/lang/String; public fun ()V diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/swiperefresh/RefreshEvent.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/swiperefresh/RefreshEvent.kt index 65669d8b61ab3d..6cdeceb74d2183 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/swiperefresh/RefreshEvent.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/swiperefresh/RefreshEvent.kt @@ -15,9 +15,9 @@ import com.facebook.react.uimanager.events.Event internal class RefreshEvent : Event { @Deprecated("Use constructor with surfaceId", ReplaceWith("RefreshEvent(surfaceId, viewTag)")) - protected constructor(viewTag: Int) : this(ViewUtil.NO_SURFACE_ID, viewTag) + constructor(viewTag: Int) : this(ViewUtil.NO_SURFACE_ID, viewTag) - protected constructor(surfaceId: Int, viewTag: Int) : super(surfaceId, viewTag) + constructor(surfaceId: Int, viewTag: Int) : super(surfaceId, viewTag) override public fun getEventName(): String { return "topRefresh" diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/swiperefresh/SwipeRefreshLayoutManager.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/swiperefresh/SwipeRefreshLayoutManager.java deleted file mode 100644 index 255855f3cab4d2..00000000000000 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/swiperefresh/SwipeRefreshLayoutManager.java +++ /dev/null @@ -1,194 +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.views.swiperefresh; - -import static com.facebook.react.views.swiperefresh.SwipeRefreshLayoutManager.REACT_CLASS; - -import android.graphics.Color; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.swiperefreshlayout.widget.SwipeRefreshLayout; -import androidx.swiperefreshlayout.widget.SwipeRefreshLayout.OnRefreshListener; -import com.facebook.react.bridge.ColorPropConverter; -import com.facebook.react.bridge.Dynamic; -import com.facebook.react.bridge.ReadableArray; -import com.facebook.react.bridge.ReadableType; -import com.facebook.react.common.MapBuilder; -import com.facebook.react.module.annotations.ReactModule; -import com.facebook.react.uimanager.ThemedReactContext; -import com.facebook.react.uimanager.UIManagerHelper; -import com.facebook.react.uimanager.ViewGroupManager; -import com.facebook.react.uimanager.ViewManagerDelegate; -import com.facebook.react.uimanager.ViewProps; -import com.facebook.react.uimanager.annotations.ReactProp; -import com.facebook.react.uimanager.events.EventDispatcher; -import com.facebook.react.viewmanagers.AndroidSwipeRefreshLayoutManagerDelegate; -import com.facebook.react.viewmanagers.AndroidSwipeRefreshLayoutManagerInterface; -import java.util.HashMap; -import java.util.Map; - -/** - * ViewManager for {@link ReactSwipeRefreshLayout} which allows the user to "pull to refresh" a - * child view. Emits an {@code onRefresh} event when this happens. - */ -@ReactModule(name = REACT_CLASS) -public class SwipeRefreshLayoutManager extends ViewGroupManager - implements AndroidSwipeRefreshLayoutManagerInterface { - - public static final String REACT_CLASS = "AndroidSwipeRefreshLayout"; - - private final ViewManagerDelegate mDelegate; - - public SwipeRefreshLayoutManager() { - mDelegate = new AndroidSwipeRefreshLayoutManagerDelegate<>(this); - } - - @Override - protected ReactSwipeRefreshLayout createViewInstance(ThemedReactContext reactContext) { - return new ReactSwipeRefreshLayout(reactContext); - } - - @Override - public String getName() { - return REACT_CLASS; - } - - @Override - @ReactProp(name = ViewProps.ENABLED, defaultBoolean = true) - public void setEnabled(ReactSwipeRefreshLayout view, boolean enabled) { - view.setEnabled(enabled); - } - - @Override - @ReactProp(name = "colors", customType = "ColorArray") - public void setColors(ReactSwipeRefreshLayout view, @Nullable ReadableArray colors) { - if (colors != null) { - int[] colorValues = new int[colors.size()]; - for (int i = 0; i < colors.size(); i++) { - if (colors.getType(i) == ReadableType.Map) { - colorValues[i] = ColorPropConverter.getColor(colors.getMap(i), view.getContext()); - } else { - colorValues[i] = colors.getInt(i); - } - } - view.setColorSchemeColors(colorValues); - } else { - view.setColorSchemeColors(); - } - } - - @Override - @ReactProp(name = "progressBackgroundColor", customType = "Color") - public void setProgressBackgroundColor(ReactSwipeRefreshLayout view, Integer color) { - view.setProgressBackgroundColorSchemeColor(color == null ? Color.TRANSPARENT : color); - } - - // TODO(T46143833): Remove this method once the 'size' prop has been migrated to String in JS. - public void setSize(ReactSwipeRefreshLayout view, int value) { - view.setSize(value); - } - - @Override - public void setSize(ReactSwipeRefreshLayout view, String size) { - if (size == null || size.equals("default")) { - view.setSize(SwipeRefreshLayout.DEFAULT); - } else if (size.equals("large")) { - view.setSize(SwipeRefreshLayout.LARGE); - } else { - throw new IllegalArgumentException("Size must be 'default' or 'large', received: " + size); - } - } - - // This prop temporarily takes both 0 and 1 as well as 'default' and 'large'. - // 0 and 1 are deprecated and will be removed in a future release. - // See T46143833 - @ReactProp(name = "size") - public void setSize(ReactSwipeRefreshLayout view, Dynamic size) { - if (size.isNull()) { - view.setSize(SwipeRefreshLayout.DEFAULT); - } else if (size.getType() == ReadableType.Number) { - view.setSize(size.asInt()); - } else if (size.getType() == ReadableType.String) { - setSize(view, size.asString()); - } else { - throw new IllegalArgumentException("Size must be 'default' or 'large'"); - } - } - - @Override - @ReactProp(name = "refreshing") - public void setRefreshing(ReactSwipeRefreshLayout view, boolean refreshing) { - view.setRefreshing(refreshing); - } - - @Override - @ReactProp(name = "progressViewOffset", defaultFloat = 0) - public void setProgressViewOffset(final ReactSwipeRefreshLayout view, final float offset) { - view.setProgressViewOffset(offset); - } - - @Override - public void setNativeRefreshing(ReactSwipeRefreshLayout view, boolean value) { - setRefreshing(view, value); - } - - @Override - protected void addEventEmitters( - final ThemedReactContext reactContext, final ReactSwipeRefreshLayout view) { - view.setOnRefreshListener( - new OnRefreshListener() { - @Override - public void onRefresh() { - EventDispatcher eventDispatcher = - UIManagerHelper.getEventDispatcherForReactTag(reactContext, view.getId()); - if (eventDispatcher != null) { - eventDispatcher.dispatchEvent( - new RefreshEvent(UIManagerHelper.getSurfaceId(view), view.getId())); - } - } - }); - } - - @Override - public void receiveCommand( - @NonNull ReactSwipeRefreshLayout root, String commandId, @Nullable ReadableArray args) { - switch (commandId) { - case "setNativeRefreshing": - if (args != null) { - setRefreshing(root, args.getBoolean(0)); - } - break; - } - } - - @Nullable - @Override - public Map getExportedViewConstants() { - return MapBuilder.of( - "SIZE", - MapBuilder.of("DEFAULT", SwipeRefreshLayout.DEFAULT, "LARGE", SwipeRefreshLayout.LARGE)); - } - - @Override - public Map getExportedCustomDirectEventTypeConstants() { - @Nullable - Map baseEventTypeConstants = super.getExportedCustomDirectEventTypeConstants(); - Map eventTypeConstants = - baseEventTypeConstants == null ? new HashMap() : baseEventTypeConstants; - eventTypeConstants.putAll( - MapBuilder.builder() - .put("topRefresh", MapBuilder.of("registrationName", "onRefresh")) - .build()); - return eventTypeConstants; - } - - @Override - protected ViewManagerDelegate getDelegate() { - return mDelegate; - } -} diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/swiperefresh/SwipeRefreshLayoutManager.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/swiperefresh/SwipeRefreshLayoutManager.kt new file mode 100644 index 00000000000000..c52887b07d7483 --- /dev/null +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/swiperefresh/SwipeRefreshLayoutManager.kt @@ -0,0 +1,153 @@ +/* + * 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.views.swiperefresh + +import android.graphics.Color +import androidx.swiperefreshlayout.widget.SwipeRefreshLayout +import com.facebook.react.bridge.ColorPropConverter +import com.facebook.react.bridge.Dynamic +import com.facebook.react.bridge.ReadableArray +import com.facebook.react.bridge.ReadableType +import com.facebook.react.module.annotations.ReactModule +import com.facebook.react.uimanager.ThemedReactContext +import com.facebook.react.uimanager.UIManagerHelper +import com.facebook.react.uimanager.ViewGroupManager +import com.facebook.react.uimanager.ViewManagerDelegate +import com.facebook.react.uimanager.ViewProps +import com.facebook.react.uimanager.annotations.ReactProp +import com.facebook.react.viewmanagers.AndroidSwipeRefreshLayoutManagerDelegate +import com.facebook.react.viewmanagers.AndroidSwipeRefreshLayoutManagerInterface +import java.util.HashMap + +/** + * ViewManager for [ReactSwipeRefreshLayout] which allows the user to "pull to refresh" a child + * view. Emits an `onRefresh` event when this happens. + */ +@ReactModule(name = SwipeRefreshLayoutManager.REACT_CLASS) +internal open class SwipeRefreshLayoutManager : + ViewGroupManager(), + AndroidSwipeRefreshLayoutManagerInterface { + + private val delegate: ViewManagerDelegate = + AndroidSwipeRefreshLayoutManagerDelegate(this) + + override fun createViewInstance(reactContext: ThemedReactContext): ReactSwipeRefreshLayout = + ReactSwipeRefreshLayout(reactContext) + + override fun getName(): String = REACT_CLASS + + @ReactProp(name = ViewProps.ENABLED, defaultBoolean = true) + override fun setEnabled(view: ReactSwipeRefreshLayout, enabled: Boolean) { + view.isEnabled = enabled + } + + @ReactProp(name = "colors", customType = "ColorArray") + override fun setColors(view: ReactSwipeRefreshLayout, colors: ReadableArray?) { + if (colors != null) { + val colorValues = IntArray(colors.size()) + for (i in 0.. view.setSize(SwipeRefreshLayout.DEFAULT) + size.type == ReadableType.Number -> view.setSize(size.asInt()) + size.type == ReadableType.String -> this.setSize(view, size.asString()) + else -> throw IllegalArgumentException("Size must be 'default' or 'large'") + } + } + + @ReactProp(name = "refreshing") + override fun setRefreshing(view: ReactSwipeRefreshLayout, refreshing: Boolean) { + view.isRefreshing = refreshing + } + + @ReactProp(name = "progressViewOffset", defaultFloat = 0f) + override fun setProgressViewOffset(view: ReactSwipeRefreshLayout, offset: Float) { + view.setProgressViewOffset(offset) + } + + override fun setNativeRefreshing(view: ReactSwipeRefreshLayout, value: Boolean) { + setRefreshing(view, value) + } + + override fun addEventEmitters(reactContext: ThemedReactContext, view: ReactSwipeRefreshLayout) { + view.setOnRefreshListener { + val eventDispatcher = UIManagerHelper.getEventDispatcherForReactTag(reactContext, view.id) + eventDispatcher?.dispatchEvent(RefreshEvent(UIManagerHelper.getSurfaceId(view), view.id)) + } + } + + override fun receiveCommand( + root: ReactSwipeRefreshLayout, + commandId: String, + args: ReadableArray? + ) { + when (commandId) { + "setNativeRefreshing" -> + if (args != null) { + setRefreshing(root, args.getBoolean(0)) + } + else -> {} + } + } + + override fun getExportedViewConstants(): MutableMap = + mutableMapOf( + "SIZE" to + mutableMapOf( + "DEFAULT" to SwipeRefreshLayout.DEFAULT, "LARGE" to SwipeRefreshLayout.LARGE)) + + override fun getExportedCustomDirectEventTypeConstants(): MutableMap { + val baseEventTypeConstants = super.getExportedCustomDirectEventTypeConstants() + val eventTypeConstants: MutableMap = baseEventTypeConstants ?: HashMap() + eventTypeConstants.putAll( + mutableMapOf("topRefresh" to mutableMapOf("registrationName" to "onRefresh"))) + return eventTypeConstants + } + + override fun getDelegate(): ViewManagerDelegate = delegate + + companion object { + const val REACT_CLASS: String = "AndroidSwipeRefreshLayout" + } +}