@@ -25,7 +25,9 @@ import okio.sink
2525import okio.source
2626import java.io.File
2727import java.io.FileNotFoundException
28+ import java.io.IOException
2829import java.io.InterruptedIOException
30+ import kotlin.math.max
2931import kotlin.time.Clock
3032import kotlin.time.Duration.Companion.minutes
3133
@@ -168,6 +170,53 @@ class ReleaseManager private constructor(
168170 return Result .success(outputFile)
169171 }
170172
173+ private suspend fun performResourceDownload (resourceAsset : DownloadableAsset , initialSize : Long ) {
174+ val guessSize = if (resourceAsset.size == null ) null else initialSize + resourceAsset.size
175+ val guessInitial = if (guessSize == null ) 0 else initialSize
176+ _uiState .value = ReleaseManagerState .InDownload (guessInitial, guessSize)
177+
178+ val outputDir = getTempResourcesDirectory()
179+ val outputPath = outputDir.path
180+
181+ val finalDir = LaunchUtils .getGeodeResourcesDirectory(applicationContext)
182+
183+ try {
184+ if (! outputDir.exists()) {
185+ outputDir.mkdirs()
186+ }
187+
188+ DownloadUtils .downloadStream(
189+ httpClient,
190+ resourceAsset.url,
191+ onProgress = { progress, outOf ->
192+ _uiState .value = ReleaseManagerState .InDownload (initialSize + progress, outOf + initialSize)
193+ },
194+ onResponse = { body ->
195+ DownloadUtils .copyZipStreamToDirectory(
196+ body.byteStream(),
197+ outputDir
198+ )
199+
200+ if (finalDir.exists()) {
201+ finalDir.deleteRecursively()
202+ }
203+
204+ if (! outputDir.renameTo(finalDir)) {
205+ throw IOException (" failed to rename resources output directory" )
206+ }
207+ }
208+ )
209+ } catch (e: Exception ) {
210+ sendError(e)
211+ return
212+ } finally {
213+ val tempPathClone = File (outputPath)
214+ runCatching {
215+ if (tempPathClone.exists()) tempPathClone.deleteRecursively()
216+ }
217+ }
218+ }
219+
171220 private suspend fun performUpdate (release : Downloadable ) {
172221 val releaseAsset = release.getDownload()
173222 if (releaseAsset == null ) {
@@ -177,8 +226,14 @@ class ReleaseManager private constructor(
177226 return
178227 }
179228
229+ val resourcesAsset = release.getResourcesDownload()
230+
231+ var releaseSize = releaseAsset.size ? : 0L
232+ val resourcesSize = resourcesAsset?.size ? : 0L
233+
180234 // set an initial download size
181- _uiState .value = ReleaseManagerState .InDownload (0 , releaseAsset.size)
235+ val initialSize = if (releaseAsset.size == null && resourcesAsset?.size == null ) null else releaseSize + resourcesSize
236+ _uiState .value = ReleaseManagerState .InDownload (0 , initialSize)
182237
183238 val outputFile = getTempFile()
184239 // clone the file instance as renameTo may move the original file
@@ -197,7 +252,9 @@ class ReleaseManager private constructor(
197252 httpClient,
198253 releaseAsset.url,
199254 onProgress = { progress, outOf ->
200- _uiState .value = ReleaseManagerState .InDownload (progress, outOf)
255+ _uiState .value = ReleaseManagerState .InDownload (progress, outOf + resourcesSize)
256+
257+ releaseSize = max(outOf, releaseSize)
201258 },
202259 onResponse = { body ->
203260 DownloadUtils .extractFileFromZipStream(
@@ -231,6 +288,10 @@ class ReleaseManager private constructor(
231288 }
232289 }
233290
291+ if (resourcesAsset != null ) {
292+ performResourceDownload(resourcesAsset, releaseSize)
293+ }
294+
234295 // extraction performed
235296 updatePreferences(release)
236297
@@ -251,7 +312,7 @@ class ReleaseManager private constructor(
251312 val sharedPreferences = PreferenceUtils .get(applicationContext)
252313
253314 val originalFileHash = sharedPreferences.getString(PreferenceUtils .Key .CURRENT_RELEASE_MODIFIED )
254- ? : return false
315+ ? : return true
255316
256317 val currentFileHash = computeFileHash(geodeFile)
257318 return originalFileHash != currentFileHash
@@ -401,25 +462,41 @@ class ReleaseManager private constructor(
401462 return File (geodeDirectory, geodeName)
402463 }
403464
465+ private fun createRandomString (): String {
466+ val alphabet = (' A' .. ' Z' ) + (' a' .. ' z' ) + (' 0' .. ' 9' )
467+ return buildString(8 ) {
468+ repeat(8 ) { append(alphabet.random()) }
469+ }
470+ }
471+
404472 private fun getTempFile (): File {
405473 val geodeName = LaunchUtils .geodeFilename
406474 val geodeDirectory = LaunchUtils .getBaseDirectory(applicationContext)
407475
408476 // warning!! while File::createTempFile may look tempting, a certain brand of phones has a messed up implementation of it
409477 // so we're making a temp file manually (as long as it doesn't collide with the geode download, it's okay)
410478
411- val alphabet = (' A' .. ' Z' ) + (' a' .. ' z' ) + (' 0' .. ' 9' )
412- val suffix = buildString(8 ) {
413- repeat(8 ) { append(alphabet.random()) }
414- }
415-
479+ val suffix = createRandomString()
416480 val tmpName = " tmp-$suffix .$geodeName "
417481
418482 val tempFile = File (geodeDirectory, tmpName)
419483
420484 return tempFile
421485 }
422486
487+ private fun getTempResourcesDirectory (): File {
488+ val finalDir = LaunchUtils .getGeodeResourcesDirectory(applicationContext)
489+ val suffix = createRandomString()
490+ val tempDirName = " tmp-$suffix -geode.loader"
491+
492+ val parentDir = finalDir.parentFile
493+ return if (parentDir != null ) {
494+ File (finalDir.parentFile, tempDirName)
495+ } else {
496+ File (tempDirName)
497+ }
498+ }
499+
423500 /* *
424501 * Cancels the current update job.
425502 */
0 commit comments