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
This commit is contained in:
Nicola Corti
2024-07-22 13:40:07 -07:00
committed by Facebook GitHub Bot
parent 0529818d36
commit 90e4397d99
5 changed files with 405 additions and 438 deletions
@@ -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 <init> (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 <init> ()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 <init> (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 {
@@ -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);
}
}
}
@@ -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
}
}
@@ -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<ReactDrawerLayout>
implements AndroidDrawerLayoutManagerInterface<ReactDrawerLayout> {
public static final String REACT_CLASS = "AndroidDrawerLayout";
public static final int OPEN_DRAWER = 1;
public static final int CLOSE_DRAWER = 2;
private final ViewManagerDelegate<ReactDrawerLayout> 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<String, Integer> 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<String, Object> getExportedViewConstants() {
return MapBuilder.of(
"DrawerPosition", MapBuilder.of("Left", Gravity.START, "Right", Gravity.END));
}
@Override
public @Nullable Map<String, Object> getExportedCustomDirectEventTypeConstants() {
@Nullable
Map<String, Object> baseEventTypeConstants = super.getExportedCustomDirectEventTypeConstants();
Map<String, Object> eventTypeConstants =
baseEventTypeConstants == null ? new HashMap<String, Object>() : 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<ReactDrawerLayout> 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));
}
}
}
@@ -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<ReactDrawerLayout>(), AndroidDrawerLayoutManagerInterface<ReactDrawerLayout> {
private val delegate: ViewManagerDelegate<ReactDrawerLayout> =
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<String, Int> =
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<String, Any> =
MapBuilder.of<String, Any>(
DRAWER_POSITION,
MapBuilder.of(DRAWER_POSITION_LEFT, Gravity.START, DRAWER_POSITION_RIGHT, Gravity.END))
public override fun getExportedCustomDirectEventTypeConstants(): Map<String, Any> {
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<ReactDrawerLayout> = 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"
}
}