diff --git a/packages/react-native/ReactAndroid/api/ReactAndroid.api b/packages/react-native/ReactAndroid/api/ReactAndroid.api index 4216256fccb442..dbd01688b0f759 100644 --- a/packages/react-native/ReactAndroid/api/ReactAndroid.api +++ b/packages/react-native/ReactAndroid/api/ReactAndroid.api @@ -244,6 +244,7 @@ public abstract interface class com/facebook/react/ReactHost { public abstract fun reload (Ljava/lang/String;)Lcom/facebook/react/interfaces/TaskInterface; public abstract fun removeBeforeDestroyListener (Lkotlin/jvm/functions/Function0;)V public abstract fun removeReactInstanceEventListener (Lcom/facebook/react/ReactInstanceEventListener;)V + public fun setDevMenuConfiguration (Lcom/facebook/react/devsupport/DevMenuConfiguration;)V public abstract fun start ()Lcom/facebook/react/interfaces/TaskInterface; } @@ -1899,6 +1900,23 @@ public final class com/facebook/react/devsupport/DefaultDevLoadingViewImplementa public final fun setDevLoadingEnabled (Z)V } +public final class com/facebook/react/devsupport/DevMenuConfiguration { + public fun ()V + public fun (ZZZ)V + public synthetic fun (ZZZILkotlin/jvm/internal/DefaultConstructorMarker;)V + public final fun component1 ()Z + public final fun component2 ()Z + public final fun component3 ()Z + public final fun copy (ZZZ)Lcom/facebook/react/devsupport/DevMenuConfiguration; + public static synthetic fun copy$default (Lcom/facebook/react/devsupport/DevMenuConfiguration;ZZZILjava/lang/Object;)Lcom/facebook/react/devsupport/DevMenuConfiguration; + public fun equals (Ljava/lang/Object;)Z + public final fun getAreKeyboardShortcutsEnabled ()Z + public fun hashCode ()I + public final fun isDevMenuEnabled ()Z + public final fun isShakeGestureEnabled ()Z + public fun toString ()Ljava/lang/String; +} + public class com/facebook/react/devsupport/DevServerHelper { public fun (Lcom/facebook/react/modules/debug/interfaces/DeveloperSettings;Landroid/content/Context;Lcom/facebook/react/packagerconnection/PackagerConnectionSettings;)V public final fun closeInspectorConnection ()V @@ -1940,17 +1958,20 @@ public abstract class com/facebook/react/devsupport/DevSupportManagerBase : com/ public fun getCurrentActivity ()Landroid/app/Activity; public final fun getCurrentReactContext ()Lcom/facebook/react/bridge/ReactContext; public final fun getDevLoadingViewManager ()Lcom/facebook/react/devsupport/interfaces/DevLoadingViewManager; + public fun getDevMenuEnabled ()Z public final fun getDevServerHelper ()Lcom/facebook/react/devsupport/DevServerHelper; public final fun getDevSettings ()Lcom/facebook/react/modules/debug/interfaces/DeveloperSettings; public final fun getDevSupportEnabled ()Z public fun getDownloadedJSBundleFile ()Ljava/lang/String; public final fun getJSAppBundleName ()Ljava/lang/String; + public fun getKeyboardShortcutsEnabled ()Z public final fun getLastErrorCookie ()I public final fun getLastErrorStack ()[Lcom/facebook/react/devsupport/interfaces/StackFrame; public final fun getLastErrorTitle ()Ljava/lang/String; public final fun getLastErrorType ()Lcom/facebook/react/devsupport/interfaces/ErrorType; public final fun getReactInstanceDevHelper ()Lcom/facebook/react/devsupport/ReactInstanceDevHelper; public fun getRedBoxHandler ()Lcom/facebook/react/devsupport/interfaces/RedBoxHandler; + public final fun getShakeGestureEnabled ()Z public fun getSourceMapUrl ()Ljava/lang/String; public fun getSourceUrl ()Ljava/lang/String; protected abstract fun getUniqueTag ()Ljava/lang/String; @@ -1969,14 +1990,17 @@ public abstract class com/facebook/react/devsupport/DevSupportManagerBase : com/ public fun reloadSettings ()V public fun setAdditionalOptionForPackager (Ljava/lang/String;Ljava/lang/String;)V public final fun setDevLoadingViewManager (Lcom/facebook/react/devsupport/interfaces/DevLoadingViewManager;)V + public fun setDevMenuEnabled (Z)V public final fun setDevSupportEnabled (Z)V public fun setFpsDebugEnabled (Z)V public fun setHotModuleReplacementEnabled (Z)V + public fun setKeyboardShortcutsEnabled (Z)V public final fun setLastErrorCookie (I)V public final fun setLastErrorStack ([Lcom/facebook/react/devsupport/interfaces/StackFrame;)V public final fun setLastErrorTitle (Ljava/lang/String;)V public final fun setLastErrorType (Lcom/facebook/react/devsupport/interfaces/ErrorType;)V public fun setPackagerLocationCustomizer (Lcom/facebook/react/devsupport/interfaces/DevSupportManager$PackagerLocationCustomizer;)V + public final fun setShakeGestureEnabled (Z)V protected final fun showDevLoadingViewForRemoteJSEnabled ()V public fun showDevOptionsDialog ()V public fun showNewJSError (Ljava/lang/String;Lcom/facebook/react/bridge/ReadableArray;I)V @@ -2033,14 +2057,17 @@ public class com/facebook/react/devsupport/ReleaseDevSupportManager : com/facebo public fun downloadBundleResourceFromUrlSync (Ljava/lang/String;Ljava/io/File;)Ljava/io/File; public fun getCurrentActivity ()Landroid/app/Activity; public fun getCurrentReactContext ()Lcom/facebook/react/bridge/ReactContext; + public fun getDevMenuEnabled ()Z public fun getDevSettings ()Lcom/facebook/react/modules/debug/interfaces/DeveloperSettings; public fun getDevSupportEnabled ()Z public fun getDownloadedJSBundleFile ()Ljava/lang/String; + public fun getKeyboardShortcutsEnabled ()Z public fun getLastErrorCookie ()I public fun getLastErrorStack ()[Lcom/facebook/react/devsupport/interfaces/StackFrame; public fun getLastErrorTitle ()Ljava/lang/String; public fun getLastErrorType ()Lcom/facebook/react/devsupport/interfaces/ErrorType; public fun getRedBoxHandler ()Lcom/facebook/react/devsupport/interfaces/RedBoxHandler; + public fun getShakeGestureEnabled ()Z public fun getSourceMapUrl ()Ljava/lang/String; public fun getSourceUrl ()Ljava/lang/String; public fun handleException (Ljava/lang/Exception;)V @@ -2057,10 +2084,13 @@ public class com/facebook/react/devsupport/ReleaseDevSupportManager : com/facebo public fun reloadJSFromServer (Ljava/lang/String;Lcom/facebook/react/devsupport/interfaces/BundleLoadCallback;)V public fun reloadSettings ()V public fun setAdditionalOptionForPackager (Ljava/lang/String;Ljava/lang/String;)V + public fun setDevMenuEnabled (Z)V public fun setDevSupportEnabled (Z)V public fun setFpsDebugEnabled (Z)V public fun setHotModuleReplacementEnabled (Z)V + public fun setKeyboardShortcutsEnabled (Z)V public fun setPackagerLocationCustomizer (Lcom/facebook/react/devsupport/interfaces/DevSupportManager$PackagerLocationCustomizer;)V + public fun setShakeGestureEnabled (Z)V public fun showDevOptionsDialog ()V public fun showNewJSError (Ljava/lang/String;Lcom/facebook/react/bridge/ReadableArray;I)V public fun showNewJavaError (Ljava/lang/String;Ljava/lang/Throwable;)V @@ -2134,14 +2164,17 @@ public abstract interface class com/facebook/react/devsupport/interfaces/DevSupp public abstract fun downloadBundleResourceFromUrlSync (Ljava/lang/String;Ljava/io/File;)Ljava/io/File; public abstract fun getCurrentActivity ()Landroid/app/Activity; public abstract fun getCurrentReactContext ()Lcom/facebook/react/bridge/ReactContext; + public abstract fun getDevMenuEnabled ()Z public abstract fun getDevSettings ()Lcom/facebook/react/modules/debug/interfaces/DeveloperSettings; public abstract fun getDevSupportEnabled ()Z public abstract fun getDownloadedJSBundleFile ()Ljava/lang/String; + public abstract fun getKeyboardShortcutsEnabled ()Z public abstract fun getLastErrorCookie ()I public abstract fun getLastErrorStack ()[Lcom/facebook/react/devsupport/interfaces/StackFrame; public abstract fun getLastErrorTitle ()Ljava/lang/String; public abstract fun getLastErrorType ()Lcom/facebook/react/devsupport/interfaces/ErrorType; public abstract fun getRedBoxHandler ()Lcom/facebook/react/devsupport/interfaces/RedBoxHandler; + public abstract fun getShakeGestureEnabled ()Z public abstract fun getSourceMapUrl ()Ljava/lang/String; public abstract fun getSourceUrl ()Ljava/lang/String; public abstract fun handleReloadJS ()V @@ -2158,10 +2191,13 @@ public abstract interface class com/facebook/react/devsupport/interfaces/DevSupp public abstract fun reloadJSFromServer (Ljava/lang/String;Lcom/facebook/react/devsupport/interfaces/BundleLoadCallback;)V public abstract fun reloadSettings ()V public abstract fun setAdditionalOptionForPackager (Ljava/lang/String;Ljava/lang/String;)V + public abstract fun setDevMenuEnabled (Z)V public abstract fun setDevSupportEnabled (Z)V public abstract fun setFpsDebugEnabled (Z)V public abstract fun setHotModuleReplacementEnabled (Z)V + public abstract fun setKeyboardShortcutsEnabled (Z)V public abstract fun setPackagerLocationCustomizer (Lcom/facebook/react/devsupport/interfaces/DevSupportManager$PackagerLocationCustomizer;)V + public abstract fun setShakeGestureEnabled (Z)V public abstract fun showDevOptionsDialog ()V public abstract fun showNewJSError (Ljava/lang/String;Lcom/facebook/react/bridge/ReadableArray;I)V public abstract fun showNewJavaError (Ljava/lang/String;Ljava/lang/Throwable;)V @@ -3073,6 +3109,7 @@ public final class com/facebook/react/runtime/ReactHostImpl : com/facebook/react public fun reload (Ljava/lang/String;)Lcom/facebook/react/interfaces/TaskInterface; public fun removeBeforeDestroyListener (Lkotlin/jvm/functions/Function0;)V public fun removeReactInstanceEventListener (Lcom/facebook/react/ReactInstanceEventListener;)V + public fun setDevMenuConfiguration (Lcom/facebook/react/devsupport/DevMenuConfiguration;)V public fun start ()Lcom/facebook/react/interfaces/TaskInterface; } diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/ReactDelegate.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/ReactDelegate.kt index e7886bf38cf764..cdc8a111eba109 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/ReactDelegate.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/ReactDelegate.kt @@ -393,7 +393,11 @@ public open class ReactDelegate { public fun shouldShowDevMenuOrReload(keyCode: Int, event: KeyEvent?): Boolean { val devSupportManager = devSupportManager // shouldShowDevMenuOrReload is a Dev API and not supported in RELEASE mode. - if (devSupportManager == null || devSupportManager is ReleaseDevSupportManager) { + if ( + devSupportManager == null || + devSupportManager is ReleaseDevSupportManager || + !devSupportManager.keyboardShortcutsEnabled + ) { return false } diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/ReactHost.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/ReactHost.kt index 8119107cd82266..583bc2aed37d52 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/ReactHost.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/ReactHost.kt @@ -14,6 +14,7 @@ import android.os.Bundle import com.facebook.react.bridge.ReactContext import com.facebook.react.bridge.queue.ReactQueueConfiguration import com.facebook.react.common.LifecycleState +import com.facebook.react.devsupport.DevMenuConfiguration import com.facebook.react.devsupport.interfaces.DevSupportManager import com.facebook.react.interfaces.TaskInterface import com.facebook.react.interfaces.fabric.ReactSurface @@ -189,4 +190,7 @@ public interface ReactHost { /** Remove a listener previously added with [addReactInstanceEventListener]. */ public fun removeReactInstanceEventListener(listener: ReactInstanceEventListener) + + /** Set the DevMenu configuration. */ + public fun setDevMenuConfiguration(config: DevMenuConfiguration): Unit = Unit } diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/devsupport/DevMenuConfiguration.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/devsupport/DevMenuConfiguration.kt new file mode 100644 index 00000000000000..41151406462cb3 --- /dev/null +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/devsupport/DevMenuConfiguration.kt @@ -0,0 +1,16 @@ +/* + * 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.devsupport + +import com.facebook.react.BuildConfig + +public data class DevMenuConfiguration( + val isDevMenuEnabled: Boolean = BuildConfig.DEBUG, + val isShakeGestureEnabled: Boolean = true, + val areKeyboardShortcutsEnabled: Boolean = true, +) diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/devsupport/DevSupportManagerBase.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/devsupport/DevSupportManagerBase.kt index e6ac477e44cd87..4eba285185e372 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/devsupport/DevSupportManagerBase.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/devsupport/DevSupportManagerBase.kt @@ -130,6 +130,22 @@ public abstract class DevSupportManagerBase( reloadSettings() } + final override var shakeGestureEnabled: Boolean + get() = isShakeGestureEnabled + set(isShakeGestureEnabled) { + if (this.isShakeGestureEnabled == isShakeGestureEnabled) { + return + } + + if (isShakeGestureEnabled) { + startShakeDetector() + } else { + stopShakeDetector() + } + + this.isShakeGestureEnabled = isShakeGestureEnabled + } + override val sourceMapUrl: String get() = jsAppBundleName?.let { devServerHelper.getSourceMapUrl(it) } ?: "" @@ -173,6 +189,7 @@ public abstract class DevSupportManagerBase( private var isReceiverRegistered = false private var isShakeDetectorStarted = false private var isDevSupportEnabled = false + private var isShakeGestureEnabled = true private var isPackagerConnected = false private val errorCustomizers: MutableList = mutableListOf() private var packagerLocationCustomizer: PackagerLocationCustomizer? = null @@ -187,6 +204,9 @@ public abstract class DevSupportManagerBase( private var perfMonitorOverlayManager: PerfMonitorOverlayManager? = null private var tracingStateProvider: TracingStateProvider? = null + public override var keyboardShortcutsEnabled: Boolean = true + public override var devMenuEnabled: Boolean = true + init { // We store JS bundle loaded from dev server in a single destination in app's data dir. // In case when someone schedule 2 subsequent reloads it may happen that JS thread will @@ -332,7 +352,12 @@ public abstract class DevSupportManagerBase( } override fun showDevOptionsDialog() { - if (devOptionsDialog != null || !isDevSupportEnabled || ActivityManager.isUserAMonkey()) { + if ( + devOptionsDialog != null || + !isDevSupportEnabled || + ActivityManager.isUserAMonkey() || + !devMenuEnabled + ) { return } val options = LinkedHashMap() @@ -848,6 +873,17 @@ public abstract class DevSupportManagerBase( } } + private fun startShakeDetector() { + val sensorManager = applicationContext.getSystemService(Context.SENSOR_SERVICE) as SensorManager + shakeDetector.start(sensorManager) + isShakeDetectorStarted = true + } + + private fun stopShakeDetector() { + shakeDetector.stop() + isShakeDetectorStarted = false + } + private fun reload() { UiThreadUtil.assertOnUiThread() @@ -857,11 +893,8 @@ public abstract class DevSupportManagerBase( debugOverlayController?.setFpsDebugViewVisible(devSettings.isFpsDebugEnabled) // start shake gesture detector - if (!isShakeDetectorStarted) { - val sensorManager = - applicationContext.getSystemService(Context.SENSOR_SERVICE) as SensorManager - shakeDetector.start(sensorManager) - isShakeDetectorStarted = true + if (!isShakeDetectorStarted && isShakeGestureEnabled) { + startShakeDetector() } // register reload app broadcast receiver @@ -915,8 +948,7 @@ public abstract class DevSupportManagerBase( // stop shake gesture detector if (isShakeDetectorStarted) { - shakeDetector.stop() - isShakeDetectorStarted = false + stopShakeDetector() } // unregister app reload broadcast receiver diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/devsupport/ReleaseDevSupportManager.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/devsupport/ReleaseDevSupportManager.kt index 4491955f315564..9aa0253eaf7f23 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/devsupport/ReleaseDevSupportManager.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/devsupport/ReleaseDevSupportManager.kt @@ -72,6 +72,18 @@ public open class ReleaseDevSupportManager : DevSupportManager { get() = false @Suppress("UNUSED_PARAMETER") set(isDevSupportEnabled: Boolean): Unit = Unit + public override var devMenuEnabled: Boolean + get() = false + @Suppress("UNUSED_PARAMETER") set(isDevMenuEnabled: Boolean): Unit = Unit + + public override var shakeGestureEnabled: Boolean + get() = false + @Suppress("UNUSED_PARAMETER") set(isShakeGestureEnabled: Boolean): Unit = Unit + + public override var keyboardShortcutsEnabled: Boolean + get() = false + @Suppress("UNUSED_PARAMETER") set(areKeyboardShortcutsEnabled: Boolean): Unit = Unit + public override val devSettings: DeveloperSettings? get() = null diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/devsupport/interfaces/DevSupportManager.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/devsupport/interfaces/DevSupportManager.kt index 3356e1dc3ac56e..aaa7950e104109 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/devsupport/interfaces/DevSupportManager.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/devsupport/interfaces/DevSupportManager.kt @@ -36,6 +36,9 @@ public interface DevSupportManager : JSExceptionHandler { public val currentActivity: Activity? public val currentReactContext: ReactContext? + public var devMenuEnabled: Boolean + public var shakeGestureEnabled: Boolean + public var keyboardShortcutsEnabled: Boolean public var devSupportEnabled: Boolean public fun showNewJavaError(message: String?, e: Throwable) diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/runtime/ReactHostImpl.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/runtime/ReactHostImpl.kt index 258a8791462543..a3c6a37f4b16ee 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/runtime/ReactHostImpl.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/runtime/ReactHostImpl.kt @@ -39,6 +39,7 @@ import com.facebook.react.common.annotations.FrameworkAPI import com.facebook.react.common.annotations.UnstableReactNativeAPI import com.facebook.react.common.build.ReactBuildConfig import com.facebook.react.devsupport.DefaultDevSupportManagerFactory +import com.facebook.react.devsupport.DevMenuConfiguration import com.facebook.react.devsupport.DevSupportManagerBase import com.facebook.react.devsupport.DevSupportManagerFactory import com.facebook.react.devsupport.InspectorFlags @@ -365,6 +366,12 @@ public class ReactHostImpl( reactInstanceEventListeners.remove(listener) } + override fun setDevMenuConfiguration(config: DevMenuConfiguration) { + devSupportManager.devMenuEnabled = config.isDevMenuEnabled + devSupportManager.shakeGestureEnabled = config.isShakeGestureEnabled + devSupportManager.keyboardShortcutsEnabled = config.areKeyboardShortcutsEnabled + } + /** * Entrypoint to reload the ReactInstance. If the ReactInstance is destroying, will wait until * destroy is finished, before reloading. diff --git a/packages/rn-tester/android/app/src/main/java/com/facebook/react/uiapp/RNTesterActivity.kt b/packages/rn-tester/android/app/src/main/java/com/facebook/react/uiapp/RNTesterActivity.kt index 4df826527df0c7..1095c7ff6acbd3 100644 --- a/packages/rn-tester/android/app/src/main/java/com/facebook/react/uiapp/RNTesterActivity.kt +++ b/packages/rn-tester/android/app/src/main/java/com/facebook/react/uiapp/RNTesterActivity.kt @@ -19,6 +19,7 @@ import com.facebook.react.FBRNTesterEndToEndHelper import com.facebook.react.ReactActivity import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint.fabricEnabled import com.facebook.react.defaults.DefaultReactActivityDelegate +import com.facebook.react.devsupport.DevMenuConfiguration import java.io.FileDescriptor import java.io.PrintWriter @@ -66,6 +67,16 @@ internal class RNTesterActivity : ReactActivity() { fullyDrawnReporter.addReporter() maybeUpdateBackgroundColor() + reactDelegate?.reactHost?.let { reactHost -> + val devMenuConfiguration = + DevMenuConfiguration( + isDevMenuEnabled = true, + isShakeGestureEnabled = true, + areKeyboardShortcutsEnabled = true, + ) + reactHost.setDevMenuConfiguration(devMenuConfiguration) + } + // register insets listener to update margins on the ReactRootView to avoid overlap w/ system // bars reactDelegate?.reactRootView?.let { rootView ->