Skip to content

Commit

Permalink
Adding interface to wrap surface interactions for LogBox
Browse files Browse the repository at this point in the history
Summary:
This diff introduces a new interface named `SurfaceDelegate`. The interface abstracts the API for interacting with a surface, which is required for platforms other than mobile to implement how it wants to show and hide a surface. For existing Mobile use cases, the `LogBoxDialogSurfaceDelegate` is provided as a fallback solution so everything still works.

Changelog:
[Android][Added] - Add SurfaceDelegate abstraction to support interaction in multiple platforms and provide default implementation in LogBoxModule

Reviewed By: mdvacca

Differential Revision: D31132285

fbshipit-source-id: 13315a8bc5b7bcaee9b5e53ef5c6f6cc8cb01f31
  • Loading branch information
ryancat authored and facebook-github-bot committed Sep 28, 2021
1 parent f2eecae commit 652cb54
Show file tree
Hide file tree
Showing 14 changed files with 288 additions and 53 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -232,7 +233,8 @@ public static ReactInstanceManagerBuilder builder() {
int minTimeLeftInFrameForNonBatchedOperationMs,
@Nullable JSIModulePackage jsiModulePackage,
@Nullable Map<String, RequestHandler> customPackagerCommandHandlers,
@Nullable ReactPackageTurboModuleManagerDelegate.Builder tmmDelegateBuilder) {
@Nullable ReactPackageTurboModuleManagerDelegate.Builder tmmDelegateBuilder,
@Nullable SurfaceDelegateFactory surfaceDelegateFactory) {
FLog.d(TAG, "ReactInstanceManager.ctor()");
initializeSoLoaderIfNecessary(applicationContext);

Expand All @@ -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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -63,6 +64,7 @@ public class ReactInstanceManagerBuilder {
private @Nullable JSIModulePackage mJSIModulesPackage;
private @Nullable Map<String, RequestHandler> mCustomPackagerCommandHandlers;
private @Nullable ReactPackageTurboModuleManagerDelegate.Builder mTMMDelegateBuilder;
private @Nullable SurfaceDelegateFactory mSurfaceDelegateFactory;

/* package protected */ ReactInstanceManagerBuilder() {}

Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -322,7 +338,8 @@ public ReactInstanceManager build() {
mMinTimeLeftInFrameForNonBatchedOperationMs,
mJSIModulesPackage,
mCustomPackagerCommandHandlers,
mTMMDelegateBuilder);
mTMMDelegateBuilder,
mSurfaceDelegateFactory);
}

private JavaScriptExecutorFactory getDefaultJSExecutorFactory(
Expand Down
18 changes: 18 additions & 0 deletions ReactAndroid/src/main/java/com/facebook/react/ReactNativeHost.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -71,6 +73,7 @@ protected ReactInstanceManager createReactInstanceManager() {
.setUseDeveloperSupport(getUseDeveloperSupport())
.setDevSupportManagerFactory(getDevSupportManagerFactory())
.setRequireActivity(getShouldRequireActivity())
.setSurfaceDelegateFactory(getSurfaceDelegateFactory())
.setRedBoxHandler(getRedBoxHandler())
.setJavaScriptExecutorFactory(getJavaScriptExecutorFactory())
.setUIImplementationProvider(getUIImplementationProvider())
Expand Down Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
@@ -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();
}
Original file line number Diff line number Diff line change
@@ -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);
}
Original file line number Diff line number Diff line change
Expand Up @@ -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"),
],
)
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -71,7 +72,8 @@ public BridgeDevSupportManager(
@Nullable RedBoxHandler redBoxHandler,
@Nullable DevBundleDownloadListener devBundleDownloadListener,
int minNumShakes,
@Nullable Map<String, RequestHandler> customPackagerCommandHandlers) {
@Nullable Map<String, RequestHandler> customPackagerCommandHandlers,
@Nullable SurfaceDelegateFactory surfaceDelegateFactory) {
super(
applicationContext,
reactInstanceManagerHelper,
Expand All @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -41,6 +42,7 @@ public DevSupportManager create(
null,
null,
minNumShakes,
null,
null);
}

Expand All @@ -53,7 +55,8 @@ public DevSupportManager create(
@Nullable RedBoxHandler redBoxHandler,
@Nullable DevBundleDownloadListener devBundleDownloadListener,
int minNumShakes,
@Nullable Map<String, RequestHandler> customPackagerCommandHandlers) {
@Nullable Map<String, RequestHandler> customPackagerCommandHandlers,
@Nullable SurfaceDelegateFactory surfaceDelegateFactory) {
if (!enableOnCreate) {
return new DisabledDevSupportManager();
}
Expand All @@ -76,7 +79,8 @@ public DevSupportManager create(
RedBoxHandler.class,
DevBundleDownloadListener.class,
int.class,
Map.class);
Map.class,
SurfaceDelegateFactory.class);
return (DevSupportManager)
constructor.newInstance(
applicationContext,
Expand All @@ -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"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -118,6 +120,7 @@ public interface CallbackWithBundleLoader {
private @Nullable Map<String, RequestHandler> mCustomPackagerCommandHandlers;

private @Nullable Activity currentActivity;
private @Nullable final SurfaceDelegateFactory mSurfaceDelegateFactory;

public DevSupportManagerBase(
Context applicationContext,
Expand All @@ -127,7 +130,8 @@ public DevSupportManagerBase(
@Nullable RedBoxHandler redBoxHandler,
@Nullable DevBundleDownloadListener devBundleDownloadListener,
int minNumShakes,
@Nullable Map<String, RequestHandler> customPackagerCommandHandlers) {
@Nullable Map<String, RequestHandler> customPackagerCommandHandlers,
@Nullable SurfaceDelegateFactory surfaceDelegateFactory) {
mReactInstanceDevHelper = reactInstanceDevHelper;
mApplicationContext = applicationContext;
mJSAppBundleName = packagerPathForJSBundleName;
Expand Down Expand Up @@ -202,7 +206,8 @@ public void onReceive(Context context, Intent intent) {

mRedBoxHandler = redBoxHandler;
mDevLoadingViewController = new DevLoadingViewController(reactInstanceDevHelper);
}
mSurfaceDelegateFactory = surfaceDelegateFactory;
};

protected abstract String getUniqueTag();

Expand Down Expand Up @@ -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);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -23,5 +24,6 @@ DevSupportManager create(
@Nullable RedBoxHandler redBoxHandler,
@Nullable DevBundleDownloadListener devBundleDownloadListener,
int minNumShakes,
@Nullable Map<String, RequestHandler> customPackagerCommandHandlers);
@Nullable Map<String, RequestHandler> customPackagerCommandHandlers,
@Nullable SurfaceDelegateFactory surfaceDelegateFactory);
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
}
}
Loading

0 comments on commit 652cb54

Please sign in to comment.