expo-updates 0.17.0 → 0.18.0
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 +24 -0
- package/README.md +8 -1
- package/android/build.gradle +13 -13
- package/android/src/main/AndroidManifest.xml +1 -2
- package/android/src/main/java/expo/modules/updates/UpdatesController.kt +102 -14
- package/android/src/main/java/expo/modules/updates/UpdatesInterface.kt +3 -1
- package/android/src/main/java/expo/modules/updates/UpdatesModule.kt +133 -68
- package/android/src/main/java/expo/modules/updates/UpdatesPackage.kt +61 -13
- package/android/src/main/java/expo/modules/updates/UpdatesService.kt +4 -0
- package/android/src/main/java/expo/modules/updates/UpdatesUtils.kt +23 -13
- package/android/src/main/java/expo/modules/updates/loader/FileDownloader.kt +8 -1
- package/android/src/main/java/expo/modules/updates/loader/LoaderTask.kt +103 -22
- package/android/src/main/java/expo/modules/updates/manifest/LegacyUpdateManifest.kt +8 -4
- package/android/src/main/java/expo/modules/updates/manifest/NewUpdateManifest.kt +1 -1
- package/android/src/main/java/expo/modules/updates/selectionpolicy/LoaderSelectionPolicy.kt +8 -0
- package/android/src/main/java/expo/modules/updates/selectionpolicy/LoaderSelectionPolicyFilterAware.kt +25 -1
- package/android/src/main/java/expo/modules/updates/selectionpolicy/SelectionPolicy.kt +16 -0
- package/android/src/main/java/expo/modules/updates/statemachine/UpdatesStateChangeEventSender.kt +10 -0
- package/android/src/main/java/expo/modules/updates/statemachine/UpdatesStateContext.kt +80 -0
- package/android/src/main/java/expo/modules/updates/statemachine/UpdatesStateError.kt +16 -0
- package/android/src/main/java/expo/modules/updates/statemachine/UpdatesStateEvent.kt +54 -0
- package/android/src/main/java/expo/modules/updates/statemachine/UpdatesStateEventType.kt +16 -0
- package/android/src/main/java/expo/modules/updates/statemachine/UpdatesStateMachine.kt +150 -0
- package/android/src/main/java/expo/modules/updates/statemachine/UpdatesStateValue.kt +11 -0
- package/build/Updates.d.ts +3 -2
- package/build/Updates.d.ts.map +1 -1
- package/build/Updates.js +5 -4
- package/build/Updates.js.map +1 -1
- package/build/statemachine/UpdatesStateMachine.d.ts +50 -0
- package/build/statemachine/UpdatesStateMachine.d.ts.map +1 -0
- package/build/statemachine/UpdatesStateMachine.js +116 -0
- package/build/statemachine/UpdatesStateMachine.js.map +1 -0
- package/e2e/fixtures/App-apitest.tsx +22 -6
- package/e2e/fixtures/App.tsx +70 -13
- package/e2e/fixtures/Updates.e2e.ts +225 -4
- package/e2e/fixtures/UpdatesE2ETestModule.kt +1 -1
- package/e2e/fixtures/project_files/eas-hooks/eas-build-on-success.sh +3 -0
- package/e2e/setup/project.js +31 -2
- package/expo-module.config.json +9 -0
- package/expo-updates-gradle-plugin/build.gradle.kts +39 -0
- package/expo-updates-gradle-plugin/src/main/kotlin/expo/modules/updates/ExpoUpdatesPlugin.kt +103 -0
- package/ios/EXUpdates/AppController.swift +145 -45
- package/ios/EXUpdates/AppLoader/AppLoaderTask.swift +135 -43
- package/ios/EXUpdates/AppLoader/EmbeddedAppLoader.swift +2 -1
- package/ios/EXUpdates/AppLoader/UpdateResponse.swift +5 -3
- package/ios/EXUpdates/EXUpdatesService.h +1 -0
- package/ios/EXUpdates/EXUpdatesService.m +5 -0
- package/ios/EXUpdates/Exceptions.swift +16 -0
- package/ios/EXUpdates/SelectionPolicy/LoaderSelectionPolicy.swift +15 -2
- package/ios/EXUpdates/SelectionPolicy/LoaderSelectionPolicyFilterAware.swift +26 -1
- package/ios/EXUpdates/SelectionPolicy/SelectionPolicy.swift +15 -2
- package/ios/EXUpdates/Update/Update.swift +1 -1
- package/ios/EXUpdates/UpdatesModule.swift +41 -104
- package/ios/EXUpdates/UpdatesStateMachine.swift +416 -0
- package/ios/EXUpdates/UpdatesUtils.swift +268 -38
- package/ios/EXUpdates.podspec +7 -0
- package/ios/Tests/SelectionPolicyFilterAwareSpec.swift +49 -0
- package/ios/Tests/UpdatesStateMachineSpec.swift +149 -0
- package/package.json +11 -10
- package/plugin/build/withUpdates.js +1 -0
- package/plugin/src/withUpdates.ts +2 -0
- package/scripts/createManifest.js +11 -1
- package/src/Updates.ts +5 -4
- package/src/statemachine/UpdatesStateMachine.ts +164 -0
- package/scripts/create-manifest-android.gradle +0 -122
package/CHANGELOG.md
CHANGED
|
@@ -10,6 +10,29 @@
|
|
|
10
10
|
|
|
11
11
|
### 💡 Others
|
|
12
12
|
|
|
13
|
+
## 0.18.0 — 2023-06-22
|
|
14
|
+
|
|
15
|
+
_This version does not introduce any user-facing changes._
|
|
16
|
+
|
|
17
|
+
## 0.17.1 — 2023-06-21
|
|
18
|
+
|
|
19
|
+
### 📚 3rd party library updates
|
|
20
|
+
|
|
21
|
+
- Updated `junit` to `4.13.2`. ([#22395](https://github.com/expo/expo/pull/22395) by [@josephyanks](https://github.com/josephyanks))
|
|
22
|
+
|
|
23
|
+
### 🎉 New features
|
|
24
|
+
|
|
25
|
+
- [Android] Load updates in background thread and prevent ANR from initial launch. ([#20273](https://github.com/expo/expo/pull/20273) by [@kudo](https://github.com/kudo))
|
|
26
|
+
- Added support for React Native 0.72. ([#22588](https://github.com/expo/expo/pull/22588) by [@kudo](https://github.com/kudo))
|
|
27
|
+
- Added the Brotli compression support for EAS Update on Android. ([#22982](https://github.com/expo/expo/pull/22982) by [@kudo](https://github.com/kudo))
|
|
28
|
+
- [Android][iOS] State machine implementation. ([#22845](https://github.com/expo/expo/pull/22845) by [@douglowder](https://github.com/douglowder))
|
|
29
|
+
|
|
30
|
+
### 🐛 Bug fixes
|
|
31
|
+
|
|
32
|
+
- [Android] Resolve up the project root when creating production manifest. ([#22044](https://github.com/expo/expo/pull/22044) by [@EvanBacon](https://github.com/EvanBacon))
|
|
33
|
+
- Fixed Android build warnings for Gradle version 8. ([#22537](https://github.com/expo/expo/pull/22537), [#22609](https://github.com/expo/expo/pull/22609) by [@kudo](https://github.com/kudo))
|
|
34
|
+
- Fixed broken `create-manifest-android.gradle` on Android Gradle version 8. ([#22538](https://github.com/expo/expo/pull/22538) by [@kudo](https://github.com/kudo))
|
|
35
|
+
|
|
13
36
|
## 0.17.0 — 2023-05-08
|
|
14
37
|
|
|
15
38
|
### 🛠 Breaking changes
|
|
@@ -37,6 +60,7 @@
|
|
|
37
60
|
- Fix empty body no-op multipart response. ([#22227](https://github.com/expo/expo/pull/22227) by [@wschurman](https://github.com/wschurman))
|
|
38
61
|
- Put extra data mutation in transaction. ([#22252](https://github.com/expo/expo/pull/22252) by [@wschurman](https://github.com/wschurman))
|
|
39
62
|
- [iOS] fix bizarre bug when downloading update twice. ([#22355](https://github.com/expo/expo/pull/22355) by [@douglowder](https://github.com/douglowder))
|
|
63
|
+
- Fix rollback to embedded logic. ([#22433](https://github.com/expo/expo/pull/22433), [#22434](https://github.com/expo/expo/pull/22434) by [@wschurman](https://github.com/wschurman))
|
|
40
64
|
|
|
41
65
|
### 💡 Others
|
|
42
66
|
|
package/README.md
CHANGED
|
@@ -1,4 +1,11 @@
|
|
|
1
|
-
|
|
1
|
+
<p>
|
|
2
|
+
<a href="https://docs.expo.dev/versions/latest/sdk/updates/">
|
|
3
|
+
<img
|
|
4
|
+
src="../../.github/resources/expo-updates.svg"
|
|
5
|
+
alt="expo-updates"
|
|
6
|
+
height="64" />
|
|
7
|
+
</a>
|
|
8
|
+
</p>
|
|
2
9
|
|
|
3
10
|
`expo-updates` fetches and manages updates to your app stored on a remote server.
|
|
4
11
|
|
package/android/build.gradle
CHANGED
|
@@ -4,12 +4,10 @@ apply plugin: 'kotlin-kapt'
|
|
|
4
4
|
apply plugin: 'maven-publish'
|
|
5
5
|
|
|
6
6
|
group = 'host.exp.exponent'
|
|
7
|
-
version = '0.
|
|
7
|
+
version = '0.18.0'
|
|
8
8
|
|
|
9
9
|
def ex_updates_native_debug = System.getenv("EX_UPDATES_NATIVE_DEBUG") == "1" ? "true" : "false"
|
|
10
10
|
|
|
11
|
-
apply from: "../scripts/create-manifest-android.gradle"
|
|
12
|
-
|
|
13
11
|
buildscript {
|
|
14
12
|
def expoModulesCorePlugin = new File(project(":expo-modules-core").projectDir.absolutePath, "ExpoModulesCorePlugin.gradle")
|
|
15
13
|
if (expoModulesCorePlugin.exists()) {
|
|
@@ -40,19 +38,11 @@ buildscript {
|
|
|
40
38
|
}
|
|
41
39
|
}
|
|
42
40
|
|
|
43
|
-
// Creating sources with comments
|
|
44
|
-
task androidSourcesJar(type: Jar) {
|
|
45
|
-
classifier = 'sources'
|
|
46
|
-
from android.sourceSets.main.java.srcDirs
|
|
47
|
-
}
|
|
48
|
-
|
|
49
41
|
afterEvaluate {
|
|
50
42
|
publishing {
|
|
51
43
|
publications {
|
|
52
44
|
release(MavenPublication) {
|
|
53
45
|
from components.release
|
|
54
|
-
// Add additional sourcesJar to artifacts
|
|
55
|
-
artifact(androidSourcesJar)
|
|
56
46
|
}
|
|
57
47
|
}
|
|
58
48
|
repositories {
|
|
@@ -75,14 +65,18 @@ android {
|
|
|
75
65
|
jvmTarget = JavaVersion.VERSION_11.majorVersion
|
|
76
66
|
}
|
|
77
67
|
|
|
68
|
+
namespace "expo.modules.updates"
|
|
78
69
|
defaultConfig {
|
|
79
70
|
minSdkVersion safeExtGet("minSdkVersion", 21)
|
|
80
71
|
targetSdkVersion safeExtGet("targetSdkVersion", 33)
|
|
81
72
|
versionCode 31
|
|
82
|
-
versionName '0.
|
|
73
|
+
versionName '0.18.0'
|
|
83
74
|
consumerProguardFiles("proguard-rules.pro")
|
|
84
75
|
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
|
76
|
+
|
|
85
77
|
buildConfigField("boolean", "EX_UPDATES_NATIVE_DEBUG", ex_updates_native_debug)
|
|
78
|
+
buildConfigField("boolean", "EX_UPDATES_ANDROID_DELAY_LOAD_APP", boolish(findProperty("EX_UPDATES_ANDROID_DELAY_LOAD_APP") ?: true).toString())
|
|
79
|
+
|
|
86
80
|
// uncomment below to export the database schema when making changes
|
|
87
81
|
/* javaCompileOptions {
|
|
88
82
|
annotationProcessorOptions {
|
|
@@ -102,6 +96,11 @@ android {
|
|
|
102
96
|
androidTest.assets.srcDirs += files("$projectDir/src/androidTest/schemas".toString())
|
|
103
97
|
androidTest.assets.srcDirs += files("$projectDir/src/androidTest/certificates".toString())
|
|
104
98
|
}
|
|
99
|
+
publishing {
|
|
100
|
+
singleVariant("release") {
|
|
101
|
+
withSourcesJar()
|
|
102
|
+
}
|
|
103
|
+
}
|
|
105
104
|
}
|
|
106
105
|
|
|
107
106
|
dependencies {
|
|
@@ -121,6 +120,7 @@ dependencies {
|
|
|
121
120
|
|
|
122
121
|
implementation("com.squareup.okhttp3:okhttp:4.9.2")
|
|
123
122
|
implementation("com.squareup.okhttp3:okhttp-urlconnection:4.9.2")
|
|
123
|
+
implementation("com.squareup.okhttp3:okhttp-brotli:4.9.2")
|
|
124
124
|
implementation("com.squareup.okio:okio:2.9.0")
|
|
125
125
|
implementation("commons-codec:commons-codec:1.10")
|
|
126
126
|
implementation("commons-io:commons-io:2.6")
|
|
@@ -128,7 +128,7 @@ dependencies {
|
|
|
128
128
|
implementation("org.apache.commons:commons-lang3:3.9")
|
|
129
129
|
implementation("org.bouncycastle:bcutil-jdk15to18:1.70")
|
|
130
130
|
|
|
131
|
-
testImplementation 'junit:junit:4.13.
|
|
131
|
+
testImplementation 'junit:junit:4.13.2'
|
|
132
132
|
testImplementation 'androidx.test:core:1.4.0'
|
|
133
133
|
testImplementation 'io.mockk:mockk:1.12.3'
|
|
134
134
|
testImplementation "org.jetbrains.kotlin:kotlin-test-junit:${getKotlinVersion()}"
|
|
@@ -12,6 +12,7 @@ import com.facebook.react.ReactInstanceManager
|
|
|
12
12
|
import com.facebook.react.ReactNativeHost
|
|
13
13
|
import com.facebook.react.bridge.Arguments
|
|
14
14
|
import com.facebook.react.bridge.JSBundleLoader
|
|
15
|
+
import com.facebook.react.bridge.WritableMap
|
|
15
16
|
import expo.modules.updates.db.BuildData
|
|
16
17
|
import expo.modules.updates.db.DatabaseHolder
|
|
17
18
|
import expo.modules.updates.db.Reaper
|
|
@@ -25,7 +26,7 @@ import expo.modules.updates.launcher.Launcher
|
|
|
25
26
|
import expo.modules.updates.launcher.Launcher.LauncherCallback
|
|
26
27
|
import expo.modules.updates.launcher.NoDatabaseLauncher
|
|
27
28
|
import expo.modules.updates.loader.*
|
|
28
|
-
import expo.modules.updates.loader.LoaderTask.
|
|
29
|
+
import expo.modules.updates.loader.LoaderTask.RemoteUpdateStatus
|
|
29
30
|
import expo.modules.updates.loader.LoaderTask.LoaderTaskCallback
|
|
30
31
|
import expo.modules.updates.logging.UpdatesErrorCode
|
|
31
32
|
import expo.modules.updates.logging.UpdatesLogReader
|
|
@@ -33,6 +34,12 @@ import expo.modules.updates.logging.UpdatesLogger
|
|
|
33
34
|
import expo.modules.updates.manifest.UpdateManifest
|
|
34
35
|
import expo.modules.updates.selectionpolicy.SelectionPolicy
|
|
35
36
|
import expo.modules.updates.selectionpolicy.SelectionPolicyFactory
|
|
37
|
+
import expo.modules.updates.statemachine.UpdatesStateChangeEventSender
|
|
38
|
+
import expo.modules.updates.statemachine.UpdatesStateContext
|
|
39
|
+
import expo.modules.updates.statemachine.UpdatesStateEvent
|
|
40
|
+
import expo.modules.updates.statemachine.UpdatesStateEventType
|
|
41
|
+
import expo.modules.updates.statemachine.UpdatesStateMachine
|
|
42
|
+
import expo.modules.updates.statemachine.UpdatesStateValue
|
|
36
43
|
import java.io.File
|
|
37
44
|
import java.lang.ref.WeakReference
|
|
38
45
|
|
|
@@ -58,7 +65,7 @@ import java.lang.ref.WeakReference
|
|
|
58
65
|
class UpdatesController private constructor(
|
|
59
66
|
context: Context,
|
|
60
67
|
var updatesConfiguration: UpdatesConfiguration
|
|
61
|
-
) {
|
|
68
|
+
) : UpdatesStateChangeEventSender {
|
|
62
69
|
private var reactNativeHost: WeakReference<ReactNativeHost>? = if (context is ReactApplication) {
|
|
63
70
|
WeakReference((context as ReactApplication).reactNativeHost)
|
|
64
71
|
} else {
|
|
@@ -67,6 +74,7 @@ class UpdatesController private constructor(
|
|
|
67
74
|
|
|
68
75
|
var updatesDirectory: File? = null
|
|
69
76
|
var updatesDirectoryException: Exception? = null
|
|
77
|
+
var stateMachine: UpdatesStateMachine = UpdatesStateMachine(context, this)
|
|
70
78
|
|
|
71
79
|
private var launcher: Launcher? = null
|
|
72
80
|
val databaseHolder = DatabaseHolder(UpdatesDatabase.getInstance(context))
|
|
@@ -273,6 +281,22 @@ class UpdatesController private constructor(
|
|
|
273
281
|
return true
|
|
274
282
|
}
|
|
275
283
|
|
|
284
|
+
override fun onRemoteCheckForUpdateStarted() {
|
|
285
|
+
stateMachine.processEvent(UpdatesStateEvent.Check())
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
override fun onRemoteCheckForUpdateFinished(result: LoaderTask.RemoteCheckResult) {
|
|
289
|
+
var event = UpdatesStateEvent.CheckComplete()
|
|
290
|
+
if (result.manifest != null) {
|
|
291
|
+
event = UpdatesStateEvent.CheckCompleteWithUpdate(
|
|
292
|
+
result.manifest
|
|
293
|
+
)
|
|
294
|
+
} else if (result.isRollBackToEmbedded == true) {
|
|
295
|
+
event = UpdatesStateEvent.CheckCompleteWithRollback()
|
|
296
|
+
}
|
|
297
|
+
stateMachine.processEvent(event)
|
|
298
|
+
}
|
|
299
|
+
|
|
276
300
|
override fun onRemoteUpdateManifestResponseManifestLoaded(updateManifest: UpdateManifest) {
|
|
277
301
|
remoteLoadStatus = ErrorRecoveryDelegate.RemoteLoadStatus.NEW_UPDATE_LOADING
|
|
278
302
|
}
|
|
@@ -285,13 +309,34 @@ class UpdatesController private constructor(
|
|
|
285
309
|
notifyController()
|
|
286
310
|
}
|
|
287
311
|
|
|
288
|
-
override fun
|
|
289
|
-
|
|
312
|
+
override fun onRemoteUpdateLoadStarted() {
|
|
313
|
+
stateMachine.processEvent(UpdatesStateEvent.Download())
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
override fun onRemoteUpdateAssetLoaded(
|
|
317
|
+
asset: AssetEntity,
|
|
318
|
+
successfulAssetCount: Int,
|
|
319
|
+
failedAssetCount: Int,
|
|
320
|
+
totalAssetCount: Int
|
|
321
|
+
) {
|
|
322
|
+
val body = mapOf(
|
|
323
|
+
"assetInfo" to mapOf(
|
|
324
|
+
"name" to asset.embeddedAssetFilename,
|
|
325
|
+
"successfulAssetCount" to successfulAssetCount,
|
|
326
|
+
"failedAssetCount" to failedAssetCount,
|
|
327
|
+
"totalAssetCount" to totalAssetCount
|
|
328
|
+
)
|
|
329
|
+
)
|
|
330
|
+
logger.info("AppController appLoaderTask didLoadAsset: $body", UpdatesErrorCode.None, null, asset.expectedHash)
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
override fun onRemoteUpdateFinished(
|
|
334
|
+
status: RemoteUpdateStatus,
|
|
290
335
|
update: UpdateEntity?,
|
|
291
336
|
exception: Exception?
|
|
292
337
|
) {
|
|
293
338
|
when (status) {
|
|
294
|
-
|
|
339
|
+
RemoteUpdateStatus.ERROR -> {
|
|
295
340
|
if (exception == null) {
|
|
296
341
|
throw AssertionError("Background update with error status must have a nonnull exception object")
|
|
297
342
|
}
|
|
@@ -299,9 +344,31 @@ class UpdatesController private constructor(
|
|
|
299
344
|
remoteLoadStatus = ErrorRecoveryDelegate.RemoteLoadStatus.IDLE
|
|
300
345
|
val params = Arguments.createMap()
|
|
301
346
|
params.putString("message", exception.message)
|
|
302
|
-
|
|
347
|
+
sendLegacyUpdateEventToJS(UPDATE_ERROR_EVENT, params)
|
|
348
|
+
|
|
349
|
+
// Since errors can happen through a number of paths, we do these checks
|
|
350
|
+
// to make sure the state machine is valid
|
|
351
|
+
when (stateMachine.state) {
|
|
352
|
+
UpdatesStateValue.Idle -> {
|
|
353
|
+
stateMachine.processEvent(UpdatesStateEvent.Download())
|
|
354
|
+
stateMachine.processEvent(
|
|
355
|
+
UpdatesStateEvent.DownloadError(exception.message ?: "")
|
|
356
|
+
)
|
|
357
|
+
}
|
|
358
|
+
UpdatesStateValue.Checking -> {
|
|
359
|
+
stateMachine.processEvent(
|
|
360
|
+
UpdatesStateEvent.CheckError(exception.message ?: "")
|
|
361
|
+
)
|
|
362
|
+
}
|
|
363
|
+
else -> {
|
|
364
|
+
// .downloading
|
|
365
|
+
stateMachine.processEvent(
|
|
366
|
+
UpdatesStateEvent.DownloadError(exception.message ?: "")
|
|
367
|
+
)
|
|
368
|
+
}
|
|
369
|
+
}
|
|
303
370
|
}
|
|
304
|
-
|
|
371
|
+
RemoteUpdateStatus.UPDATE_AVAILABLE -> {
|
|
305
372
|
if (update == null) {
|
|
306
373
|
throw AssertionError("Background update with error status must have a nonnull update object")
|
|
307
374
|
}
|
|
@@ -309,16 +376,19 @@ class UpdatesController private constructor(
|
|
|
309
376
|
logger.info("UpdatesController onBackgroundUpdateFinished: Update available", UpdatesErrorCode.None)
|
|
310
377
|
val params = Arguments.createMap()
|
|
311
378
|
params.putString("manifestString", update.manifest.toString())
|
|
312
|
-
|
|
379
|
+
sendLegacyUpdateEventToJS(UPDATE_AVAILABLE_EVENT, params)
|
|
380
|
+
stateMachine.processEvent(
|
|
381
|
+
UpdatesStateEvent.DownloadCompleteWithUpdate(update.manifest)
|
|
382
|
+
)
|
|
313
383
|
}
|
|
314
|
-
|
|
384
|
+
RemoteUpdateStatus.NO_UPDATE_AVAILABLE -> {
|
|
315
385
|
remoteLoadStatus = ErrorRecoveryDelegate.RemoteLoadStatus.IDLE
|
|
316
386
|
logger.error("UpdatesController onBackgroundUpdateFinished: No update available", UpdatesErrorCode.NoUpdatesAvailable)
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
387
|
+
sendLegacyUpdateEventToJS(UPDATE_NO_UPDATE_AVAILABLE_EVENT, null)
|
|
388
|
+
// TODO: handle rollbacks properly, but this works for now
|
|
389
|
+
if (stateMachine.state == UpdatesStateValue.Downloading) {
|
|
390
|
+
stateMachine.processEvent(UpdatesStateEvent.DownloadComplete())
|
|
391
|
+
}
|
|
322
392
|
}
|
|
323
393
|
}
|
|
324
394
|
errorRecovery.notifyNewRemoteLoadStatus(remoteLoadStatus)
|
|
@@ -438,6 +508,8 @@ class UpdatesController private constructor(
|
|
|
438
508
|
return
|
|
439
509
|
}
|
|
440
510
|
|
|
511
|
+
stateMachine.processEvent(UpdatesStateEvent.Restart())
|
|
512
|
+
|
|
441
513
|
val oldLaunchAssetFile = launcher!!.launchAssetFile
|
|
442
514
|
|
|
443
515
|
val databaseLocal = getDatabase()
|
|
@@ -482,11 +554,24 @@ class UpdatesController private constructor(
|
|
|
482
554
|
if (shouldRunReaper) {
|
|
483
555
|
runReaper()
|
|
484
556
|
}
|
|
557
|
+
stateMachine.reset()
|
|
485
558
|
}
|
|
486
559
|
}
|
|
487
560
|
)
|
|
488
561
|
}
|
|
489
562
|
|
|
563
|
+
override fun sendUpdateStateChangeEventToBridge(eventType: UpdatesStateEventType, context: UpdatesStateContext) {
|
|
564
|
+
sendEventToJS(UPDATES_STATE_CHANGE_EVENT_NAME, eventType.type, context.writableMap)
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
fun sendLegacyUpdateEventToJS(eventType: String, params: WritableMap?) {
|
|
568
|
+
sendEventToJS(UPDATES_EVENT_NAME, eventType, params)
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
private fun sendEventToJS(eventName: String, eventType: String, params: WritableMap?) {
|
|
572
|
+
UpdatesUtils.sendEventToReactNative(reactNativeHost, logger, eventName, eventType, params)
|
|
573
|
+
}
|
|
574
|
+
|
|
490
575
|
companion object {
|
|
491
576
|
private val TAG = UpdatesController::class.java.simpleName
|
|
492
577
|
|
|
@@ -494,6 +579,9 @@ class UpdatesController private constructor(
|
|
|
494
579
|
private const val UPDATE_NO_UPDATE_AVAILABLE_EVENT = "noUpdateAvailable"
|
|
495
580
|
private const val UPDATE_ERROR_EVENT = "error"
|
|
496
581
|
|
|
582
|
+
private const val UPDATES_EVENT_NAME = "Expo.nativeUpdatesEvent"
|
|
583
|
+
private const val UPDATES_STATE_CHANGE_EVENT_NAME = "Expo.nativeUpdatesStateChangeEvent"
|
|
584
|
+
|
|
497
585
|
private var singletonInstance: UpdatesController? = null
|
|
498
586
|
@JvmStatic val instance: UpdatesController
|
|
499
587
|
get() {
|
|
@@ -10,7 +10,8 @@ import java.io.File
|
|
|
10
10
|
|
|
11
11
|
// this unused import must stay because of versioning
|
|
12
12
|
/* ktlint-disable no-unused-imports */
|
|
13
|
-
import expo.modules.updates.
|
|
13
|
+
import expo.modules.updates.statemachine.UpdatesStateMachine
|
|
14
|
+
|
|
14
15
|
/* ktlint-enable no-unused-imports */
|
|
15
16
|
|
|
16
17
|
/**
|
|
@@ -23,6 +24,7 @@ interface UpdatesInterface {
|
|
|
23
24
|
val directory: File?
|
|
24
25
|
val databaseHolder: DatabaseHolder
|
|
25
26
|
val fileDownloader: FileDownloader
|
|
27
|
+
val stateMachine: UpdatesStateMachine?
|
|
26
28
|
|
|
27
29
|
val isEmergencyLaunch: Boolean
|
|
28
30
|
val isEmbeddedLaunch: Boolean
|
|
@@ -18,6 +18,7 @@ import expo.modules.updates.logging.UpdatesLogEntry
|
|
|
18
18
|
import expo.modules.updates.logging.UpdatesLogReader
|
|
19
19
|
import expo.modules.updates.logging.UpdatesLogger
|
|
20
20
|
import expo.modules.updates.manifest.ManifestMetadata
|
|
21
|
+
import expo.modules.updates.statemachine.UpdatesStateEvent
|
|
21
22
|
import java.util.Date
|
|
22
23
|
|
|
23
24
|
// these unused imports must stay because of versioning
|
|
@@ -40,6 +41,8 @@ class UpdatesModule(
|
|
|
40
41
|
) : ExportedModule(context) {
|
|
41
42
|
private inline fun <reified T> moduleRegistry() = moduleRegistryDelegate.getFromModuleRegistry<T>()
|
|
42
43
|
|
|
44
|
+
private val logger = UpdatesLogger(context)
|
|
45
|
+
|
|
43
46
|
private val updatesService: UpdatesInterface? by moduleRegistry()
|
|
44
47
|
|
|
45
48
|
override fun onCreate(moduleRegistry: ModuleRegistry) {
|
|
@@ -141,69 +144,91 @@ class UpdatesModule(
|
|
|
141
144
|
)
|
|
142
145
|
return
|
|
143
146
|
}
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
databaseHolder.
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
147
|
+
updatesServiceLocal.stateMachine?.processEvent(UpdatesStateEvent.Check())
|
|
148
|
+
AsyncTask.execute {
|
|
149
|
+
val databaseHolder = updatesServiceLocal.databaseHolder
|
|
150
|
+
val extraHeaders = FileDownloader.getExtraHeadersForRemoteUpdateRequest(
|
|
151
|
+
databaseHolder.database,
|
|
152
|
+
updatesServiceLocal.configuration,
|
|
153
|
+
updatesServiceLocal.launchedUpdate,
|
|
154
|
+
updatesServiceLocal.embeddedUpdate
|
|
155
|
+
)
|
|
156
|
+
databaseHolder.releaseDatabase()
|
|
157
|
+
updatesServiceLocal.fileDownloader.downloadRemoteUpdate(
|
|
158
|
+
updatesServiceLocal.configuration,
|
|
159
|
+
extraHeaders,
|
|
160
|
+
context,
|
|
161
|
+
object : RemoteUpdateDownloadCallback {
|
|
162
|
+
override fun onFailure(message: String, e: Exception) {
|
|
163
|
+
promise.reject("ERR_UPDATES_CHECK", message, e)
|
|
164
|
+
Log.e(TAG, message, e)
|
|
165
|
+
}
|
|
161
166
|
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
167
|
+
override fun onSuccess(updateResponse: UpdateResponse) {
|
|
168
|
+
val updateDirective = updateResponse.directiveUpdateResponsePart?.updateDirective
|
|
169
|
+
val updateManifest = updateResponse.manifestUpdateResponsePart?.updateManifest
|
|
170
|
+
|
|
171
|
+
val updateInfo = Bundle()
|
|
172
|
+
if (updateDirective != null) {
|
|
173
|
+
if (updateDirective is UpdateDirective.RollBackToEmbeddedUpdateDirective) {
|
|
174
|
+
updateInfo.putBoolean("isRollBackToEmbedded", true)
|
|
175
|
+
promise.resolve(updateInfo)
|
|
176
|
+
updatesServiceLocal.stateMachine?.processEvent(
|
|
177
|
+
UpdatesStateEvent.CheckCompleteWithRollback()
|
|
178
|
+
)
|
|
179
|
+
return
|
|
180
|
+
}
|
|
181
|
+
}
|
|
165
182
|
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
if (updateDirective is UpdateDirective.RollBackToEmbeddedUpdateDirective) {
|
|
169
|
-
updateInfo.putBoolean("isRollBackToEmbedded", true)
|
|
183
|
+
if (updateManifest == null) {
|
|
184
|
+
updateInfo.putBoolean("isAvailable", false)
|
|
170
185
|
promise.resolve(updateInfo)
|
|
186
|
+
updatesServiceLocal.stateMachine?.processEvent(
|
|
187
|
+
UpdatesStateEvent.CheckComplete()
|
|
188
|
+
)
|
|
171
189
|
return
|
|
172
190
|
}
|
|
173
|
-
}
|
|
174
191
|
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
}
|
|
192
|
+
val launchedUpdate = updatesServiceLocal.launchedUpdate
|
|
193
|
+
if (launchedUpdate == null) {
|
|
194
|
+
// this shouldn't ever happen, but if we don't have anything to compare
|
|
195
|
+
// the new manifest to, let the user know an update is available
|
|
196
|
+
updateInfo.putBoolean("isAvailable", true)
|
|
197
|
+
updateInfo.putString("manifestString", updateManifest.manifest.toString())
|
|
198
|
+
promise.resolve(updateInfo)
|
|
199
|
+
updatesServiceLocal.stateMachine?.processEvent(
|
|
200
|
+
UpdatesStateEvent.CheckCompleteWithUpdate(
|
|
201
|
+
updateManifest.manifest.getRawJson()
|
|
202
|
+
)
|
|
203
|
+
)
|
|
204
|
+
return
|
|
205
|
+
}
|
|
190
206
|
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
207
|
+
if (updatesServiceLocal.selectionPolicy.shouldLoadNewUpdate(
|
|
208
|
+
updateManifest.updateEntity,
|
|
209
|
+
launchedUpdate,
|
|
210
|
+
updateResponse.responseHeaderData?.manifestFilters
|
|
211
|
+
)
|
|
212
|
+
) {
|
|
213
|
+
updateInfo.putBoolean("isAvailable", true)
|
|
214
|
+
updateInfo.putString("manifestString", updateManifest.manifest.toString())
|
|
215
|
+
promise.resolve(updateInfo)
|
|
216
|
+
updatesServiceLocal.stateMachine?.processEvent(
|
|
217
|
+
UpdatesStateEvent.CheckCompleteWithUpdate(
|
|
218
|
+
updateManifest.manifest.getRawJson()
|
|
219
|
+
)
|
|
220
|
+
)
|
|
221
|
+
} else {
|
|
222
|
+
updateInfo.putBoolean("isAvailable", false)
|
|
223
|
+
promise.resolve(updateInfo)
|
|
224
|
+
updatesServiceLocal.stateMachine?.processEvent(
|
|
225
|
+
UpdatesStateEvent.CheckComplete()
|
|
226
|
+
)
|
|
227
|
+
}
|
|
203
228
|
}
|
|
204
229
|
}
|
|
205
|
-
|
|
206
|
-
|
|
230
|
+
)
|
|
231
|
+
}
|
|
207
232
|
} catch (e: IllegalStateException) {
|
|
208
233
|
promise.reject(
|
|
209
234
|
"ERR_UPDATES_CHECK",
|
|
@@ -223,6 +248,7 @@ class UpdatesModule(
|
|
|
223
248
|
)
|
|
224
249
|
return
|
|
225
250
|
}
|
|
251
|
+
updatesServiceLocal.stateMachine?.processEvent(UpdatesStateEvent.Download())
|
|
226
252
|
AsyncTask.execute {
|
|
227
253
|
val databaseHolder = updatesServiceLocal.databaseHolder
|
|
228
254
|
RemoteLoader(
|
|
@@ -238,6 +264,9 @@ class UpdatesModule(
|
|
|
238
264
|
override fun onFailure(e: Exception) {
|
|
239
265
|
databaseHolder.releaseDatabase()
|
|
240
266
|
promise.reject("ERR_UPDATES_FETCH", "Failed to download new update", e)
|
|
267
|
+
updatesServiceLocal.stateMachine?.processEvent(
|
|
268
|
+
UpdatesStateEvent.DownloadError("Failed to download new update: ${e.message}")
|
|
269
|
+
)
|
|
241
270
|
}
|
|
242
271
|
|
|
243
272
|
override fun onAssetLoaded(
|
|
@@ -276,9 +305,15 @@ class UpdatesModule(
|
|
|
276
305
|
|
|
277
306
|
if (loaderResult.updateDirective is UpdateDirective.RollBackToEmbeddedUpdateDirective) {
|
|
278
307
|
updateInfo.putBoolean("isRollBackToEmbedded", true)
|
|
308
|
+
updatesServiceLocal.stateMachine?.processEvent(
|
|
309
|
+
UpdatesStateEvent.DownloadCompleteWithRollback()
|
|
310
|
+
)
|
|
279
311
|
} else {
|
|
280
312
|
if (loaderResult.updateEntity == null) {
|
|
281
313
|
updateInfo.putBoolean("isNew", false)
|
|
314
|
+
updatesServiceLocal.stateMachine?.processEvent(
|
|
315
|
+
UpdatesStateEvent.DownloadComplete()
|
|
316
|
+
)
|
|
282
317
|
} else {
|
|
283
318
|
updatesServiceLocal.resetSelectionPolicy()
|
|
284
319
|
updateInfo.putBoolean("isNew", true)
|
|
@@ -286,6 +321,9 @@ class UpdatesModule(
|
|
|
286
321
|
"manifestString",
|
|
287
322
|
loaderResult.updateEntity.manifest.toString()
|
|
288
323
|
)
|
|
324
|
+
updatesServiceLocal.stateMachine?.processEvent(
|
|
325
|
+
UpdatesStateEvent.DownloadCompleteWithUpdate(loaderResult.updateEntity.manifest)
|
|
326
|
+
)
|
|
289
327
|
}
|
|
290
328
|
}
|
|
291
329
|
|
|
@@ -295,15 +333,14 @@ class UpdatesModule(
|
|
|
295
333
|
)
|
|
296
334
|
}
|
|
297
335
|
} catch (e: IllegalStateException) {
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
"The updates module controller has not been properly initialized. If you're using a development client, you cannot fetch updates. Otherwise, make sure you have called the native method UpdatesController.initialize()."
|
|
301
|
-
)
|
|
336
|
+
val message = "The updates module controller has not been properly initialized. If you're using a development client, you cannot fetch updates. Otherwise, make sure you have called the native method UpdatesController.initialize()."
|
|
337
|
+
promise.reject("ERR_UPDATES_FETCH", message)
|
|
302
338
|
}
|
|
303
339
|
}
|
|
304
340
|
|
|
305
341
|
@ExpoMethod
|
|
306
342
|
fun getExtraParamsAsync(promise: Promise) {
|
|
343
|
+
logger.debug("Called getExtraParamsAsync")
|
|
307
344
|
val updatesServiceLocal = updatesService
|
|
308
345
|
if (!updatesServiceLocal!!.configuration.isEnabled) {
|
|
309
346
|
promise.reject(
|
|
@@ -315,17 +352,36 @@ class UpdatesModule(
|
|
|
315
352
|
|
|
316
353
|
AsyncTask.execute {
|
|
317
354
|
val databaseHolder = updatesServiceLocal.databaseHolder
|
|
318
|
-
|
|
319
|
-
ManifestMetadata.getExtraParams(
|
|
355
|
+
try {
|
|
356
|
+
val result = ManifestMetadata.getExtraParams(
|
|
320
357
|
databaseHolder.database,
|
|
321
358
|
updatesServiceLocal.configuration,
|
|
322
359
|
)
|
|
323
|
-
|
|
360
|
+
databaseHolder.releaseDatabase()
|
|
361
|
+
val resultMap = when (result) {
|
|
362
|
+
null -> Bundle()
|
|
363
|
+
else -> {
|
|
364
|
+
Bundle().apply {
|
|
365
|
+
result.forEach {
|
|
366
|
+
putString(it.key, it.value)
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
promise.resolve(resultMap)
|
|
372
|
+
} catch (e: Exception) {
|
|
373
|
+
databaseHolder.releaseDatabase()
|
|
374
|
+
promise.reject(
|
|
375
|
+
"ERR_UPDATES_FETCH",
|
|
376
|
+
"Exception in getExtraParamsAsync: ${e.message}, ${e.stackTraceToString()}"
|
|
377
|
+
)
|
|
378
|
+
}
|
|
324
379
|
}
|
|
325
380
|
}
|
|
326
381
|
|
|
327
382
|
@ExpoMethod
|
|
328
383
|
fun setExtraParamAsync(key: String, value: String?, promise: Promise) {
|
|
384
|
+
logger.debug("Called setExtraParamAsync with key = $key, value = $value")
|
|
329
385
|
val updatesServiceLocal = updatesService
|
|
330
386
|
if (!updatesServiceLocal!!.configuration.isEnabled) {
|
|
331
387
|
promise.reject(
|
|
@@ -337,13 +393,22 @@ class UpdatesModule(
|
|
|
337
393
|
|
|
338
394
|
AsyncTask.execute {
|
|
339
395
|
val databaseHolder = updatesServiceLocal.databaseHolder
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
396
|
+
try {
|
|
397
|
+
ManifestMetadata.setExtraParam(
|
|
398
|
+
databaseHolder.database,
|
|
399
|
+
updatesServiceLocal.configuration,
|
|
400
|
+
key,
|
|
401
|
+
value
|
|
402
|
+
)
|
|
403
|
+
databaseHolder.releaseDatabase()
|
|
404
|
+
promise.resolve(null)
|
|
405
|
+
} catch (e: Exception) {
|
|
406
|
+
databaseHolder.releaseDatabase()
|
|
407
|
+
promise.reject(
|
|
408
|
+
"ERR_UPDATES_FETCH",
|
|
409
|
+
"Exception in setExtraParamAsync: ${e.message}, ${e.stackTraceToString()}"
|
|
410
|
+
)
|
|
411
|
+
}
|
|
347
412
|
}
|
|
348
413
|
}
|
|
349
414
|
|