From 90e4397d992e5a390f2c73b3d901e23f31ddffba Mon Sep 17 00:00:00 2001 From: Nicola Corti Date: Mon, 22 Jul 2024 13:40:07 -0700 Subject: [PATCH] Converted com.facebook.react.views.drawer to Kotlin (#45573) Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/45573 This makes this module now be fully in Kotlin instead of having mixed Java/Kotlin sources. Changelog: [Internal] [Changed] - Converted com.facebook.react.modules.dialog to Kotlin Reviewed By: tdn120 Differential Revision: D60035771 fbshipit-source-id: b45fd099c0b353768ab6580eb6a4a3dccf68f07d --- .../ReactAndroid/api/ReactAndroid.api | 28 +- .../react/views/drawer/ReactDrawerLayout.java | 123 ------- .../react/views/drawer/ReactDrawerLayout.kt | 125 ++++++++ .../drawer/ReactDrawerLayoutManager.java | 302 ------------------ .../views/drawer/ReactDrawerLayoutManager.kt | 265 +++++++++++++++ 5 files changed, 405 insertions(+), 438 deletions(-) delete mode 100644 packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/drawer/ReactDrawerLayout.java create mode 100644 packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/drawer/ReactDrawerLayout.kt delete mode 100644 packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/drawer/ReactDrawerLayoutManager.java create mode 100644 packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/drawer/ReactDrawerLayoutManager.kt diff --git a/packages/react-native/ReactAndroid/api/ReactAndroid.api b/packages/react-native/ReactAndroid/api/ReactAndroid.api index 8cc66c6cd97..e9f4fc5a079 100644 --- a/packages/react-native/ReactAndroid/api/ReactAndroid.api +++ b/packages/react-native/ReactAndroid/api/ReactAndroid.api @@ -6176,20 +6176,27 @@ public final class com/facebook/react/views/debuggingoverlay/TraceUpdate { public final fun getRectangle ()Landroid/graphics/RectF; } -public class com/facebook/react/views/drawer/ReactDrawerLayoutManager : com/facebook/react/uimanager/ViewGroupManager, com/facebook/react/viewmanagers/AndroidDrawerLayoutManagerInterface { +public final class com/facebook/react/views/drawer/ReactDrawerLayout : androidx/drawerlayout/widget/DrawerLayout { + public fun (Lcom/facebook/react/bridge/ReactContext;)V + public fun onInterceptTouchEvent (Landroid/view/MotionEvent;)Z + public fun onTouchEvent (Landroid/view/MotionEvent;)Z +} + +public final class com/facebook/react/views/drawer/ReactDrawerLayoutManager : com/facebook/react/uimanager/ViewGroupManager, com/facebook/react/viewmanagers/AndroidDrawerLayoutManagerInterface { public static final field CLOSE_DRAWER I + public static final field COMMAND_CLOSE_DRAWER Ljava/lang/String; + public static final field COMMAND_OPEN_DRAWER Ljava/lang/String; + public static final field Companion Lcom/facebook/react/views/drawer/ReactDrawerLayoutManager$Companion; public static final field OPEN_DRAWER I 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/drawer/ReactDrawerLayout;)V + public synthetic fun addEventEmitters (Lcom/facebook/react/uimanager/ThemedReactContext;Landroid/view/View;)V public synthetic fun addView (Landroid/view/View;Landroid/view/View;I)V public synthetic fun addView (Landroid/view/ViewGroup;Landroid/view/View;I)V public fun addView (Lcom/facebook/react/views/drawer/ReactDrawerLayout;Landroid/view/View;I)V public synthetic fun closeDrawer (Landroid/view/View;)V public fun closeDrawer (Lcom/facebook/react/views/drawer/ReactDrawerLayout;)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/drawer/ReactDrawerLayout; + public synthetic fun createViewInstance (Lcom/facebook/react/uimanager/ThemedReactContext;)Landroid/view/View; public fun getCommandsMap ()Ljava/util/Map; public fun getDelegate ()Lcom/facebook/react/uimanager/ViewManagerDelegate; public fun getExportedCustomDirectEventTypeConstants ()Ljava/util/Map; @@ -6207,10 +6214,10 @@ public class com/facebook/react/views/drawer/ReactDrawerLayoutManager : com/face public synthetic fun setDrawerLockMode (Landroid/view/View;Ljava/lang/String;)V public fun setDrawerLockMode (Lcom/facebook/react/views/drawer/ReactDrawerLayout;Ljava/lang/String;)V public synthetic fun setDrawerPosition (Landroid/view/View;Ljava/lang/String;)V - public fun setDrawerPosition (Lcom/facebook/react/views/drawer/ReactDrawerLayout;Lcom/facebook/react/bridge/Dynamic;)V + public final fun setDrawerPosition (Lcom/facebook/react/views/drawer/ReactDrawerLayout;Lcom/facebook/react/bridge/Dynamic;)V public fun setDrawerPosition (Lcom/facebook/react/views/drawer/ReactDrawerLayout;Ljava/lang/String;)V public synthetic fun setDrawerWidth (Landroid/view/View;Ljava/lang/Float;)V - public fun setDrawerWidth (Lcom/facebook/react/views/drawer/ReactDrawerLayout;F)V + public final fun setDrawerWidth (Lcom/facebook/react/views/drawer/ReactDrawerLayout;F)V public fun setDrawerWidth (Lcom/facebook/react/views/drawer/ReactDrawerLayout;Ljava/lang/Float;)V public synthetic fun setElevation (Landroid/view/View;F)V public fun setElevation (Lcom/facebook/react/views/drawer/ReactDrawerLayout;F)V @@ -6227,12 +6234,7 @@ public class com/facebook/react/views/drawer/ReactDrawerLayoutManager$$PropsSett public fun setProperty (Lcom/facebook/react/views/drawer/ReactDrawerLayoutManager;Lcom/facebook/react/views/drawer/ReactDrawerLayout;Ljava/lang/String;Ljava/lang/Object;)V } -public class com/facebook/react/views/drawer/ReactDrawerLayoutManager$DrawerEventEmitter : androidx/drawerlayout/widget/DrawerLayout$DrawerListener { - public fun (Landroidx/drawerlayout/widget/DrawerLayout;Lcom/facebook/react/uimanager/events/EventDispatcher;)V - public fun onDrawerClosed (Landroid/view/View;)V - public fun onDrawerOpened (Landroid/view/View;)V - public fun onDrawerSlide (Landroid/view/View;F)V - public fun onDrawerStateChanged (I)V +public final class com/facebook/react/views/drawer/ReactDrawerLayoutManager$Companion { } public final class com/facebook/react/views/drawer/events/DrawerClosedEvent : com/facebook/react/uimanager/events/Event { diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/drawer/ReactDrawerLayout.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/drawer/ReactDrawerLayout.java deleted file mode 100644 index 3d32892f7b9..00000000000 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/drawer/ReactDrawerLayout.java +++ /dev/null @@ -1,123 +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.drawer; - -import android.view.Gravity; -import android.view.MotionEvent; -import android.view.View; -import android.view.accessibility.AccessibilityEvent; -import androidx.core.view.AccessibilityDelegateCompat; -import androidx.core.view.ViewCompat; -import androidx.core.view.accessibility.AccessibilityNodeInfoCompat; -import androidx.drawerlayout.widget.DrawerLayout; -import com.facebook.common.logging.FLog; -import com.facebook.infer.annotation.Nullsafe; -import com.facebook.react.R; -import com.facebook.react.bridge.ReactContext; -import com.facebook.react.common.ReactConstants; -import com.facebook.react.uimanager.ReactAccessibilityDelegate.AccessibilityRole; -import com.facebook.react.uimanager.events.NativeGestureUtil; - -/** - * Wrapper view for {@link DrawerLayout}. It manages the properties that can be set on the drawer - * and contains some ReactNative-specific functionality. - */ -/* package */ @Nullsafe(Nullsafe.Mode.LOCAL) -class ReactDrawerLayout extends DrawerLayout { - - public static final int DEFAULT_DRAWER_WIDTH = LayoutParams.MATCH_PARENT; - private int mDrawerPosition = Gravity.START; - private int mDrawerWidth = DEFAULT_DRAWER_WIDTH; - private boolean mDragging = false; - - public ReactDrawerLayout(ReactContext reactContext) { - super(reactContext); - ViewCompat.setAccessibilityDelegate( - this, - new AccessibilityDelegateCompat() { - @Override - public void onInitializeAccessibilityNodeInfo( - View host, AccessibilityNodeInfoCompat info) { - super.onInitializeAccessibilityNodeInfo(host, info); - - final AccessibilityRole accessibilityRole = AccessibilityRole.fromViewTag(host); - if (accessibilityRole != null) { - info.setClassName(AccessibilityRole.getValue(accessibilityRole)); - } - } - - @Override - public void onInitializeAccessibilityEvent(View host, AccessibilityEvent event) { - super.onInitializeAccessibilityEvent(host, event); - final AccessibilityRole accessibilityRole = - (AccessibilityRole) host.getTag(R.id.accessibility_role); - if (accessibilityRole != null) { - event.setClassName(AccessibilityRole.getValue(accessibilityRole)); - } - } - }); - } - - @Override - public boolean onInterceptTouchEvent(MotionEvent ev) { - try { - if (super.onInterceptTouchEvent(ev)) { - NativeGestureUtil.notifyNativeGestureStarted(this, ev); - mDragging = true; - return true; - } - } catch (IllegalArgumentException e) { - // Log and ignore the error. This seems to be a bug in the android SDK and - // this is the commonly accepted workaround. - // https://tinyurl.com/mw6qkod (Stack Overflow) - FLog.w(ReactConstants.TAG, "Error intercepting touch event.", e); - } - - return false; - } - - @Override - public boolean onTouchEvent(MotionEvent ev) { - int action = ev.getActionMasked(); - if (action == MotionEvent.ACTION_UP && mDragging) { - NativeGestureUtil.notifyNativeGestureEnded(this, ev); - mDragging = false; - } - return super.onTouchEvent(ev); - } - - /* package */ void openDrawer() { - openDrawer(mDrawerPosition); - } - - /* package */ void closeDrawer() { - closeDrawer(mDrawerPosition); - } - - /* package */ void setDrawerPosition(int drawerPosition) { - mDrawerPosition = drawerPosition; - setDrawerProperties(); - } - - /* package */ void setDrawerWidth(int drawerWidthInPx) { - mDrawerWidth = drawerWidthInPx; - setDrawerProperties(); - } - - // Sets the properties of the drawer, after the navigationView has been set. - /* package */ void setDrawerProperties() { - if (this.getChildCount() == 2) { - View drawerView = this.getChildAt(1); - LayoutParams layoutParams = (LayoutParams) drawerView.getLayoutParams(); - layoutParams.gravity = mDrawerPosition; - layoutParams.width = mDrawerWidth; - drawerView.setLayoutParams(layoutParams); - drawerView.setClickable(true); - } - } -} diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/drawer/ReactDrawerLayout.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/drawer/ReactDrawerLayout.kt new file mode 100644 index 00000000000..10235238b41 --- /dev/null +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/drawer/ReactDrawerLayout.kt @@ -0,0 +1,125 @@ +/* + * 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.drawer + +import android.annotation.SuppressLint +import android.view.Gravity +import android.view.MotionEvent +import android.view.View +import android.view.accessibility.AccessibilityEvent +import androidx.core.view.AccessibilityDelegateCompat +import androidx.core.view.ViewCompat +import androidx.core.view.accessibility.AccessibilityNodeInfoCompat +import androidx.drawerlayout.widget.DrawerLayout +import com.facebook.common.logging.FLog +import com.facebook.react.R +import com.facebook.react.bridge.ReactContext +import com.facebook.react.common.ReactConstants +import com.facebook.react.uimanager.ReactAccessibilityDelegate.AccessibilityRole +import com.facebook.react.uimanager.events.NativeGestureUtil.notifyNativeGestureEnded +import com.facebook.react.uimanager.events.NativeGestureUtil.notifyNativeGestureStarted + +/** + * Wrapper view for [DrawerLayout]. It manages the properties that can be set on the drawer and + * contains some ReactNative-specific functionality. + */ +public class ReactDrawerLayout(reactContext: ReactContext) : DrawerLayout(reactContext) { + private var drawerPosition = Gravity.START + private var drawerWidth = DEFAULT_DRAWER_WIDTH + private var dragging = false + + init { + ViewCompat.setAccessibilityDelegate( + this, + object : AccessibilityDelegateCompat() { + override fun onInitializeAccessibilityNodeInfo( + host: View, + info: AccessibilityNodeInfoCompat + ) { + super.onInitializeAccessibilityNodeInfo(host, info) + + val accessibilityRole = AccessibilityRole.fromViewTag(host) + if (accessibilityRole != null) { + info.className = AccessibilityRole.getValue(accessibilityRole) + } + } + + override fun onInitializeAccessibilityEvent(host: View, event: AccessibilityEvent) { + super.onInitializeAccessibilityEvent(host, event) + val accessibilityRole = host.getTag(R.id.accessibility_role) + if (accessibilityRole is AccessibilityRole) { + event.className = AccessibilityRole.getValue(accessibilityRole) + } + } + }) + } + + override fun onInterceptTouchEvent(ev: MotionEvent): Boolean { + try { + if (super.onInterceptTouchEvent(ev)) { + notifyNativeGestureStarted(this, ev) + dragging = true + return true + } + } catch (e: IllegalArgumentException) { + // Log and ignore the error. This seems to be a bug in the android SDK and + // this is the commonly accepted workaround. + // https://tinyurl.com/mw6qkod (Stack Overflow) + FLog.w(ReactConstants.TAG, "Error intercepting touch event.", e) + } + + return false + } + + override fun onTouchEvent(ev: MotionEvent): Boolean { + val action = ev.actionMasked + if (action == MotionEvent.ACTION_UP && dragging) { + notifyNativeGestureEnded(this, ev) + dragging = false + } + return super.onTouchEvent(ev) + } + + @SuppressLint("WrongConstant") + internal fun openDrawer() { + openDrawer(drawerPosition) + } + + @SuppressLint("WrongConstant") + internal fun closeDrawer() { + closeDrawer(drawerPosition) + } + + internal fun setDrawerPosition(newDrawerPosition: Int) { + drawerPosition = newDrawerPosition + setDrawerProperties() + } + + internal fun setDrawerWidth(drawerWidthInPx: Int) { + drawerWidth = drawerWidthInPx + setDrawerProperties() + } + + /* + Sets the properties of the drawer, after the navigationView has been set. + */ + internal fun setDrawerProperties() { + if (this.childCount == 2) { + val drawerView = this.getChildAt(1) + val layoutParams = drawerView.layoutParams as LayoutParams + layoutParams.gravity = drawerPosition + layoutParams.width = drawerWidth + drawerView.layoutParams = layoutParams + drawerView.isClickable = true + } + } + + internal companion object { + internal const val DEFAULT_DRAWER_WIDTH: Int = LayoutParams.MATCH_PARENT + } +} diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/drawer/ReactDrawerLayoutManager.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/drawer/ReactDrawerLayoutManager.java deleted file mode 100644 index 097f9888213..00000000000 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/drawer/ReactDrawerLayoutManager.java +++ /dev/null @@ -1,302 +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.drawer; - -import android.view.Gravity; -import android.view.View; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.drawerlayout.widget.DrawerLayout; -import com.facebook.common.logging.FLog; -import com.facebook.infer.annotation.Nullsafe; -import com.facebook.react.bridge.Dynamic; -import com.facebook.react.bridge.JSApplicationIllegalArgumentException; -import com.facebook.react.bridge.ReadableArray; -import com.facebook.react.bridge.ReadableType; -import com.facebook.react.common.MapBuilder; -import com.facebook.react.common.ReactConstants; -import com.facebook.react.module.annotations.ReactModule; -import com.facebook.react.uimanager.PixelUtil; -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.annotations.ReactProp; -import com.facebook.react.uimanager.events.EventDispatcher; -import com.facebook.react.viewmanagers.AndroidDrawerLayoutManagerDelegate; -import com.facebook.react.viewmanagers.AndroidDrawerLayoutManagerInterface; -import com.facebook.react.views.drawer.events.DrawerClosedEvent; -import com.facebook.react.views.drawer.events.DrawerOpenedEvent; -import com.facebook.react.views.drawer.events.DrawerSlideEvent; -import com.facebook.react.views.drawer.events.DrawerStateChangedEvent; -import java.util.HashMap; -import java.util.Map; - -/** View Manager for {@link ReactDrawerLayout} components. */ -@Nullsafe(Nullsafe.Mode.LOCAL) -@ReactModule(name = ReactDrawerLayoutManager.REACT_CLASS) -public class ReactDrawerLayoutManager extends ViewGroupManager - implements AndroidDrawerLayoutManagerInterface { - - public static final String REACT_CLASS = "AndroidDrawerLayout"; - - public static final int OPEN_DRAWER = 1; - public static final int CLOSE_DRAWER = 2; - - private final ViewManagerDelegate mDelegate; - - public ReactDrawerLayoutManager() { - mDelegate = new AndroidDrawerLayoutManagerDelegate<>(this); - } - - @Override - public @NonNull String getName() { - return REACT_CLASS; - } - - @Override - protected void addEventEmitters( - @NonNull ThemedReactContext reactContext, ReactDrawerLayout view) { - EventDispatcher eventDispatcher = - UIManagerHelper.getEventDispatcherForReactTag(reactContext, view.getId()); - if (eventDispatcher == null) { - return; - } - - view.addDrawerListener(new DrawerEventEmitter(view, eventDispatcher)); - } - - @Override - protected @NonNull ReactDrawerLayout createViewInstance(@NonNull ThemedReactContext context) { - return new ReactDrawerLayout(context); - } - - @Override - public void setDrawerPosition(ReactDrawerLayout view, @Nullable String value) { - if (value == null) { - view.setDrawerPosition(Gravity.START); - } else { - setDrawerPositionInternal(view, value); - } - } - - @ReactProp(name = "drawerPosition") - public void setDrawerPosition(ReactDrawerLayout view, Dynamic drawerPosition) { - if (drawerPosition.isNull()) { - view.setDrawerPosition(Gravity.START); - } else if (drawerPosition.getType() == ReadableType.Number) { - final int drawerPositionNum = drawerPosition.asInt(); - - if (Gravity.START == drawerPositionNum || Gravity.END == drawerPositionNum) { - view.setDrawerPosition(drawerPositionNum); - } else { - FLog.w(ReactConstants.TAG, "Unknown drawerPosition " + drawerPositionNum); - view.setDrawerPosition(Gravity.START); - } - } else if (drawerPosition.getType() == ReadableType.String) { - setDrawerPositionInternal(view, drawerPosition.asString()); - } else { - FLog.w(ReactConstants.TAG, "drawerPosition must be a string or int"); - view.setDrawerPosition(Gravity.START); - } - } - - private void setDrawerPositionInternal(ReactDrawerLayout view, String drawerPosition) { - if (drawerPosition.equals("left")) { - view.setDrawerPosition(Gravity.START); - } else if (drawerPosition.equals("right")) { - view.setDrawerPosition(Gravity.END); - } else { - FLog.w( - ReactConstants.TAG, - "drawerPosition must be 'left' or 'right', received" + drawerPosition); - view.setDrawerPosition(Gravity.START); - } - } - - @ReactProp(name = "drawerWidth", defaultFloat = Float.NaN) - public void setDrawerWidth(ReactDrawerLayout view, float width) { - int widthInPx = - Float.isNaN(width) - ? ReactDrawerLayout.DEFAULT_DRAWER_WIDTH - : Math.round(PixelUtil.toPixelFromDIP(width)); - view.setDrawerWidth(widthInPx); - } - - @Override - public void setDrawerWidth(ReactDrawerLayout view, @Nullable Float width) { - int widthInPx = - width == null - ? ReactDrawerLayout.DEFAULT_DRAWER_WIDTH - : Math.round(PixelUtil.toPixelFromDIP(width)); - view.setDrawerWidth(widthInPx); - } - - @Override - @ReactProp(name = "drawerLockMode") - public void setDrawerLockMode(ReactDrawerLayout view, @Nullable String drawerLockMode) { - if (drawerLockMode == null || "unlocked".equals(drawerLockMode)) { - view.setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED); - } else if ("locked-closed".equals(drawerLockMode)) { - view.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED); - } else if ("locked-open".equals(drawerLockMode)) { - view.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_OPEN); - } else { - FLog.w(ReactConstants.TAG, "Unknown drawerLockMode " + drawerLockMode); - view.setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED); - } - } - - @Override - public void openDrawer(ReactDrawerLayout view) { - view.openDrawer(); - } - - @Override - public void closeDrawer(ReactDrawerLayout view) { - view.closeDrawer(); - } - - @Override - @ReactProp(name = "keyboardDismissMode") - public void setKeyboardDismissMode(ReactDrawerLayout view, @Nullable String value) {} - - @Override - @ReactProp(name = "drawerBackgroundColor", customType = "Color") - public void setDrawerBackgroundColor(ReactDrawerLayout view, @Nullable Integer value) {} - - @Override - @ReactProp(name = "statusBarBackgroundColor", customType = "Color") - public void setStatusBarBackgroundColor(ReactDrawerLayout view, @Nullable Integer value) {} - - @Override - public void setElevation(@NonNull ReactDrawerLayout view, float elevation) { - view.setDrawerElevation(PixelUtil.toPixelFromDIP(elevation)); - } - - @Override - public boolean needsCustomLayoutForChildren() { - // Return true, since DrawerLayout will lay out it's own children. - return true; - } - - @Override - public @Nullable Map getCommandsMap() { - return MapBuilder.of("openDrawer", OPEN_DRAWER, "closeDrawer", CLOSE_DRAWER); - } - - @Override - public void receiveCommand( - @NonNull ReactDrawerLayout root, int commandId, @Nullable ReadableArray args) { - switch (commandId) { - case OPEN_DRAWER: - root.openDrawer(); - break; - case CLOSE_DRAWER: - root.closeDrawer(); - break; - } - } - - @Override - public void receiveCommand( - @NonNull ReactDrawerLayout root, String commandId, @Nullable ReadableArray args) { - switch (commandId) { - case "openDrawer": - root.openDrawer(); - break; - case "closeDrawer": - root.closeDrawer(); - break; - } - } - - @Override - public @Nullable Map getExportedViewConstants() { - return MapBuilder.of( - "DrawerPosition", MapBuilder.of("Left", Gravity.START, "Right", Gravity.END)); - } - - @Override - public @Nullable Map getExportedCustomDirectEventTypeConstants() { - @Nullable - Map baseEventTypeConstants = super.getExportedCustomDirectEventTypeConstants(); - Map eventTypeConstants = - baseEventTypeConstants == null ? new HashMap() : baseEventTypeConstants; - eventTypeConstants.putAll( - MapBuilder.of( - DrawerSlideEvent.EVENT_NAME, MapBuilder.of("registrationName", "onDrawerSlide"), - DrawerOpenedEvent.EVENT_NAME, MapBuilder.of("registrationName", "onDrawerOpen"), - DrawerClosedEvent.EVENT_NAME, MapBuilder.of("registrationName", "onDrawerClose"), - DrawerStateChangedEvent.EVENT_NAME, - MapBuilder.of("registrationName", "onDrawerStateChanged"))); - return eventTypeConstants; - } - - /** - * This method is overridden because of two reasons: 1. A drawer must have exactly two children 2. - * The second child that is added, is the navigationView, which gets panned from the side. - */ - @Override - public void addView(ReactDrawerLayout parent, View child, int index) { - if (getChildCount(parent) >= 2) { - throw new JSApplicationIllegalArgumentException( - "The Drawer cannot have more than two children"); - } - if (index != 0 && index != 1) { - throw new JSApplicationIllegalArgumentException( - "The only valid indices for drawer's child are 0 or 1. Got " + index + " instead."); - } - parent.addView(child, index); - parent.setDrawerProperties(); - } - - @Override - public ViewManagerDelegate getDelegate() { - return mDelegate; - } - - public static class DrawerEventEmitter implements DrawerLayout.DrawerListener { - - private final DrawerLayout mDrawerLayout; - private final EventDispatcher mEventDispatcher; - - public DrawerEventEmitter(DrawerLayout drawerLayout, EventDispatcher eventDispatcher) { - mDrawerLayout = drawerLayout; - mEventDispatcher = eventDispatcher; - } - - @Override - public void onDrawerSlide(@NonNull View view, float v) { - mEventDispatcher.dispatchEvent( - new DrawerSlideEvent( - UIManagerHelper.getSurfaceId(mDrawerLayout), mDrawerLayout.getId(), v)); - } - - @Override - public void onDrawerOpened(@NonNull View view) { - mEventDispatcher.dispatchEvent( - new DrawerOpenedEvent( - UIManagerHelper.getSurfaceId(mDrawerLayout), mDrawerLayout.getId())); - } - - @Override - public void onDrawerClosed(@NonNull View view) { - mEventDispatcher.dispatchEvent( - new DrawerClosedEvent( - UIManagerHelper.getSurfaceId(mDrawerLayout), mDrawerLayout.getId())); - } - - @Override - public void onDrawerStateChanged(int i) { - mEventDispatcher.dispatchEvent( - new DrawerStateChangedEvent( - UIManagerHelper.getSurfaceId(mDrawerLayout), mDrawerLayout.getId(), i)); - } - } -} diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/drawer/ReactDrawerLayoutManager.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/drawer/ReactDrawerLayoutManager.kt new file mode 100644 index 00000000000..e9e61826b0c --- /dev/null +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/drawer/ReactDrawerLayoutManager.kt @@ -0,0 +1,265 @@ +/* + * 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.drawer + +import android.view.Gravity +import android.view.View +import androidx.drawerlayout.widget.DrawerLayout +import androidx.drawerlayout.widget.DrawerLayout.DrawerListener +import com.facebook.common.logging.FLog +import com.facebook.react.bridge.Dynamic +import com.facebook.react.bridge.JSApplicationIllegalArgumentException +import com.facebook.react.bridge.ReadableArray +import com.facebook.react.bridge.ReadableType +import com.facebook.react.common.MapBuilder +import com.facebook.react.common.ReactConstants +import com.facebook.react.module.annotations.ReactModule +import com.facebook.react.uimanager.PixelUtil.toPixelFromDIP +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.annotations.ReactProp +import com.facebook.react.uimanager.events.EventDispatcher +import com.facebook.react.viewmanagers.AndroidDrawerLayoutManagerDelegate +import com.facebook.react.viewmanagers.AndroidDrawerLayoutManagerInterface +import com.facebook.react.views.drawer.events.DrawerClosedEvent +import com.facebook.react.views.drawer.events.DrawerOpenedEvent +import com.facebook.react.views.drawer.events.DrawerSlideEvent +import com.facebook.react.views.drawer.events.DrawerStateChangedEvent + +/** View Manager for [ReactDrawerLayout] components. */ +@ReactModule(name = ReactDrawerLayoutManager.REACT_CLASS) +public class ReactDrawerLayoutManager : + ViewGroupManager(), AndroidDrawerLayoutManagerInterface { + + private val delegate: ViewManagerDelegate = + AndroidDrawerLayoutManagerDelegate(this) + + public override fun getName(): String = REACT_CLASS + + protected override fun addEventEmitters( + reactContext: ThemedReactContext, + view: ReactDrawerLayout + ) { + val eventDispatcher = + UIManagerHelper.getEventDispatcherForReactTag(reactContext, view.id) ?: return + view.addDrawerListener(DrawerEventEmitter(view, eventDispatcher)) + } + + protected override fun createViewInstance(context: ThemedReactContext): ReactDrawerLayout = + ReactDrawerLayout(context) + + public override fun setDrawerPosition(view: ReactDrawerLayout, value: String?): Unit = + if (value == null) { + view.setDrawerPosition(Gravity.START) + } else { + setDrawerPositionInternal(view, value) + } + + @ReactProp(name = "drawerPosition") + public fun setDrawerPosition(view: ReactDrawerLayout, drawerPosition: Dynamic) { + when { + drawerPosition.isNull -> view.setDrawerPosition(Gravity.START) + drawerPosition.type == ReadableType.Number -> { + val drawerPositionNum = drawerPosition.asInt() + if (Gravity.START == drawerPositionNum || Gravity.END == drawerPositionNum) { + view.setDrawerPosition(drawerPositionNum) + } else { + FLog.w(ReactConstants.TAG, "Unknown drawerPosition $drawerPositionNum") + view.setDrawerPosition(Gravity.START) + } + } + + drawerPosition.type == ReadableType.String -> + setDrawerPositionInternal(view, drawerPosition.asString()) + + else -> { + FLog.w(ReactConstants.TAG, "drawerPosition must be a string or int") + view.setDrawerPosition(Gravity.START) + } + } + } + + private fun setDrawerPositionInternal(view: ReactDrawerLayout, drawerPosition: String) { + when (drawerPosition) { + "left" -> view.setDrawerPosition(Gravity.START) + "right" -> view.setDrawerPosition(Gravity.END) + else -> { + FLog.w( + ReactConstants.TAG, "drawerPosition must be 'left' or 'right', received$drawerPosition") + view.setDrawerPosition(Gravity.START) + } + } + } + + @ReactProp(name = "drawerWidth", defaultFloat = Float.NaN) + public fun setDrawerWidth(view: ReactDrawerLayout, width: Float) { + val widthInPx = + if (width.isNaN()) { + ReactDrawerLayout.DEFAULT_DRAWER_WIDTH + } else { + Math.round(toPixelFromDIP(width)) + } + view.setDrawerWidth(widthInPx) + } + + public override fun setDrawerWidth(view: ReactDrawerLayout, width: Float?) { + val widthInPx = + if (width == null) { + ReactDrawerLayout.DEFAULT_DRAWER_WIDTH + } else { + Math.round(toPixelFromDIP(width)) + } + view.setDrawerWidth(widthInPx) + } + + @ReactProp(name = "drawerLockMode") + public override fun setDrawerLockMode(view: ReactDrawerLayout, drawerLockMode: String?) { + when (drawerLockMode) { + null, + "unlocked" -> view.setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED) + "locked-closed" -> view.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED) + "locked-open" -> view.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_OPEN) + else -> { + FLog.w(ReactConstants.TAG, "Unknown drawerLockMode $drawerLockMode") + view.setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED) + } + } + } + + public override fun openDrawer(view: ReactDrawerLayout): Unit = view.openDrawer() + + public override fun closeDrawer(view: ReactDrawerLayout): Unit = view.closeDrawer() + + @ReactProp(name = "keyboardDismissMode") + public override fun setKeyboardDismissMode(view: ReactDrawerLayout, value: String?): Unit = Unit + + @ReactProp(name = "drawerBackgroundColor", customType = "Color") + public override fun setDrawerBackgroundColor(view: ReactDrawerLayout, value: Int?): Unit = Unit + + @ReactProp(name = "statusBarBackgroundColor", customType = "Color") + public override fun setStatusBarBackgroundColor(view: ReactDrawerLayout, value: Int?): Unit = Unit + + public override fun setElevation(view: ReactDrawerLayout, elevation: Float) { + view.drawerElevation = toPixelFromDIP(elevation) + } + + public override fun needsCustomLayoutForChildren(): Boolean { + // Return true, since DrawerLayout will lay out it's own children. + return true + } + + public override fun getCommandsMap(): Map = + MapBuilder.of(COMMAND_OPEN_DRAWER, OPEN_DRAWER, COMMAND_CLOSE_DRAWER, CLOSE_DRAWER) + + @Deprecated( + message = + "This method is deprecated. Use receiveCommand(ReactDrawerLayout, String, ReadableArray) instead", + replaceWith = ReplaceWith("receiveCommand(ReactDrawerLayout, String, ReadableArray)")) + public override fun receiveCommand( + root: ReactDrawerLayout, + commandId: Int, + args: ReadableArray? + ) { + when (commandId) { + OPEN_DRAWER -> root.openDrawer() + CLOSE_DRAWER -> root.closeDrawer() + } + } + + public override fun receiveCommand( + root: ReactDrawerLayout, + commandId: String, + args: ReadableArray? + ) { + when (commandId) { + COMMAND_OPEN_DRAWER -> root.openDrawer() + COMMAND_CLOSE_DRAWER -> root.closeDrawer() + } + } + + public override fun getExportedViewConstants(): Map = + MapBuilder.of( + DRAWER_POSITION, + MapBuilder.of(DRAWER_POSITION_LEFT, Gravity.START, DRAWER_POSITION_RIGHT, Gravity.END)) + + public override fun getExportedCustomDirectEventTypeConstants(): Map { + val baseEventTypeConstants = super.getExportedCustomDirectEventTypeConstants() + val eventTypeConstants = baseEventTypeConstants ?: mutableMapOf() + eventTypeConstants.putAll( + MapBuilder.of( + DrawerSlideEvent.EVENT_NAME, + MapBuilder.of("registrationName", "onDrawerSlide"), + DrawerOpenedEvent.EVENT_NAME, + MapBuilder.of("registrationName", "onDrawerOpen"), + DrawerClosedEvent.EVENT_NAME, + MapBuilder.of("registrationName", "onDrawerClose"), + DrawerStateChangedEvent.EVENT_NAME, + MapBuilder.of("registrationName", "onDrawerStateChanged"))) + return eventTypeConstants + } + + /** + * This method is overridden because of two reasons: + * 1. A drawer must have exactly two children + * 2. The second child that is added, is the navigationView, which gets panned from the side. + */ + public override fun addView(parent: ReactDrawerLayout, child: View, index: Int) { + if (getChildCount(parent) >= 2) { + throw JSApplicationIllegalArgumentException("The Drawer cannot have more than two children") + } + if (index != 0 && index != 1) { + throw JSApplicationIllegalArgumentException( + "The only valid indices for drawer's child are 0 or 1. Got $index instead.") + } + parent.addView(child, index) + parent.setDrawerProperties() + } + + public override fun getDelegate(): ViewManagerDelegate = delegate + + internal class DrawerEventEmitter( + private val drawerLayout: DrawerLayout, + private val eventDispatcher: EventDispatcher + ) : DrawerListener { + override fun onDrawerSlide(view: View, v: Float) { + eventDispatcher.dispatchEvent( + DrawerSlideEvent(UIManagerHelper.getSurfaceId(drawerLayout), drawerLayout.id, v)) + } + + override fun onDrawerOpened(view: View) { + eventDispatcher.dispatchEvent( + DrawerOpenedEvent(UIManagerHelper.getSurfaceId(drawerLayout), drawerLayout.id)) + } + + override fun onDrawerClosed(view: View) { + eventDispatcher.dispatchEvent( + DrawerClosedEvent(UIManagerHelper.getSurfaceId(drawerLayout), drawerLayout.id)) + } + + override fun onDrawerStateChanged(i: Int) { + eventDispatcher.dispatchEvent( + DrawerStateChangedEvent(UIManagerHelper.getSurfaceId(drawerLayout), drawerLayout.id, i)) + } + } + + public companion object { + public const val REACT_CLASS: String = "AndroidDrawerLayout" + + public const val OPEN_DRAWER: Int = 1 + public const val CLOSE_DRAWER: Int = 2 + + public const val COMMAND_OPEN_DRAWER: String = "openDrawer" + public const val COMMAND_CLOSE_DRAWER: String = "closeDrawer" + + private const val DRAWER_POSITION: String = "DrawerPosition" + private const val DRAWER_POSITION_LEFT: String = "Left" + private const val DRAWER_POSITION_RIGHT: String = "Right" + } +}