expo 53.0.18 → 53.0.19
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/android/build.gradle
CHANGED
|
@@ -32,7 +32,7 @@ buildscript {
|
|
|
32
32
|
def reactNativeVersion = project.extensions.getByType(ExpoModuleExtension).reactNativeVersion
|
|
33
33
|
|
|
34
34
|
group = 'host.exp.exponent'
|
|
35
|
-
version = '53.0.
|
|
35
|
+
version = '53.0.19'
|
|
36
36
|
|
|
37
37
|
expoModule {
|
|
38
38
|
// We can't prebuild the module because it depends on the generated files.
|
|
@@ -43,7 +43,7 @@ android {
|
|
|
43
43
|
namespace "expo.core"
|
|
44
44
|
defaultConfig {
|
|
45
45
|
versionCode 1
|
|
46
|
-
versionName "53.0.
|
|
46
|
+
versionName "53.0.19"
|
|
47
47
|
consumerProguardFiles("proguard-rules.pro")
|
|
48
48
|
}
|
|
49
49
|
testOptions {
|
|
@@ -29,12 +29,13 @@ import expo.modules.core.interfaces.ReactActivityHandler.DelayLoadAppHandler
|
|
|
29
29
|
import expo.modules.core.interfaces.ReactActivityLifecycleListener
|
|
30
30
|
import expo.modules.kotlin.Utils
|
|
31
31
|
import expo.modules.rncompatibility.ReactNativeFeatureFlags
|
|
32
|
-
import kotlinx.coroutines.CancellationException
|
|
33
32
|
import kotlinx.coroutines.CompletableDeferred
|
|
34
33
|
import kotlinx.coroutines.CoroutineScope
|
|
35
34
|
import kotlinx.coroutines.CoroutineStart
|
|
36
35
|
import kotlinx.coroutines.Dispatchers
|
|
37
36
|
import kotlinx.coroutines.launch
|
|
37
|
+
import kotlinx.coroutines.sync.Mutex
|
|
38
|
+
import kotlinx.coroutines.sync.withLock
|
|
38
39
|
import java.lang.reflect.Field
|
|
39
40
|
import java.lang.reflect.Method
|
|
40
41
|
import java.lang.reflect.Modifier
|
|
@@ -71,6 +72,14 @@ class ReactActivityDelegateWrapper(
|
|
|
71
72
|
*/
|
|
72
73
|
private val loadAppReady = CompletableDeferred<Unit>()
|
|
73
74
|
|
|
75
|
+
/**
|
|
76
|
+
* A mutex to ensure all coroutines in a scope are running in atomic way.
|
|
77
|
+
*
|
|
78
|
+
* This is to act like [Activity] lifecycle,
|
|
79
|
+
* e.g. all work in [onResume] should be executed after all work in [onCreate] finished.
|
|
80
|
+
*/
|
|
81
|
+
private val mutex = Mutex()
|
|
82
|
+
|
|
74
83
|
/**
|
|
75
84
|
* A [CoroutineScope] that binds its lifecycle as [ReactActivityDelegateWrapper].
|
|
76
85
|
* This is used for [onDestroy] because we need a longer lifecycle scope to call [onDestroy]
|
|
@@ -110,7 +119,7 @@ class ReactActivityDelegateWrapper(
|
|
|
110
119
|
}
|
|
111
120
|
|
|
112
121
|
override fun loadApp(appKey: String?) {
|
|
113
|
-
|
|
122
|
+
launchLifecycleScopeWithLock(start = CoroutineStart.UNDISPATCHED) {
|
|
114
123
|
loadAppImpl(appKey, supportsDelayLoad = true)
|
|
115
124
|
}
|
|
116
125
|
}
|
|
@@ -138,7 +147,7 @@ class ReactActivityDelegateWrapper(
|
|
|
138
147
|
// Instead we intercept `ReactActivityDelegate.onCreate` and replace the `mReactDelegate` with our version.
|
|
139
148
|
// That's not ideal but works.
|
|
140
149
|
|
|
141
|
-
|
|
150
|
+
launchLifecycleScopeWithLock(start = CoroutineStart.UNDISPATCHED) {
|
|
142
151
|
awaitDelayLoadAppWhenReady(delayLoadAppHandler)
|
|
143
152
|
loadAppReady.complete(Unit)
|
|
144
153
|
|
|
@@ -184,7 +193,7 @@ class ReactActivityDelegateWrapper(
|
|
|
184
193
|
}
|
|
185
194
|
|
|
186
195
|
override fun onResume() {
|
|
187
|
-
|
|
196
|
+
launchLifecycleScopeWithLock {
|
|
188
197
|
loadAppReady.await()
|
|
189
198
|
delegate.onResume()
|
|
190
199
|
reactActivityLifecycleListeners.forEach { listener ->
|
|
@@ -194,10 +203,7 @@ class ReactActivityDelegateWrapper(
|
|
|
194
203
|
}
|
|
195
204
|
|
|
196
205
|
override fun onPause() {
|
|
197
|
-
|
|
198
|
-
if (!loadAppReady.isCompleted) {
|
|
199
|
-
loadAppReady.completeExceptionally(CancellationException("Activity paused before app loaded"))
|
|
200
|
-
}
|
|
206
|
+
launchLifecycleScopeWithLock {
|
|
201
207
|
loadAppReady.await()
|
|
202
208
|
reactActivityLifecycleListeners.forEach { listener ->
|
|
203
209
|
listener.onPause(activity)
|
|
@@ -219,7 +225,7 @@ class ReactActivityDelegateWrapper(
|
|
|
219
225
|
}
|
|
220
226
|
|
|
221
227
|
override fun onUserLeaveHint() {
|
|
222
|
-
|
|
228
|
+
launchLifecycleScopeWithLock {
|
|
223
229
|
loadAppReady.await()
|
|
224
230
|
reactActivityLifecycleListeners.forEach { listener ->
|
|
225
231
|
listener.onUserLeaveHint(activity)
|
|
@@ -232,26 +238,24 @@ class ReactActivityDelegateWrapper(
|
|
|
232
238
|
// Note: use our `coroutineScope` for onDestroy here
|
|
233
239
|
// because the lifecycleScope destroyed before the executions.
|
|
234
240
|
applicationCoroutineScope.launch {
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
241
|
+
mutex.withLock {
|
|
242
|
+
loadAppReady.await()
|
|
243
|
+
reactActivityLifecycleListeners.forEach { listener ->
|
|
244
|
+
listener.onDestroy(activity)
|
|
245
|
+
}
|
|
246
|
+
if (delayLoadAppHandler != null) {
|
|
247
|
+
try {
|
|
248
|
+
// For the delay load case, we may enter a different call flow than react-native.
|
|
249
|
+
// For example, Activity stopped before delay load finished.
|
|
250
|
+
// We stop before the ReactActivityDelegate gets a chance to set up.
|
|
251
|
+
// In this case, we should catch the exceptions.
|
|
252
|
+
delegate.onDestroy()
|
|
253
|
+
} catch (e: Exception) {
|
|
254
|
+
Log.e(TAG, "Exception occurred during onDestroy with delayed app loading", e)
|
|
255
|
+
}
|
|
256
|
+
} else {
|
|
249
257
|
delegate.onDestroy()
|
|
250
|
-
} catch (e: Exception) {
|
|
251
|
-
Log.e(TAG, "Exception occurred during onDestroy with delayed app loading", e)
|
|
252
258
|
}
|
|
253
|
-
} else {
|
|
254
|
-
delegate.onDestroy()
|
|
255
259
|
}
|
|
256
260
|
}
|
|
257
261
|
}
|
|
@@ -270,7 +274,7 @@ class ReactActivityDelegateWrapper(
|
|
|
270
274
|
*
|
|
271
275
|
* TODO (@bbarthec): fix it upstream?
|
|
272
276
|
*/
|
|
273
|
-
|
|
277
|
+
launchLifecycleScopeWithLock {
|
|
274
278
|
loadAppReady.await()
|
|
275
279
|
if (!ReactNativeFeatureFlags.enableBridgelessArchitecture && delegate.reactInstanceManager.currentReactContext == null) {
|
|
276
280
|
val reactContextListener = object : ReactInstanceEventListener {
|
|
@@ -279,7 +283,9 @@ class ReactActivityDelegateWrapper(
|
|
|
279
283
|
delegate.onActivityResult(requestCode, resultCode, data)
|
|
280
284
|
}
|
|
281
285
|
}
|
|
282
|
-
return@
|
|
286
|
+
return@launchLifecycleScopeWithLock delegate.reactInstanceManager.addReactInstanceEventListener(
|
|
287
|
+
reactContextListener
|
|
288
|
+
)
|
|
283
289
|
}
|
|
284
290
|
|
|
285
291
|
delegate.onActivityResult(requestCode, resultCode, data)
|
|
@@ -342,21 +348,21 @@ class ReactActivityDelegateWrapper(
|
|
|
342
348
|
}
|
|
343
349
|
|
|
344
350
|
override fun onWindowFocusChanged(hasFocus: Boolean) {
|
|
345
|
-
|
|
351
|
+
launchLifecycleScopeWithLock {
|
|
346
352
|
loadAppReady.await()
|
|
347
353
|
delegate.onWindowFocusChanged(hasFocus)
|
|
348
354
|
}
|
|
349
355
|
}
|
|
350
356
|
|
|
351
357
|
override fun requestPermissions(permissions: Array<out String>?, requestCode: Int, listener: PermissionListener?) {
|
|
352
|
-
|
|
358
|
+
launchLifecycleScopeWithLock {
|
|
353
359
|
loadAppReady.await()
|
|
354
360
|
delegate.requestPermissions(permissions, requestCode, listener)
|
|
355
361
|
}
|
|
356
362
|
}
|
|
357
363
|
|
|
358
364
|
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>?, grantResults: IntArray?) {
|
|
359
|
-
|
|
365
|
+
launchLifecycleScopeWithLock {
|
|
360
366
|
loadAppReady.await()
|
|
361
367
|
delegate.onRequestPermissionsResult(requestCode, permissions, grantResults)
|
|
362
368
|
}
|
|
@@ -383,7 +389,7 @@ class ReactActivityDelegateWrapper(
|
|
|
383
389
|
}
|
|
384
390
|
|
|
385
391
|
override fun onConfigurationChanged(newConfig: Configuration?) {
|
|
386
|
-
|
|
392
|
+
launchLifecycleScopeWithLock {
|
|
387
393
|
loadAppReady.await()
|
|
388
394
|
delegate.onConfigurationChanged(newConfig)
|
|
389
395
|
}
|
|
@@ -470,6 +476,17 @@ class ReactActivityDelegateWrapper(
|
|
|
470
476
|
}
|
|
471
477
|
}
|
|
472
478
|
|
|
479
|
+
private fun launchLifecycleScopeWithLock(
|
|
480
|
+
start: CoroutineStart = CoroutineStart.DEFAULT,
|
|
481
|
+
block: suspend CoroutineScope.() -> Unit
|
|
482
|
+
) {
|
|
483
|
+
activity.lifecycleScope.launch(start = start) {
|
|
484
|
+
mutex.withLock {
|
|
485
|
+
block()
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
|
|
473
490
|
/**
|
|
474
491
|
* Set the [loadAppReady] to completed state.
|
|
475
492
|
* This is only for unit tests when a test setups mocks and skips the [Activity] lifecycle.
|
|
@@ -173,60 +173,6 @@ internal class ReactActivityDelegateWrapperDelayLoadTest {
|
|
|
173
173
|
verify(exactly = 1) { spyDelegate.onDestroy() }
|
|
174
174
|
}
|
|
175
175
|
|
|
176
|
-
@Test
|
|
177
|
-
fun `should cancel pending resume if activity pause before delay load finished`() = runTest {
|
|
178
|
-
every { ExpoModulesPackage.Companion.packageList } returns listOf(mockPackageWithDelay)
|
|
179
|
-
|
|
180
|
-
val callbackSlot = slot<Runnable>()
|
|
181
|
-
every { delayLoadAppHandler.whenReady(capture(callbackSlot)) } answers {
|
|
182
|
-
// Don't call the callback immediately to simulate delay
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
activityController = Robolectric.buildActivity(MockActivity::class.java)
|
|
186
|
-
.also {
|
|
187
|
-
val activity = it.get()
|
|
188
|
-
(activity.application as MockApplication).bindCurrentActivity(activity)
|
|
189
|
-
}
|
|
190
|
-
.setup()
|
|
191
|
-
val spyDelegateWrapper = activity.reactActivityDelegate as ReactActivityDelegateWrapper
|
|
192
|
-
val spyDelegate = spyDelegateWrapper.delegate
|
|
193
|
-
|
|
194
|
-
verify(exactly = 1) { spyDelegateWrapper.onCreate(any()) }
|
|
195
|
-
verify(exactly = 1) { spyDelegateWrapper.onResume() }
|
|
196
|
-
verify(exactly = 0) { spyDelegate.onResume() }
|
|
197
|
-
|
|
198
|
-
activityController.pause()
|
|
199
|
-
callbackSlot.captured.run()
|
|
200
|
-
verify(exactly = 0) { spyDelegate.onResume() }
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
@Test
|
|
204
|
-
fun `should cancel pending resume if activity stop before delay load finished`() = runTest {
|
|
205
|
-
every { ExpoModulesPackage.Companion.packageList } returns listOf(mockPackageWithDelay)
|
|
206
|
-
|
|
207
|
-
val callbackSlot = slot<Runnable>()
|
|
208
|
-
every { delayLoadAppHandler.whenReady(capture(callbackSlot)) } answers {
|
|
209
|
-
// Don't call the callback immediately to simulate delay
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
activityController = Robolectric.buildActivity(MockActivity::class.java)
|
|
213
|
-
.also {
|
|
214
|
-
val activity = it.get()
|
|
215
|
-
(activity.application as MockApplication).bindCurrentActivity(activity)
|
|
216
|
-
}
|
|
217
|
-
.setup()
|
|
218
|
-
val spyDelegateWrapper = activity.reactActivityDelegate as ReactActivityDelegateWrapper
|
|
219
|
-
val spyDelegate = spyDelegateWrapper.delegate
|
|
220
|
-
|
|
221
|
-
verify(exactly = 1) { spyDelegateWrapper.onCreate(any()) }
|
|
222
|
-
verify(exactly = 1) { spyDelegateWrapper.onResume() }
|
|
223
|
-
verify(exactly = 0) { spyDelegate.onResume() }
|
|
224
|
-
|
|
225
|
-
activityController.pause().stop()
|
|
226
|
-
callbackSlot.captured.run()
|
|
227
|
-
verify(exactly = 0) { spyDelegate.onResume() }
|
|
228
|
-
}
|
|
229
|
-
|
|
230
176
|
@Test
|
|
231
177
|
fun `should cancel pending resume if activity destroy before delay load finished`() = runTest {
|
|
232
178
|
every { ExpoModulesPackage.Companion.packageList } returns listOf(mockPackageWithDelay)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "expo",
|
|
3
|
-
"version": "53.0.
|
|
3
|
+
"version": "53.0.19",
|
|
4
4
|
"description": "The Expo SDK",
|
|
5
5
|
"main": "src/Expo.ts",
|
|
6
6
|
"module": "src/Expo.ts",
|
|
@@ -73,9 +73,9 @@
|
|
|
73
73
|
"homepage": "https://github.com/expo/expo/tree/main/packages/expo",
|
|
74
74
|
"dependencies": {
|
|
75
75
|
"@babel/runtime": "^7.20.0",
|
|
76
|
-
"@expo/cli": "0.24.
|
|
77
|
-
"@expo/config": "~11.0.
|
|
78
|
-
"@expo/config-plugins": "~10.1.
|
|
76
|
+
"@expo/cli": "0.24.20",
|
|
77
|
+
"@expo/config": "~11.0.13",
|
|
78
|
+
"@expo/config-plugins": "~10.1.2",
|
|
79
79
|
"@expo/fingerprint": "0.13.4",
|
|
80
80
|
"@expo/metro-config": "0.20.17",
|
|
81
81
|
"@expo/vector-icons": "^14.0.0",
|
|
@@ -119,5 +119,5 @@
|
|
|
119
119
|
"optional": true
|
|
120
120
|
}
|
|
121
121
|
},
|
|
122
|
-
"gitHead": "
|
|
122
|
+
"gitHead": "134c147ee4274f9688929ac66cfef950947659d0"
|
|
123
123
|
}
|