diff --git a/ReactAndroid/src/main/java/com/facebook/react/ReactInstanceManager.java b/ReactAndroid/src/main/java/com/facebook/react/ReactInstanceManager.java index 559ebb01a234f7..63b237f25f570b 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/ReactInstanceManager.java +++ b/ReactAndroid/src/main/java/com/facebook/react/ReactInstanceManager.java @@ -76,6 +76,7 @@ import com.facebook.react.bridge.queue.ReactQueueConfigurationSpec; import com.facebook.react.common.LifecycleState; import com.facebook.react.common.ReactConstants; +import com.facebook.react.common.SurfaceDelegateFactory; import com.facebook.react.common.annotations.VisibleForTesting; import com.facebook.react.config.ReactFeatureFlags; import com.facebook.react.devsupport.DevSupportManagerFactory; @@ -232,7 +233,8 @@ public static ReactInstanceManagerBuilder builder() { int minTimeLeftInFrameForNonBatchedOperationMs, @Nullable JSIModulePackage jsiModulePackage, @Nullable Map customPackagerCommandHandlers, - @Nullable ReactPackageTurboModuleManagerDelegate.Builder tmmDelegateBuilder) { + @Nullable ReactPackageTurboModuleManagerDelegate.Builder tmmDelegateBuilder, + @Nullable SurfaceDelegateFactory surfaceDelegateFactory) { FLog.d(TAG, "ReactInstanceManager.ctor()"); initializeSoLoaderIfNecessary(applicationContext); @@ -259,7 +261,8 @@ public static ReactInstanceManagerBuilder builder() { redBoxHandler, devBundleDownloadListener, minNumShakes, - customPackagerCommandHandlers); + customPackagerCommandHandlers, + surfaceDelegateFactory); Systrace.endSection(TRACE_TAG_REACT_JAVA_BRIDGE); mBridgeIdleDebugListener = bridgeIdleDebugListener; mLifecycleState = initialLifecycleState; diff --git a/ReactAndroid/src/main/java/com/facebook/react/ReactInstanceManagerBuilder.java b/ReactAndroid/src/main/java/com/facebook/react/ReactInstanceManagerBuilder.java index 0064ca021c878f..ddfb5507ec4b47 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/ReactInstanceManagerBuilder.java +++ b/ReactAndroid/src/main/java/com/facebook/react/ReactInstanceManagerBuilder.java @@ -22,6 +22,7 @@ import com.facebook.react.bridge.NativeModuleCallExceptionHandler; import com.facebook.react.bridge.NotThreadSafeBridgeIdleDebugListener; import com.facebook.react.common.LifecycleState; +import com.facebook.react.common.SurfaceDelegateFactory; import com.facebook.react.devsupport.DefaultDevSupportManagerFactory; import com.facebook.react.devsupport.DevSupportManagerFactory; import com.facebook.react.devsupport.RedBoxHandler; @@ -63,6 +64,7 @@ public class ReactInstanceManagerBuilder { private @Nullable JSIModulePackage mJSIModulesPackage; private @Nullable Map mCustomPackagerCommandHandlers; private @Nullable ReactPackageTurboModuleManagerDelegate.Builder mTMMDelegateBuilder; + private @Nullable SurfaceDelegateFactory mSurfaceDelegateFactory; /* package protected */ ReactInstanceManagerBuilder() {} @@ -195,6 +197,20 @@ public ReactInstanceManagerBuilder setRequireActivity(boolean requireActivity) { return this; } + /** + * When the {@link SurfaceDelegateFactory} is provided, it will be used for native modules to get + * a {@link SurfaceDelegate} to interact with the platform specific surface that they that needs + * to be rendered in. For mobile platform this is default to be null so that these modules will + * need to provide a default surface delegate. One example of such native module is LogBoxModule, + * which is rendered in mobile platform with LogBoxDialog, while in VR platform with custom layer + * provided by runtime. + */ + public ReactInstanceManagerBuilder setSurfaceDelegateFactory( + @Nullable final SurfaceDelegateFactory surfaceDelegateFactory) { + mSurfaceDelegateFactory = surfaceDelegateFactory; + return this; + } + /** * Sets the initial lifecycle state of the host. For example, if the host is already resumed at * creation time, we wouldn't expect an onResume call until we get an onPause call. @@ -322,7 +338,8 @@ public ReactInstanceManager build() { mMinTimeLeftInFrameForNonBatchedOperationMs, mJSIModulesPackage, mCustomPackagerCommandHandlers, - mTMMDelegateBuilder); + mTMMDelegateBuilder, + mSurfaceDelegateFactory); } private JavaScriptExecutorFactory getDefaultJSExecutorFactory( diff --git a/ReactAndroid/src/main/java/com/facebook/react/ReactNativeHost.java b/ReactAndroid/src/main/java/com/facebook/react/ReactNativeHost.java index 68b1f3e367f644..5ee746a4f6cf57 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/ReactNativeHost.java +++ b/ReactAndroid/src/main/java/com/facebook/react/ReactNativeHost.java @@ -15,6 +15,8 @@ import com.facebook.react.bridge.ReactMarker; import com.facebook.react.bridge.ReactMarkerConstants; import com.facebook.react.common.LifecycleState; +import com.facebook.react.common.SurfaceDelegate; +import com.facebook.react.common.SurfaceDelegateFactory; import com.facebook.react.devsupport.DevSupportManagerFactory; import com.facebook.react.devsupport.RedBoxHandler; import com.facebook.react.uimanager.UIImplementationProvider; @@ -71,6 +73,7 @@ protected ReactInstanceManager createReactInstanceManager() { .setUseDeveloperSupport(getUseDeveloperSupport()) .setDevSupportManagerFactory(getDevSupportManagerFactory()) .setRequireActivity(getShouldRequireActivity()) + .setSurfaceDelegateFactory(getSurfaceDelegateFactory()) .setRedBoxHandler(getRedBoxHandler()) .setJavaScriptExecutorFactory(getJavaScriptExecutorFactory()) .setUIImplementationProvider(getUIImplementationProvider()) @@ -132,6 +135,21 @@ public boolean getShouldRequireActivity() { return true; } + /** + * Return the {@link SurfaceDelegateFactory} used by NativeModules to get access to a {@link + * SurfaceDelegate} to interact with a surface. By default in the mobile platform the {@link + * SurfaceDelegate} it returns is null, and the NativeModule needs to implement its own {@link + * SurfaceDelegate} to decide how it would interact with its own container surface. + */ + public SurfaceDelegateFactory getSurfaceDelegateFactory() { + return new SurfaceDelegateFactory() { + @Override + public @Nullable SurfaceDelegate createSurfaceDelegate(String moduleName) { + return null; + } + }; + } + /** * Returns the name of the main module. Determines the URL used to fetch the JS bundle from Metro. * It is only used when dev support is enabled. This is the first file to be executed once the diff --git a/ReactAndroid/src/main/java/com/facebook/react/common/SurfaceDelegate.java b/ReactAndroid/src/main/java/com/facebook/react/common/SurfaceDelegate.java new file mode 100644 index 00000000000000..91e24105e1f37c --- /dev/null +++ b/ReactAndroid/src/main/java/com/facebook/react/common/SurfaceDelegate.java @@ -0,0 +1,40 @@ +/* + * 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.common; + +/** + * Interface for handling a surface in React Native. In mobile platform a surface can be any + * container that holds some {@link View}. For example, a Dialog can be a surface to wrap content + * view object as needed. In VR platform, a surface is provided by Shell panel app sdk, which + * requires custom logic to show/hide. NativeModules requires a surface will delegate interactions + * with the surface to a SurfaceDelegate. + */ +public interface SurfaceDelegate { + /** + * Create the React content view that uses the appKey as the React application name + * + * @param appKey + */ + void createContentView(String appKey); + + /** + * Check if the content view is created and ready to be shown + * + * @return true if the content view is ready to be shown + */ + boolean isContentViewReady(); + + /** Destroy the React content view to avoid memory leak */ + void destroyContentView(); + + /** Show the surface containing the React content view */ + void show(); + + /** Hide the surface containing the React content view */ + void hide(); +} diff --git a/ReactAndroid/src/main/java/com/facebook/react/common/SurfaceDelegateFactory.java b/ReactAndroid/src/main/java/com/facebook/react/common/SurfaceDelegateFactory.java new file mode 100644 index 00000000000000..938f4b1e3e30d4 --- /dev/null +++ b/ReactAndroid/src/main/java/com/facebook/react/common/SurfaceDelegateFactory.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.common; + +/** + * Factory to create a {@link SurfaceDelegate}. The moduleName is needed to help the factory decide + * which surface to return {@link SurfaceDelegate} that the given module should use to interact + * with. + */ +public interface SurfaceDelegateFactory { + /** + * Create a {@link SurfaceDelegate} instance which is used to interact with a surface of platform + * the app is running in. + * + * @param moduleName the module name that will be using the surface + * @return {@link SurfaceDelegate} instance + */ + SurfaceDelegate createSurfaceDelegate(String moduleName); +} diff --git a/ReactAndroid/src/main/java/com/facebook/react/devsupport/BUCK b/ReactAndroid/src/main/java/com/facebook/react/devsupport/BUCK index 88de5f78c8741d..f47c5129460f14 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/devsupport/BUCK +++ b/ReactAndroid/src/main/java/com/facebook/react/devsupport/BUCK @@ -57,6 +57,7 @@ rn_android_library( react_native_dep("third-party/android/androidx:annotation"), react_native_dep("third-party/java/jsr-305:jsr-305"), react_native_target("java/com/facebook/react/bridge:bridge"), + react_native_target("java/com/facebook/react/common:common"), react_native_target("java/com/facebook/react/modules/debug:interfaces"), ], ) diff --git a/ReactAndroid/src/main/java/com/facebook/react/devsupport/BridgeDevSupportManager.java b/ReactAndroid/src/main/java/com/facebook/react/devsupport/BridgeDevSupportManager.java index 0efbec7cf4c2c9..30c0840f90dfb8 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/devsupport/BridgeDevSupportManager.java +++ b/ReactAndroid/src/main/java/com/facebook/react/devsupport/BridgeDevSupportManager.java @@ -22,6 +22,7 @@ import com.facebook.react.bridge.ReactMarkerConstants; import com.facebook.react.bridge.UiThreadUtil; import com.facebook.react.common.ReactConstants; +import com.facebook.react.common.SurfaceDelegateFactory; import com.facebook.react.common.futures.SimpleSettableFuture; import com.facebook.react.devsupport.interfaces.DevBundleDownloadListener; import com.facebook.react.devsupport.interfaces.DevOptionHandler; @@ -71,7 +72,8 @@ public BridgeDevSupportManager( @Nullable RedBoxHandler redBoxHandler, @Nullable DevBundleDownloadListener devBundleDownloadListener, int minNumShakes, - @Nullable Map customPackagerCommandHandlers) { + @Nullable Map customPackagerCommandHandlers, + @Nullable SurfaceDelegateFactory surfaceDelegateFactory) { super( applicationContext, reactInstanceManagerHelper, @@ -80,7 +82,8 @@ public BridgeDevSupportManager( redBoxHandler, devBundleDownloadListener, minNumShakes, - customPackagerCommandHandlers); + customPackagerCommandHandlers, + surfaceDelegateFactory); if (getDevSettings().isStartSamplingProfilerOnInit()) { // Only start the profiler. If its already running, there is an error diff --git a/ReactAndroid/src/main/java/com/facebook/react/devsupport/DefaultDevSupportManagerFactory.java b/ReactAndroid/src/main/java/com/facebook/react/devsupport/DefaultDevSupportManagerFactory.java index 3b0233ace205b4..0ef0ca065557f9 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/devsupport/DefaultDevSupportManagerFactory.java +++ b/ReactAndroid/src/main/java/com/facebook/react/devsupport/DefaultDevSupportManagerFactory.java @@ -9,6 +9,7 @@ import android.content.Context; import androidx.annotation.Nullable; +import com.facebook.react.common.SurfaceDelegateFactory; import com.facebook.react.devsupport.interfaces.DevBundleDownloadListener; import com.facebook.react.devsupport.interfaces.DevSupportManager; import com.facebook.react.packagerconnection.RequestHandler; @@ -41,6 +42,7 @@ public DevSupportManager create( null, null, minNumShakes, + null, null); } @@ -53,7 +55,8 @@ public DevSupportManager create( @Nullable RedBoxHandler redBoxHandler, @Nullable DevBundleDownloadListener devBundleDownloadListener, int minNumShakes, - @Nullable Map customPackagerCommandHandlers) { + @Nullable Map customPackagerCommandHandlers, + @Nullable SurfaceDelegateFactory surfaceDelegateFactory) { if (!enableOnCreate) { return new DisabledDevSupportManager(); } @@ -76,7 +79,8 @@ public DevSupportManager create( RedBoxHandler.class, DevBundleDownloadListener.class, int.class, - Map.class); + Map.class, + SurfaceDelegateFactory.class); return (DevSupportManager) constructor.newInstance( applicationContext, @@ -86,7 +90,8 @@ public DevSupportManager create( redBoxHandler, devBundleDownloadListener, minNumShakes, - customPackagerCommandHandlers); + customPackagerCommandHandlers, + surfaceDelegateFactory); } catch (Exception e) { throw new RuntimeException( "Requested enabled DevSupportManager, but BridgeDevSupportManager class was not found" 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 7a7c8c21c52293..3fed94df3f3e1c 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/devsupport/DevSupportManagerBase.java +++ b/ReactAndroid/src/main/java/com/facebook/react/devsupport/DevSupportManagerBase.java @@ -41,6 +41,8 @@ import com.facebook.react.common.DebugServerException; import com.facebook.react.common.ReactConstants; import com.facebook.react.common.ShakeDetector; +import com.facebook.react.common.SurfaceDelegate; +import com.facebook.react.common.SurfaceDelegateFactory; import com.facebook.react.devsupport.DevServerHelper.PackagerCommandListener; import com.facebook.react.devsupport.interfaces.BundleLoadCallback; import com.facebook.react.devsupport.interfaces.DevBundleDownloadListener; @@ -118,6 +120,7 @@ public interface CallbackWithBundleLoader { private @Nullable Map mCustomPackagerCommandHandlers; private @Nullable Activity currentActivity; + private @Nullable final SurfaceDelegateFactory mSurfaceDelegateFactory; public DevSupportManagerBase( Context applicationContext, @@ -127,7 +130,8 @@ public DevSupportManagerBase( @Nullable RedBoxHandler redBoxHandler, @Nullable DevBundleDownloadListener devBundleDownloadListener, int minNumShakes, - @Nullable Map customPackagerCommandHandlers) { + @Nullable Map customPackagerCommandHandlers, + @Nullable SurfaceDelegateFactory surfaceDelegateFactory) { mReactInstanceDevHelper = reactInstanceDevHelper; mApplicationContext = applicationContext; mJSAppBundleName = packagerPathForJSBundleName; @@ -202,7 +206,8 @@ public void onReceive(Context context, Intent intent) { mRedBoxHandler = redBoxHandler; mDevLoadingViewController = new DevLoadingViewController(reactInstanceDevHelper); - } + mSurfaceDelegateFactory = surfaceDelegateFactory; + }; protected abstract String getUniqueTag(); @@ -1204,4 +1209,18 @@ public void setPackagerLocationCustomizer( DevSupportManager.PackagerLocationCustomizer packagerLocationCustomizer) { mPackagerLocationCustomizer = packagerLocationCustomizer; } + + @Override + public @Nullable Activity getCurrentActivity() { + return mReactInstanceDevHelper.getCurrentActivity(); + } + + @Override + public @Nullable SurfaceDelegate createSurfaceDelegate(String moduleName) { + if (mSurfaceDelegateFactory == null) { + return null; + } + + return mSurfaceDelegateFactory.createSurfaceDelegate(moduleName); + } } diff --git a/ReactAndroid/src/main/java/com/facebook/react/devsupport/DevSupportManagerFactory.java b/ReactAndroid/src/main/java/com/facebook/react/devsupport/DevSupportManagerFactory.java index e279b6b047ea13..8bdfe8dd1a6ad5 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/devsupport/DevSupportManagerFactory.java +++ b/ReactAndroid/src/main/java/com/facebook/react/devsupport/DevSupportManagerFactory.java @@ -9,6 +9,7 @@ import android.content.Context; import androidx.annotation.Nullable; +import com.facebook.react.common.SurfaceDelegateFactory; import com.facebook.react.devsupport.interfaces.DevBundleDownloadListener; import com.facebook.react.devsupport.interfaces.DevSupportManager; import com.facebook.react.packagerconnection.RequestHandler; @@ -23,5 +24,6 @@ DevSupportManager create( @Nullable RedBoxHandler redBoxHandler, @Nullable DevBundleDownloadListener devBundleDownloadListener, int minNumShakes, - @Nullable Map customPackagerCommandHandlers); + @Nullable Map customPackagerCommandHandlers, + @Nullable SurfaceDelegateFactory surfaceDelegateFactory); } 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 b3738654b61540..3d54a3db40f857 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/devsupport/DisabledDevSupportManager.java +++ b/ReactAndroid/src/main/java/com/facebook/react/devsupport/DisabledDevSupportManager.java @@ -7,11 +7,13 @@ package com.facebook.react.devsupport; +import android.app.Activity; import android.view.View; import androidx.annotation.Nullable; import com.facebook.react.bridge.DefaultNativeModuleCallExceptionHandler; import com.facebook.react.bridge.ReactContext; import com.facebook.react.bridge.ReadableArray; +import com.facebook.react.common.SurfaceDelegate; import com.facebook.react.devsupport.interfaces.BundleLoadCallback; import com.facebook.react.devsupport.interfaces.DevOptionHandler; import com.facebook.react.devsupport.interfaces.DevSplitBundleCallback; @@ -175,4 +177,14 @@ public void setPackagerLocationCustomizer( public void handleException(Exception e) { mDefaultNativeModuleCallExceptionHandler.handleException(e); } + + @Override + public @Nullable Activity getCurrentActivity() { + return null; + } + + @Override + public @Nullable SurfaceDelegate createSurfaceDelegate(String moduleName) { + return null; + } } diff --git a/ReactAndroid/src/main/java/com/facebook/react/devsupport/LogBoxDialogSurfaceDelegate.java b/ReactAndroid/src/main/java/com/facebook/react/devsupport/LogBoxDialogSurfaceDelegate.java new file mode 100644 index 00000000000000..1a40476c84119d --- /dev/null +++ b/ReactAndroid/src/main/java/com/facebook/react/devsupport/LogBoxDialogSurfaceDelegate.java @@ -0,0 +1,92 @@ +/* + * 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.infer.annotation.Assertions; +import com.facebook.react.common.SurfaceDelegate; +import com.facebook.react.devsupport.interfaces.DevSupportManager; +import com.facebook.react.util.RNLog; + +/** + * The implementation of SurfaceDelegate with {@link Activity}. This is the default SurfaceDelegate + * for Mobile. + */ +public class LogBoxDialogSurfaceDelegate implements SurfaceDelegate { + + private @Nullable View mReactRootView; + private @Nullable LogBoxDialog mDialog; + private final DevSupportManager mDevSupportManager; + + LogBoxDialogSurfaceDelegate(DevSupportManager devSupportManager) { + mDevSupportManager = devSupportManager; + } + + @Override + public void createContentView(String appKey) { + Assertions.assertCondition( + appKey.equals("LogBox"), "This surface manager can only create LogBox React application"); + mReactRootView = mDevSupportManager.createRootView("LogBox"); + if (mReactRootView == null) { + RNLog.e("Unable to launch logbox because react was unable to create the root view"); + } + } + + @Override + public boolean isContentViewReady() { + return mReactRootView != null; + } + + @Override + public void destroyContentView() { + if (mReactRootView != null) { + mDevSupportManager.destroyRootView(mReactRootView); + mReactRootView = null; + } + } + + @Override + public void show() { + if (isSurfaceVisible() || !isContentViewReady()) { + return; + } + + final @Nullable Activity context = mDevSupportManager.getCurrentActivity(); + if (context == null || context.isFinishing()) { + RNLog.e( + "Unable to launch logbox because react activity " + + "is not available, here is the error that logbox would've displayed: "); + return; + } + + mDialog = new LogBoxDialog(context, mReactRootView); + mDialog.setCancelable(false); + mDialog.show(); + } + + @Override + public void hide() { + if (!isSurfaceVisible()) { + return; + } + + if (mReactRootView != null && mReactRootView.getParent() != null) { + ((ViewGroup) mReactRootView.getParent()).removeView(mReactRootView); + } + + mDialog.dismiss(); + mDialog = null; + } + + private boolean isSurfaceVisible() { + return mDialog != null; + } +} diff --git a/ReactAndroid/src/main/java/com/facebook/react/devsupport/LogBoxModule.java b/ReactAndroid/src/main/java/com/facebook/react/devsupport/LogBoxModule.java index e4f705ed1237f8..1fc7b8b56d6a1a 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/devsupport/LogBoxModule.java +++ b/ReactAndroid/src/main/java/com/facebook/react/devsupport/LogBoxModule.java @@ -7,16 +7,13 @@ package com.facebook.react.devsupport; -import android.app.Activity; -import android.view.View; -import android.view.ViewGroup; import androidx.annotation.Nullable; import com.facebook.fbreact.specs.NativeLogBoxSpec; import com.facebook.react.bridge.ReactApplicationContext; import com.facebook.react.bridge.UiThreadUtil; +import com.facebook.react.common.SurfaceDelegate; import com.facebook.react.devsupport.interfaces.DevSupportManager; import com.facebook.react.module.annotations.ReactModule; -import com.facebook.react.util.RNLog; @ReactModule(name = LogBoxModule.NAME) public class LogBoxModule extends NativeLogBoxSpec { @@ -24,24 +21,30 @@ public class LogBoxModule extends NativeLogBoxSpec { public static final String NAME = "LogBox"; private final DevSupportManager mDevSupportManager; - private @Nullable View mReactRootView; - private @Nullable LogBoxDialog mLogBoxDialog; + private final SurfaceDelegate mSurfaceDelegate; + /** + * LogBoxModule can be rendered in different surface. By default, it will use LogBoxDialog to wrap + * the content of logs. In other platform (for example VR), a surfaceDelegate can be provided so + * that the content can be wrapped in custom surface. + */ public LogBoxModule(ReactApplicationContext reactContext, DevSupportManager devSupportManager) { super(reactContext); mDevSupportManager = devSupportManager; + @Nullable SurfaceDelegate surfaceDelegate = devSupportManager.createSurfaceDelegate(NAME); + if (surfaceDelegate != null) { + mSurfaceDelegate = surfaceDelegate; + } else { + mSurfaceDelegate = new LogBoxDialogSurfaceDelegate(devSupportManager); + } + UiThreadUtil.runOnUiThread( new Runnable() { @Override public void run() { - if (mReactRootView == null && mDevSupportManager != null) { - mReactRootView = mDevSupportManager.createRootView("LogBox"); - if (mReactRootView == null) { - RNLog.e("Unable to launch logbox because react was unable to create the root view"); - } - } + mSurfaceDelegate.createContentView("LogBox"); } }); } @@ -53,26 +56,17 @@ public String getName() { @Override public void show() { - if (mReactRootView != null) { - UiThreadUtil.runOnUiThread( - new Runnable() { - @Override - public void run() { - if (mLogBoxDialog == null && mReactRootView != null) { - Activity context = getCurrentActivity(); - if (context == null || context.isFinishing()) { - RNLog.e( - "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(); - } - } - }); + if (!mSurfaceDelegate.isContentViewReady()) { + return; } + + UiThreadUtil.runOnUiThread( + new Runnable() { + @Override + public void run() { + mSurfaceDelegate.show(); + } + }); } @Override @@ -81,13 +75,7 @@ public void hide() { new Runnable() { @Override public void run() { - if (mLogBoxDialog != null) { - if (mReactRootView != null && mReactRootView.getParent() != null) { - ((ViewGroup) mReactRootView.getParent()).removeView(mReactRootView); - } - mLogBoxDialog.dismiss(); - mLogBoxDialog = null; - } + mSurfaceDelegate.hide(); } }); } @@ -98,10 +86,7 @@ public void invalidate() { new Runnable() { @Override public void run() { - if (mReactRootView != null) { - mDevSupportManager.destroyRootView(mReactRootView); - mReactRootView = null; - } + mSurfaceDelegate.destroyContentView(); } }); } 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 a780d523723421..183f58868b6f02 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,11 +7,13 @@ package com.facebook.react.devsupport.interfaces; +import android.app.Activity; import android.view.View; import androidx.annotation.Nullable; import com.facebook.react.bridge.NativeModuleCallExceptionHandler; import com.facebook.react.bridge.ReactContext; import com.facebook.react.bridge.ReadableArray; +import com.facebook.react.common.SurfaceDelegate; import com.facebook.react.modules.debug.interfaces.DeveloperSettings; import java.io.File; @@ -107,4 +109,16 @@ public interface PackagerLocationCustomizer { } void setPackagerLocationCustomizer(PackagerLocationCustomizer packagerLocationCustomizer); + + @Nullable + Activity getCurrentActivity(); + + /** + * Create the surface delegate that the provided module should use to interact with + * + * @param moduleName the module name that helps decide which surface it should interact with + * @return a {@link SurfaceDelegate} instance + */ + @Nullable + SurfaceDelegate createSurfaceDelegate(String moduleName); }