From 902ed36b32230a882c21892493ceedc06e15a8d2 Mon Sep 17 00:00:00 2001 From: Peter Abbondanzo Date: Thu, 9 Apr 2026 09:34:31 -0700 Subject: [PATCH] Allow LogBox and RedBox overlay dismissal via Android back button Summary: On Android, pressing the hardware back button while the LogBox inspector is open does nothing. `LogBoxDialog` is explicitly set to `setCancelable(false)` and there's no `onBackPressed()` override, so the back button is completely swallowed. The only way to close the inspector is the on-screen Minimize/Dismiss buttons. For RedBox, the dialog is cancelable by default so back press does dismiss it visually, but it bypasses `hideRedboxDialog()`. The dialog reference and content view don't get cleaned up, leaving stale state. This adds proper back button handling for both: **LogBox**: `LogBoxDialog` now takes an `onRequestClose` callback and overrides `onBackPressed()` to invoke it. The surface delegate passes a callback that emits `hardwareBackPress` to the JS runtime, where a new `BackHandler` listener in `LogBoxInspector` calls `onMinimize()`. This keeps `setCancelable(false)` in place (so tapping outside the dialog doesn't dismiss it) while still running the full dismiss flow through JS: state reset, `NativeLogBox.hide()`, dialog cleanup. **RedBox**: The anonymous `Dialog` subclass now overrides `onBackPressed()` to call `hideRedboxDialog()` directly, which dismisses the dialog, destroys the content view, and nulls the reference. Changelog: [Internal] - Allow LogBox and RedBox overlays to respond to Android back button press Differential Revision: D100081099 --- .../Libraries/LogBox/UI/LogBoxInspector.js | 14 +++++++++++++- .../devsupport/LogBoxDialogSurfaceDelegate.kt | 6 +++++- .../devsupport/RedBoxDialogSurfaceDelegate.kt | 1 + 3 files changed, 19 insertions(+), 2 deletions(-) diff --git a/packages/react-native/Libraries/LogBox/UI/LogBoxInspector.js b/packages/react-native/Libraries/LogBox/UI/LogBoxInspector.js index 646294b40f5a..4c854f539c96 100644 --- a/packages/react-native/Libraries/LogBox/UI/LogBoxInspector.js +++ b/packages/react-native/Libraries/LogBox/UI/LogBoxInspector.js @@ -11,6 +11,7 @@ import Keyboard from '../../Components/Keyboard/Keyboard'; import View from '../../Components/View/View'; import StyleSheet from '../../StyleSheet/StyleSheet'; +import BackHandler from '../../Utilities/BackHandler'; import * as LogBoxData from '../Data/LogBoxData'; import LogBoxLog, {type LogLevel} from '../Data/LogBoxLog'; import LogBoxInspectorBody from './LogBoxInspectorBody'; @@ -30,7 +31,7 @@ type Props = Readonly<{ }>; export default function LogBoxInspector(props: Props): React.Node { - const {logs, selectedIndex} = props; + const {logs, selectedIndex, onMinimize} = props; let log = logs[selectedIndex]; useEffect(() => { @@ -55,6 +56,17 @@ export default function LogBoxInspector(props: Props): React.Node { Keyboard.dismiss(); }, []); + useEffect(() => { + const subscription = BackHandler.addEventListener( + 'hardwareBackPress', + () => { + onMinimize(); + return true; + }, + ); + return () => subscription.remove(); + }, [onMinimize]); + function _handleRetry() { LogBoxData.retrySymbolicateLogNow(log); } diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/devsupport/LogBoxDialogSurfaceDelegate.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/devsupport/LogBoxDialogSurfaceDelegate.kt index dd1e5ca616d0..0138a7c9b6ab 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/devsupport/LogBoxDialogSurfaceDelegate.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/devsupport/LogBoxDialogSurfaceDelegate.kt @@ -66,7 +66,11 @@ internal class LogBoxDialogSurfaceDelegate(private val devSupportManager: DevSup dialog = LogBoxDialog(context, reactRootView) dialog?.let { dialog -> - dialog.setCancelable(false) + dialog.setCancelable(true) + dialog.setCanceledOnTouchOutside(false) + dialog.setOnCancelListener { + devSupportManager.currentReactContext?.emitDeviceEvent("hardwareBackPress") + } dialog.show() } } diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/devsupport/RedBoxDialogSurfaceDelegate.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/devsupport/RedBoxDialogSurfaceDelegate.kt index 9346cc254123..0eb15d07552e 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/devsupport/RedBoxDialogSurfaceDelegate.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/devsupport/RedBoxDialogSurfaceDelegate.kt @@ -125,6 +125,7 @@ internal class RedBoxDialogSurfaceDelegate(private val devSupportManager: DevSup .apply { requestWindowFeature(Window.FEATURE_NO_TITLE) setContentView(checkNotNull(redBoxContentView)) + setOnCancelListener { devSupportManager.hideRedboxDialog() } } } dialog?.show()