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.
- package/CHANGELOG.md +10 -0
- package/README.md +3 -3
- package/android/build.gradle +2 -2
- package/android/src/debug/java/expo/modules/devmenu/DevMenuActivity.kt +27 -12
- package/android/src/debug/java/expo/modules/devmenu/DevMenuAppInfo.kt +3 -3
- package/android/src/debug/java/expo/modules/devmenu/DevMenuDefaultExtensionSettings.kt +1 -1
- package/android/src/debug/java/expo/modules/devmenu/DevMenuDevSettings.kt +5 -5
- package/android/src/debug/java/expo/modules/devmenu/DevMenuManager.kt +56 -49
- package/android/src/debug/java/expo/modules/devmenu/DevMenuReactHost.kt +148 -0
- package/android/src/debug/java/expo/modules/devmenu/{DevMenuHost.kt → DevMenuReactNativeHost.kt} +4 -6
- package/android/src/debug/java/expo/modules/devmenu/react/DevMenuAwareReactActivity.kt +8 -1
- package/android/src/main/java/com/facebook/react/devsupport/DevMenuReactInternalSettings.kt +1 -1
- package/android/src/main/java/expo/modules/devmenu/DevMenuDefaultDelegate.kt +3 -4
- package/android/src/main/java/expo/modules/devmenu/DevMenuPackage.kt +7 -1
- package/android/src/main/java/expo/modules/devmenu/devtools/DevMenuDevToolsDelegate.kt +4 -4
- package/android/src/main/java/expo/modules/devmenu/extensions/DevMenuExtension.kt +6 -4
- package/android/src/main/java/expo/modules/devmenu/modules/DevMenuInternalModule.kt +2 -3
- package/android/src/main/java/expo/modules/devmenu/react/DevMenuPackagerCommandHandlersSwapper.kt +6 -14
- package/android/src/main/java/expo/modules/devmenu/react/DevMenuShakeDetectorListenerSwapper.kt +3 -8
- package/android/src/main/java/expo/modules/devmenu/websockets/DevMenuCommandHandlersProvider.kt +12 -12
- package/android/src/react-native-74/main/expo/modules/devmenu/react/DevMenuPackagerConnectionSettings.kt +1 -1
- package/android/src/release/java/expo/modules/devmenu/DevMenuManager.kt +5 -6
- package/build/DevMenu.js.map +1 -1
- package/build/ExpoDevMenu.web.d.ts.map +1 -1
- 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 [
|
|
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
|
-
-
|
|
43
|
-
+
|
|
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`
|
package/android/build.gradle
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
apply plugin: 'com.android.library'
|
|
2
2
|
|
|
3
3
|
group = 'host.exp.exponent'
|
|
4
|
-
version = '5.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.
|
|
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.
|
|
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
|
|
118
|
+
val reactHost = DevMenuManager.delegate?.reactHost() ?: return
|
|
106
119
|
val supportsDevelopment = DevMenuManager.delegate?.supportsDevelopment() ?: false
|
|
107
120
|
|
|
108
121
|
if (supportsDevelopment) {
|
|
109
|
-
val 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()
|
|
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(
|
|
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 =
|
|
39
|
+
val jsExecutorName = reactHost.jsExecutorName
|
|
40
40
|
val engine = when {
|
|
41
41
|
jsExecutorName.contains("Hermes") -> "Hermes"
|
|
42
42
|
jsExecutorName.contains("V8") -> "V8"
|
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
package expo.modules.devmenu
|
|
2
2
|
|
|
3
3
|
import android.os.Bundle
|
|
4
|
-
import
|
|
4
|
+
import expo.interfaces.devmenu.ReactHostWrapper
|
|
5
5
|
import expo.modules.devmenu.devtools.DevMenuDevToolsDelegate
|
|
6
6
|
|
|
7
7
|
object DevMenuDevSettings {
|
|
8
|
-
fun getDevSettings(
|
|
9
|
-
val devDelegate = DevMenuDevToolsDelegate(DevMenuManager,
|
|
8
|
+
fun getDevSettings(reactHost: ReactHostWrapper): Bundle {
|
|
9
|
+
val devDelegate = DevMenuDevToolsDelegate(DevMenuManager, reactHost)
|
|
10
10
|
val devSettings = devDelegate.devSettings
|
|
11
11
|
|
|
12
|
-
val jsBundleURL =
|
|
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 =
|
|
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.
|
|
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:
|
|
63
|
-
private var
|
|
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
|
|
74
|
-
return delegate?.
|
|
76
|
+
fun getReactHost(): ReactHostWrapper? {
|
|
77
|
+
return delegate?.reactHost()
|
|
75
78
|
}
|
|
76
79
|
|
|
77
80
|
private val delegateReactContext: ReactContext?
|
|
78
|
-
get() = delegate?.
|
|
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.
|
|
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
|
|
96
|
-
val
|
|
98
|
+
val reactContext = delegateReactContext ?: return emptyList()
|
|
99
|
+
val uniqueExtensionClasses = reactContext
|
|
97
100
|
.nativeModules
|
|
98
101
|
.filterIsInstance<DevMenuExtensionInterface>()
|
|
99
|
-
.
|
|
102
|
+
.filterIsInstance<NativeModule>()
|
|
103
|
+
.map { it.javaClass }
|
|
100
104
|
.toSet()
|
|
101
105
|
|
|
102
|
-
return
|
|
103
|
-
.map {
|
|
104
|
-
|
|
106
|
+
return uniqueExtensionClasses
|
|
107
|
+
.map { extensionClass ->
|
|
108
|
+
reactContext.getNativeModule(extensionClass) as DevMenuExtensionInterface
|
|
105
109
|
}
|
|
106
110
|
}
|
|
107
111
|
|
|
108
|
-
private val cachedDevMenuDataSources by KeyValueCachedProperty<
|
|
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
|
|
117
|
-
return cachedDevMenuDataSources[
|
|
120
|
+
val delegateReactHost = delegate?.reactHost() ?: return emptyList()
|
|
121
|
+
return cachedDevMenuDataSources[delegateReactHost]
|
|
118
122
|
}
|
|
119
123
|
|
|
120
|
-
private val cachedDevMenuScreens by KeyValueCachedProperty<
|
|
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
|
|
129
|
-
return cachedDevMenuScreens[
|
|
132
|
+
val delegateReactHost = delegate?.reactHost() ?: return emptyList()
|
|
133
|
+
return cachedDevMenuScreens[delegateReactHost]
|
|
130
134
|
}
|
|
131
135
|
|
|
132
|
-
private val cachedDevMenuItems by KeyValueCachedProperty<
|
|
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
|
|
140
|
-
return cachedDevMenuItems[
|
|
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 =
|
|
178
|
+
devMenuHost = ReactHostWrapper(
|
|
179
|
+
reactNativeHost = DevMenuReactNativeHost(application, useDeveloperSupport),
|
|
180
|
+
reactHost = DevMenuReactHost.create(application, useDeveloperSupport)
|
|
181
|
+
)
|
|
175
182
|
UiThreadUtil.runOnUiThread {
|
|
176
|
-
devMenuHost.
|
|
183
|
+
devMenuHost.start()
|
|
177
184
|
}
|
|
178
185
|
}
|
|
179
186
|
}
|
|
180
187
|
|
|
181
|
-
private fun
|
|
182
|
-
|
|
188
|
+
private fun setUpReactInstance(reactHost: ReactHostWrapper) {
|
|
189
|
+
currentReactInstance = WeakReference(reactHost)
|
|
183
190
|
|
|
184
|
-
val handlers = DevMenuCommandHandlersProvider(this,
|
|
191
|
+
val handlers = DevMenuCommandHandlersProvider(this, reactHost)
|
|
185
192
|
.createCommandHandlers()
|
|
186
193
|
|
|
187
194
|
DevMenuPackagerCommandHandlersSwapper()
|
|
188
195
|
.swapPackagerCommandHandlers(
|
|
189
|
-
|
|
196
|
+
reactHost,
|
|
190
197
|
handlers
|
|
191
198
|
)
|
|
192
199
|
|
|
193
200
|
DevMenuShakeDetectorListenerSwapper()
|
|
194
201
|
.swapShakeDetectorListener(
|
|
195
|
-
|
|
202
|
+
reactHost
|
|
196
203
|
) {}
|
|
197
204
|
|
|
198
|
-
if (
|
|
199
|
-
|
|
205
|
+
if (reactHost.currentReactContext == null) {
|
|
206
|
+
reactHost.addReactInstanceEventListener(object : ReactInstanceEventListener {
|
|
200
207
|
override fun onReactContextInitialized(context: ReactContext) {
|
|
201
|
-
if (
|
|
208
|
+
if (currentReactInstance.get() === reactHost) {
|
|
202
209
|
handleLoadedDelegateContext(context)
|
|
203
210
|
}
|
|
204
|
-
|
|
211
|
+
reactHost.removeReactInstanceEventListener(this)
|
|
205
212
|
}
|
|
206
213
|
})
|
|
207
214
|
} else {
|
|
208
|
-
handleLoadedDelegateContext(
|
|
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(
|
|
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
|
|
253
|
+
val reactHost = delegate?.reactHost() ?: return Bundle.EMPTY
|
|
247
254
|
|
|
248
|
-
return DevMenuAppInfo.getAppInfo(
|
|
255
|
+
return DevMenuAppInfo.getAppInfo(reactHost, reactContext)
|
|
249
256
|
}
|
|
250
257
|
|
|
251
258
|
fun getDevSettings(): Bundle {
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
return DevMenuDevSettings.getDevSettings(
|
|
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
|
-
|
|
438
|
+
setUpReactInstance(this.reactHost())
|
|
432
439
|
}
|
|
433
440
|
}
|
|
434
441
|
|
|
435
|
-
override fun
|
|
436
|
-
setDelegate(DevMenuDefaultDelegate(
|
|
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():
|
|
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
|
|
488
|
-
if (
|
|
489
|
-
|
|
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
|
+
}
|
package/android/src/debug/java/expo/modules/devmenu/{DevMenuHost.kt → DevMenuReactNativeHost.kt}
RENAMED
|
@@ -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
|
|
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() =
|
|
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.
|
|
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.
|
|
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:
|
|
11
|
+
private val delegateHost: ReactHostWrapper
|
|
13
12
|
) : DevMenuDelegateInterface {
|
|
14
13
|
override fun appInfo(): Bundle? = null
|
|
15
14
|
|
|
16
|
-
override fun
|
|
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.
|
|
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
|
-
|
|
19
|
+
reactHost: ReactHostWrapper
|
|
20
20
|
) {
|
|
21
21
|
private val _reactDevManager = WeakReference(
|
|
22
|
-
|
|
22
|
+
reactHost.devSupportManager
|
|
23
23
|
)
|
|
24
24
|
private val _reactContext = WeakReference(
|
|
25
|
-
|
|
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
|
|
30
|
-
if (
|
|
31
|
-
Log.w(DEV_MENU_TAG, "Couldn't export dev-menu items, because the react instance
|
|
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,
|
|
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
|
|
49
|
-
val
|
|
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()
|
package/android/src/main/java/expo/modules/devmenu/react/DevMenuPackagerCommandHandlersSwapper.kt
CHANGED
|
@@ -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
|
-
|
|
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(
|
|
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
|
-
|
|
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
|
|
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(
|
package/android/src/main/java/expo/modules/devmenu/react/DevMenuShakeDetectorListenerSwapper.kt
CHANGED
|
@@ -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
|
|
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
|
-
|
|
12
|
+
reactHost: ReactHostWrapper,
|
|
14
13
|
newListener: ShakeDetector.ShakeListener
|
|
15
14
|
) {
|
|
16
15
|
try {
|
|
17
|
-
val 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) {
|
package/android/src/main/java/expo/modules/devmenu/websockets/DevMenuCommandHandlersProvider.kt
CHANGED
|
@@ -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
|
-
|
|
15
|
+
reactHost: ReactHostWrapper
|
|
16
16
|
) {
|
|
17
|
-
private val
|
|
18
|
-
private val
|
|
19
|
-
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
|
-
|
|
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 =
|
|
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
|
|
40
|
-
val devDelegate = DevMenuDevToolsDelegate(manager,
|
|
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 =
|
|
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 =
|
|
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
|
-
|
|
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.
|
|
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
|
|
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
|
|
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():
|
|
90
|
+
override fun getMenuHost(): ReactHostWrapper {
|
|
92
91
|
throw IllegalStateException(DEV_MENU_IS_NOT_AVAILABLE)
|
|
93
92
|
}
|
|
94
93
|
|
package/build/DevMenu.js.map
CHANGED
|
@@ -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
|
|
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
|
|
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.
|
|
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.
|
|
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
|
|
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": "
|
|
73
|
+
"gitHead": "0897aeadb926491a457bcd67d83360956994ee82"
|
|
74
74
|
}
|