From fc7eb91f56e586890b479db315c6d903851c794a Mon Sep 17 00:00:00 2001 From: Xin Chen Date: Wed, 9 Feb 2022 15:13:42 -0800 Subject: [PATCH] Add RedBoxSurfaceDelegate to DevSupportManagerBase to abstract surface logic to show RedBox Summary: This diff adds `RedBoxSurfaceDelegate` to replace existing logic in `DevSupportManagerBase` to abstract how we show/hide the RedBox surface. The delegate will wrap a RedBoxDialog instance, which is used to show/hide the dialog for default behavior (when there is no surface delegate for redbox got provided). I also updated the interface for delegate to accomodate new use cases: - Add `isShowing` for the `SurfaceDelegate` - Add a list of getters for `DevSupportManager` for data access in the delegate - (Update 2/7) Separate Dialog from `RedBoxDialog`, and re-named it to `RedBoxContentView`. This is to make it clear that the delegate is responsible to provide actual surface implementation (Dialog). The content view is meant to be shared. Changelog: [Android][Internal] Reviewed By: javache Differential Revision: D33987835 fbshipit-source-id: 57c20648e7f2ec8238963feca27ccd5518e7931d --- .../react/common/SurfaceDelegate.java | 3 + .../devsupport/DevSupportManagerBase.java | 87 ++++++------- .../devsupport/DisabledDevSupportManager.java | 17 +++ .../devsupport/DoubleTapReloadRecognizer.java | 2 +- .../LogBoxDialogSurfaceDelegate.java | 9 +- ...dBoxDialog.java => RedBoxContentView.java} | 104 +++++++--------- .../RedBoxDialogSurfaceDelegate.java | 117 ++++++++++++++++++ .../interfaces/DevSupportManager.java | 7 ++ 8 files changed, 232 insertions(+), 114 deletions(-) rename ReactAndroid/src/main/java/com/facebook/react/devsupport/{RedBoxDialog.java => RedBoxContentView.java} (80%) create mode 100644 ReactAndroid/src/main/java/com/facebook/react/devsupport/RedBoxDialogSurfaceDelegate.java diff --git a/ReactAndroid/src/main/java/com/facebook/react/common/SurfaceDelegate.java b/ReactAndroid/src/main/java/com/facebook/react/common/SurfaceDelegate.java index 821de3f3a3f..a6ba792926e 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/common/SurfaceDelegate.java +++ b/ReactAndroid/src/main/java/com/facebook/react/common/SurfaceDelegate.java @@ -37,4 +37,7 @@ public interface SurfaceDelegate { /** Hide the surface containing the React content view */ void hide(); + + /** Check if the surface is currently showing */ + boolean isShowing(); } diff --git a/ReactAndroid/src/main/java/com/facebook/react/devsupport/DevSupportManagerBase.java b/ReactAndroid/src/main/java/com/facebook/react/devsupport/DevSupportManagerBase.java index ab96ecfdfdd..7b1ba63c521 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/devsupport/DevSupportManagerBase.java +++ b/ReactAndroid/src/main/java/com/facebook/react/devsupport/DevSupportManagerBase.java @@ -97,7 +97,7 @@ public abstract class DevSupportManagerBase implements DevSupportManager { private final DefaultNativeModuleCallExceptionHandler mDefaultNativeModuleCallExceptionHandler; private final DevLoadingViewController mDevLoadingViewController; - private @Nullable RedBoxDialog mRedBoxDialog; + private @Nullable SurfaceDelegate mRedBoxSurfaceDelegate; private @Nullable AlertDialog mDevOptionsDialog; private @Nullable DebugOverlayController mDebugOverlayController; private boolean mDevLoadingViewVisible = false; @@ -273,7 +273,8 @@ public abstract class DevSupportManagerBase implements DevSupportManager { mErrorCustomizers.add(errorCustomizer); } - private Pair processErrorCustomizers(Pair errorInfo) { + @Override + public Pair processErrorCustomizers(Pair errorInfo) { if (mErrorCustomizers == null) { return errorInfo; } else { @@ -297,33 +298,25 @@ public abstract class DevSupportManagerBase implements DevSupportManager { // Since we only show the first JS error in a succession of JS errors, make sure we only // update the error message for that error message. This assumes that updateJSError // belongs to the most recent showNewJSError - if (mRedBoxDialog == null - || !mRedBoxDialog.isShowing() - || errorCookie != mLastErrorCookie) { + if (!mRedBoxSurfaceDelegate.isShowing() || errorCookie != mLastErrorCookie) { return; } - StackFrame[] stack = StackTraceHelper.convertJsStackTrace(details); - Pair errorInfo = - processErrorCustomizers(Pair.create(message, stack)); - mRedBoxDialog.setExceptionDetails(errorInfo.first, errorInfo.second); - updateLastErrorInfo(message, stack, errorCookie, ErrorType.JS); - // JS errors are reported here after source mapping. - if (mRedBoxHandler != null) { - mRedBoxHandler.handleRedbox(message, stack, ErrorType.JS); - mRedBoxDialog.resetReporting(); - } - mRedBoxDialog.show(); + + // The RedBox surface delegate will always show the latest error + updateLastErrorInfo( + message, StackTraceHelper.convertJsStackTrace(details), errorCookie, ErrorType.JS); + mRedBoxSurfaceDelegate.show(); } }); } @Override public void hideRedboxDialog() { - // dismiss redbox if exists - if (mRedBoxDialog != null) { - mRedBoxDialog.dismiss(); - mRedBoxDialog = null; + if (mRedBoxSurfaceDelegate == null) { + return; } + + mRedBoxSurfaceDelegate.hide(); } public @Nullable View createRootView(String appKey) { @@ -350,41 +343,27 @@ public abstract class DevSupportManagerBase implements DevSupportManager { new Runnable() { @Override public void run() { - Activity context = mReactInstanceDevHelper.getCurrentActivity(); - if (context != null && !context.isFinishing() && currentActivity != context) { - currentActivity = context; - // Create a new RedBox when currentActivity get updated - mRedBoxDialog = - new RedBoxDialog(currentActivity, DevSupportManagerBase.this, mRedBoxHandler); + if (mRedBoxSurfaceDelegate == null) { + @Nullable SurfaceDelegate redBoxSurfaceDelegate = createSurfaceDelegate("RedBox"); + if (redBoxSurfaceDelegate != null) { + mRedBoxSurfaceDelegate = redBoxSurfaceDelegate; + } else { + mRedBoxSurfaceDelegate = + new RedBoxDialogSurfaceDelegate(DevSupportManagerBase.this); + } + + mRedBoxSurfaceDelegate.createContentView("RedBox"); } - if (currentActivity == null || currentActivity.isFinishing()) { - FLog.e( - ReactConstants.TAG, - "Unable to launch redbox because react activity " - + "is not available, here is the error that redbox would've displayed: " - + message); - return; - } - if (mRedBoxDialog == null) { - mRedBoxDialog = - new RedBoxDialog(currentActivity, DevSupportManagerBase.this, mRedBoxHandler); - } - if (mRedBoxDialog.isShowing()) { + + if (mRedBoxSurfaceDelegate.isShowing()) { // Sometimes errors cause multiple errors to be thrown in JS in quick succession. Only // show the first and most actionable one. return; } - Pair errorInfo = - processErrorCustomizers(Pair.create(message, stack)); - mRedBoxDialog.setExceptionDetails(errorInfo.first, errorInfo.second); + + // The RedBox surface delegate will always show the latest error updateLastErrorInfo(message, stack, errorCookie, errorType); - // Only report native errors here. JS errors are reported - // inside {@link #updateJSError} after source mapping. - if (mRedBoxHandler != null && errorType == ErrorType.NATIVE) { - mRedBoxHandler.handleRedbox(message, stack, ErrorType.NATIVE); - } - mRedBoxDialog.resetReporting(); - mRedBoxDialog.show(); + mRedBoxSurfaceDelegate.show(); } }); } @@ -623,6 +602,11 @@ public abstract class DevSupportManagerBase implements DevSupportManager { return mDevSettings; } + @Override + public RedBoxHandler getRedBoxHandler() { + return mRedBoxHandler; + } + @Override public void onNewReactContextCreated(ReactContext reactContext) { resetCurrentContext(reactContext); @@ -888,6 +872,11 @@ public abstract class DevSupportManagerBase implements DevSupportManager { return mLastErrorStack; } + @Override + public int getLastErrorCookie() { + return mLastErrorCookie; + } + @Override public @Nullable ErrorType getLastErrorType() { return mLastErrorType; 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 c198ac1610b..f44cdeb332e 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/devsupport/DisabledDevSupportManager.java +++ b/ReactAndroid/src/main/java/com/facebook/react/devsupport/DisabledDevSupportManager.java @@ -8,6 +8,7 @@ package com.facebook.react.devsupport; import android.app.Activity; +import android.util.Pair; import android.view.View; import androidx.annotation.Nullable; import com.facebook.react.bridge.DefaultNativeModuleCallExceptionHandler; @@ -21,6 +22,7 @@ import com.facebook.react.devsupport.interfaces.DevSupportManager; import com.facebook.react.devsupport.interfaces.ErrorCustomizer; import com.facebook.react.devsupport.interfaces.ErrorType; import com.facebook.react.devsupport.interfaces.PackagerStatusCallback; +import com.facebook.react.devsupport.interfaces.RedBoxHandler; import com.facebook.react.devsupport.interfaces.StackFrame; import com.facebook.react.modules.debug.interfaces.DeveloperSettings; import java.io.File; @@ -94,6 +96,11 @@ public class DisabledDevSupportManager implements DevSupportManager { return null; } + @Override + public RedBoxHandler getRedBoxHandler() { + return null; + } + @Override public void onNewReactContextCreated(ReactContext reactContext) {} @@ -166,9 +173,19 @@ public class DisabledDevSupportManager implements DevSupportManager { return null; } + @Override + public int getLastErrorCookie() { + return 0; + } + @Override public void registerErrorCustomizer(ErrorCustomizer errorCustomizer) {} + @Override + public Pair processErrorCustomizers(Pair errorInfo) { + return errorInfo; + } + @Override public void setPackagerLocationCustomizer( DevSupportManager.PackagerLocationCustomizer packagerLocationCustomizer) {} diff --git a/ReactAndroid/src/main/java/com/facebook/react/devsupport/DoubleTapReloadRecognizer.java b/ReactAndroid/src/main/java/com/facebook/react/devsupport/DoubleTapReloadRecognizer.java index b178267dd0b..c54335e9720 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/devsupport/DoubleTapReloadRecognizer.java +++ b/ReactAndroid/src/main/java/com/facebook/react/devsupport/DoubleTapReloadRecognizer.java @@ -14,7 +14,7 @@ import android.widget.EditText; /** * A class allows recognizing double key tap of "R", used to reload JS in {@link - * AbstractReactActivity}, {@link RedBoxDialog} and {@link ReactActivity}. + * AbstractReactActivity}, {@link RedBoxDialogSurfaceDelegate} and {@link ReactActivity}. */ public class DoubleTapReloadRecognizer { private boolean mDoRefresh = false; diff --git a/ReactAndroid/src/main/java/com/facebook/react/devsupport/LogBoxDialogSurfaceDelegate.java b/ReactAndroid/src/main/java/com/facebook/react/devsupport/LogBoxDialogSurfaceDelegate.java index c9668d29f80..ed465a18383 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/devsupport/LogBoxDialogSurfaceDelegate.java +++ b/ReactAndroid/src/main/java/com/facebook/react/devsupport/LogBoxDialogSurfaceDelegate.java @@ -55,7 +55,7 @@ public class LogBoxDialogSurfaceDelegate implements SurfaceDelegate { @Override public void show() { - if (isSurfaceVisible() || !isContentViewReady()) { + if (isShowing() || !isContentViewReady()) { return; } @@ -74,7 +74,7 @@ public class LogBoxDialogSurfaceDelegate implements SurfaceDelegate { @Override public void hide() { - if (!isSurfaceVisible()) { + if (!isShowing()) { return; } @@ -86,7 +86,8 @@ public class LogBoxDialogSurfaceDelegate implements SurfaceDelegate { mDialog = null; } - private boolean isSurfaceVisible() { - return mDialog != null; + @Override + public boolean isShowing() { + return mDialog != null && mDialog.isShowing(); } } diff --git a/ReactAndroid/src/main/java/com/facebook/react/devsupport/RedBoxDialog.java b/ReactAndroid/src/main/java/com/facebook/react/devsupport/RedBoxContentView.java similarity index 80% rename from ReactAndroid/src/main/java/com/facebook/react/devsupport/RedBoxDialog.java rename to ReactAndroid/src/main/java/com/facebook/react/devsupport/RedBoxContentView.java index b4827f6bb28..2232a2bd0b3 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/devsupport/RedBoxDialog.java +++ b/ReactAndroid/src/main/java/com/facebook/react/devsupport/RedBoxContentView.java @@ -7,21 +7,20 @@ package com.facebook.react.devsupport; -import android.app.Dialog; import android.content.Context; import android.graphics.Color; import android.net.Uri; import android.os.AsyncTask; import android.text.SpannedString; import android.text.method.LinkMovementMethod; -import android.view.KeyEvent; +import android.util.Pair; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; -import android.view.Window; import android.widget.AdapterView; import android.widget.BaseAdapter; import android.widget.Button; +import android.widget.LinearLayout; import android.widget.ListView; import android.widget.ProgressBar; import android.widget.TextView; @@ -32,6 +31,7 @@ import com.facebook.react.R; import com.facebook.react.common.MapBuilder; import com.facebook.react.common.ReactConstants; import com.facebook.react.devsupport.interfaces.DevSupportManager; +import com.facebook.react.devsupport.interfaces.ErrorType; import com.facebook.react.devsupport.interfaces.RedBoxHandler; import com.facebook.react.devsupport.interfaces.RedBoxHandler.ReportCompletedListener; import com.facebook.react.devsupport.interfaces.StackFrame; @@ -42,15 +42,10 @@ import okhttp3.RequestBody; import org.json.JSONObject; /** Dialog for displaying JS errors in an eye-catching form (red box). */ -/* package */ class RedBoxDialog implements AdapterView.OnItemClickListener { +public class RedBoxContentView extends LinearLayout implements AdapterView.OnItemClickListener { - private final DevSupportManager mDevSupportManager; - private final DoubleTapReloadRecognizer mDoubleTapReloadRecognizer; - private final @Nullable RedBoxHandler mRedBoxHandler; - private final View mContentView; - private final Context mContext; - - private @Nullable Dialog mDialog; + private @Nullable RedBoxHandler mRedBoxHandler; + private DevSupportManager mDevSupportManager; private ListView mStackView; private Button mReloadJsButton; private Button mDismissButton; @@ -239,42 +234,50 @@ import org.json.JSONObject; } } - protected RedBoxDialog( - Context context, DevSupportManager devSupportManager, @Nullable RedBoxHandler redBoxHandler) { - mContext = context; - mContentView = (View) LayoutInflater.from(context).inflate(R.layout.redbox_view, null); + public RedBoxContentView(Context context) { + super(context); + } + public RedBoxContentView setDevSupportManager(DevSupportManager devSupportManager) { mDevSupportManager = devSupportManager; - mDoubleTapReloadRecognizer = new DoubleTapReloadRecognizer(); - mRedBoxHandler = redBoxHandler; + return this; + } - mStackView = (ListView) mContentView.findViewById(R.id.rn_redbox_stack); + public RedBoxContentView setRedBoxHandler(@Nullable RedBoxHandler redBoxHandler) { + mRedBoxHandler = redBoxHandler; + return this; + } + + public void init() { + LayoutInflater.from(getContext()).inflate(R.layout.redbox_view, this); + + mStackView = (ListView) findViewById(R.id.rn_redbox_stack); mStackView.setOnItemClickListener(this); - mReloadJsButton = (Button) mContentView.findViewById(R.id.rn_redbox_reload_button); + mReloadJsButton = (Button) findViewById(R.id.rn_redbox_reload_button); mReloadJsButton.setOnClickListener( new View.OnClickListener() { @Override public void onClick(View v) { - mDevSupportManager.handleReloadJS(); + Assertions.assertNotNull(mDevSupportManager).handleReloadJS(); } }); - mDismissButton = (Button) mContentView.findViewById(R.id.rn_redbox_dismiss_button); + mDismissButton = (Button) findViewById(R.id.rn_redbox_dismiss_button); mDismissButton.setOnClickListener( new View.OnClickListener() { @Override public void onClick(View v) { - dismiss(); + Assertions.assertNotNull(mDevSupportManager).hideRedboxDialog(); } }); if (mRedBoxHandler != null && mRedBoxHandler.isReportEnabled()) { - mLoadingIndicator = (ProgressBar) mContentView.findViewById(R.id.rn_redbox_loading_indicator); - mLineSeparator = (View) mContentView.findViewById(R.id.rn_redbox_line_separator); - mReportTextView = (TextView) mContentView.findViewById(R.id.rn_redbox_report_label); + mLoadingIndicator = (ProgressBar) findViewById(R.id.rn_redbox_loading_indicator); + mLineSeparator = (View) findViewById(R.id.rn_redbox_line_separator); + mReportTextView = (TextView) findViewById(R.id.rn_redbox_report_label); mReportTextView.setMovementMethod(LinkMovementMethod.getInstance()); mReportTextView.setHighlightColor(Color.TRANSPARENT); - mReportButton = (Button) mContentView.findViewById(R.id.rn_redbox_report_button); + mReportButton = (Button) findViewById(R.id.rn_redbox_report_button); mReportButton.setOnClickListener(mReportButtonOnClickListener); } } @@ -298,44 +301,25 @@ import org.json.JSONObject; @Override public void onItemClick(AdapterView parent, View view, int position, long id) { - new OpenStackFrameTask(mDevSupportManager) + new OpenStackFrameTask(Assertions.assertNotNull(mDevSupportManager)) .executeOnExecutor( AsyncTask.THREAD_POOL_EXECUTOR, (StackFrame) mStackView.getAdapter().getItem(position)); } - public boolean onKeyUp(int keyCode, KeyEvent event) { - if (keyCode == KeyEvent.KEYCODE_MENU) { - mDevSupportManager.showDevOptionsDialog(); - return true; + /** Refresh the content view with latest errors from dev support manager */ + public void refreshContentView() { + @Nullable String message = mDevSupportManager.getLastErrorTitle(); + @Nullable StackFrame[] stack = mDevSupportManager.getLastErrorStack(); + @Nullable ErrorType errorType = mDevSupportManager.getLastErrorType(); + Pair errorInfo = + mDevSupportManager.processErrorCustomizers(Pair.create(message, stack)); + setExceptionDetails(errorInfo.first, errorInfo.second); + + // JS errors are reported here after source mapping. + RedBoxHandler redBoxHandler = mDevSupportManager.getRedBoxHandler(); + if (redBoxHandler != null) { + redBoxHandler.handleRedbox(message, stack, errorType); + resetReporting(); } - if (mDoubleTapReloadRecognizer.didDoubleTapR(keyCode, mDialog.getCurrentFocus())) { - mDevSupportManager.handleReloadJS(); - } - return mDialog.onKeyUp(keyCode, event); - } - - public Context getContext() { - return mContext; - } - - public View getContentView() { - return mContentView; - } - - public boolean isShowing() { - return mDialog.isShowing(); - } - - public void show() { - if (mDialog == null) { - mDialog = new Dialog(mContext, R.style.Theme_Catalyst_RedBox); - mDialog.requestWindowFeature(Window.FEATURE_NO_TITLE); - } - mDialog.setContentView(mContentView); - mDialog.show(); - } - - public void dismiss() { - mDialog.dismiss(); } } diff --git a/ReactAndroid/src/main/java/com/facebook/react/devsupport/RedBoxDialogSurfaceDelegate.java b/ReactAndroid/src/main/java/com/facebook/react/devsupport/RedBoxDialogSurfaceDelegate.java new file mode 100644 index 00000000000..97ef306afa8 --- /dev/null +++ b/ReactAndroid/src/main/java/com/facebook/react/devsupport/RedBoxDialogSurfaceDelegate.java @@ -0,0 +1,117 @@ +/* + * 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 android.app.Activity; +import android.app.Dialog; +import android.view.KeyEvent; +import android.view.Window; +import androidx.annotation.Nullable; +import com.facebook.common.logging.FLog; +import com.facebook.react.R; +import com.facebook.react.common.ReactConstants; +import com.facebook.react.common.SurfaceDelegate; +import com.facebook.react.devsupport.interfaces.DevSupportManager; +import com.facebook.react.devsupport.interfaces.RedBoxHandler; + +/** + * The implementation of SurfaceDelegate with {@link Activity}. This is the default SurfaceDelegate + * for Mobile. + */ +public class RedBoxDialogSurfaceDelegate implements SurfaceDelegate { + + private final DoubleTapReloadRecognizer mDoubleTapReloadRecognizer; + private final DevSupportManager mDevSupportManager; + + private @Nullable Dialog mDialog; + private @Nullable RedBoxContentView mRedBoxContentView; + + public RedBoxDialogSurfaceDelegate(DevSupportManager devSupportManager) { + mDevSupportManager = devSupportManager; + mDoubleTapReloadRecognizer = new DoubleTapReloadRecognizer(); + } + + @Override + public void createContentView(String appKey) { + // The content view is created in android instead of using react app. Hence the appKey is not + // used here. + RedBoxHandler redBoxHandler = mDevSupportManager.getRedBoxHandler(); + Activity context = mDevSupportManager.getCurrentActivity(); + // Create a new RedBox when currentActivity get updated + mRedBoxContentView = new RedBoxContentView(context); + mRedBoxContentView + .setDevSupportManager(mDevSupportManager) + .setRedBoxHandler(redBoxHandler) + .init(); + } + + @Override + public boolean isContentViewReady() { + return mRedBoxContentView != null; + } + + @Override + public void destroyContentView() { + mRedBoxContentView = null; + } + + @Override + public void show() { + @Nullable String message = mDevSupportManager.getLastErrorTitle(); + Activity context = mDevSupportManager.getCurrentActivity(); + if (context == null || context.isFinishing()) { + FLog.e( + ReactConstants.TAG, + "Unable to launch redbox because react activity " + + "is not available, here is the error that redbox would've displayed: " + + (message != null ? message : "N/A")); + return; + } + + if (mRedBoxContentView == null || mRedBoxContentView.getContext() != context) { + // Create a new RedBox when currentActivity get updated + createContentView("RedBox"); + } + + mRedBoxContentView.refreshContentView(); + if (mDialog == null) { + mDialog = + new Dialog(context, R.style.Theme_Catalyst_RedBox) { + @Override + public boolean onKeyUp(int keyCode, KeyEvent event) { + if (keyCode == KeyEvent.KEYCODE_MENU) { + mDevSupportManager.showDevOptionsDialog(); + return true; + } + if (mDoubleTapReloadRecognizer.didDoubleTapR(keyCode, getCurrentFocus())) { + mDevSupportManager.handleReloadJS(); + } + return super.onKeyUp(keyCode, event); + } + }; + mDialog.requestWindowFeature(Window.FEATURE_NO_TITLE); + mDialog.setContentView(mRedBoxContentView); + } + mDialog.show(); + } + + @Override + public void hide() { + // dismiss redbox if exists + if (mDialog != null) { + mDialog.dismiss(); + destroyContentView(); + mDialog = null; + } + } + + @Override + public boolean isShowing() { + return mDialog != null && mDialog.isShowing(); + } +} 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 f830561f677..40079b1e55e 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 @@ -8,6 +8,7 @@ package com.facebook.react.devsupport.interfaces; import android.app.Activity; +import android.util.Pair; import android.view.View; import androidx.annotation.Nullable; import com.facebook.react.bridge.NativeModuleCallExceptionHandler; @@ -51,6 +52,8 @@ public interface DevSupportManager extends NativeModuleCallExceptionHandler { DeveloperSettings getDevSettings(); + RedBoxHandler getRedBoxHandler(); + void onNewReactContextCreated(ReactContext reactContext); void onReactInstanceDestroyed(ReactContext reactContext); @@ -97,8 +100,12 @@ public interface DevSupportManager extends NativeModuleCallExceptionHandler { @Nullable ErrorType getLastErrorType(); + int getLastErrorCookie(); + void registerErrorCustomizer(ErrorCustomizer errorCustomizer); + Pair processErrorCustomizers(Pair errorInfo); + /** * The PackagerLocationCustomizer allows you to have a dynamic packager location that is * determined right before loading the packager. Your customizer must call |callback|, as loading