expo-dev-launcher 4.0.0 → 4.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (33) hide show
  1. package/CHANGELOG.md +10 -0
  2. package/android/build.gradle +1 -1
  3. package/android/src/debug/java/expo/modules/devlauncher/DevLauncherController.kt +43 -16
  4. package/android/src/debug/java/expo/modules/devlauncher/DevLauncherPackageDelegate.kt +1 -1
  5. package/android/src/debug/java/expo/modules/devlauncher/helpers/DevLauncherReactUtils.kt +158 -46
  6. package/android/src/debug/java/expo/modules/devlauncher/launcher/DevLauncherActivity.kt +10 -8
  7. package/android/src/debug/java/expo/modules/devlauncher/launcher/DevLauncherNetworkInterceptor.kt +12 -11
  8. package/android/src/debug/java/expo/modules/devlauncher/launcher/DevLauncherReactHost.kt +109 -0
  9. package/android/src/debug/java/expo/modules/devlauncher/launcher/{DevLauncherClientHost.kt → DevLauncherReactNativeHost.kt} +2 -2
  10. package/android/src/debug/java/expo/modules/devlauncher/launcher/errors/DevLauncherErrorActivity.kt +1 -1
  11. package/android/src/debug/java/expo/modules/devlauncher/launcher/errors/DevLauncherUncaughtExceptionHandler.kt +4 -3
  12. package/android/src/debug/java/expo/modules/devlauncher/launcher/loaders/DevLauncherAppLoader.kt +6 -6
  13. package/android/src/debug/java/expo/modules/devlauncher/launcher/loaders/DevLauncherAppLoaderFactory.kt +2 -3
  14. package/android/src/debug/java/expo/modules/devlauncher/launcher/loaders/DevLauncherExpoAppLoader.kt +2 -2
  15. package/android/src/debug/java/expo/modules/devlauncher/launcher/loaders/DevLauncherLocalAppLoader.kt +2 -2
  16. package/android/src/debug/java/expo/modules/devlauncher/launcher/loaders/DevLauncherPublishedAppLoader.kt +2 -2
  17. package/android/src/debug/java/expo/modules/devlauncher/launcher/loaders/DevLauncherReactNativeAppLoader.kt +2 -2
  18. package/android/src/debug/java/expo/modules/devlauncher/react/DevLauncherDevSupportManagerSwapper.kt +119 -57
  19. package/android/src/main/java/com/facebook/react/devsupport/NonFinalBridgeDevSupportManager.java +273 -0
  20. package/android/src/main/java/com/facebook/react/runtime/NonFinalBridgelessDevSupportManager.java +177 -0
  21. package/android/src/main/java/expo/modules/devlauncher/launcher/DevLauncherControllerInterface.kt +4 -4
  22. package/android/src/react-native-74/debug/expo/modules/devlauncher/rncompatibility/DevLauncherBridgeDevSupportManager.kt +75 -0
  23. package/android/src/react-native-74/debug/expo/modules/devlauncher/rncompatibility/DevLauncherBridgelessDevSupportManager.kt +55 -0
  24. package/android/src/react-native-74/debug/expo/modules/devlauncher/rncompatibility/DevLauncherDevSupportManagerFactory.kt +3 -3
  25. package/android/src/release/java/expo/modules/devlauncher/DevLauncherController.kt +13 -9
  26. package/android/src/release/java/expo/modules/devlauncher/launcher/DevLauncherReactHost.kt +11 -0
  27. package/android/src/release/java/expo/modules/devlauncher/launcher/{DevLauncherClientHost.kt → DevLauncherReactNativeHost.kt} +1 -1
  28. package/android/src/testDebug/java/expo/modules/devlauncher/DevLauncherControllerTest.kt +3 -3
  29. package/android/src/testDebug/java/expo/modules/devlauncher/launcher/loaders/DevLauncherAppLoaderFactoryTest.kt +3 -4
  30. package/build/DevLauncherErrorManager.js.map +1 -1
  31. package/package.json +4 -4
  32. package/plugin/build/pluginConfig.js +2 -1
  33. package/android/src/react-native-74/debug/expo/modules/devlauncher/rncompatibility/DevLauncherDevSupportManager.kt +0 -286
@@ -0,0 +1,273 @@
1
+ /*
2
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ */
7
+
8
+ package com.facebook.react.devsupport;
9
+
10
+ import android.content.Context;
11
+ import android.widget.Toast;
12
+
13
+ import androidx.annotation.Nullable;
14
+
15
+ import com.facebook.common.logging.FLog;
16
+ import com.facebook.debug.holder.PrinterHolder;
17
+ import com.facebook.debug.tags.ReactDebugOverlayTags;
18
+ import com.facebook.infer.annotation.Assertions;
19
+ import com.facebook.react.R;
20
+ import com.facebook.react.bridge.CatalystInstance;
21
+ import com.facebook.react.bridge.JSBundleLoader;
22
+ import com.facebook.react.bridge.JavaJSExecutor;
23
+ import com.facebook.react.bridge.JavaScriptExecutorFactory;
24
+ import com.facebook.react.bridge.ReactMarker;
25
+ import com.facebook.react.bridge.ReactMarkerConstants;
26
+ import com.facebook.react.bridge.UiThreadUtil;
27
+ import com.facebook.react.common.ReactConstants;
28
+ import com.facebook.react.common.SurfaceDelegateFactory;
29
+ import com.facebook.react.common.futures.SimpleSettableFuture;
30
+ import com.facebook.react.devsupport.interfaces.DevBundleDownloadListener;
31
+ import com.facebook.react.devsupport.interfaces.DevLoadingViewManager;
32
+ import com.facebook.react.devsupport.interfaces.DevOptionHandler;
33
+ import com.facebook.react.devsupport.interfaces.DevSplitBundleCallback;
34
+ import com.facebook.react.devsupport.interfaces.RedBoxHandler;
35
+ import com.facebook.react.packagerconnection.RequestHandler;
36
+
37
+ import java.io.File;
38
+ import java.io.IOException;
39
+ import java.util.Map;
40
+ import java.util.concurrent.ExecutionException;
41
+ import java.util.concurrent.TimeUnit;
42
+ import java.util.concurrent.TimeoutException;
43
+
44
+ //
45
+ // Expo: This is a copy of react-native's {@link com.facebook.react.devsupport.BridgeDevSupportManager}
46
+ // just removing the "final" modifier that we can inherit and reuse.
47
+ // From time to time for react-native upgrade, just follow the steps to update the code
48
+ // 1. Copy the contents from BridgeDevSupportManager to this file.
49
+ // 2. Rename the class to NonFinalBridgeDevSupportManager.
50
+ // 3. Remove the "final" modifier.
51
+ // 4. Revert the comment
52
+ //
53
+
54
+ /**
55
+ * Interface for accessing and interacting with development features. Following features
56
+ * are supported through this manager class:
57
+ * 1) Displaying JS errors (aka RedBox)
58
+ * 2) Displaying developers menu (Reload JS, Debug JS)
59
+ * 3) Communication with developer server in order to download updated JS bundle
60
+ * 4) Starting/stopping broadcast receiver for js reload signals
61
+ * 5) Starting/stopping motion sensor listener that recognize shake gestures which in turn may
62
+ * trigger developers menu.
63
+ * 6) Launching developers settings view
64
+ *
65
+ * This class automatically monitors the state of registered views and activities to which they are
66
+ * bound to make sure that we don't display overlay or that we we don't listen for sensor events
67
+ * when app is backgrounded.
68
+ *
69
+ * {@link com.facebook.react.ReactInstanceManager} implementation is responsible for instantiating
70
+ * this class as well as for populating with a reference to {@link CatalystInstance} whenever
71
+ * instance manager recreates it (through {@link #onNewReactContextCreated). Also, instance manager
72
+ * is responsible for enabling/disabling dev support in case when app is backgrounded or when all
73
+ * the views has been detached from the instance (through {@link #setDevSupportEnabled} method).
74
+ */
75
+ public class NonFinalBridgeDevSupportManager extends DevSupportManagerBase {
76
+ private boolean mIsSamplingProfilerEnabled = false;
77
+ private ReactInstanceDevHelper mReactInstanceManagerHelper;
78
+ private @Nullable DevLoadingViewManager mDevLoadingViewManager;
79
+
80
+ public NonFinalBridgeDevSupportManager(
81
+ Context applicationContext,
82
+ ReactInstanceDevHelper reactInstanceManagerHelper,
83
+ @Nullable String packagerPathForJSBundleName,
84
+ boolean enableOnCreate,
85
+ @Nullable RedBoxHandler redBoxHandler,
86
+ @Nullable DevBundleDownloadListener devBundleDownloadListener,
87
+ int minNumShakes,
88
+ @Nullable Map<String, RequestHandler> customPackagerCommandHandlers,
89
+ @Nullable SurfaceDelegateFactory surfaceDelegateFactory,
90
+ @Nullable DevLoadingViewManager devLoadingViewManager) {
91
+ super(
92
+ applicationContext,
93
+ reactInstanceManagerHelper,
94
+ packagerPathForJSBundleName,
95
+ enableOnCreate,
96
+ redBoxHandler,
97
+ devBundleDownloadListener,
98
+ minNumShakes,
99
+ customPackagerCommandHandlers,
100
+ surfaceDelegateFactory,
101
+ devLoadingViewManager);
102
+
103
+ if (getDevSettings().isStartSamplingProfilerOnInit()) {
104
+ // Only start the profiler. If its already running, there is an error
105
+ if (!mIsSamplingProfilerEnabled) {
106
+ toggleJSSamplingProfiler();
107
+ } else {
108
+ Toast.makeText(
109
+ applicationContext,
110
+ "JS Sampling Profiler was already running, so did not start the sampling profiler",
111
+ Toast.LENGTH_LONG)
112
+ .show();
113
+ }
114
+ }
115
+
116
+ addCustomDevOption(
117
+ applicationContext.getString(R.string.catalyst_sample_profiler_toggle),
118
+ new DevOptionHandler() {
119
+ @Override
120
+ public void onOptionSelected() {
121
+ toggleJSSamplingProfiler();
122
+ }
123
+ });
124
+ }
125
+
126
+ @Override
127
+ protected String getUniqueTag() {
128
+ return "Bridge";
129
+ }
130
+
131
+ @Override
132
+ public void loadSplitBundleFromServer(
133
+ final String bundlePath, final DevSplitBundleCallback callback) {
134
+ fetchSplitBundleAndCreateBundleLoader(
135
+ bundlePath,
136
+ new CallbackWithBundleLoader() {
137
+ @Override
138
+ public void onSuccess(JSBundleLoader bundleLoader) {
139
+ bundleLoader.loadScript(getCurrentContext().getCatalystInstance());
140
+ getCurrentContext()
141
+ .getJSModule(HMRClient.class)
142
+ .registerBundle(getDevServerHelper().getDevServerSplitBundleURL(bundlePath));
143
+ callback.onSuccess();
144
+ }
145
+
146
+ @Override
147
+ public void onError(String url, Throwable cause) {
148
+ callback.onError(url, cause);
149
+ }
150
+ });
151
+ }
152
+
153
+ private WebsocketJavaScriptExecutor.JSExecutorConnectCallback getExecutorConnectCallback(
154
+ final SimpleSettableFuture<Boolean> future) {
155
+ return new WebsocketJavaScriptExecutor.JSExecutorConnectCallback() {
156
+ @Override
157
+ public void onSuccess() {
158
+ future.set(true);
159
+ hideDevLoadingView();
160
+ }
161
+
162
+ @Override
163
+ public void onFailure(final Throwable cause) {
164
+ hideDevLoadingView();
165
+ FLog.e(ReactConstants.TAG, "Failed to connect to debugger!", cause);
166
+ future.setException(
167
+ new IOException(
168
+ getApplicationContext().getString(com.facebook.react.R.string.catalyst_debug_error),
169
+ cause));
170
+ }
171
+ };
172
+ }
173
+
174
+ private void reloadJSInProxyMode() {
175
+ // When using js proxy, there is no need to fetch JS bundle as proxy executor will do that
176
+ // anyway
177
+ getDevServerHelper().launchJSDevtools();
178
+
179
+ JavaJSExecutor.Factory factory =
180
+ new JavaJSExecutor.Factory() {
181
+ @Override
182
+ public JavaJSExecutor create() throws Exception {
183
+ WebsocketJavaScriptExecutor executor = new WebsocketJavaScriptExecutor();
184
+ SimpleSettableFuture<Boolean> future = new SimpleSettableFuture<>();
185
+ executor.connect(
186
+ getDevServerHelper().getWebsocketProxyURL(), getExecutorConnectCallback(future));
187
+ // TODO(t9349129) Don't use timeout
188
+ try {
189
+ future.get(90, TimeUnit.SECONDS);
190
+ return executor;
191
+ } catch (ExecutionException e) {
192
+ throw (Exception) e.getCause();
193
+ } catch (InterruptedException | TimeoutException e) {
194
+ throw new RuntimeException(e);
195
+ }
196
+ }
197
+ };
198
+ getReactInstanceDevHelper().onReloadWithJSDebugger(factory);
199
+ }
200
+
201
+ @Override
202
+ public void handleReloadJS() {
203
+
204
+ UiThreadUtil.assertOnUiThread();
205
+
206
+ ReactMarker.logMarker(
207
+ ReactMarkerConstants.RELOAD,
208
+ getDevSettings().getPackagerConnectionSettings().getDebugServerHost());
209
+
210
+ // dismiss redbox if exists
211
+ hideRedboxDialog();
212
+
213
+ if (getDevSettings().isRemoteJSDebugEnabled()) {
214
+ PrinterHolder.getPrinter()
215
+ .logMessage(ReactDebugOverlayTags.RN_CORE, "RNCore: load from Proxy");
216
+ showDevLoadingViewForRemoteJSEnabled();
217
+ reloadJSInProxyMode();
218
+ } else {
219
+ PrinterHolder.getPrinter()
220
+ .logMessage(ReactDebugOverlayTags.RN_CORE, "RNCore: load from Server");
221
+ String bundleURL =
222
+ getDevServerHelper()
223
+ .getDevServerBundleURL(Assertions.assertNotNull(getJSAppBundleName()));
224
+ reloadJSFromServer(bundleURL);
225
+ }
226
+ }
227
+
228
+ /** Starts of stops the sampling profiler */
229
+ private void toggleJSSamplingProfiler() {
230
+ JavaScriptExecutorFactory javaScriptExecutorFactory =
231
+ getReactInstanceDevHelper().getJavaScriptExecutorFactory();
232
+ if (!mIsSamplingProfilerEnabled) {
233
+ try {
234
+ javaScriptExecutorFactory.startSamplingProfiler();
235
+ Toast.makeText(getApplicationContext(), "Starting Sampling Profiler", Toast.LENGTH_SHORT)
236
+ .show();
237
+ } catch (UnsupportedOperationException e) {
238
+ Toast.makeText(
239
+ getApplicationContext(),
240
+ javaScriptExecutorFactory.toString() + " does not support Sampling Profiler",
241
+ Toast.LENGTH_LONG)
242
+ .show();
243
+ } finally {
244
+ mIsSamplingProfilerEnabled = true;
245
+ }
246
+ } else {
247
+ try {
248
+ final String outputPath =
249
+ File.createTempFile(
250
+ "sampling-profiler-trace", ".cpuprofile", getApplicationContext().getCacheDir())
251
+ .getPath();
252
+ javaScriptExecutorFactory.stopSamplingProfiler(outputPath);
253
+ Toast.makeText(
254
+ getApplicationContext(),
255
+ "Saved results from Profiler to " + outputPath,
256
+ Toast.LENGTH_LONG)
257
+ .show();
258
+ } catch (IOException e) {
259
+ FLog.e(
260
+ ReactConstants.TAG,
261
+ "Could not create temporary file for saving results from Sampling Profiler");
262
+ } catch (UnsupportedOperationException e) {
263
+ Toast.makeText(
264
+ getApplicationContext(),
265
+ javaScriptExecutorFactory.toString() + "does not support Sampling Profiler",
266
+ Toast.LENGTH_LONG)
267
+ .show();
268
+ } finally {
269
+ mIsSamplingProfilerEnabled = false;
270
+ }
271
+ }
272
+ }
273
+ }
@@ -0,0 +1,177 @@
1
+ /*
2
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ */
7
+
8
+ package com.facebook.react.runtime;
9
+
10
+ import android.app.Activity;
11
+ import android.content.Context;
12
+ import android.os.Bundle;
13
+ import android.view.View;
14
+ import com.facebook.debug.holder.PrinterHolder;
15
+ import com.facebook.debug.tags.ReactDebugOverlayTags;
16
+ import com.facebook.infer.annotation.Assertions;
17
+ import com.facebook.infer.annotation.Nullsafe;
18
+ import com.facebook.react.bridge.JSBundleLoader;
19
+ import com.facebook.react.bridge.JavaJSExecutor;
20
+ import com.facebook.react.bridge.JavaScriptExecutorFactory;
21
+ import com.facebook.react.bridge.ReactContext;
22
+ import com.facebook.react.bridge.UiThreadUtil;
23
+ import com.facebook.react.devsupport.DevSupportManagerBase;
24
+ import com.facebook.react.devsupport.HMRClient;
25
+ import com.facebook.react.devsupport.ReactInstanceDevHelper;
26
+ import com.facebook.react.devsupport.interfaces.DevSplitBundleCallback;
27
+ import com.facebook.react.modules.core.DeviceEventManagerModule;
28
+ import com.facebook.react.runtime.internal.bolts.Continuation;
29
+ import com.facebook.react.runtime.internal.bolts.Task;
30
+ import javax.annotation.Nullable;
31
+
32
+ //
33
+ // Expo: This is a copy of react-native's {@link com.facebook.react.runtime.BridgelessDevSupportManager}
34
+ // just removing the "final" modifier that we can inherit and reuse.
35
+ // From time to time for react-native upgrade, just follow the steps to update the code
36
+ // 1. Copy the contents from BridgelessDevSupportManager to this file.
37
+ // 2. Rename the class to NonFinalBridgelessDevSupportManager.
38
+ // 3. Add "public" modifier
39
+ // 4. Revert the comment
40
+ //
41
+
42
+ /**
43
+ * An implementation of {@link com.facebook.react.devsupport.interfaces.DevSupportManager} that
44
+ * extends the functionality in {@link DevSupportManagerBase} with some additional, more flexible
45
+ * APIs for asynchronously loading the JS bundle.
46
+ */
47
+ @Nullsafe(Nullsafe.Mode.LOCAL)
48
+ public class NonFinalBridgelessDevSupportManager extends DevSupportManagerBase {
49
+
50
+ private final ReactHostImpl mReactHost;
51
+
52
+ public NonFinalBridgelessDevSupportManager(
53
+ ReactHostImpl host, Context context, @Nullable String packagerPathForJSBundleName) {
54
+ super(
55
+ context.getApplicationContext(),
56
+ createInstanceDevHelper(host),
57
+ packagerPathForJSBundleName,
58
+ true /* enableOnCreate */,
59
+ null /* redBoxHandler */,
60
+ null /* devBundleDownloadListener */,
61
+ 2 /* minNumShakes */,
62
+ null /* customPackagerCommandHandlers */,
63
+ null /* surfaceDelegateFactory */,
64
+ null /* devLoadingViewManager */);
65
+ mReactHost = host;
66
+ }
67
+
68
+ @Override
69
+ protected String getUniqueTag() {
70
+ return "Bridgeless";
71
+ }
72
+
73
+ @Override
74
+ public void loadSplitBundleFromServer(
75
+ final String bundlePath, final DevSplitBundleCallback callback) {
76
+ fetchSplitBundleAndCreateBundleLoader(
77
+ bundlePath,
78
+ new CallbackWithBundleLoader() {
79
+ @Override
80
+ public void onSuccess(final JSBundleLoader bundleLoader) {
81
+ mReactHost
82
+ .loadBundle(bundleLoader)
83
+ .onSuccess(
84
+ new Continuation<Boolean, Void>() {
85
+ @Override
86
+ public Void then(Task<Boolean> task) {
87
+ if (task.getResult().equals(Boolean.TRUE)) {
88
+ String bundleURL =
89
+ getDevServerHelper().getDevServerSplitBundleURL(bundlePath);
90
+ ReactContext reactContext = mReactHost.getCurrentReactContext();
91
+ if (reactContext != null) {
92
+ reactContext.getJSModule(HMRClient.class).registerBundle(bundleURL);
93
+ }
94
+ callback.onSuccess();
95
+ }
96
+ return null;
97
+ }
98
+ });
99
+ }
100
+
101
+ @Override
102
+ public void onError(String url, Throwable cause) {
103
+ callback.onError(url, cause);
104
+ }
105
+ });
106
+ }
107
+
108
+ @Override
109
+ public void handleReloadJS() {
110
+ UiThreadUtil.assertOnUiThread();
111
+
112
+ // dismiss redbox if exists
113
+ hideRedboxDialog();
114
+ mReactHost.reload("BridgelessDevSupportManager.handleReloadJS()");
115
+
116
+ PrinterHolder.getPrinter()
117
+ .logMessage(ReactDebugOverlayTags.RN_CORE, "RNCore: load from Server");
118
+ String bundleURL =
119
+ getDevServerHelper().getDevServerBundleURL(Assertions.assertNotNull(getJSAppBundleName()));
120
+ reloadJSFromServer(bundleURL);
121
+ }
122
+
123
+ private static ReactInstanceDevHelper createInstanceDevHelper(final ReactHostImpl reactHost) {
124
+ return new ReactInstanceDevHelper() {
125
+ @Override
126
+ public void onReloadWithJSDebugger(JavaJSExecutor.Factory proxyExecutorFactory) {
127
+ // Not implemented
128
+ }
129
+
130
+ @Override
131
+ public void onJSBundleLoadedFromServer() {
132
+ // Not implemented
133
+ }
134
+
135
+ @Override
136
+ public void toggleElementInspector() {
137
+ ReactContext reactContext = reactHost.getCurrentReactContext();
138
+ if (reactContext != null) {
139
+ reactContext
140
+ .getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
141
+ .emit("toggleElementInspector", null);
142
+ }
143
+ }
144
+
145
+ @androidx.annotation.Nullable
146
+ @Override
147
+ public Activity getCurrentActivity() {
148
+ return reactHost.getLastUsedActivity();
149
+ }
150
+
151
+ @Override
152
+ public JavaScriptExecutorFactory getJavaScriptExecutorFactory() {
153
+ throw new IllegalStateException("Not implemented for bridgeless mode");
154
+ }
155
+
156
+ @androidx.annotation.Nullable
157
+ @Override
158
+ public View createRootView(String appKey) {
159
+ Activity currentActivity = getCurrentActivity();
160
+ if (currentActivity != null && !reactHost.isSurfaceWithModuleNameAttached(appKey)) {
161
+ ReactSurfaceImpl reactSurface =
162
+ ReactSurfaceImpl.createWithView(currentActivity, appKey, new Bundle());
163
+ reactSurface.attach(reactHost);
164
+ reactSurface.start();
165
+
166
+ return reactSurface.getView();
167
+ }
168
+ return null;
169
+ }
170
+
171
+ @Override
172
+ public void destroyRootView(View rootView) {
173
+ // Not implemented
174
+ }
175
+ };
176
+ }
177
+ }
@@ -4,12 +4,12 @@ import android.content.Intent
4
4
  import android.net.Uri
5
5
  import com.facebook.react.ReactActivity
6
6
  import com.facebook.react.ReactActivityDelegate
7
- import com.facebook.react.ReactNativeHost
8
7
  import com.facebook.react.bridge.ReactContext
9
8
  import expo.modules.devlauncher.DevLauncherController
9
+ import expo.interfaces.devmenu.ReactHostWrapper
10
10
  import expo.modules.manifests.core.Manifest
11
- import expo.modules.updatesinterface.UpdatesInterfaceCallbacks
12
11
  import expo.modules.updatesinterface.UpdatesInterface
12
+ import expo.modules.updatesinterface.UpdatesInterfaceCallbacks
13
13
  import kotlinx.coroutines.CoroutineScope
14
14
 
15
15
  interface DevLauncherControllerInterface :
@@ -26,9 +26,9 @@ interface DevLauncherControllerInterface :
26
26
 
27
27
  val manifest: Manifest?
28
28
  val manifestURL: Uri?
29
- val devClientHost: DevLauncherClientHost
29
+ val devClientHost: ReactHostWrapper
30
30
  val mode: DevLauncherController.Mode
31
- val appHost: ReactNativeHost
31
+ val appHost: ReactHostWrapper
32
32
  val latestLoadedApp: Uri?
33
33
  val useDeveloperSupport: Boolean
34
34
  var updatesInterface: UpdatesInterface?
@@ -0,0 +1,75 @@
1
+ package expo.modules.devlauncher.rncompatibility
2
+
3
+ import android.content.Context
4
+ import android.util.Log
5
+ import com.facebook.react.devsupport.NonFinalBridgeDevSupportManager
6
+ import com.facebook.react.devsupport.ReactInstanceDevHelper
7
+ import com.facebook.react.devsupport.interfaces.DevBundleDownloadListener
8
+ import com.facebook.react.devsupport.interfaces.RedBoxHandler
9
+ import com.facebook.react.packagerconnection.RequestHandler
10
+ import expo.modules.devlauncher.DevLauncherController
11
+ import expo.modules.devlauncher.koin.DevLauncherKoinComponent
12
+ import expo.modules.devlauncher.koin.optInject
13
+ import expo.modules.devlauncher.launcher.DevLauncherControllerInterface
14
+ import expo.modules.devlauncher.launcher.errors.DevLauncherAppError
15
+ import expo.modules.devlauncher.launcher.errors.DevLauncherErrorActivity
16
+
17
+ class DevLauncherBridgeDevSupportManager(
18
+ applicationContext: Context?,
19
+ reactInstanceDevHelper: ReactInstanceDevHelper?,
20
+ packagerPathForJSBundleName: String?,
21
+ enableOnCreate: Boolean,
22
+ redBoxHandler: RedBoxHandler?,
23
+ devBundleDownloadListener: DevBundleDownloadListener?,
24
+ minNumShakes: Int,
25
+ customPackagerCommandHandlers: MutableMap<String, RequestHandler>?
26
+ ) : NonFinalBridgeDevSupportManager(
27
+ applicationContext,
28
+ reactInstanceDevHelper,
29
+ packagerPathForJSBundleName,
30
+ enableOnCreate,
31
+ redBoxHandler,
32
+ devBundleDownloadListener,
33
+ minNumShakes,
34
+ customPackagerCommandHandlers,
35
+ null,
36
+ null
37
+ ),
38
+ DevLauncherKoinComponent {
39
+ private val controller: DevLauncherControllerInterface? by optInject()
40
+
41
+ override fun showNewJavaError(message: String?, e: Throwable) {
42
+ Log.e("DevLauncher", "$message", e)
43
+ if (!DevLauncherController.wasInitialized()) {
44
+ Log.e("DevLauncher", "DevLauncher wasn't initialized. Couldn't intercept native error handling.")
45
+ super.showNewJavaError(message, e)
46
+ return
47
+ }
48
+
49
+ val activity = reactInstanceDevHelper?.currentActivity
50
+ if (activity == null || activity.isFinishing || activity.isDestroyed) {
51
+ return
52
+ }
53
+
54
+ controller?.onAppLoadedWithError()
55
+ DevLauncherErrorActivity.showError(activity, DevLauncherAppError(message, e))
56
+ }
57
+
58
+ override fun getUniqueTag() = "DevLauncherApp - Bridge"
59
+
60
+ override fun startInspector() {
61
+ // no-op for the default `startInspector` which would be implicitly called
62
+ // right after `ReactInstanceManager` construction.
63
+ // For dev-launcher, we should inject the correct dev server address and
64
+ // call our customized `startInspectorWhenDevLauncherReady`.
65
+ // Check `DevLauncherReactUtils.injectReactInterceptor()` for details.
66
+ }
67
+
68
+ fun startInspectorWhenDevLauncherReady() {
69
+ super.startInspector()
70
+ }
71
+
72
+ companion object {
73
+ fun getDevHelperInternalFieldName() = "mReactInstanceDevHelper"
74
+ }
75
+ }
@@ -0,0 +1,55 @@
1
+ package expo.modules.devlauncher.rncompatibility
2
+
3
+ import android.content.Context
4
+ import android.util.Log
5
+ import com.facebook.react.runtime.NonFinalBridgelessDevSupportManager
6
+ import com.facebook.react.runtime.ReactHostImpl
7
+ import expo.modules.devlauncher.DevLauncherController
8
+ import expo.modules.devlauncher.koin.DevLauncherKoinComponent
9
+ import expo.modules.devlauncher.koin.optInject
10
+ import expo.modules.devlauncher.launcher.DevLauncherControllerInterface
11
+ import expo.modules.devlauncher.launcher.errors.DevLauncherAppError
12
+ import expo.modules.devlauncher.launcher.errors.DevLauncherErrorActivity
13
+
14
+ class DevLauncherBridgelessDevSupportManager(
15
+ host: ReactHostImpl,
16
+ context: Context,
17
+ packagerPathForJSBundleName: String?
18
+ ) : NonFinalBridgelessDevSupportManager(host, context, packagerPathForJSBundleName), DevLauncherKoinComponent {
19
+ private val controller: DevLauncherControllerInterface? by optInject()
20
+
21
+ override fun showNewJavaError(message: String?, e: Throwable) {
22
+ Log.e("DevLauncher", "$message", e)
23
+ if (!DevLauncherController.wasInitialized()) {
24
+ Log.e("DevLauncher", "DevLauncher wasn't initialized. Couldn't intercept native error handling.")
25
+ super.showNewJavaError(message, e)
26
+ return
27
+ }
28
+
29
+ val activity = reactInstanceDevHelper?.currentActivity
30
+ if (activity == null || activity.isFinishing || activity.isDestroyed) {
31
+ return
32
+ }
33
+
34
+ controller?.onAppLoadedWithError()
35
+ DevLauncherErrorActivity.showError(activity, DevLauncherAppError(message, e))
36
+ }
37
+
38
+ override fun getUniqueTag() = "DevLauncherApp - Bridgeless"
39
+
40
+ override fun startInspector() {
41
+ // no-op for the default `startInspector` which would be implicitly called
42
+ // right after `ReactInstanceManager` construction.
43
+ // For dev-launcher, we should inject the correct dev server address and
44
+ // call our customized `startInspectorWhenDevLauncherReady`.
45
+ // Check `DevLauncherReactUtils.injectReactInterceptor()` for details.
46
+ }
47
+
48
+ fun startInspectorWhenDevLauncherReady() {
49
+ super.startInspector()
50
+ }
51
+
52
+ companion object {
53
+ fun getDevHelperInternalFieldName() = "mReactInstanceDevHelper"
54
+ }
55
+ }
@@ -14,7 +14,7 @@ import com.facebook.react.packagerconnection.RequestHandler
14
14
  class DevLauncherDevSupportManagerFactory : DevSupportManagerFactory {
15
15
  override fun create(
16
16
  applicationContext: Context,
17
- reactInstanceManagerHelper: ReactInstanceDevHelper,
17
+ reactInstanceDevHelper: ReactInstanceDevHelper,
18
18
  packagerPathForJSBundleName: String?,
19
19
  enableOnCreate: Boolean,
20
20
  redBoxHandler: RedBoxHandler?,
@@ -27,9 +27,9 @@ class DevLauncherDevSupportManagerFactory : DevSupportManagerFactory {
27
27
  return if (!enableOnCreate) {
28
28
  DisabledDevSupportManager()
29
29
  } else {
30
- DevLauncherDevSupportManager(
30
+ DevLauncherBridgeDevSupportManager(
31
31
  applicationContext,
32
- reactInstanceManagerHelper,
32
+ reactInstanceDevHelper,
33
33
  packagerPathForJSBundleName,
34
34
  enableOnCreate,
35
35
  redBoxHandler,
@@ -5,13 +5,12 @@ import android.content.Intent
5
5
  import android.net.Uri
6
6
  import com.facebook.react.ReactActivity
7
7
  import com.facebook.react.ReactActivityDelegate
8
- import com.facebook.react.ReactNativeHost
8
+ import com.facebook.react.ReactApplication
9
9
  import com.facebook.react.bridge.ReactContext
10
- import expo.modules.devlauncher.launcher.DevLauncherClientHost
10
+ import expo.interfaces.devmenu.ReactHostWrapper
11
+ import expo.modules.devlauncher.launcher.DevLauncherAppEntry
11
12
  import expo.modules.devlauncher.launcher.DevLauncherControllerInterface
12
13
  import expo.modules.devlauncher.launcher.DevLauncherReactActivityDelegateSupplier
13
- import expo.modules.devlauncher.launcher.DevLauncherAppEntry
14
-
15
14
  import expo.modules.manifests.core.Manifest
16
15
  import expo.modules.updatesinterface.UpdatesInterface
17
16
  import kotlinx.coroutines.CoroutineScope
@@ -29,7 +28,7 @@ class DevLauncherController private constructor() : DevLauncherControllerInterfa
29
28
  override val mode: Mode
30
29
  get() = throw IllegalStateException(DEV_LAUNCHER_IS_NOT_AVAILABLE)
31
30
 
32
- override val devClientHost: DevLauncherClientHost
31
+ override val devClientHost: ReactHostWrapper
33
32
  get() = throw IllegalStateException(DEV_LAUNCHER_IS_NOT_AVAILABLE)
34
33
 
35
34
  override val manifest: Manifest
@@ -38,7 +37,7 @@ class DevLauncherController private constructor() : DevLauncherControllerInterfa
38
37
  override val manifestURL: Uri
39
38
  get() = throw IllegalStateException(DEV_LAUNCHER_IS_NOT_AVAILABLE)
40
39
 
41
- override val appHost: ReactNativeHost
40
+ override val appHost: ReactHostWrapper
42
41
  get() = throw IllegalStateException(DEV_LAUNCHER_IS_NOT_AVAILABLE)
43
42
 
44
43
  override var updatesInterface: UpdatesInterface?
@@ -100,14 +99,19 @@ class DevLauncherController private constructor() : DevLauncherControllerInterfa
100
99
  }
101
100
 
102
101
  @JvmStatic
103
- fun initialize(context: Context, reactNativeHost: ReactNativeHost) {
102
+ internal fun initialize(context: Context, reactHost: ReactHostWrapper) {
104
103
  check(sInstance == null) { "DevelopmentClientController was initialized." }
105
104
  sInstance = DevLauncherController()
106
105
  }
107
106
 
108
107
  @JvmStatic
109
- fun initialize(context: Context, appHost: ReactNativeHost, cazz: Class<*>) {
110
- initialize(context, appHost)
108
+ fun initialize(context: Context, reactHost: ReactHostWrapper, launcherClass: Class<*>? = null) {
109
+ initialize(context, reactHost)
110
+ }
111
+
112
+ @JvmStatic
113
+ fun initialize(reactApplication: ReactApplication, additionalPackages: List<*>? = null, launcherClass: Class<*>? = null) {
114
+ initialize(reactApplication as Context, ReactHostWrapper(reactApplication.reactNativeHost, reactApplication.reactHost))
111
115
  }
112
116
 
113
117
  @JvmStatic