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.
- package/CHANGELOG.md +10 -0
- package/android/build.gradle +1 -1
- package/android/src/debug/java/expo/modules/devlauncher/DevLauncherController.kt +43 -16
- package/android/src/debug/java/expo/modules/devlauncher/DevLauncherPackageDelegate.kt +1 -1
- package/android/src/debug/java/expo/modules/devlauncher/helpers/DevLauncherReactUtils.kt +158 -46
- package/android/src/debug/java/expo/modules/devlauncher/launcher/DevLauncherActivity.kt +10 -8
- package/android/src/debug/java/expo/modules/devlauncher/launcher/DevLauncherNetworkInterceptor.kt +12 -11
- package/android/src/debug/java/expo/modules/devlauncher/launcher/DevLauncherReactHost.kt +109 -0
- package/android/src/debug/java/expo/modules/devlauncher/launcher/{DevLauncherClientHost.kt → DevLauncherReactNativeHost.kt} +2 -2
- package/android/src/debug/java/expo/modules/devlauncher/launcher/errors/DevLauncherErrorActivity.kt +1 -1
- package/android/src/debug/java/expo/modules/devlauncher/launcher/errors/DevLauncherUncaughtExceptionHandler.kt +4 -3
- package/android/src/debug/java/expo/modules/devlauncher/launcher/loaders/DevLauncherAppLoader.kt +6 -6
- package/android/src/debug/java/expo/modules/devlauncher/launcher/loaders/DevLauncherAppLoaderFactory.kt +2 -3
- package/android/src/debug/java/expo/modules/devlauncher/launcher/loaders/DevLauncherExpoAppLoader.kt +2 -2
- package/android/src/debug/java/expo/modules/devlauncher/launcher/loaders/DevLauncherLocalAppLoader.kt +2 -2
- package/android/src/debug/java/expo/modules/devlauncher/launcher/loaders/DevLauncherPublishedAppLoader.kt +2 -2
- package/android/src/debug/java/expo/modules/devlauncher/launcher/loaders/DevLauncherReactNativeAppLoader.kt +2 -2
- package/android/src/debug/java/expo/modules/devlauncher/react/DevLauncherDevSupportManagerSwapper.kt +119 -57
- package/android/src/main/java/com/facebook/react/devsupport/NonFinalBridgeDevSupportManager.java +273 -0
- package/android/src/main/java/com/facebook/react/runtime/NonFinalBridgelessDevSupportManager.java +177 -0
- package/android/src/main/java/expo/modules/devlauncher/launcher/DevLauncherControllerInterface.kt +4 -4
- package/android/src/react-native-74/debug/expo/modules/devlauncher/rncompatibility/DevLauncherBridgeDevSupportManager.kt +75 -0
- package/android/src/react-native-74/debug/expo/modules/devlauncher/rncompatibility/DevLauncherBridgelessDevSupportManager.kt +55 -0
- package/android/src/react-native-74/debug/expo/modules/devlauncher/rncompatibility/DevLauncherDevSupportManagerFactory.kt +3 -3
- package/android/src/release/java/expo/modules/devlauncher/DevLauncherController.kt +13 -9
- package/android/src/release/java/expo/modules/devlauncher/launcher/DevLauncherReactHost.kt +11 -0
- package/android/src/release/java/expo/modules/devlauncher/launcher/{DevLauncherClientHost.kt → DevLauncherReactNativeHost.kt} +1 -1
- package/android/src/testDebug/java/expo/modules/devlauncher/DevLauncherControllerTest.kt +3 -3
- package/android/src/testDebug/java/expo/modules/devlauncher/launcher/loaders/DevLauncherAppLoaderFactoryTest.kt +3 -4
- package/build/DevLauncherErrorManager.js.map +1 -1
- package/package.json +4 -4
- package/plugin/build/pluginConfig.js +2 -1
- package/android/src/react-native-74/debug/expo/modules/devlauncher/rncompatibility/DevLauncherDevSupportManager.kt +0 -286
package/android/src/main/java/com/facebook/react/devsupport/NonFinalBridgeDevSupportManager.java
ADDED
|
@@ -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
|
+
}
|
package/android/src/main/java/com/facebook/react/runtime/NonFinalBridgelessDevSupportManager.java
ADDED
|
@@ -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
|
+
}
|
package/android/src/main/java/expo/modules/devlauncher/launcher/DevLauncherControllerInterface.kt
CHANGED
|
@@ -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:
|
|
29
|
+
val devClientHost: ReactHostWrapper
|
|
30
30
|
val mode: DevLauncherController.Mode
|
|
31
|
-
val appHost:
|
|
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
|
-
|
|
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
|
-
|
|
30
|
+
DevLauncherBridgeDevSupportManager(
|
|
31
31
|
applicationContext,
|
|
32
|
-
|
|
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.
|
|
8
|
+
import com.facebook.react.ReactApplication
|
|
9
9
|
import com.facebook.react.bridge.ReactContext
|
|
10
|
-
import expo.
|
|
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:
|
|
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:
|
|
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,
|
|
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,
|
|
110
|
-
initialize(context,
|
|
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
|