expo-dev-menu 5.0.0 → 5.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 (25) hide show
  1. package/CHANGELOG.md +10 -0
  2. package/README.md +3 -3
  3. package/android/build.gradle +2 -2
  4. package/android/src/debug/java/expo/modules/devmenu/DevMenuActivity.kt +27 -12
  5. package/android/src/debug/java/expo/modules/devmenu/DevMenuAppInfo.kt +3 -3
  6. package/android/src/debug/java/expo/modules/devmenu/DevMenuDefaultExtensionSettings.kt +1 -1
  7. package/android/src/debug/java/expo/modules/devmenu/DevMenuDevSettings.kt +5 -5
  8. package/android/src/debug/java/expo/modules/devmenu/DevMenuManager.kt +56 -49
  9. package/android/src/debug/java/expo/modules/devmenu/DevMenuReactHost.kt +148 -0
  10. package/android/src/debug/java/expo/modules/devmenu/{DevMenuHost.kt → DevMenuReactNativeHost.kt} +4 -6
  11. package/android/src/debug/java/expo/modules/devmenu/react/DevMenuAwareReactActivity.kt +8 -1
  12. package/android/src/main/java/com/facebook/react/devsupport/DevMenuReactInternalSettings.kt +1 -1
  13. package/android/src/main/java/expo/modules/devmenu/DevMenuDefaultDelegate.kt +3 -4
  14. package/android/src/main/java/expo/modules/devmenu/DevMenuPackage.kt +7 -1
  15. package/android/src/main/java/expo/modules/devmenu/devtools/DevMenuDevToolsDelegate.kt +4 -4
  16. package/android/src/main/java/expo/modules/devmenu/extensions/DevMenuExtension.kt +6 -4
  17. package/android/src/main/java/expo/modules/devmenu/modules/DevMenuInternalModule.kt +2 -3
  18. package/android/src/main/java/expo/modules/devmenu/react/DevMenuPackagerCommandHandlersSwapper.kt +6 -14
  19. package/android/src/main/java/expo/modules/devmenu/react/DevMenuShakeDetectorListenerSwapper.kt +3 -8
  20. package/android/src/main/java/expo/modules/devmenu/websockets/DevMenuCommandHandlersProvider.kt +12 -12
  21. package/android/src/react-native-74/main/expo/modules/devmenu/react/DevMenuPackagerConnectionSettings.kt +1 -1
  22. package/android/src/release/java/expo/modules/devmenu/DevMenuManager.kt +5 -6
  23. package/build/DevMenu.js.map +1 -1
  24. package/build/ExpoDevMenu.web.d.ts.map +1 -1
  25. package/package.json +4 -4
package/CHANGELOG.md CHANGED
@@ -10,6 +10,16 @@
10
10
 
11
11
  ### 💡 Others
12
12
 
13
+ ## 5.0.2 — 2024-04-22
14
+
15
+ _This version does not introduce any user-facing changes._
16
+
17
+ ## 5.0.1 — 2024-04-19
18
+
19
+ ### 🎉 New features
20
+
21
+ - Added bridgeless mode support on Android. ([#28162](https://github.com/expo/expo/pull/28162) by [@kudo](https://github.com/kudo))
22
+
13
23
  ## 5.0.0 — 2024-04-18
14
24
 
15
25
  ### 🛠 Breaking changes
package/README.md CHANGED
@@ -36,11 +36,11 @@ To update the JavaScript code inside the `app` folder, you need to run the `dev-
36
36
 
37
37
  1. Navigate to the `dev-menu` package: `cd packages/expo-dev-menu`
38
38
  2. Start the Metro bundler: `yarn start`
39
- 3. To use your local bundler on Android, open [DevMenuHost.java](/packages/expo-dev-menu/android/src/debug/java/expo/modules/devmenu/DevMenuHost.kt) and set `getUseDeveloperSupport` to `true`.
39
+ 3. To use your local bundler on Android, open [DevMenuManager.kt](/packages/expo-dev-menu/android/src/debug/java/expo/modules/devmenu/DevMenuManager.kt) and set `useDeveloperSupport` to `true`.
40
40
 
41
41
  ```diff
42
- - override fun getUseDeveloperSupport() = false
43
- + override fun getUseDeveloperSupport() = true
42
+ - val useDeveloperSupport = false
43
+ + val useDeveloperSupport = true
44
44
  ```
45
45
 
46
46
  4. Play with your changes on a simulator or device through `bare-expo`
@@ -1,7 +1,7 @@
1
1
  apply plugin: 'com.android.library'
2
2
 
3
3
  group = 'host.exp.exponent'
4
- version = '5.0.0'
4
+ version = '5.0.2'
5
5
 
6
6
  def expoModulesCorePlugin = new File(project(":expo-modules-core").projectDir.absolutePath, "ExpoModulesCorePlugin.gradle")
7
7
  apply from: expoModulesCorePlugin
@@ -20,7 +20,7 @@ android {
20
20
  namespace "expo.modules.devmenu"
21
21
  defaultConfig {
22
22
  versionCode 10
23
- versionName '5.0.0'
23
+ versionName '5.0.2'
24
24
  }
25
25
 
26
26
  buildTypes {
@@ -1,3 +1,5 @@
1
+ @file:Suppress("DEPRECATION")
2
+
1
3
  package expo.modules.devmenu
2
4
 
3
5
  import android.os.Build
@@ -11,15 +13,15 @@ import androidx.core.view.doOnLayout
11
13
  import com.facebook.react.ReactActivity
12
14
  import com.facebook.react.ReactActivityDelegate
13
15
  import com.facebook.react.ReactDelegate
14
- import com.facebook.react.ReactInstanceManager
15
16
  import com.facebook.react.ReactRootView
17
+ import com.facebook.react.config.ReactFeatureFlags
16
18
  import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint.fabricEnabled
17
19
  import com.facebook.react.defaults.DefaultReactActivityDelegate
18
- import com.facebook.react.devsupport.interfaces.DevSupportManager
20
+ import com.facebook.react.interfaces.fabric.ReactSurface
19
21
  import com.google.android.material.bottomsheet.BottomSheetBehavior
20
22
  import expo.modules.devmenu.helpers.getPrivateDeclaredFieldValue
21
23
  import expo.modules.devmenu.helpers.setPrivateDeclaredFieldValue
22
- import java.util.*
24
+ import java.util.UUID
23
25
 
24
26
  /**
25
27
  * The dev menu is launched using this activity.
@@ -47,6 +49,11 @@ class DevMenuActivity : ReactActivity() {
47
49
  super.loadApp(appKey)
48
50
  if (!rootViewWasInitialized()) {
49
51
  rootView = reactDelegate.reactRootView
52
+ if (ReactFeatureFlags.enableBridgelessArchitecture) {
53
+ reactSurface = reactDelegate::class.java.getPrivateDeclaredFieldValue(
54
+ "mReactSurface", reactDelegate
55
+ )
56
+ }
50
57
  }
51
58
  appWasLoaded = true
52
59
  return
@@ -56,6 +63,10 @@ class DevMenuActivity : ReactActivity() {
56
63
  .setPrivateDeclaredFieldValue("mFabricEnabled", reactDelegate, fabricEnabled)
57
64
  ReactDelegate::class.java
58
65
  .setPrivateDeclaredFieldValue("mReactRootView", reactDelegate, rootView)
66
+ if (ReactFeatureFlags.enableBridgelessArchitecture) {
67
+ ReactDelegate::class.java
68
+ .setPrivateDeclaredFieldValue("mReactSurface", reactDelegate, reactSurface)
69
+ }
59
70
 
60
71
  // Removes the root view from the previous activity
61
72
  (rootView.parent as? ViewGroup)?.removeView(rootView)
@@ -64,7 +75,9 @@ class DevMenuActivity : ReactActivity() {
64
75
  plainActivity.setContentView(reactDelegate.reactRootView)
65
76
  }
66
77
 
67
- override fun getReactNativeHost() = DevMenuManager.getMenuHost()
78
+ override fun getReactNativeHost() = requireNotNull(DevMenuManager.getMenuHost()).reactNativeHost
79
+
80
+ override fun getReactHost() = requireNotNull(DevMenuManager.getMenuHost()).reactHost
68
81
 
69
82
  override fun getLaunchOptions() = Bundle().apply {
70
83
  putString("uuid", UUID.randomUUID().toString())
@@ -102,16 +115,11 @@ class DevMenuActivity : ReactActivity() {
102
115
 
103
116
  override fun onStart() {
104
117
  super.onStart()
105
- val instanceManager = DevMenuManager.delegate?.reactInstanceManager() ?: return
118
+ val reactHost = DevMenuManager.delegate?.reactHost() ?: return
106
119
  val supportsDevelopment = DevMenuManager.delegate?.supportsDevelopment() ?: false
107
120
 
108
121
  if (supportsDevelopment) {
109
- val devSupportManager: DevSupportManager =
110
- ReactInstanceManager::class.java.getPrivateDeclaredFieldValue(
111
- "mDevSupportManager",
112
- instanceManager
113
- )
114
-
122
+ val devSupportManager = requireNotNull(reactHost.devSupportManager)
115
123
  devSupportManager.devSupportEnabled = true
116
124
  }
117
125
  }
@@ -121,6 +129,7 @@ class DevMenuActivity : ReactActivity() {
121
129
 
122
130
  val mainLayout = findViewById<CoordinatorLayout>(R.id.main_layout)
123
131
  val bottomSheet = findViewById<FrameLayout>(R.id.bottom_sheet)
132
+ (view?.parent as? ViewGroup)?.removeView(view)
124
133
  bottomSheet.addView(view)
125
134
 
126
135
  BottomSheetBehavior.from(bottomSheet).apply {
@@ -152,7 +161,13 @@ class DevMenuActivity : ReactActivity() {
152
161
  companion object {
153
162
  var appWasLoaded = false
154
163
  private lateinit var rootView: ReactRootView
164
+ private lateinit var reactSurface: ReactSurface
155
165
 
156
- private fun rootViewWasInitialized() = ::rootView.isInitialized
166
+ private fun rootViewWasInitialized(): Boolean {
167
+ if (ReactFeatureFlags.enableBridgelessArchitecture) {
168
+ return ::rootView.isInitialized && ::reactSurface.isInitialized
169
+ }
170
+ return ::rootView.isInitialized
171
+ }
157
172
  }
158
173
  }
@@ -2,11 +2,11 @@ package expo.modules.devmenu
2
2
 
3
3
  import android.content.pm.PackageManager
4
4
  import android.os.Bundle
5
- import com.facebook.react.ReactInstanceManager
6
5
  import com.facebook.react.bridge.ReactContext
6
+ import expo.interfaces.devmenu.ReactHostWrapper
7
7
 
8
8
  object DevMenuAppInfo {
9
- fun getAppInfo(instanceManager: ReactInstanceManager, reactContext: ReactContext): Bundle {
9
+ fun getAppInfo(reactHost: ReactHostWrapper, reactContext: ReactContext): Bundle {
10
10
  val packageManager = reactContext.packageManager
11
11
  val packageName = reactContext.packageName
12
12
  val packageInfo = packageManager.getPackageInfo(packageName, 0)
@@ -36,7 +36,7 @@ object DevMenuAppInfo {
36
36
  hostUrl = DevMenuManager.currentManifestURL
37
37
  }
38
38
 
39
- val jsExecutorName = instanceManager.jsExecutorName
39
+ val jsExecutorName = reactHost.jsExecutorName
40
40
  val engine = when {
41
41
  jsExecutorName.contains("Hermes") -> "Hermes"
42
42
  jsExecutorName.contains("V8") -> "V8"
@@ -10,6 +10,6 @@ class DevMenuDefaultExtensionSettings(
10
10
  return false
11
11
  }
12
12
 
13
- return manager.getReactInstanceManager()?.devSupportManager != null
13
+ return manager.getReactHost()?.devSupportManager != null
14
14
  }
15
15
  }
@@ -1,15 +1,15 @@
1
1
  package expo.modules.devmenu
2
2
 
3
3
  import android.os.Bundle
4
- import com.facebook.react.ReactInstanceManager
4
+ import expo.interfaces.devmenu.ReactHostWrapper
5
5
  import expo.modules.devmenu.devtools.DevMenuDevToolsDelegate
6
6
 
7
7
  object DevMenuDevSettings {
8
- fun getDevSettings(reactInstanceManager: ReactInstanceManager): Bundle {
9
- val devDelegate = DevMenuDevToolsDelegate(DevMenuManager, reactInstanceManager)
8
+ fun getDevSettings(reactHost: ReactHostWrapper): Bundle {
9
+ val devDelegate = DevMenuDevToolsDelegate(DevMenuManager, reactHost)
10
10
  val devSettings = devDelegate.devSettings
11
11
 
12
- val jsBundleURL = reactInstanceManager.devSupportManager.jsBundleURLForRemoteDebugging
12
+ val jsBundleURL = reactHost.devSupportManager?.jsBundleURLForRemoteDebugging ?: ""
13
13
 
14
14
  if (devSettings != null) {
15
15
  return Bundle().apply {
@@ -24,7 +24,7 @@ object DevMenuDevSettings {
24
24
  putBoolean(
25
25
  "isJSInspectorAvailable",
26
26
  run {
27
- val jsExecutorName = reactInstanceManager.jsExecutorName
27
+ val jsExecutorName = reactHost.jsExecutorName
28
28
  jsExecutorName.contains("Hermes") || jsExecutorName.contains("V8")
29
29
  }
30
30
  )
@@ -11,16 +11,16 @@ import android.util.Log
11
11
  import android.view.KeyEvent
12
12
  import android.view.MotionEvent
13
13
  import android.view.inputmethod.InputMethodManager
14
- import com.facebook.react.ReactInstanceManager
15
- import com.facebook.react.ReactNativeHost
14
+ import com.facebook.react.ReactInstanceEventListener
16
15
  import com.facebook.react.bridge.*
17
- import com.facebook.react.views.text.ReactFontManager
18
16
  import com.facebook.react.modules.core.DeviceEventManagerModule
17
+ import com.facebook.react.views.text.ReactFontManager
19
18
  import expo.interfaces.devmenu.DevMenuDelegateInterface
20
19
  import expo.interfaces.devmenu.DevMenuExtensionInterface
21
20
  import expo.interfaces.devmenu.DevMenuExtensionSettingsInterface
22
21
  import expo.interfaces.devmenu.DevMenuManagerInterface
23
22
  import expo.interfaces.devmenu.DevMenuPreferencesInterface
23
+ import expo.interfaces.devmenu.ReactHostWrapper
24
24
  import expo.interfaces.devmenu.items.DevMenuCallableProvider
25
25
  import expo.interfaces.devmenu.items.DevMenuDataSourceInterface
26
26
  import expo.interfaces.devmenu.items.DevMenuDataSourceItem
@@ -53,14 +53,17 @@ object DevMenuManager : DevMenuManagerInterface, LifecycleEventListener {
53
53
  val metroClient: DevMenuMetroClient by lazy { DevMenuMetroClient() }
54
54
  private var fontsWereLoaded = false
55
55
 
56
+ // change it and run `yarn start` in `expo-dev-menu` to launch dev menu from local packager
57
+ private val useDeveloperSupport = false
58
+
56
59
  private var shakeDetector: ShakeDetector? = null
57
60
  private var threeFingerLongPressDetector: ThreeFingerLongPressDetector? = null
58
61
  private var preferences: DevMenuPreferencesInterface? = null
59
62
  internal var delegate: DevMenuDelegateInterface? = null
60
63
  private var extensionSettings: DevMenuExtensionSettingsInterface = DevMenuDefaultExtensionSettings(this)
61
64
  private var shouldLaunchDevMenuOnStart: Boolean = false
62
- private lateinit var devMenuHost: DevMenuHost
63
- private var currentReactInstanceManager: WeakReference<ReactInstanceManager?> = WeakReference(null)
65
+ private lateinit var devMenuHost: ReactHostWrapper
66
+ private var currentReactInstance: WeakReference<ReactHostWrapper?> = WeakReference(null)
64
67
  private var currentScreenName: String? = null
65
68
  private var canLaunchDevMenuOnStart = true
66
69
  var testInterceptor: DevMenuTestInterceptor = DevMenuDisabledTestInterceptor()
@@ -70,18 +73,18 @@ object DevMenuManager : DevMenuManagerInterface, LifecycleEventListener {
70
73
 
71
74
  //region helpers
72
75
 
73
- fun getReactInstanceManager(): ReactInstanceManager? {
74
- return delegate?.reactInstanceManager()
76
+ fun getReactHost(): ReactHostWrapper? {
77
+ return delegate?.reactHost()
75
78
  }
76
79
 
77
80
  private val delegateReactContext: ReactContext?
78
- get() = delegate?.reactInstanceManager()?.currentReactContext
81
+ get() = delegate?.reactHost()?.currentReactContext
79
82
 
80
83
  private val delegateActivity: Activity?
81
84
  get() = delegateReactContext?.currentActivity
82
85
 
83
86
  private val hostReactContext: ReactContext?
84
- get() = devMenuHost.reactInstanceManager.currentReactContext
87
+ get() = devMenuHost.currentReactContext
85
88
 
86
89
  private val hostActivity: Activity?
87
90
  get() = hostReactContext?.currentActivity
@@ -92,20 +95,21 @@ object DevMenuManager : DevMenuManagerInterface, LifecycleEventListener {
92
95
  */
93
96
  private val delegateExtensions: Collection<DevMenuExtensionInterface>
94
97
  get() {
95
- val catalystInstance = delegateReactContext?.catalystInstance ?: return emptyList()
96
- val uniqueExtensionNames = catalystInstance
98
+ val reactContext = delegateReactContext ?: return emptyList()
99
+ val uniqueExtensionClasses = reactContext
97
100
  .nativeModules
98
101
  .filterIsInstance<DevMenuExtensionInterface>()
99
- .map { it.getName() }
102
+ .filterIsInstance<NativeModule>()
103
+ .map { it.javaClass }
100
104
  .toSet()
101
105
 
102
- return uniqueExtensionNames
103
- .map { extensionName ->
104
- catalystInstance.getNativeModule(extensionName) as DevMenuExtensionInterface
106
+ return uniqueExtensionClasses
107
+ .map { extensionClass ->
108
+ reactContext.getNativeModule(extensionClass) as DevMenuExtensionInterface
105
109
  }
106
110
  }
107
111
 
108
- private val cachedDevMenuDataSources by KeyValueCachedProperty<ReactInstanceManager, List<DevMenuDataSourceInterface>> {
112
+ private val cachedDevMenuDataSources by KeyValueCachedProperty<ReactHostWrapper, List<DevMenuDataSourceInterface>> {
109
113
  delegateExtensions
110
114
  .map { it.devMenuDataSources(extensionSettings) ?: emptyList() }
111
115
  .flatten()
@@ -113,11 +117,11 @@ object DevMenuManager : DevMenuManagerInterface, LifecycleEventListener {
113
117
 
114
118
  private val dataSources: List<DevMenuDataSourceInterface>
115
119
  get() {
116
- val delegateBridge = delegate?.reactInstanceManager() ?: return emptyList()
117
- return cachedDevMenuDataSources[delegateBridge]
120
+ val delegateReactHost = delegate?.reactHost() ?: return emptyList()
121
+ return cachedDevMenuDataSources[delegateReactHost]
118
122
  }
119
123
 
120
- private val cachedDevMenuScreens by KeyValueCachedProperty<ReactInstanceManager, List<DevMenuScreen>> {
124
+ private val cachedDevMenuScreens by KeyValueCachedProperty<ReactHostWrapper, List<DevMenuScreen>> {
121
125
  delegateExtensions
122
126
  .map { it.devMenuScreens(extensionSettings) ?: emptyList() }
123
127
  .flatten()
@@ -125,19 +129,19 @@ object DevMenuManager : DevMenuManagerInterface, LifecycleEventListener {
125
129
 
126
130
  private val delegateScreens: List<DevMenuScreen>
127
131
  get() {
128
- val delegateBridge = delegate?.reactInstanceManager() ?: return emptyList()
129
- return cachedDevMenuScreens[delegateBridge]
132
+ val delegateReactHost = delegate?.reactHost() ?: return emptyList()
133
+ return cachedDevMenuScreens[delegateReactHost]
130
134
  }
131
135
 
132
- private val cachedDevMenuItems by KeyValueCachedProperty<ReactInstanceManager, List<DevMenuItemsContainerInterface>> {
136
+ private val cachedDevMenuItems by KeyValueCachedProperty<ReactHostWrapper, List<DevMenuItemsContainerInterface>> {
133
137
  delegateExtensions
134
138
  .mapNotNull { it.devMenuItems(extensionSettings) }
135
139
  }
136
140
 
137
141
  private val delegateMenuItemsContainers: List<DevMenuItemsContainerInterface>
138
142
  get() {
139
- val delegateBridge = delegate?.reactInstanceManager() ?: return emptyList()
140
- return cachedDevMenuItems[delegateBridge]
143
+ val delegateReactHost = delegate?.reactHost() ?: return emptyList()
144
+ return cachedDevMenuItems[delegateReactHost]
141
145
  }
142
146
 
143
147
  private val delegateRootMenuItems: List<DevMenuScreenItem>
@@ -171,41 +175,44 @@ object DevMenuManager : DevMenuManagerInterface, LifecycleEventListener {
171
175
  @Suppress("UNCHECKED_CAST")
172
176
  private fun maybeInitDevMenuHost(application: Application) {
173
177
  if (!this::devMenuHost.isInitialized) {
174
- devMenuHost = DevMenuHost(application)
178
+ devMenuHost = ReactHostWrapper(
179
+ reactNativeHost = DevMenuReactNativeHost(application, useDeveloperSupport),
180
+ reactHost = DevMenuReactHost.create(application, useDeveloperSupport)
181
+ )
175
182
  UiThreadUtil.runOnUiThread {
176
- devMenuHost.reactInstanceManager.createReactContextInBackground()
183
+ devMenuHost.start()
177
184
  }
178
185
  }
179
186
  }
180
187
 
181
- private fun setUpReactInstanceManager(reactInstanceManager: ReactInstanceManager) {
182
- currentReactInstanceManager = WeakReference(reactInstanceManager)
188
+ private fun setUpReactInstance(reactHost: ReactHostWrapper) {
189
+ currentReactInstance = WeakReference(reactHost)
183
190
 
184
- val handlers = DevMenuCommandHandlersProvider(this, reactInstanceManager)
191
+ val handlers = DevMenuCommandHandlersProvider(this, reactHost)
185
192
  .createCommandHandlers()
186
193
 
187
194
  DevMenuPackagerCommandHandlersSwapper()
188
195
  .swapPackagerCommandHandlers(
189
- reactInstanceManager,
196
+ reactHost,
190
197
  handlers
191
198
  )
192
199
 
193
200
  DevMenuShakeDetectorListenerSwapper()
194
201
  .swapShakeDetectorListener(
195
- reactInstanceManager
202
+ reactHost
196
203
  ) {}
197
204
 
198
- if (reactInstanceManager.currentReactContext == null) {
199
- reactInstanceManager.addReactInstanceEventListener(object : ReactInstanceManager.ReactInstanceEventListener {
205
+ if (reactHost.currentReactContext == null) {
206
+ reactHost.addReactInstanceEventListener(object : ReactInstanceEventListener {
200
207
  override fun onReactContextInitialized(context: ReactContext) {
201
- if (currentReactInstanceManager.get() === reactInstanceManager) {
208
+ if (currentReactInstance.get() === reactHost) {
202
209
  handleLoadedDelegateContext(context)
203
210
  }
204
- reactInstanceManager.removeReactInstanceEventListener(this)
211
+ reactHost.removeReactInstanceEventListener(this)
205
212
  }
206
213
  })
207
214
  } else {
208
- handleLoadedDelegateContext(reactInstanceManager.currentReactContext!!)
215
+ handleLoadedDelegateContext(reactHost.currentReactContext!!)
209
216
  }
210
217
  }
211
218
 
@@ -225,7 +232,7 @@ object DevMenuManager : DevMenuManagerInterface, LifecycleEventListener {
225
232
  reactContext.currentActivity?.application
226
233
  ?: reactContext.applicationContext as Application
227
234
  )
228
- maybeStartDetectors(devMenuHost.getContext())
235
+ maybeStartDetectors(reactContext.applicationContext)
229
236
  preferences = (
230
237
  testInterceptor.overrideSettings()
231
238
  ?: DevMenuPreferencesHandle(reactContext)
@@ -243,15 +250,15 @@ object DevMenuManager : DevMenuManagerInterface, LifecycleEventListener {
243
250
 
244
251
  fun getAppInfo(): Bundle {
245
252
  val reactContext = delegateReactContext ?: return Bundle.EMPTY
246
- val instanceManager = delegate?.reactInstanceManager() ?: return Bundle.EMPTY
253
+ val reactHost = delegate?.reactHost() ?: return Bundle.EMPTY
247
254
 
248
- return DevMenuAppInfo.getAppInfo(instanceManager, reactContext)
255
+ return DevMenuAppInfo.getAppInfo(reactHost, reactContext)
249
256
  }
250
257
 
251
258
  fun getDevSettings(): Bundle {
252
- if (delegate?.reactInstanceManager() != null) {
253
- val reactInstanceManager = delegate!!.reactInstanceManager()
254
- return DevMenuDevSettings.getDevSettings(reactInstanceManager)
259
+ val reactHost = delegate?.reactHost()
260
+ if (reactHost != null) {
261
+ return DevMenuDevSettings.getDevSettings(reactHost)
255
262
  }
256
263
 
257
264
  return Bundle.EMPTY
@@ -428,12 +435,12 @@ object DevMenuManager : DevMenuManagerInterface, LifecycleEventListener {
428
435
  delegateReactContext?.removeLifecycleEventListener(this)
429
436
 
430
437
  delegate = newDelegate.apply {
431
- setUpReactInstanceManager(this.reactInstanceManager())
438
+ setUpReactInstance(this.reactHost())
432
439
  }
433
440
  }
434
441
 
435
- override fun initializeWithReactNativeHost(reactNativeHost: ReactNativeHost) {
436
- setDelegate(DevMenuDefaultDelegate(reactNativeHost))
442
+ override fun initializeWithReactHost(reactHost: ReactHostWrapper) {
443
+ setDelegate(DevMenuDefaultDelegate(reactHost))
437
444
  }
438
445
 
439
446
  override fun dispatchCallable(actionId: String, args: ReadableMap?) {
@@ -477,16 +484,16 @@ object DevMenuManager : DevMenuManagerInterface, LifecycleEventListener {
477
484
 
478
485
  override fun getSettings(): DevMenuPreferencesInterface? = preferences
479
486
 
480
- override fun getMenuHost(): ReactNativeHost = devMenuHost
487
+ override fun getMenuHost(): ReactHostWrapper = devMenuHost
481
488
 
482
489
  override fun setCanLaunchDevMenuOnStart(canLaunchDevMenuOnStart: Boolean) {
483
490
  this.canLaunchDevMenuOnStart = canLaunchDevMenuOnStart
484
491
  }
485
492
 
486
493
  override fun synchronizeDelegate() {
487
- val newReactInstanceManager = requireNotNull(delegate).reactInstanceManager()
488
- if (newReactInstanceManager != currentReactInstanceManager.get()) {
489
- setUpReactInstanceManager(newReactInstanceManager)
494
+ val newReactInstance = requireNotNull(delegate).reactHost()
495
+ if (newReactInstance != currentReactInstance.get()) {
496
+ setUpReactInstance(newReactInstance)
490
497
  }
491
498
  }
492
499
 
@@ -0,0 +1,148 @@
1
+ package expo.modules.devmenu
2
+
3
+ import android.app.Application
4
+ import android.content.Context
5
+ import android.util.Log
6
+ import com.facebook.react.JSEngineResolutionAlgorithm
7
+ import com.facebook.react.ReactHost
8
+ import com.facebook.react.ReactPackage
9
+ import com.facebook.react.bridge.JSBundleLoader
10
+ import com.facebook.react.common.annotations.UnstableReactNativeAPI
11
+ import com.facebook.react.defaults.DefaultComponentsRegistry
12
+ import com.facebook.react.defaults.DefaultReactHostDelegate
13
+ import com.facebook.react.defaults.DefaultTurboModuleManagerDelegate
14
+ import com.facebook.react.devsupport.DevMenuReactInternalSettings
15
+ import com.facebook.react.devsupport.DevServerHelper
16
+ import com.facebook.react.fabric.ComponentFactory
17
+ import com.facebook.react.interfaces.exceptionmanager.ReactJsExceptionHandler
18
+ import com.facebook.react.runtime.JSCInstance
19
+ import com.facebook.react.runtime.ReactHostImpl
20
+ import com.facebook.react.runtime.hermes.HermesInstance
21
+ import com.facebook.react.shell.MainReactPackage
22
+ import com.facebook.soloader.SoLoader
23
+ import devmenu.com.th3rdwave.safeareacontext.SafeAreaProviderManager
24
+ import expo.modules.adapters.react.ModuleRegistryAdapter
25
+ import expo.modules.adapters.react.ReactModuleRegistryProvider
26
+ import expo.modules.devmenu.modules.DevMenuInternalModule
27
+ import expo.modules.devmenu.modules.DevMenuPreferences
28
+ import expo.modules.kotlin.ModulesProvider
29
+ import java.io.BufferedReader
30
+ import java.io.FileNotFoundException
31
+ import java.io.InputStreamReader
32
+
33
+ object DevMenuReactHost {
34
+
35
+ @OptIn(UnstableReactNativeAPI::class)
36
+ fun create(application: Application, useDeveloperSupport: Boolean): ReactHost {
37
+ val jsBundleAssetPath = "EXDevMenuApp.android.js"
38
+ val jsBundleLoader =
39
+ JSBundleLoader.createAssetLoader(application, "assets://$jsBundleAssetPath", true)
40
+ val jsResolutionAlgorithm = createJSEngineResolutionAlgorithm(application)
41
+ val jsRuntimeFactory = if (jsResolutionAlgorithm == JSEngineResolutionAlgorithm.JSC) {
42
+ JSCInstance()
43
+ } else {
44
+ HermesInstance()
45
+ }
46
+ val jsMainModuleName = "index"
47
+ val defaultReactHostDelegate =
48
+ DefaultReactHostDelegate(
49
+ jsMainModulePath = jsMainModuleName,
50
+ jsBundleLoader = jsBundleLoader,
51
+ reactPackages = getPackages(),
52
+ jsRuntimeFactory = jsRuntimeFactory,
53
+ turboModuleManagerDelegateBuilder = DefaultTurboModuleManagerDelegate.Builder()
54
+ )
55
+ val reactJsExceptionHandler = ReactJsExceptionHandler { _ -> }
56
+ val componentFactory = ComponentFactory()
57
+ DefaultComponentsRegistry.register(componentFactory)
58
+ val reactHost = ReactHostImpl(
59
+ application,
60
+ defaultReactHostDelegate,
61
+ componentFactory,
62
+ useDeveloperSupport,
63
+ reactJsExceptionHandler,
64
+ useDeveloperSupport
65
+ )
66
+ .apply {
67
+ jsEngineResolutionAlgorithm = jsResolutionAlgorithm
68
+ }
69
+ if (useDeveloperSupport) {
70
+ injectDevServerSettings(application.applicationContext, reactHost)
71
+ }
72
+ return reactHost
73
+ }
74
+
75
+ private fun getPackages(): List<ReactPackage> {
76
+ val packages = mutableListOf(
77
+ MainReactPackage(null),
78
+ ModuleRegistryAdapter(
79
+ ReactModuleRegistryProvider(emptyList()),
80
+ object : ModulesProvider {
81
+ override fun getModulesList() =
82
+ listOf(
83
+ DevMenuInternalModule::class.java,
84
+ DevMenuPreferences::class.java,
85
+ SafeAreaProviderManager::class.java
86
+ )
87
+ }
88
+ ),
89
+ DevMenuPackage()
90
+ )
91
+
92
+ try {
93
+ val devLauncherPackage = Class.forName("expo.modules.devlauncher.DevLauncherPackage")
94
+ val pkg = devLauncherPackage.getConstructor().newInstance() as ReactPackage
95
+ packages.add(pkg)
96
+ } catch (e: ClassNotFoundException) {
97
+ // dev launcher is not installed in this project
98
+ }
99
+
100
+ return packages
101
+ }
102
+
103
+ private fun createJSEngineResolutionAlgorithm(application: Application): JSEngineResolutionAlgorithm {
104
+ SoLoader.init(application.applicationContext, /* native exopackage */ false)
105
+ if (SoLoader.getLibraryPath("libjsc.so") != null) {
106
+ return JSEngineResolutionAlgorithm.JSC
107
+ }
108
+ return JSEngineResolutionAlgorithm.HERMES
109
+ }
110
+
111
+ /**
112
+ * To use a different packager url, we need to replace internal RN objects.
113
+ */
114
+ private fun injectDevServerSettings(applicationContext: Context, reactHost: ReactHostImpl) {
115
+ try {
116
+ val serverIp = BufferedReader(
117
+ InputStreamReader(applicationContext.assets.open("dev-menu-packager-host"))
118
+ ).use {
119
+ it.readLine()
120
+ }
121
+
122
+ val devMenuInternalReactSettings = DevMenuReactInternalSettings(serverIp, applicationContext)
123
+
124
+ val devSupportManager = reactHost.devSupportManager
125
+ val devSupportManagerBaseClass = devSupportManager.javaClass.superclass!!
126
+ setPrivateField(
127
+ obj = devSupportManager,
128
+ objClass = devSupportManagerBaseClass,
129
+ field = "mDevSettings",
130
+ newValue = devMenuInternalReactSettings
131
+ )
132
+
133
+ val devServerHelper: DevServerHelper = getPrivateFiled(devSupportManager, devSupportManagerBaseClass, "mDevServerHelper")
134
+ setPrivateField(
135
+ obj = devServerHelper,
136
+ objClass = devServerHelper.javaClass,
137
+ field = "mSettings",
138
+ newValue = devMenuInternalReactSettings
139
+ )
140
+
141
+ Log.i(DEV_MENU_TAG, "DevSettings was correctly injected.")
142
+ } catch (e: FileNotFoundException) {
143
+ Log.e(DEV_MENU_TAG, "Couldn't find `dev-menu-packager-host` file.", e)
144
+ } catch (e: Exception) {
145
+ Log.e(DEV_MENU_TAG, "Couldn't inject DevSettings object.", e)
146
+ }
147
+ }
148
+ }
@@ -1,7 +1,6 @@
1
1
  package expo.modules.devmenu
2
2
 
3
3
  import android.app.Application
4
- import android.content.Context
5
4
  import android.util.Log
6
5
  import com.facebook.react.ReactInstanceManager
7
6
  import com.facebook.react.ReactPackage
@@ -24,7 +23,8 @@ import java.io.InputStreamReader
24
23
  /**
25
24
  * Class that represents react host used by dev menu.
26
25
  */
27
- class DevMenuHost(application: Application) : DefaultReactNativeHost(application) {
26
+ class DevMenuReactNativeHost(application: Application, private val useDeveloperSupport: Boolean) :
27
+ DefaultReactNativeHost(application) {
28
28
 
29
29
  override fun getPackages(): List<ReactPackage> {
30
30
  val packages = mutableListOf(
@@ -54,14 +54,12 @@ class DevMenuHost(application: Application) : DefaultReactNativeHost(application
54
54
  return packages
55
55
  }
56
56
 
57
- override fun getUseDeveloperSupport() = false // change it and run `yarn start` in `expo-dev-menu` to launch dev menu from local packager
57
+ override fun getUseDeveloperSupport() = useDeveloperSupport
58
58
 
59
59
  override fun getBundleAssetName() = "EXDevMenuApp.android.js"
60
60
 
61
61
  override fun getJSMainModuleName() = "index"
62
62
 
63
- fun getContext(): Context = super.getApplication()
64
-
65
63
  override fun getJavaScriptExecutorFactory(): JavaScriptExecutorFactory? {
66
64
  return createNonDebuggableJavaScriptExecutorFactory(application)
67
65
  }
@@ -70,8 +68,8 @@ class DevMenuHost(application: Application) : DefaultReactNativeHost(application
70
68
  val reactInstanceManager = super.createReactInstanceManager()
71
69
 
72
70
  if (useDeveloperSupport) {
73
- // To use a different packager url, we need to replace internal RN objects.
74
71
  try {
72
+ // To use a different packager url, we need to replace internal RN objects.
75
73
  val serverIp = BufferedReader(
76
74
  InputStreamReader(super.getApplication().assets.open("dev-menu-packager-host"))
77
75
  ).use {
@@ -4,6 +4,8 @@ import android.os.Bundle
4
4
  import android.view.KeyEvent
5
5
  import android.view.MotionEvent
6
6
  import com.facebook.react.ReactActivity
7
+ import com.facebook.react.ReactApplication
8
+ import expo.interfaces.devmenu.ReactHostWrapper
7
9
  import expo.modules.devmenu.DevMenuManager
8
10
 
9
11
  /**
@@ -14,7 +16,12 @@ abstract class DevMenuAwareReactActivity : ReactActivity() {
14
16
  override fun onPostCreate(savedInstanceState: Bundle?) {
15
17
  super.onPostCreate(savedInstanceState)
16
18
  if (!DevMenuManager.isInitialized()) {
17
- DevMenuManager.initializeWithReactNativeHost(reactNativeHost)
19
+ DevMenuManager.initializeWithReactHost(
20
+ ReactHostWrapper(
21
+ reactNativeHost = reactNativeHost,
22
+ reactHost = (applicationContext as ReactApplication).reactHost
23
+ )
24
+ )
18
25
  } else {
19
26
  DevMenuManager.synchronizeDelegate()
20
27
  }
@@ -9,7 +9,7 @@ import expo.modules.devmenu.react.DevMenuPackagerConnectionSettings
9
9
 
10
10
  /**
11
11
  * Class representing react's internal [DevInternalSettings] class, which we want to replace to change [packagerConnectionSettings] and others settings.
12
- * It is only use when [expo.modules.devmenu.DevMenuHost.getUseDeveloperSupport] returns true.
12
+ * It is only use when [expo.modules.devmenu.DevMenuReactNativeHost.getUseDeveloperSupport] returns true.
13
13
  */
14
14
  internal class DevMenuReactInternalSettings(
15
15
  serverIp: String,
@@ -1,17 +1,16 @@
1
1
  package expo.modules.devmenu
2
2
 
3
3
  import android.os.Bundle
4
- import com.facebook.react.ReactInstanceManager
5
- import com.facebook.react.ReactNativeHost
6
4
  import expo.interfaces.devmenu.DevMenuDelegateInterface
5
+ import expo.interfaces.devmenu.ReactHostWrapper
7
6
 
8
7
  /**
9
8
  * Basic [DevMenuDelegateInterface] implementation.
10
9
  */
11
10
  class DevMenuDefaultDelegate(
12
- private val delegateHost: ReactNativeHost
11
+ private val delegateHost: ReactHostWrapper
13
12
  ) : DevMenuDelegateInterface {
14
13
  override fun appInfo(): Bundle? = null
15
14
 
16
- override fun reactInstanceManager(): ReactInstanceManager = delegateHost.reactInstanceManager
15
+ override fun reactHost(): ReactHostWrapper = delegateHost
17
16
  }
@@ -12,6 +12,7 @@ import com.facebook.react.bridge.NativeModule
12
12
  import com.facebook.react.bridge.ReactApplicationContext
13
13
  import com.facebook.react.uimanager.ReactShadowNode
14
14
  import com.facebook.react.uimanager.ViewManager
15
+ import expo.interfaces.devmenu.ReactHostWrapper
15
16
  import expo.modules.core.interfaces.Package
16
17
  import expo.modules.core.interfaces.ReactActivityHandler
17
18
  import expo.modules.core.interfaces.ReactActivityLifecycleListener
@@ -37,7 +38,12 @@ class DevMenuPackage : Package, ReactPackage {
37
38
  object : ReactActivityLifecycleListener {
38
39
  override fun onCreate(activity: Activity, savedInstanceState: Bundle?) {
39
40
  if (!DevMenuManager.isInitialized()) {
40
- DevMenuManager.initializeWithReactNativeHost((activity.application as ReactApplication).reactNativeHost)
41
+ DevMenuManager.initializeWithReactHost(
42
+ ReactHostWrapper(
43
+ reactNativeHost = (activity.application as ReactApplication).reactNativeHost,
44
+ reactHost = (activity.application as ReactApplication).reactHost
45
+ )
46
+ )
41
47
  } else {
42
48
  DevMenuManager.synchronizeDelegate()
43
49
  }
@@ -5,10 +5,10 @@ import android.content.Intent
5
5
  import android.net.Uri
6
6
  import android.provider.Settings
7
7
  import android.util.Log
8
- import com.facebook.react.ReactInstanceManager
9
8
  import com.facebook.react.bridge.UiThreadUtil
10
9
  import com.facebook.react.devsupport.DevMenuInternalSettingsWrapper
11
10
  import expo.interfaces.devmenu.DevMenuManagerInterface
11
+ import expo.interfaces.devmenu.ReactHostWrapper
12
12
  import expo.modules.devmenu.DEV_MENU_TAG
13
13
  import expo.modules.devmenu.DevMenuManager
14
14
  import kotlinx.coroutines.launch
@@ -16,13 +16,13 @@ import java.lang.ref.WeakReference
16
16
 
17
17
  class DevMenuDevToolsDelegate(
18
18
  private val manager: DevMenuManagerInterface,
19
- reactInstanceManager: ReactInstanceManager
19
+ reactHost: ReactHostWrapper
20
20
  ) {
21
21
  private val _reactDevManager = WeakReference(
22
- reactInstanceManager.devSupportManager
22
+ reactHost.devSupportManager
23
23
  )
24
24
  private val _reactContext = WeakReference(
25
- reactInstanceManager.currentReactContext
25
+ reactHost.currentReactContext
26
26
  )
27
27
 
28
28
  val reactDevManager
@@ -5,6 +5,7 @@ import android.view.KeyEvent
5
5
  import com.facebook.react.bridge.ReactApplicationContext
6
6
  import com.facebook.react.bridge.ReactContextBaseJavaModule
7
7
  import com.facebook.react.devsupport.HMRClient
8
+ import com.facebook.react.module.annotations.ReactModule
8
9
  import expo.interfaces.devmenu.DevMenuExtensionInterface
9
10
  import expo.interfaces.devmenu.DevMenuExtensionSettingsInterface
10
11
  import expo.interfaces.devmenu.items.DevMenuDataSourceInterface
@@ -16,6 +17,7 @@ import expo.modules.devmenu.DEV_MENU_TAG
16
17
  import expo.modules.devmenu.DevMenuManager
17
18
  import expo.modules.devmenu.devtools.DevMenuDevToolsDelegate
18
19
 
20
+ @ReactModule(name = "ExpoDevMenuExtensions")
19
21
  class DevMenuExtension(reactContext: ReactApplicationContext) :
20
22
  ReactContextBaseJavaModule(reactContext), DevMenuExtensionInterface {
21
23
  override fun getName() = "ExpoDevMenuExtensions"
@@ -26,13 +28,13 @@ class DevMenuExtension(reactContext: ReactApplicationContext) :
26
28
  }
27
29
 
28
30
  val manager = DevMenuManager
29
- val reactInstanceManager = manager.getReactInstanceManager()
30
- if (reactInstanceManager == null) {
31
- Log.w(DEV_MENU_TAG, "Couldn't export dev-menu items, because the react instance manager isn't present.")
31
+ val reactHost = manager.getReactHost()
32
+ if (reactHost == null) {
33
+ Log.w(DEV_MENU_TAG, "Couldn't export dev-menu items, because the react instance isn't present.")
32
34
  return@export
33
35
  }
34
36
 
35
- val devDelegate = DevMenuDevToolsDelegate(settings.manager, reactInstanceManager)
37
+ val devDelegate = DevMenuDevToolsDelegate(settings.manager, reactHost)
36
38
  val reactDevManager = devDelegate.reactDevManager
37
39
  val devSettings = devDelegate.devSettings
38
40
 
@@ -45,9 +45,8 @@ class DevMenuInternalModule : Module() {
45
45
  }
46
46
 
47
47
  AsyncFunction<Unit>("openDevMenuFromReactNative") {
48
- val instanceManager = DevMenuManager.getReactInstanceManager() ?: return@AsyncFunction
49
- val devSupportManager = instanceManager.devSupportManager
50
- val activity = instanceManager.currentReactContext?.currentActivity ?: return@AsyncFunction
48
+ val devSupportManager = DevMenuManager.getReactHost()?.devSupportManager ?: return@AsyncFunction
49
+ val activity = DevMenuManager.getReactHost()?.currentReactContext?.currentActivity ?: return@AsyncFunction
51
50
 
52
51
  activity.runOnUiThread {
53
52
  DevMenuManager.closeMenu()
@@ -1,12 +1,12 @@
1
1
  package expo.modules.devmenu.react
2
2
 
3
3
  import android.util.Log
4
- import com.facebook.react.ReactInstanceManager
5
4
  import com.facebook.react.devsupport.DevServerHelper
6
5
  import com.facebook.react.devsupport.DevSupportManagerBase
7
6
  import com.facebook.react.devsupport.interfaces.DevSupportManager
8
7
  import com.facebook.react.packagerconnection.JSPackagerClient
9
8
  import com.facebook.react.packagerconnection.RequestHandler
9
+ import expo.interfaces.devmenu.ReactHostWrapper
10
10
  import expo.modules.devmenu.DevMenuManager
11
11
  import expo.modules.devmenu.helpers.getPrivateDeclaredFieldValue
12
12
  import expo.modules.devmenu.helpers.setPrivateDeclaredFieldValue
@@ -15,15 +15,11 @@ import kotlinx.coroutines.launch
15
15
 
16
16
  class DevMenuPackagerCommandHandlersSwapper {
17
17
  fun swapPackagerCommandHandlers(
18
- reactInstanceManager: ReactInstanceManager,
18
+ reactHost: ReactHostWrapper,
19
19
  handlers: Map<String, RequestHandler>
20
20
  ) {
21
21
  try {
22
- val devSupportManager: DevSupportManager =
23
- ReactInstanceManager::class.java.getPrivateDeclaredFieldValue(
24
- "mDevSupportManager",
25
- reactInstanceManager
26
- )
22
+ val devSupportManager: DevSupportManager = requireNotNull(reactHost.devSupportManager)
27
23
 
28
24
  // We don't want to add handlers into `DisabledDevSupportManager` or other custom classes
29
25
  if (devSupportManager !is DevSupportManagerBase) {
@@ -45,7 +41,7 @@ class DevMenuPackagerCommandHandlersSwapper {
45
41
  newCommandHandlers
46
42
  )
47
43
 
48
- swapCurrentCommandHandlers(reactInstanceManager, handlers)
44
+ swapCurrentCommandHandlers(reactHost, handlers)
49
45
  } catch (e: Exception) {
50
46
  Log.w("DevMenu", "Couldn't add packager command handlers to current client: ${e.message}", e)
51
47
  }
@@ -64,17 +60,13 @@ class DevMenuPackagerCommandHandlersSwapper {
64
60
  * The final solution is to spin a background task that monitors if the client is present.
65
61
  */
66
62
  private fun swapCurrentCommandHandlers(
67
- reactInstanceManager: ReactInstanceManager,
63
+ reactHost: ReactHostWrapper,
68
64
  handlers: Map<String, RequestHandler>
69
65
  ) {
70
66
  DevMenuManager.coroutineScope.launch {
71
67
  try {
72
68
  while (true) {
73
- val devSupportManager: DevSupportManagerBase =
74
- ReactInstanceManager::class.java.getPrivateDeclaredFieldValue(
75
- "mDevSupportManager",
76
- reactInstanceManager
77
- )
69
+ val devSupportManager = requireNotNull(reactHost.devSupportManager)
78
70
 
79
71
  val devServerHelper: DevServerHelper =
80
72
  DevSupportManagerBase::class.java.getPrivateDeclaredFieldValue(
@@ -1,24 +1,19 @@
1
1
  package expo.modules.devmenu.react
2
2
 
3
3
  import android.util.Log
4
- import com.facebook.react.ReactInstanceManager
5
4
  import com.facebook.react.common.ShakeDetector
6
5
  import com.facebook.react.devsupport.DevSupportManagerBase
7
- import com.facebook.react.devsupport.interfaces.DevSupportManager
6
+ import expo.interfaces.devmenu.ReactHostWrapper
8
7
  import expo.modules.devmenu.helpers.getPrivateDeclaredFieldValue
9
8
  import expo.modules.devmenu.helpers.setPrivateDeclaredFieldValue
10
9
 
11
10
  class DevMenuShakeDetectorListenerSwapper {
12
11
  fun swapShakeDetectorListener(
13
- reactInstanceManager: ReactInstanceManager,
12
+ reactHost: ReactHostWrapper,
14
13
  newListener: ShakeDetector.ShakeListener
15
14
  ) {
16
15
  try {
17
- val devSupportManager: DevSupportManager =
18
- ReactInstanceManager::class.java.getPrivateDeclaredFieldValue(
19
- "mDevSupportManager",
20
- reactInstanceManager
21
- )
16
+ val devSupportManager = requireNotNull(reactHost.devSupportManager)
22
17
 
23
18
  // We don't want to add handlers into `DisabledDevSupportManager` or other custom classes
24
19
  if (devSupportManager !is DevSupportManagerBase) {
@@ -1,43 +1,43 @@
1
1
  package expo.modules.devmenu.websockets
2
2
 
3
3
  import android.util.Log
4
- import com.facebook.react.ReactInstanceManager
5
4
  import com.facebook.react.bridge.UiThreadUtil
6
5
  import com.facebook.react.modules.core.RCTNativeAppEventEmitter
7
6
  import com.facebook.react.packagerconnection.NotificationOnlyHandler
8
7
  import expo.interfaces.devmenu.DevMenuManagerInterface
8
+ import expo.interfaces.devmenu.ReactHostWrapper
9
9
  import expo.modules.devmenu.devtools.DevMenuDevToolsDelegate
10
10
  import org.json.JSONObject
11
11
  import java.lang.ref.WeakReference
12
12
 
13
13
  class DevMenuCommandHandlersProvider(
14
14
  private val manager: DevMenuManagerInterface,
15
- reactInstanceManager: ReactInstanceManager
15
+ reactHost: ReactHostWrapper
16
16
  ) {
17
- private val _instanceManager = WeakReference(reactInstanceManager)
18
- private val instanceManager
19
- get() = _instanceManager.get()
17
+ private val _host = WeakReference(reactHost)
18
+ private val host
19
+ get() = _host.get()
20
20
 
21
21
  private val onReload = object : NotificationOnlyHandler() {
22
22
  override fun onNotification(params: Any?) {
23
23
  manager.closeMenu()
24
24
  UiThreadUtil.runOnUiThread {
25
- instanceManager?.devSupportManager?.handleReloadJS()
25
+ host?.devSupportManager?.handleReloadJS()
26
26
  }
27
27
  }
28
28
  }
29
29
 
30
30
  private val onDevMenu = object : NotificationOnlyHandler() {
31
31
  override fun onNotification(params: Any?) {
32
- val activity = instanceManager?.currentReactContext?.currentActivity ?: return
32
+ val activity = host?.currentReactContext?.currentActivity ?: return
33
33
  manager.toggleMenu(activity)
34
34
  }
35
35
  }
36
36
 
37
37
  private val onDevCommand = object : NotificationOnlyHandler() {
38
38
  override fun onNotification(params: Any?) {
39
- val instanceManager = instanceManager ?: return
40
- val devDelegate = DevMenuDevToolsDelegate(manager, instanceManager)
39
+ val host = host ?: return
40
+ val devDelegate = DevMenuDevToolsDelegate(manager, host)
41
41
 
42
42
  if (params is JSONObject) {
43
43
  val command = params.optString("name") ?: return
@@ -45,20 +45,20 @@ class DevMenuCommandHandlersProvider(
45
45
  when (command) {
46
46
  "reload" -> devDelegate.reload()
47
47
  "toggleDevMenu" -> {
48
- val activity = instanceManager.currentReactContext?.currentActivity ?: return
48
+ val activity = host.currentReactContext?.currentActivity ?: return
49
49
  manager.toggleMenu(activity)
50
50
  }
51
51
  "toggleRemoteDebugging" -> devDelegate.toggleRemoteDebugging()
52
52
  "toggleElementInspector" -> devDelegate.toggleElementInspector()
53
53
  "togglePerformanceMonitor" -> {
54
- val activity = instanceManager.currentReactContext?.currentActivity ?: return
54
+ val activity = host.currentReactContext?.currentActivity ?: return
55
55
  devDelegate.togglePerformanceMonitor(activity)
56
56
  }
57
57
  "openJSInspector" -> devDelegate.openJSInspector()
58
58
  "reconnectReactDevTools" -> {
59
59
  // Emit the `RCTDevMenuShown` for the app to reconnect react-devtools
60
60
  // https://github.com/facebook/react-native/blob/22ba1e45c52edcc345552339c238c1f5ef6dfc65/Libraries/Core/setUpReactDevTools.js#L80
61
- instanceManager.currentReactContext?.getJSModule(RCTNativeAppEventEmitter::class.java)?.emit("RCTDevMenuShown", null)
61
+ host.currentReactContext?.getJSModule(RCTNativeAppEventEmitter::class.java)?.emit("RCTDevMenuShown", null)
62
62
  }
63
63
  else -> Log.w("DevMenu", "Unknown command: $command")
64
64
  }
@@ -5,7 +5,7 @@ import com.facebook.react.packagerconnection.PackagerConnectionSettings
5
5
 
6
6
  /**
7
7
  * Class representing react's internal [PackagerConnectionSettings] class, which we want to replace to change bundler's url.
8
- * It is only use when [expo.modules.devmenu.DevMenuHost.getUseDeveloperSupport] returns true.
8
+ * It is only use when [expo.modules.devmenu.DevMenuReactNativeHost.getUseDeveloperSupport] returns true.
9
9
  */
10
10
  class DevMenuPackagerConnectionSettings(
11
11
  private val serverIp: String,
@@ -5,16 +5,15 @@ import android.content.Context
5
5
  import android.os.Bundle
6
6
  import android.view.KeyEvent
7
7
  import android.view.MotionEvent
8
- import com.facebook.react.ReactInstanceManager
9
- import com.facebook.react.ReactNativeHost
10
8
  import com.facebook.react.bridge.ReadableMap
11
9
  import expo.interfaces.devmenu.DevMenuDelegateInterface
12
10
  import expo.interfaces.devmenu.DevMenuManagerInterface
13
11
  import expo.interfaces.devmenu.DevMenuPreferencesInterface
12
+ import expo.interfaces.devmenu.ReactHostWrapper
14
13
  import expo.interfaces.devmenu.items.DevMenuDataSourceItem
15
14
  import expo.modules.devmenu.api.DevMenuMetroClient
16
- import kotlinx.coroutines.CoroutineScope
17
15
  import expo.modules.manifests.core.Manifest
16
+ import kotlinx.coroutines.CoroutineScope
18
17
 
19
18
  private const val DEV_MENU_IS_NOT_AVAILABLE = "DevMenu isn't available in release builds"
20
19
 
@@ -28,7 +27,7 @@ object DevMenuManager : DevMenuManagerInterface {
28
27
 
29
28
  var registeredCallbacks = arrayListOf<Callback>()
30
29
 
31
- fun getReactInstanceManager(): ReactInstanceManager? {
30
+ fun getReactHost(): ReactHostWrapper? {
32
31
  return null
33
32
  }
34
33
 
@@ -66,7 +65,7 @@ object DevMenuManager : DevMenuManagerInterface {
66
65
 
67
66
  override fun setDelegate(newDelegate: DevMenuDelegateInterface) = Unit
68
67
 
69
- override fun initializeWithReactNativeHost(reactNativeHost: ReactNativeHost) = Unit
68
+ override fun initializeWithReactHost(reactHost: ReactHostWrapper) = Unit
70
69
 
71
70
  override fun dispatchCallable(actionId: String, args: ReadableMap?) {
72
71
  throw IllegalStateException(DEV_MENU_IS_NOT_AVAILABLE)
@@ -88,7 +87,7 @@ object DevMenuManager : DevMenuManagerInterface {
88
87
  throw IllegalStateException(DEV_MENU_IS_NOT_AVAILABLE)
89
88
  }
90
89
 
91
- override fun getMenuHost(): ReactNativeHost {
90
+ override fun getMenuHost(): ReactHostWrapper {
92
91
  throw IllegalStateException(DEV_MENU_IS_NOT_AVAILABLE)
93
92
  }
94
93
 
@@ -1 +1 @@
1
- {"version":3,"file":"DevMenu.js","sourceRoot":"","sources":["../src/DevMenu.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAElD,OAAO,WAAW,MAAM,eAAe,CAAC;AAGxC;;GAEG;AACH,MAAM,UAAU,QAAQ;IACtB,WAAW,CAAC,QAAQ,EAAE,CAAC;AACzB,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,QAAQ;IACtB,WAAW,CAAC,QAAQ,EAAE,CAAC;AACzB,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,SAAS;IACvB,WAAW,CAAC,SAAS,EAAE,CAAC;AAC1B,CAAC;AAED,IAAI,6BAA6B,GAAG,KAAK,CAAC;AAE1C,SAAS,wBAAwB;IAC/B,IAAI,CAAC,6BAA6B,EAAE,CAAC;QACnC,kBAAkB,CAAC,WAAW,CAAC,yBAAyB,EAAE,CAAC,IAAY,EAAE,EAAE;YACzE,6BAA6B,GAAG,IAAI,CAAC;YACrC,MAAM,OAAO,GAAG,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;YAEnC,IAAI,OAAO,IAAI,IAAI,EAAE,CAAC;gBACpB,OAAO,EAAE,CAAC;YACZ,CAAC;QACH,CAAC,CAAC,CAAC;IACL,CAAC;AACH,CAAC;AAED,wBAAwB,EAAE,CAAC;AAE3B,IAAI,QAAQ,GAAG,IAAI,GAAG,EAAsB,CAAC;AAE7C;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,oBAAoB,CAAC,KAAwB;IACjE,QAAQ,GAAG,IAAI,GAAG,EAAE,CAAC;IACrB,MAAM,aAAa,GAAiD,EAAE,CAAC;IAEvE,KAAK,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,EAAE;QACrB,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC;QACvC,aAAa,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,cAAc,EAAE,IAAI,CAAC,cAAc,EAAE,CAAC,CAAC;IAC/E,CAAC,CAAC,CAAC;IAEH,OAAO,MAAM,WAAW,CAAC,mBAAmB,CAAC,aAAa,CAAC,CAAC;AAC9D,CAAC","sourcesContent":["import { DeviceEventEmitter } from 'react-native';\n\nimport ExpoDevMenu from './ExpoDevMenu';\nimport { ExpoDevMenuItem } from './ExpoDevMenu.types';\n\n/**\n * A method that opens development client menu when called.\n */\nexport function openMenu(): void {\n ExpoDevMenu.openMenu();\n}\n\n/**\n * A method that hides development client menu when called.\n */\nexport function hideMenu(): void {\n ExpoDevMenu.hideMenu();\n}\n\n/**\n * A method that closes development client menu when called.\n */\nexport function closeMenu(): void {\n ExpoDevMenu.closeMenu();\n}\n\nlet hasRegisteredCallbackListener = false;\n\nfunction registerCallbackListener() {\n if (!hasRegisteredCallbackListener) {\n DeviceEventEmitter.addListener('registeredCallbackFired', (name: string) => {\n hasRegisteredCallbackListener = true;\n const handler = handlers.get(name);\n\n if (handler != null) {\n handler();\n }\n });\n }\n}\n\nregisterCallbackListener();\n\nlet handlers = new Map<string, () => void>();\n\n/**\n * A method that allows to specify custom entries in the development client menu.\n * @param items\n */\nexport async function registerDevMenuItems(items: ExpoDevMenuItem[]): Promise<void> {\n handlers = new Map();\n const callbackNames: { name: string; shouldCollapse?: boolean }[] = [];\n\n items.forEach((item) => {\n handlers.set(item.name, item.callback);\n callbackNames.push({ name: item.name, shouldCollapse: item.shouldCollapse });\n });\n\n return await ExpoDevMenu.addDevMenuCallbacks(callbackNames);\n}\n"]}
1
+ {"version":3,"file":"DevMenu.js","sourceRoot":"","sources":["../src/DevMenu.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAElD,OAAO,WAAW,MAAM,eAAe,CAAC;AAGxC;;GAEG;AACH,MAAM,UAAU,QAAQ;IACtB,WAAW,CAAC,QAAQ,EAAE,CAAC;AACzB,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,QAAQ;IACtB,WAAW,CAAC,QAAQ,EAAE,CAAC;AACzB,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,SAAS;IACvB,WAAW,CAAC,SAAS,EAAE,CAAC;AAC1B,CAAC;AAED,IAAI,6BAA6B,GAAG,KAAK,CAAC;AAE1C,SAAS,wBAAwB;IAC/B,IAAI,CAAC,6BAA6B,EAAE;QAClC,kBAAkB,CAAC,WAAW,CAAC,yBAAyB,EAAE,CAAC,IAAY,EAAE,EAAE;YACzE,6BAA6B,GAAG,IAAI,CAAC;YACrC,MAAM,OAAO,GAAG,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;YAEnC,IAAI,OAAO,IAAI,IAAI,EAAE;gBACnB,OAAO,EAAE,CAAC;aACX;QACH,CAAC,CAAC,CAAC;KACJ;AACH,CAAC;AAED,wBAAwB,EAAE,CAAC;AAE3B,IAAI,QAAQ,GAAG,IAAI,GAAG,EAAsB,CAAC;AAE7C;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,oBAAoB,CAAC,KAAwB;IACjE,QAAQ,GAAG,IAAI,GAAG,EAAE,CAAC;IACrB,MAAM,aAAa,GAAiD,EAAE,CAAC;IAEvE,KAAK,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,EAAE;QACrB,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC;QACvC,aAAa,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,cAAc,EAAE,IAAI,CAAC,cAAc,EAAE,CAAC,CAAC;IAC/E,CAAC,CAAC,CAAC;IAEH,OAAO,MAAM,WAAW,CAAC,mBAAmB,CAAC,aAAa,CAAC,CAAC;AAC9D,CAAC","sourcesContent":["import { DeviceEventEmitter } from 'react-native';\n\nimport ExpoDevMenu from './ExpoDevMenu';\nimport { ExpoDevMenuItem } from './ExpoDevMenu.types';\n\n/**\n * A method that opens development client menu when called.\n */\nexport function openMenu(): void {\n ExpoDevMenu.openMenu();\n}\n\n/**\n * A method that hides development client menu when called.\n */\nexport function hideMenu(): void {\n ExpoDevMenu.hideMenu();\n}\n\n/**\n * A method that closes development client menu when called.\n */\nexport function closeMenu(): void {\n ExpoDevMenu.closeMenu();\n}\n\nlet hasRegisteredCallbackListener = false;\n\nfunction registerCallbackListener() {\n if (!hasRegisteredCallbackListener) {\n DeviceEventEmitter.addListener('registeredCallbackFired', (name: string) => {\n hasRegisteredCallbackListener = true;\n const handler = handlers.get(name);\n\n if (handler != null) {\n handler();\n }\n });\n }\n}\n\nregisterCallbackListener();\n\nlet handlers = new Map<string, () => void>();\n\n/**\n * A method that allows to specify custom entries in the development client menu.\n * @param items\n */\nexport async function registerDevMenuItems(items: ExpoDevMenuItem[]): Promise<void> {\n handlers = new Map();\n const callbackNames: { name: string; shouldCollapse?: boolean }[] = [];\n\n items.forEach((item) => {\n handlers.set(item.name, item.callback);\n callbackNames.push({ name: item.name, shouldCollapse: item.shouldCollapse });\n });\n\n return await ExpoDevMenu.addDevMenuCallbacks(callbackNames);\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"ExpoDevMenu.web.d.ts","sourceRoot":"","sources":["../src/ExpoDevMenu.web.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;wBAgB7C,WAAW;AAbhB,wBAaiB"}
1
+ {"version":3,"file":"ExpoDevMenu.web.d.ts","sourceRoot":"","sources":["../src/ExpoDevMenu.web.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;;AAGlD,wBAaiB"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "expo-dev-menu",
3
- "version": "5.0.0",
3
+ "version": "5.0.2",
4
4
  "description": "Expo/React Native module with the developer menu.",
5
5
  "main": "build/DevMenu.js",
6
6
  "types": "build/DevMenu.d.ts",
@@ -47,7 +47,7 @@
47
47
  ]
48
48
  },
49
49
  "dependencies": {
50
- "expo-dev-menu-interface": "1.8.0",
50
+ "expo-dev-menu-interface": "1.8.2",
51
51
  "semver": "^7.5.4"
52
52
  },
53
53
  "devDependencies": {
@@ -63,12 +63,12 @@
63
63
  "graphql": "^15.3.0",
64
64
  "graphql-tag": "^2.10.1",
65
65
  "react": "18.2.0",
66
- "react-native": "0.74.0-rc.9",
66
+ "react-native": "0.74.0",
67
67
  "url": "^0.11.0",
68
68
  "use-subscription": "^1.8.0"
69
69
  },
70
70
  "peerDependencies": {
71
71
  "expo": "*"
72
72
  },
73
- "gitHead": "4165b8d72e1b9a1889c2767534cc619e21468110"
73
+ "gitHead": "0897aeadb926491a457bcd67d83360956994ee82"
74
74
  }