diff --git a/ReactAndroid/src/main/java/com/facebook/react/CoreModulesPackage.java b/ReactAndroid/src/main/java/com/facebook/react/CoreModulesPackage.java
index 6dd813b6f62..a17763207b5 100644
--- a/ReactAndroid/src/main/java/com/facebook/react/CoreModulesPackage.java
+++ b/ReactAndroid/src/main/java/com/facebook/react/CoreModulesPackage.java
@@ -16,6 +16,7 @@ import androidx.annotation.Nullable;
import com.facebook.react.bridge.NativeModule;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactMarker;
+import com.facebook.react.devsupport.LogBoxModule;
import com.facebook.react.module.annotations.ReactModule;
import com.facebook.react.module.annotations.ReactModuleList;
import com.facebook.react.module.model.ReactModuleInfo;
@@ -49,6 +50,7 @@ import java.util.Map;
DeviceInfoModule.class,
DevSettingsModule.class,
ExceptionsManagerModule.class,
+ LogBoxModule.class,
HeadlessJsTaskSupportModule.class,
SourceCodeModule.class,
TimingModule.class,
@@ -94,6 +96,7 @@ import java.util.Map;
DeviceInfoModule.class,
DevSettingsModule.class,
ExceptionsManagerModule.class,
+ LogBoxModule.class,
HeadlessJsTaskSupportModule.class,
SourceCodeModule.class,
TimingModule.class,
@@ -142,6 +145,8 @@ import java.util.Map;
return new DevSettingsModule(reactContext, mReactInstanceManager.getDevSupportManager());
case ExceptionsManagerModule.NAME:
return new ExceptionsManagerModule(mReactInstanceManager.getDevSupportManager());
+ case LogBoxModule.NAME:
+ return new LogBoxModule(reactContext, mReactInstanceManager.getDevSupportManager());
case HeadlessJsTaskSupportModule.NAME:
return new HeadlessJsTaskSupportModule(reactContext);
case SourceCodeModule.NAME:
diff --git a/ReactAndroid/src/main/java/com/facebook/react/ReactInstanceManager.java b/ReactAndroid/src/main/java/com/facebook/react/ReactInstanceManager.java
index 8d56f3418f5..2d79c4d3fb2 100644
--- a/ReactAndroid/src/main/java/com/facebook/react/ReactInstanceManager.java
+++ b/ReactAndroid/src/main/java/com/facebook/react/ReactInstanceManager.java
@@ -304,6 +304,27 @@ public class ReactInstanceManager {
public JavaScriptExecutorFactory getJavaScriptExecutorFactory() {
return ReactInstanceManager.this.getJSExecutorFactory();
}
+
+ @Override
+ public @Nullable View createRootView(String appKey) {
+ Activity currentActivity = getCurrentActivity();
+ if (currentActivity != null) {
+ ReactRootView rootView = new ReactRootView(currentActivity);
+
+ rootView.startReactApplication(ReactInstanceManager.this, appKey, null);
+
+ return rootView;
+ }
+
+ return null;
+ }
+
+ @Override
+ public void destroyRootView(View rootView) {
+ if (rootView instanceof ReactRootView) {
+ ((ReactRootView) rootView).unmountReactApplication();
+ }
+ }
};
}
diff --git a/ReactAndroid/src/main/java/com/facebook/react/devsupport/DevSupportManagerImpl.java b/ReactAndroid/src/main/java/com/facebook/react/devsupport/DevSupportManagerImpl.java
index 2e3f651fb16..5dd361f1d83 100644
--- a/ReactAndroid/src/main/java/com/facebook/react/devsupport/DevSupportManagerImpl.java
+++ b/ReactAndroid/src/main/java/com/facebook/react/devsupport/DevSupportManagerImpl.java
@@ -19,6 +19,7 @@ import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.hardware.SensorManager;
import android.util.Pair;
+import android.view.View;
import android.widget.EditText;
import android.widget.Toast;
import androidx.annotation.Nullable;
@@ -377,6 +378,14 @@ public class DevSupportManagerImpl
}
}
+ public @Nullable View createRootView(String appKey) {
+ return mReactInstanceManagerHelper.createRootView(appKey);
+ }
+
+ public void destroyRootView(View rootView) {
+ mReactInstanceManagerHelper.destroyRootView(rootView);
+ }
+
private void hideDevOptionsDialog() {
if (mDevOptionsDialog != null) {
mDevOptionsDialog.dismiss();
diff --git a/ReactAndroid/src/main/java/com/facebook/react/devsupport/DisabledDevSupportManager.java b/ReactAndroid/src/main/java/com/facebook/react/devsupport/DisabledDevSupportManager.java
index 4f510fe1889..267a455e6eb 100644
--- a/ReactAndroid/src/main/java/com/facebook/react/devsupport/DisabledDevSupportManager.java
+++ b/ReactAndroid/src/main/java/com/facebook/react/devsupport/DisabledDevSupportManager.java
@@ -7,6 +7,7 @@
package com.facebook.react.devsupport;
+import android.view.View;
import androidx.annotation.Nullable;
import com.facebook.react.bridge.DefaultNativeModuleCallExceptionHandler;
import com.facebook.react.bridge.ReactContext;
@@ -40,6 +41,14 @@ public class DisabledDevSupportManager implements DevSupportManager {
@Override
public void showNewJSError(String message, ReadableArray details, int errorCookie) {}
+ @Override
+ public @Nullable View createRootView(String appKey) {
+ return null;
+ }
+
+ @Override
+ public void destroyRootView(View rootView) {}
+
@Override
public void updateJSError(String message, ReadableArray details, int errorCookie) {}
diff --git a/ReactAndroid/src/main/java/com/facebook/react/devsupport/LogBoxDialog.java b/ReactAndroid/src/main/java/com/facebook/react/devsupport/LogBoxDialog.java
new file mode 100644
index 00000000000..c6a5b80e8dd
--- /dev/null
+++ b/ReactAndroid/src/main/java/com/facebook/react/devsupport/LogBoxDialog.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright (c) Facebook, Inc. and its 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 android.app.Activity;
+import android.app.Dialog;
+import android.view.View;
+import android.view.Window;
+import com.facebook.react.R;
+
+/** Dialog for displaying JS errors in LogBox. */
+public class LogBoxDialog extends Dialog {
+ public LogBoxDialog(Activity context, View reactRootView) {
+ super(context, R.style.Theme_Catalyst_LogBox);
+
+ requestWindowFeature(Window.FEATURE_NO_TITLE);
+ setContentView(reactRootView);
+ }
+}
diff --git a/ReactAndroid/src/main/java/com/facebook/react/devsupport/LogBoxModule.java b/ReactAndroid/src/main/java/com/facebook/react/devsupport/LogBoxModule.java
new file mode 100644
index 00000000000..05e70b069d3
--- /dev/null
+++ b/ReactAndroid/src/main/java/com/facebook/react/devsupport/LogBoxModule.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright (c) Facebook, Inc. and its 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 android.app.Activity;
+import android.view.View;
+import android.view.ViewGroup;
+import androidx.annotation.Nullable;
+import com.facebook.common.logging.FLog;
+import com.facebook.react.bridge.ReactApplicationContext;
+import com.facebook.react.bridge.ReactContextBaseJavaModule;
+import com.facebook.react.bridge.ReactMethod;
+import com.facebook.react.bridge.UiThreadUtil;
+import com.facebook.react.common.ReactConstants;
+import com.facebook.react.devsupport.interfaces.DevSupportManager;
+import com.facebook.react.module.annotations.ReactModule;
+
+@ReactModule(name = LogBoxModule.NAME)
+public class LogBoxModule extends ReactContextBaseJavaModule {
+
+ public static final String NAME = "LogBox";
+
+ private final DevSupportManager mDevSupportManager;
+ private @Nullable View mReactRootView;
+ private @Nullable LogBoxDialog mLogBoxDialog;
+
+ public LogBoxModule(ReactApplicationContext reactContext, DevSupportManager devSupportManager) {
+ super(reactContext);
+
+ mDevSupportManager = devSupportManager;
+ UiThreadUtil.runOnUiThread(
+ new Runnable() {
+ @Override
+ public void run() {
+ if (mReactRootView == null) {
+ mReactRootView = mDevSupportManager.createRootView("LogBox");
+ if (mReactRootView == null) {
+ FLog.e(
+ ReactConstants.TAG,
+ "Unable to launch logbox because react was unable to create the root view");
+ }
+ }
+ }
+ });
+ }
+
+ @Override
+ public String getName() {
+ return NAME;
+ }
+
+ @ReactMethod
+ public void show() {
+ UiThreadUtil.runOnUiThread(
+ new Runnable() {
+ @Override
+ public void run() {
+ if (mLogBoxDialog == null) {
+ Activity context = getCurrentActivity();
+ if (context == null || context.isFinishing()) {
+ FLog.e(
+ ReactConstants.TAG,
+ "Unable to launch logbox because react activity "
+ + "is not available, here is the error that logbox would've displayed: ");
+ return;
+ }
+ mLogBoxDialog = new LogBoxDialog(context, mReactRootView);
+ mLogBoxDialog.setCancelable(false);
+ mLogBoxDialog.show();
+ }
+ }
+ });
+ }
+
+ @ReactMethod
+ public void hide() {
+ UiThreadUtil.runOnUiThread(
+ new Runnable() {
+ @Override
+ public void run() {
+ if (mLogBoxDialog != null) {
+ if (mReactRootView.getParent() != null) {
+ ((ViewGroup) mReactRootView.getParent()).removeView(mReactRootView);
+ }
+ mLogBoxDialog.dismiss();
+ mLogBoxDialog = null;
+ }
+ }
+ });
+ }
+
+ @Override
+ public void onCatalystInstanceDestroy() {
+ UiThreadUtil.runOnUiThread(
+ new Runnable() {
+ @Override
+ public void run() {
+ if (mReactRootView != null) {
+ mDevSupportManager.destroyRootView(mReactRootView);
+ mReactRootView = null;
+ }
+ }
+ });
+ }
+}
diff --git a/ReactAndroid/src/main/java/com/facebook/react/devsupport/ReactInstanceManagerDevHelper.java b/ReactAndroid/src/main/java/com/facebook/react/devsupport/ReactInstanceManagerDevHelper.java
index 60d45b1ba5b..02483586b34 100644
--- a/ReactAndroid/src/main/java/com/facebook/react/devsupport/ReactInstanceManagerDevHelper.java
+++ b/ReactAndroid/src/main/java/com/facebook/react/devsupport/ReactInstanceManagerDevHelper.java
@@ -8,6 +8,7 @@
package com.facebook.react.devsupport;
import android.app.Activity;
+import android.view.View;
import androidx.annotation.Nullable;
import com.facebook.react.bridge.JavaJSExecutor;
import com.facebook.react.bridge.JavaScriptExecutorFactory;
@@ -32,4 +33,9 @@ public interface ReactInstanceManagerDevHelper {
Activity getCurrentActivity();
JavaScriptExecutorFactory getJavaScriptExecutorFactory();
+
+ @Nullable
+ View createRootView(String appKey);
+
+ void destroyRootView(View rootView);
}
diff --git a/ReactAndroid/src/main/java/com/facebook/react/devsupport/interfaces/DevSupportManager.java b/ReactAndroid/src/main/java/com/facebook/react/devsupport/interfaces/DevSupportManager.java
index ac04bc6a6dd..43a477e3fad 100644
--- a/ReactAndroid/src/main/java/com/facebook/react/devsupport/interfaces/DevSupportManager.java
+++ b/ReactAndroid/src/main/java/com/facebook/react/devsupport/interfaces/DevSupportManager.java
@@ -7,6 +7,7 @@
package com.facebook.react.devsupport.interfaces;
+import android.view.View;
import androidx.annotation.Nullable;
import com.facebook.react.bridge.NativeModuleCallExceptionHandler;
import com.facebook.react.bridge.ReactContext;
@@ -25,6 +26,11 @@ public interface DevSupportManager extends NativeModuleCallExceptionHandler {
void addCustomDevOption(String optionName, DevOptionHandler optionHandler);
+ @Nullable
+ View createRootView(String appKey);
+
+ void destroyRootView(View rootView);
+
void showNewJSError(String message, ReadableArray details, int errorCookie);
void updateJSError(final String message, final ReadableArray details, final int errorCookie);
diff --git a/ReactAndroid/src/main/res/devsupport/values/colors.xml b/ReactAndroid/src/main/res/devsupport/values/colors.xml
index d15ee8caf7b..8ac1a132df0 100644
--- a/ReactAndroid/src/main/res/devsupport/values/colors.xml
+++ b/ReactAndroid/src/main/res/devsupport/values/colors.xml
@@ -1,4 +1,5 @@
#eecc0000
+ #ffffffff
diff --git a/ReactAndroid/src/main/res/devsupport/values/styles.xml b/ReactAndroid/src/main/res/devsupport/values/styles.xml
index e9f462c5359..cb88e438c91 100644
--- a/ReactAndroid/src/main/res/devsupport/values/styles.xml
+++ b/ReactAndroid/src/main/res/devsupport/values/styles.xml
@@ -9,10 +9,22 @@
- @android:anim/fade_out
- @android:color/white
+
+