<lambda>null1 package expo.modules.updates.loader
2
3 import android.content.Context
4 import android.util.Log
5 import expo.modules.updates.UpdatesConfiguration
6 import expo.modules.updates.db.UpdatesDatabase
7 import expo.modules.updates.db.entity.AssetEntity
8 import expo.modules.updates.db.entity.UpdateEntity
9 import expo.modules.updates.loader.FileDownloader.AssetDownloadCallback
10 import expo.modules.updates.loader.FileDownloader.RemoteUpdateDownloadCallback
11 import expo.modules.updates.manifest.EmbeddedManifest
12 import expo.modules.updates.manifest.ManifestMetadata
13 import expo.modules.updates.selectionpolicy.SelectionPolicy
14 import java.io.File
15
16 /**
17 * Subclass of [Loader] which handles downloading updates from a remote server.
18 *
19 * Unlike [EmbeddedLoader], it needs to manage file downloading. Currently, it does not skip
20 * any assets, meaning all assets must be downloaded in order for the update to be considered
21 * ready to launch.
22 */
23 class RemoteLoader internal constructor(
24 context: Context,
25 configuration: UpdatesConfiguration,
26 database: UpdatesDatabase,
27 private val mFileDownloader: FileDownloader,
28 updatesDirectory: File?,
29 private val launchedUpdate: UpdateEntity?,
30 private val loaderFiles: LoaderFiles
31 ) : Loader(context, configuration, database, updatesDirectory, loaderFiles) {
32 constructor(
33 context: Context,
34 configuration: UpdatesConfiguration,
35 database: UpdatesDatabase,
36 fileDownloader: FileDownloader,
37 updatesDirectory: File?,
38 launchedUpdate: UpdateEntity?
39 ) : this(context, configuration, database, fileDownloader, updatesDirectory, launchedUpdate, LoaderFiles())
40
41 override fun loadRemoteUpdate(
42 context: Context,
43 database: UpdatesDatabase,
44 configuration: UpdatesConfiguration,
45 callback: RemoteUpdateDownloadCallback
46 ) {
47 val embeddedUpdate = loaderFiles.readEmbeddedManifest(context, configuration)?.updateEntity
48 val extraHeaders = FileDownloader.getExtraHeadersForRemoteUpdateRequest(database, configuration, launchedUpdate, embeddedUpdate)
49 mFileDownloader.downloadRemoteUpdate(configuration, extraHeaders, context, callback)
50 }
51
52 override fun loadAsset(
53 context: Context,
54 assetEntity: AssetEntity,
55 updatesDirectory: File?,
56 configuration: UpdatesConfiguration,
57 callback: AssetDownloadCallback
58 ) {
59 mFileDownloader.downloadAsset(assetEntity, updatesDirectory, configuration, context, callback)
60 }
61
62 override fun shouldSkipAsset(assetEntity: AssetEntity): Boolean {
63 return false
64 }
65
66 companion object {
67 private val TAG = RemoteLoader::class.java.simpleName
68
69 fun processSuccessLoaderResult(
70 context: Context,
71 configuration: UpdatesConfiguration,
72 database: UpdatesDatabase,
73 selectionPolicy: SelectionPolicy,
74 directory: File?,
75 launchedUpdate: UpdateEntity?,
76 loaderResult: LoaderResult,
77 onComplete: (availableUpdate: UpdateEntity?, didRollBackToEmbedded: Boolean) -> Unit
78 ) {
79 val updateEntity = loaderResult.updateEntity
80 val updateDirective = loaderResult.updateDirective
81
82 if (updateDirective != null && updateDirective is UpdateDirective.RollBackToEmbeddedUpdateDirective) {
83 processRollBackToEmbeddedDirective(context, configuration, database, selectionPolicy, directory, launchedUpdate, updateDirective) { didRollBackToEmbedded ->
84 onComplete(null, didRollBackToEmbedded)
85 }
86 } else {
87 onComplete(updateEntity, false)
88 }
89 }
90
91 /**
92 * If directive is to roll-back to the embedded update and there is an embedded update,
93 * we need to update embedded update in the DB with the newer commitTime from the directive
94 * so that the selection policy will choose it. That way future updates can continue to be applied
95 * over this roll back, but older ones won't.
96 */
97 private fun processRollBackToEmbeddedDirective(
98 context: Context,
99 configuration: UpdatesConfiguration,
100 database: UpdatesDatabase,
101 selectionPolicy: SelectionPolicy,
102 directory: File?,
103 launchedUpdate: UpdateEntity?,
104 updateDirective: UpdateDirective.RollBackToEmbeddedUpdateDirective,
105 onComplete: (didRollBackToEmbedded: Boolean) -> Unit
106 ) {
107 if (!configuration.hasEmbeddedUpdate) {
108 onComplete(false)
109 return
110 }
111
112 val embeddedUpdate = EmbeddedManifest.get(context, configuration)!!.updateEntity
113 if (embeddedUpdate == null) {
114 onComplete(false)
115 return
116 }
117
118 val manifestFilters = ManifestMetadata.getManifestFilters(database, configuration)
119 if (!selectionPolicy.shouldLoadRollBackToEmbeddedDirective(updateDirective, embeddedUpdate, launchedUpdate, manifestFilters)) {
120 onComplete(false)
121 return
122 }
123
124 // update the embedded update commit time in the in-memory embedded update since it is a singleton
125 embeddedUpdate.commitTime = updateDirective.commitTime
126
127 // update the embedded update commit time in the database (requires loading and then updating)
128 EmbeddedLoader(context, configuration, database, directory).start(object : LoaderCallback {
129 /**
130 * This should never happen since we check for the embedded update above
131 */
132 override fun onFailure(e: Exception) {
133 Log.e(TAG, "Embedded update erroneously null when applying roll back to embedded directive", e)
134 onComplete(false)
135 }
136
137 override fun onSuccess(loaderResult: Loader.LoaderResult) {
138 val embeddedUpdateToLoad = loaderResult.updateEntity
139 database.updateDao().setUpdateCommitTime(embeddedUpdateToLoad!!, updateDirective.commitTime)
140 onComplete(true)
141 }
142
143 override fun onAssetLoaded(
144 asset: AssetEntity,
145 successfulAssetCount: Int,
146 failedAssetCount: Int,
147 totalAssetCount: Int
148 ) {
149 }
150
151 override fun onUpdateResponseLoaded(updateResponse: UpdateResponse): OnUpdateResponseLoadedResult {
152 return OnUpdateResponseLoadedResult(shouldDownloadManifestIfPresentInResponse = true)
153 }
154 })
155 }
156 }
157 }
158