expo-updates 0.18.10 → 0.19.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.
Files changed (68) hide show
  1. package/CHANGELOG.md +32 -5
  2. package/android/build.gradle +30 -5
  3. package/android/src/main/java/expo/modules/updates/UpdatesModule.kt +152 -73
  4. package/android/src/main/java/expo/modules/updates/db/dao/UpdateDao.kt +22 -9
  5. package/android/src/main/java/expo/modules/updates/loader/LoaderTask.kt +10 -73
  6. package/android/src/main/java/expo/modules/updates/loader/RemoteLoader.kt +92 -0
  7. package/android/src/main/java/expo/modules/updates/statemachine/UpdatesStateContext.kt +54 -25
  8. package/android/src/main/java/expo/modules/updates/statemachine/UpdatesStateMachine.kt +9 -4
  9. package/build/Updates.d.ts +3 -12
  10. package/build/Updates.d.ts.map +1 -1
  11. package/build/Updates.js +19 -34
  12. package/build/Updates.js.map +1 -1
  13. package/build/Updates.types.d.ts +22 -0
  14. package/build/Updates.types.d.ts.map +1 -1
  15. package/build/Updates.types.js.map +1 -1
  16. package/build/UpdatesEmitter.d.ts +22 -0
  17. package/build/UpdatesEmitter.d.ts.map +1 -0
  18. package/build/UpdatesEmitter.js +80 -0
  19. package/build/UpdatesEmitter.js.map +1 -0
  20. package/build/UpdatesHooks.d.ts.map +1 -1
  21. package/build/UpdatesHooks.js +2 -2
  22. package/build/UpdatesHooks.js.map +1 -1
  23. package/build/UseUpdates.d.ts +52 -0
  24. package/build/UseUpdates.d.ts.map +1 -0
  25. package/build/UseUpdates.js +77 -0
  26. package/build/UseUpdates.js.map +1 -0
  27. package/build/UseUpdates.types.d.ts +149 -0
  28. package/build/UseUpdates.types.d.ts.map +1 -0
  29. package/build/UseUpdates.types.js +14 -0
  30. package/build/UseUpdates.types.js.map +1 -0
  31. package/build/UseUpdatesUtils.d.ts +19 -0
  32. package/build/UseUpdatesUtils.d.ts.map +1 -0
  33. package/build/UseUpdatesUtils.js +56 -0
  34. package/build/UseUpdatesUtils.js.map +1 -0
  35. package/build/index.d.ts +4 -0
  36. package/build/index.d.ts.map +1 -1
  37. package/build/index.js +4 -0
  38. package/build/index.js.map +1 -1
  39. package/e2e/fixtures/App-apitest.tsx +97 -75
  40. package/e2e/fixtures/App.tsx +83 -28
  41. package/e2e/fixtures/Updates.e2e.ts +82 -51
  42. package/e2e/fixtures/project_files/e2e/tests/utils/update.ts +6 -2
  43. package/e2e/fixtures/project_files/eas-hooks/eas-build-pre-install.sh +3 -0
  44. package/e2e/fixtures/project_files/xcode.env.local +4 -0
  45. package/e2e/setup/project.js +1 -10
  46. package/ios/EXUpdates/AppController.swift +32 -18
  47. package/ios/EXUpdates/AppLoader/AppLoaderTask.swift +60 -78
  48. package/ios/EXUpdates/AppLoader/FileDownloader.swift +1 -4
  49. package/ios/EXUpdates/AppLoader/RemoteAppLoader.swift +105 -0
  50. package/ios/EXUpdates/Database/UpdatesDatabase.swift +1 -1
  51. package/ios/EXUpdates/DevLauncherController.swift +2 -2
  52. package/ios/EXUpdates/Exceptions.swift +0 -6
  53. package/ios/EXUpdates/SelectionPolicy/SelectionPolicies.swift +1 -2
  54. package/ios/EXUpdates/Update/BareUpdate.swift +1 -1
  55. package/ios/EXUpdates/Update/Update.swift +2 -2
  56. package/ios/EXUpdates/UpdatesModule.swift +23 -1
  57. package/ios/EXUpdates/UpdatesStateMachine.swift +41 -20
  58. package/ios/EXUpdates/UpdatesUtils.swift +91 -39
  59. package/ios/Tests/FileDownloaderManifestParsingSpec.swift +5 -5
  60. package/package.json +7 -7
  61. package/src/Updates.ts +20 -45
  62. package/src/Updates.types.ts +27 -0
  63. package/src/UpdatesEmitter.ts +97 -0
  64. package/src/UpdatesHooks.ts +2 -2
  65. package/src/UseUpdates.ts +86 -0
  66. package/src/UseUpdates.types.ts +152 -0
  67. package/src/UseUpdatesUtils.ts +79 -0
  68. package/src/index.ts +4 -0
package/CHANGELOG.md CHANGED
@@ -10,24 +10,50 @@
10
10
 
11
11
  ### 💡 Others
12
12
 
13
- ## 0.18.10 — 2023-07-12
13
+ ## 0.19.0 — 2023-07-28
14
+
15
+ ### 🎉 New features
16
+
17
+ - New useUpdates() JS API. ([#23532](https://github.com/expo/expo/pull/23532) by [@douglowder](https://github.com/douglowder))
18
+
19
+ ### 🐛 Bug fixes
20
+
21
+ - [Android] Fix rollback embedded update logic. ([#23244](https://github.com/expo/expo/pull/23244) by [@wschurman](https://github.com/wschurman))
22
+ - Correctly handle roll backs in JS module methods. ([#23356](https://github.com/expo/expo/pull/23356), [#23377](https://github.com/expo/expo/pull/23377) by [@wschurman](https://github.com/wschurman))
23
+ - [iOS] Fix unmatched selection policy. ([#23535](https://github.com/expo/expo/pull/23535) by [@chochihim](https://github.com/chochihim))
24
+ - Handle initialization errors in useUpdates(). ([#23640](https://github.com/expo/expo/pull/23640) by [@douglowder](https://github.com/douglowder))
25
+ - [iOS] Fix error handling on iOS during startup check for updates. ([#23663](https://github.com/expo/expo/pull/23663) by [@douglowder](https://github.com/douglowder))
26
+ - Last check dateTime should come from native. ([#23692](https://github.com/expo/expo/pull/23692) by [@douglowder](https://github.com/douglowder))
27
+
28
+ ### 💡 Others
29
+
30
+ - Native getter for state machine context. ([#23428](https://github.com/expo/expo/pull/23428) by [@douglowder](https://github.com/douglowder))
31
+ - [iOS] Fix template for EX_UPDATES_NATIVE_DEBUG. ([#23602](https://github.com/expo/expo/pull/23602) by [@douglowder](https://github.com/douglowder))
32
+
33
+ ## 0.18.11 - 2023-07-23
34
+
35
+ ### 💡 Others
36
+
37
+ - [Android] EX_UPDATES_ANDROID_DELAY_LOAD_APP settable by env. ([#23479](https://github.com/expo/expo/pull/23479) by [@douglowder](https://github.com/douglowder))
38
+
39
+ ## 0.18.10 - 2023-07-12
14
40
 
15
41
  ### 🐛 Bug fixes
16
42
 
17
43
  - [CLI] Add missing chalk dependency for the `expo-updates` cli. ([#23429](https://github.com/expo/expo/pull/23429) by [@byCedric](https://github.com/byCedric))
18
44
  - [CLI] Drop `fs-extra` in favor of `fs`. ([#23431](https://github.com/expo/expo/pull/23431) by [@byCedric](https://github.com/byCedric))
19
45
 
20
- ## 0.18.9 2023-07-10
46
+ ## 0.18.9 - 2023-07-10
21
47
 
22
48
  ### 🐛 Bug fixes
23
49
 
24
50
  - [ios] Allow nil scopeKey for bare/embedded updates. ([#23385](https://github.com/expo/expo/pull/23385) by [@wschurman](https://github.com/wschurman))
25
51
 
26
- ## 0.18.8 2023-07-04
52
+ ## 0.18.8 - 2023-07-04
27
53
 
28
54
  _This version does not introduce any user-facing changes._
29
55
 
30
- ## 0.18.7 2023-06-30
56
+ ## 0.18.7 - 2023-06-30
31
57
 
32
58
  ### 🐛 Bug fixes
33
59
 
@@ -35,7 +61,7 @@ _This version does not introduce any user-facing changes._
35
61
  - [iOS] Fix response header casing bug. ([#23234](https://github.com/expo/expo/pull/23234) by [@wschurman](https://github.com/wschurman))
36
62
  - Fix tsconfig paths and other SDK 49 Metro features. ([#23276](https://github.com/expo/expo/pull/23276) by [@EvanBacon](https://github.com/EvanBacon))
37
63
 
38
- ## 0.18.6 2023-06-29
64
+ ## 0.18.6 - 2023-06-29
39
65
 
40
66
  ### 🐛 Bug fixes
41
67
 
@@ -53,6 +79,7 @@ _This version does not introduce any user-facing changes._
53
79
 
54
80
  - [iOS] Use weak delegate for state machine. ([#23060](https://github.com/expo/expo/pull/23060) by [@wschurman](https://github.com/wschurman))
55
81
  - [Android] Convert LoaderTask.RemoteCheckResult to sealed class. ([#23061](https://github.com/expo/expo/pull/23061) by [@wschurman](https://github.com/wschurman))
82
+ - [iOS] Use swift enum for AppLoaderTask delegate. ([#23064](https://github.com/expo/expo/pull/23064) by [@wschurman](https://github.com/wschurman))
56
83
 
57
84
  ## 0.18.3 — 2023-06-24
58
85
 
@@ -4,9 +4,34 @@ apply plugin: 'kotlin-kapt'
4
4
  apply plugin: 'maven-publish'
5
5
 
6
6
  group = 'host.exp.exponent'
7
- version = '0.18.10'
7
+ version = '0.19.0'
8
+
9
+ // Utility method to derive boolean values from the environment or from Java properties,
10
+ // and return them as strings to be used in BuildConfig fields
11
+ def getBoolStringFromPropOrEnv(String name, Boolean defaultValue) {
12
+ def env_value = System.getenv(name)
13
+ def prop_value = findProperty(name)
14
+ def value = defaultValue.toString()
15
+ // If present, property value in gradle.properties overrides default
16
+ if (prop_value != null) {
17
+ value = boolish(prop_value).toString()
18
+ println("expo-updates: Value of ${name} was overridden by property, new value = ${value}")
19
+ }
20
+ // If present, env value overrides default and gradle.properties
21
+ if (env_value != null) {
22
+ value = boolish(env_value).toString()
23
+ println("expo-updates: Value of ${name} was overridden by environment, new value = ${value}")
24
+ }
25
+ return value
26
+ }
27
+
28
+ // If true, app will use bundled manifest and updates will be enabled, even for
29
+ // debug builds. (default false)
30
+ def exUpdatesNativeDebug = getBoolStringFromPropOrEnv("EX_UPDATES_NATIVE_DEBUG", false)
8
31
 
9
- def ex_updates_native_debug = System.getenv("EX_UPDATES_NATIVE_DEBUG") == "1" ? "true" : "false"
32
+ // If true, code will run that delays app loading until updates is initialized, to prevent ANR issues.
33
+ // (default true)
34
+ def exUpdatesAndroidDelayLoadApp = getBoolStringFromPropOrEnv("EX_UPDATES_ANDROID_DELAY_LOAD_APP", true)
10
35
 
11
36
  buildscript {
12
37
  def expoModulesCorePlugin = new File(project(":expo-modules-core").projectDir.absolutePath, "ExpoModulesCorePlugin.gradle")
@@ -70,12 +95,12 @@ android {
70
95
  minSdkVersion safeExtGet("minSdkVersion", 21)
71
96
  targetSdkVersion safeExtGet("targetSdkVersion", 33)
72
97
  versionCode 31
73
- versionName '0.18.10'
98
+ versionName '0.19.0'
74
99
  consumerProguardFiles("proguard-rules.pro")
75
100
  testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
76
101
 
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())
102
+ buildConfigField("boolean", "EX_UPDATES_NATIVE_DEBUG", exUpdatesNativeDebug)
103
+ buildConfigField("boolean", "EX_UPDATES_ANDROID_DELAY_LOAD_APP", exUpdatesAndroidDelayLoadApp)
79
104
 
80
105
  // uncomment below to export the database schema when making changes
81
106
  /* javaCompileOptions {
@@ -19,12 +19,16 @@ import expo.modules.updates.logging.UpdatesLogEntry
19
19
  import expo.modules.updates.logging.UpdatesLogReader
20
20
  import expo.modules.updates.logging.UpdatesLogger
21
21
  import expo.modules.updates.manifest.ManifestMetadata
22
+ import expo.modules.updates.statemachine.UpdatesStateContext
22
23
  import expo.modules.updates.statemachine.UpdatesStateEvent
23
24
  import java.util.Date
25
+ import expo.modules.updates.manifest.EmbeddedManifest
26
+ import expo.modules.updates.manifest.UpdateManifest
24
27
 
25
28
  // these unused imports must stay because of versioning
26
29
  /* ktlint-disable no-unused-imports */
27
30
  import expo.modules.updates.UpdatesConfiguration
31
+
28
32
  /* ktlint-enable no-unused-imports */
29
33
 
30
34
  /**
@@ -134,6 +138,28 @@ class UpdatesModule(
134
138
  }
135
139
  }
136
140
 
141
+ // Used internally by @expo/use-updates useUpdates() to get its initial state
142
+ @ExpoMethod
143
+ fun getNativeStateMachineContextAsync(promise: Promise) {
144
+ try {
145
+ val updatesServiceLocal = updatesService
146
+ if (!updatesServiceLocal!!.configuration.isEnabled) {
147
+ promise.reject(
148
+ "ERR_UPDATES_DISABLED",
149
+ "You cannot check for updates when expo-updates is not enabled."
150
+ )
151
+ return
152
+ }
153
+ val context = updatesServiceLocal.stateMachine?.context ?: UpdatesStateContext()
154
+ promise.resolve(context.bundle)
155
+ } catch (e: IllegalStateException) {
156
+ promise.reject(
157
+ "ERR_UPDATES_CHECK",
158
+ "The updates module controller has not been properly initialized. If you're using a development client, you cannot check for updates. Otherwise, make sure you have called the native method UpdatesController.initialize()."
159
+ )
160
+ }
161
+ }
162
+
137
163
  @ExpoMethod
138
164
  fun checkForUpdateAsync(promise: Promise) {
139
165
  try {
@@ -169,42 +195,46 @@ class UpdatesModule(
169
195
  val updateDirective = updateResponse.directiveUpdateResponsePart?.updateDirective
170
196
  val updateManifest = updateResponse.manifestUpdateResponsePart?.updateManifest
171
197
 
172
- val updateInfo = Bundle()
198
+ val launchedUpdate = updatesServiceLocal.launchedUpdate
199
+
173
200
  if (updateDirective != null) {
174
201
  if (updateDirective is UpdateDirective.RollBackToEmbeddedUpdateDirective) {
175
- updateInfo.putBoolean("isRollBackToEmbedded", true)
176
- updateInfo.putBoolean("isAvailable", false)
177
- promise.resolve(updateInfo)
178
- updatesServiceLocal.stateMachine?.processEvent(
179
- UpdatesStateEvent.CheckCompleteWithRollback()
180
- )
202
+ if (!updatesServiceLocal.configuration.hasEmbeddedUpdate) {
203
+ promise.resolveWithCheckForUpdateAsyncResult(CheckForUpdateAsyncResult.NoUpdateAvailable(), updatesServiceLocal)
204
+ return
205
+ }
206
+
207
+ val embeddedUpdate = EmbeddedManifest.get(context, updatesServiceLocal.configuration)!!.updateEntity
208
+ if (embeddedUpdate == null) {
209
+ promise.resolveWithCheckForUpdateAsyncResult(CheckForUpdateAsyncResult.NoUpdateAvailable(), updatesServiceLocal)
210
+ return
211
+ }
212
+
213
+ if (!updatesServiceLocal.selectionPolicy.shouldLoadRollBackToEmbeddedDirective(
214
+ updateDirective,
215
+ embeddedUpdate,
216
+ launchedUpdate,
217
+ updateResponse.responseHeaderData?.manifestFilters
218
+ )
219
+ ) {
220
+ promise.resolveWithCheckForUpdateAsyncResult(CheckForUpdateAsyncResult.NoUpdateAvailable(), updatesServiceLocal)
221
+ return
222
+ }
223
+
224
+ promise.resolveWithCheckForUpdateAsyncResult(CheckForUpdateAsyncResult.RollBackToEmbedded(), updatesServiceLocal)
181
225
  return
182
226
  }
183
227
  }
184
228
 
185
229
  if (updateManifest == null) {
186
- updateInfo.putBoolean("isRollBackToEmbedded", false)
187
- updateInfo.putBoolean("isAvailable", false)
188
- promise.resolve(updateInfo)
189
- updatesServiceLocal.stateMachine?.processEvent(
190
- UpdatesStateEvent.CheckCompleteUnavailable()
191
- )
230
+ promise.resolveWithCheckForUpdateAsyncResult(CheckForUpdateAsyncResult.NoUpdateAvailable(), updatesServiceLocal)
192
231
  return
193
232
  }
194
233
 
195
- val launchedUpdate = updatesServiceLocal.launchedUpdate
196
234
  if (launchedUpdate == null) {
197
235
  // this shouldn't ever happen, but if we don't have anything to compare
198
236
  // the new manifest to, let the user know an update is available
199
- updateInfo.putBoolean("isRollBackToEmbedded", false)
200
- updateInfo.putBoolean("isAvailable", true)
201
- updateInfo.putString("manifestString", updateManifest.manifest.toString())
202
- promise.resolve(updateInfo)
203
- updatesServiceLocal.stateMachine?.processEvent(
204
- UpdatesStateEvent.CheckCompleteWithUpdate(
205
- updateManifest.manifest.getRawJson()
206
- )
207
- )
237
+ promise.resolveWithCheckForUpdateAsyncResult(CheckForUpdateAsyncResult.UpdateAvailable(updateManifest), updatesServiceLocal)
208
238
  return
209
239
  }
210
240
 
@@ -214,22 +244,9 @@ class UpdatesModule(
214
244
  updateResponse.responseHeaderData?.manifestFilters
215
245
  )
216
246
  ) {
217
- updateInfo.putBoolean("isRollBackToEmbedded", false)
218
- updateInfo.putBoolean("isAvailable", true)
219
- updateInfo.putString("manifestString", updateManifest.manifest.toString())
220
- promise.resolve(updateInfo)
221
- updatesServiceLocal.stateMachine?.processEvent(
222
- UpdatesStateEvent.CheckCompleteWithUpdate(
223
- updateManifest.manifest.getRawJson()
224
- )
225
- )
247
+ promise.resolveWithCheckForUpdateAsyncResult(CheckForUpdateAsyncResult.UpdateAvailable(updateManifest), updatesServiceLocal)
226
248
  } else {
227
- updateInfo.putBoolean("isRollBackToEmbedded", false)
228
- updateInfo.putBoolean("isAvailable", false)
229
- promise.resolve(updateInfo)
230
- updatesServiceLocal.stateMachine?.processEvent(
231
- UpdatesStateEvent.CheckCompleteUnavailable()
232
- )
249
+ promise.resolveWithCheckForUpdateAsyncResult(CheckForUpdateAsyncResult.NoUpdateAvailable(), updatesServiceLocal)
233
250
  }
234
251
  }
235
252
  }
@@ -243,6 +260,51 @@ class UpdatesModule(
243
260
  }
244
261
  }
245
262
 
263
+ private sealed class CheckForUpdateAsyncResult(private val status: Status) {
264
+ private enum class Status {
265
+ NO_UPDATE_AVAILABLE,
266
+ UPDATE_AVAILABLE,
267
+ ROLL_BACK_TO_EMBEDDED
268
+ }
269
+
270
+ class NoUpdateAvailable : CheckForUpdateAsyncResult(Status.NO_UPDATE_AVAILABLE)
271
+ class UpdateAvailable(val updateManifest: UpdateManifest) : CheckForUpdateAsyncResult(Status.UPDATE_AVAILABLE)
272
+ class RollBackToEmbedded : CheckForUpdateAsyncResult(Status.ROLL_BACK_TO_EMBEDDED)
273
+ }
274
+
275
+ private fun Promise.resolveWithCheckForUpdateAsyncResult(checkForUpdateAsyncResult: CheckForUpdateAsyncResult, updatesServiceLocal: UpdatesInterface) {
276
+ resolve(
277
+ Bundle().apply {
278
+ when (checkForUpdateAsyncResult) {
279
+ is CheckForUpdateAsyncResult.NoUpdateAvailable -> {
280
+ putBoolean("isRollBackToEmbedded", false)
281
+ putBoolean("isAvailable", false)
282
+ }
283
+
284
+ is CheckForUpdateAsyncResult.RollBackToEmbedded -> {
285
+ putBoolean("isRollBackToEmbedded", true)
286
+ putBoolean("isAvailable", false)
287
+ }
288
+
289
+ is CheckForUpdateAsyncResult.UpdateAvailable -> {
290
+ putBoolean("isRollBackToEmbedded", false)
291
+ putBoolean("isAvailable", true)
292
+ putString("manifestString", checkForUpdateAsyncResult.updateManifest.manifest.toString())
293
+ }
294
+ }
295
+ }
296
+ )
297
+ updatesServiceLocal.stateMachine?.processEvent(
298
+ when (checkForUpdateAsyncResult) {
299
+ is CheckForUpdateAsyncResult.NoUpdateAvailable -> UpdatesStateEvent.CheckCompleteUnavailable()
300
+ is CheckForUpdateAsyncResult.RollBackToEmbedded -> UpdatesStateEvent.CheckCompleteWithRollback()
301
+ is CheckForUpdateAsyncResult.UpdateAvailable -> UpdatesStateEvent.CheckCompleteWithUpdate(
302
+ checkForUpdateAsyncResult.updateManifest.manifest.getRawJson()
303
+ )
304
+ }
305
+ )
306
+ }
307
+
246
308
  @ExpoMethod
247
309
  fun fetchUpdateAsync(promise: Promise) {
248
310
  try {
@@ -257,10 +319,11 @@ class UpdatesModule(
257
319
  updatesServiceLocal.stateMachine?.processEvent(UpdatesStateEvent.Download())
258
320
  AsyncTask.execute {
259
321
  val databaseHolder = updatesServiceLocal.databaseHolder
322
+ val database = databaseHolder.database
260
323
  RemoteLoader(
261
324
  context,
262
325
  updatesServiceLocal.configuration,
263
- databaseHolder.database,
326
+ database,
264
327
  updatesServiceLocal.fileDownloader,
265
328
  updatesServiceLocal.directory,
266
329
  updatesServiceLocal.launchedUpdate
@@ -294,7 +357,8 @@ class UpdatesModule(
294
357
  )
295
358
  }
296
359
 
297
- val updateManifest = updateResponse.manifestUpdateResponsePart?.updateManifest ?: return Loader.OnUpdateResponseLoadedResult(shouldDownloadManifestIfPresentInResponse = false)
360
+ val updateManifest = updateResponse.manifestUpdateResponsePart?.updateManifest
361
+ ?: return Loader.OnUpdateResponseLoadedResult(shouldDownloadManifestIfPresentInResponse = false)
298
362
 
299
363
  return Loader.OnUpdateResponseLoadedResult(
300
364
  shouldDownloadManifestIfPresentInResponse = updatesServiceLocal.selectionPolicy.shouldLoadNewUpdate(
@@ -306,44 +370,59 @@ class UpdatesModule(
306
370
  }
307
371
 
308
372
  override fun onSuccess(loaderResult: Loader.LoaderResult) {
309
- databaseHolder.releaseDatabase()
310
- val updateInfo = Bundle()
311
-
312
- if (loaderResult.updateDirective is UpdateDirective.RollBackToEmbeddedUpdateDirective) {
313
- updateInfo.putBoolean("isRollBackToEmbedded", true)
314
- updateInfo.putBoolean("isNew", false)
315
-
316
- updatesServiceLocal.stateMachine?.processEvent(
317
- UpdatesStateEvent.DownloadCompleteWithRollback()
318
- )
319
- } else {
320
- updateInfo.putBoolean("isRollBackToEmbedded", false)
321
-
322
- if (loaderResult.updateEntity == null) {
323
- updateInfo.putBoolean("isNew", false)
324
- updatesServiceLocal.stateMachine?.processEvent(
325
- UpdatesStateEvent.DownloadComplete()
326
- )
327
- } else {
328
- updatesServiceLocal.resetSelectionPolicy()
329
- updateInfo.putBoolean("isNew", true)
330
-
331
- // We need the explicit casting here because when in versioned expo-updates,
332
- // the UpdateEntity and UpdatesModule are in different package namespace,
333
- // Kotlin cannot do the smart casting for that case.
334
- val updateEntity = loaderResult.updateEntity as UpdateEntity
335
-
336
- updateInfo.putString(
337
- "manifestString",
338
- updateEntity.manifest.toString()
373
+ RemoteLoader.processSuccessLoaderResult(
374
+ context,
375
+ updatesServiceLocal.configuration,
376
+ database,
377
+ updatesServiceLocal.selectionPolicy,
378
+ updatesServiceLocal.directory,
379
+ updatesServiceLocal.launchedUpdate,
380
+ loaderResult
381
+ ) { availableUpdate, didRollBackToEmbedded ->
382
+ databaseHolder.releaseDatabase()
383
+
384
+ if (didRollBackToEmbedded) {
385
+ promise.resolve(
386
+ Bundle().apply {
387
+ putBoolean("isRollBackToEmbedded", true)
388
+ putBoolean("isNew", false)
389
+ }
339
390
  )
340
391
  updatesServiceLocal.stateMachine?.processEvent(
341
- UpdatesStateEvent.DownloadCompleteWithUpdate(updateEntity.manifest!!)
392
+ UpdatesStateEvent.DownloadCompleteWithRollback()
342
393
  )
394
+ } else {
395
+ if (availableUpdate == null) {
396
+ promise.resolve(
397
+ Bundle().apply {
398
+ putBoolean("isRollBackToEmbedded", false)
399
+ putBoolean("isNew", false)
400
+ }
401
+ )
402
+ updatesServiceLocal.stateMachine?.processEvent(
403
+ UpdatesStateEvent.DownloadComplete()
404
+ )
405
+ } else {
406
+ updatesServiceLocal.resetSelectionPolicy()
407
+
408
+ // We need the explicit casting here because when in versioned expo-updates,
409
+ // the UpdateEntity and UpdatesModule are in different package namespace,
410
+ // Kotlin cannot do the smart casting for that case.
411
+ val updateEntity = loaderResult.updateEntity as UpdateEntity
412
+
413
+ promise.resolve(
414
+ Bundle().apply {
415
+ putBoolean("isRollBackToEmbedded", false)
416
+ putBoolean("isNew", true)
417
+ putString("manifestString", updateEntity.manifest.toString())
418
+ }
419
+ )
420
+ updatesServiceLocal.stateMachine?.processEvent(
421
+ UpdatesStateEvent.DownloadCompleteWithUpdate(updateEntity.manifest!!)
422
+ )
423
+ }
343
424
  }
344
425
  }
345
-
346
- promise.resolve(updateInfo)
347
426
  }
348
427
  }
349
428
  )
@@ -34,9 +34,6 @@ abstract class UpdateDao {
34
34
  @Query("UPDATE updates SET status = :status WHERE id = :id;")
35
35
  abstract fun _markUpdateWithStatus(status: UpdateStatus, id: UUID)
36
36
 
37
- @Update
38
- abstract fun _updateUpdate(update: UpdateEntity)
39
-
40
37
  @Query(
41
38
  "UPDATE updates SET status = :status WHERE id IN (" +
42
39
  "SELECT DISTINCT update_id FROM updates_assets WHERE asset_id IN (:missingAssetIds));"
@@ -78,14 +75,20 @@ abstract class UpdateDao {
78
75
 
79
76
  fun setUpdateScopeKey(update: UpdateEntity, newScopeKey: String) {
80
77
  update.scopeKey = newScopeKey
81
- _updateUpdate(update)
78
+ _setUpdateScopeKeyInternal(update.id, newScopeKey)
82
79
  }
83
80
 
81
+ @Query("UPDATE updates SET scope_key = :newScopeKey WHERE id = :id;")
82
+ abstract fun _setUpdateScopeKeyInternal(id: UUID, newScopeKey: String)
83
+
84
84
  fun setUpdateCommitTime(update: UpdateEntity, commitTime: Date) {
85
85
  update.commitTime = commitTime
86
- _updateUpdate(update)
86
+ _setUpdateCommitTime(update.id, commitTime)
87
87
  }
88
88
 
89
+ @Query("UPDATE updates SET commit_time = :commitTime WHERE id = :id;")
90
+ abstract fun _setUpdateCommitTime(id: UUID, commitTime: Date)
91
+
89
92
  @Transaction
90
93
  open fun markUpdateFinished(update: UpdateEntity, hasSkippedEmbeddedAssets: Boolean) {
91
94
  var statusToMark = UpdateStatus.READY
@@ -103,20 +106,30 @@ abstract class UpdateDao {
103
106
  }
104
107
 
105
108
  fun markUpdateAccessed(update: UpdateEntity) {
106
- update.lastAccessed = Date()
107
- _updateUpdate(update)
109
+ val newLastAccessed = Date()
110
+ update.lastAccessed = newLastAccessed
111
+ _markUpdateAccessed(update.id, newLastAccessed)
108
112
  }
109
113
 
114
+ @Query("UPDATE updates SET last_accessed = :lastAccessed WHERE id = :id;")
115
+ abstract fun _markUpdateAccessed(id: UUID, lastAccessed: Date)
116
+
110
117
  fun incrementSuccessfulLaunchCount(update: UpdateEntity) {
111
118
  update.successfulLaunchCount++
112
- _updateUpdate(update)
119
+ _incrementSuccessfulLaunchCount(update.id)
113
120
  }
114
121
 
122
+ @Query("UPDATE updates SET successful_launch_count = successful_launch_count + 1 WHERE id = :id;")
123
+ abstract fun _incrementSuccessfulLaunchCount(id: UUID)
124
+
115
125
  fun incrementFailedLaunchCount(update: UpdateEntity) {
116
126
  update.failedLaunchCount++
117
- _updateUpdate(update)
127
+ _incrementFailedLaunchCount(update.id)
118
128
  }
119
129
 
130
+ @Query("UPDATE updates SET failed_launch_count = failed_launch_count + 1 WHERE id = :id;")
131
+ abstract fun _incrementFailedLaunchCount(id: UUID)
132
+
120
133
  fun markUpdatesWithMissingAssets(missingAssets: List<AssetEntity>) {
121
134
  val missingAssetIds = mutableListOf<Long>()
122
135
  for (asset in missingAssets) {
@@ -372,80 +372,17 @@ class LoaderTask(
372
372
  }
373
373
 
374
374
  override fun onSuccess(loaderResult: Loader.LoaderResult) {
375
- val updateEntity = loaderResult.updateEntity
376
- val updateDirective = loaderResult.updateDirective
377
-
378
- if (updateDirective != null && updateDirective is UpdateDirective.RollBackToEmbeddedUpdateDirective) {
379
- processRollBackToEmbeddedDirective(updateDirective)
380
- } else {
381
- launchUpdate(updateEntity)
382
- }
383
- }
384
-
385
- /**
386
- * If directive is to roll-back to the embedded update and there is an embedded update,
387
- * we need to update embedded update in the DB with the newer commitTime from the directive
388
- * so that the selection policy will choose it. That way future updates can continue to be applied
389
- * over this roll back, but older ones won't.
390
- */
391
- private fun processRollBackToEmbeddedDirective(updateDirective: UpdateDirective.RollBackToEmbeddedUpdateDirective) {
392
- if (!configuration.hasEmbeddedUpdate) {
393
- launchUpdate(null)
394
- return
395
- }
396
-
397
- val embeddedUpdate = EmbeddedManifest.get(context, configuration)!!.updateEntity
398
- if (embeddedUpdate == null) {
399
- launchUpdate(null)
400
- return
401
- }
402
-
403
- val launcher = DatabaseLauncher(configuration, directory, fileDownloader, selectionPolicy)
404
- val launchableUpdate = launcher.getLaunchableUpdate(database, context)
405
- val manifestFilters = ManifestMetadata.getManifestFilters(database, configuration)
406
-
407
- if (!selectionPolicy.shouldLoadRollBackToEmbeddedDirective(updateDirective, embeddedUpdate, launchableUpdate, manifestFilters)) {
408
- launchUpdate(null)
409
- return
375
+ RemoteLoader.processSuccessLoaderResult(
376
+ context,
377
+ configuration,
378
+ database,
379
+ selectionPolicy,
380
+ directory,
381
+ candidateLauncher?.launchedUpdate,
382
+ loaderResult
383
+ ) { availableUpdate, _ ->
384
+ launchUpdate(availableUpdate)
410
385
  }
411
-
412
- // update the embedded update commit time in the in-memory embedded update since it is a singleton
413
- embeddedUpdate.commitTime = updateDirective.commitTime
414
-
415
- // update the embedded update commit time in the database (requires loading and then updating)
416
- EmbeddedLoader(context, configuration, database, directory).start(object : LoaderCallback {
417
- /**
418
- * This should never happen since we check for the embedded update above
419
- */
420
- override fun onFailure(e: Exception) {
421
- Log.e(
422
- TAG,
423
- "Embedded update erroneously null when applying roll back to embedded directive",
424
- e
425
- )
426
- launchUpdate(null)
427
- }
428
-
429
- override fun onSuccess(loaderResult: Loader.LoaderResult) {
430
- val embeddedUpdateToLoad = loaderResult.updateEntity
431
- database.updateDao().setUpdateCommitTime(embeddedUpdateToLoad!!, updateDirective.commitTime)
432
- launchUpdate(null)
433
- }
434
-
435
- override fun onAssetLoaded(
436
- asset: AssetEntity,
437
- successfulAssetCount: Int,
438
- failedAssetCount: Int,
439
- totalAssetCount: Int
440
- ) {
441
- }
442
-
443
- override fun onUpdateResponseLoaded(updateResponse: UpdateResponse): Loader.OnUpdateResponseLoadedResult {
444
- return Loader.OnUpdateResponseLoadedResult(
445
- shouldDownloadManifestIfPresentInResponse = true
446
- )
447
- }
448
- })
449
386
  }
450
387
 
451
388
  private fun launchUpdate(availableUpdate: UpdateEntity?) {