mirror of
https://github.com/geode-sdk/android-launcher.git
synced 2026-10-23 16:59:21 +00:00
improve resilience of downloadStream
This commit is contained in:
2 files changed
+67
-21
No files matched your search
@@ -19,6 +19,7 @@ import kotlinx.coroutines.launch
|
|||||||
import okhttp3.Cache
|
import okhttp3.Cache
|
||||||
import okhttp3.OkHttpClient
|
import okhttp3.OkHttpClient
|
||||||
import okio.buffer
|
import okio.buffer
|
||||||
|
import okio.sink
|
||||||
import okio.source
|
import okio.source
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.io.FileNotFoundException
|
import java.io.FileNotFoundException
|
||||||
@@ -149,12 +150,15 @@ class ReleaseManager private constructor(
|
|||||||
try {
|
try {
|
||||||
DownloadUtils.downloadStream(
|
DownloadUtils.downloadStream(
|
||||||
httpClient,
|
httpClient,
|
||||||
download.url
|
download.url,
|
||||||
).use { fileStream ->
|
onResponse = { body ->
|
||||||
outputFile.outputStream().use {
|
body.source().use { source ->
|
||||||
fileStream.copyTo(it)
|
outputFile.sink().buffer().use { sink ->
|
||||||
|
sink.writeAll(source)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
return Result.failure(e)
|
return Result.failure(e)
|
||||||
}
|
}
|
||||||
@@ -179,19 +183,22 @@ class ReleaseManager private constructor(
|
|||||||
val tempFilePath = outputFile.path
|
val tempFilePath = outputFile.path
|
||||||
|
|
||||||
try {
|
try {
|
||||||
val fileStream = DownloadUtils.downloadStream(
|
|
||||||
httpClient,
|
|
||||||
releaseAsset.url
|
|
||||||
) { progress, outOf ->
|
|
||||||
_uiState.value = ReleaseManagerState.InDownload(progress, outOf)
|
|
||||||
}
|
|
||||||
|
|
||||||
outputFile.parentFile?.mkdirs()
|
|
||||||
|
|
||||||
val geodeFile = getGeodeOutputPath()
|
val geodeFile = getGeodeOutputPath()
|
||||||
|
|
||||||
|
val geodeParent = geodeFile.parentFile
|
||||||
|
if (geodeParent != null && !geodeParent.exists()) {
|
||||||
|
geodeParent.mkdirs()
|
||||||
|
}
|
||||||
|
|
||||||
|
DownloadUtils.downloadStream(
|
||||||
|
httpClient,
|
||||||
|
releaseAsset.url,
|
||||||
|
onProgress = { progress, outOf ->
|
||||||
|
_uiState.value = ReleaseManagerState.InDownload(progress, outOf)
|
||||||
|
},
|
||||||
|
onResponse = { body ->
|
||||||
DownloadUtils.extractFileFromZipStream(
|
DownloadUtils.extractFileFromZipStream(
|
||||||
fileStream,
|
body.byteStream(),
|
||||||
outputFile.outputStream(),
|
outputFile.outputStream(),
|
||||||
geodeFile.name
|
geodeFile.name
|
||||||
)
|
)
|
||||||
@@ -201,15 +208,24 @@ class ReleaseManager private constructor(
|
|||||||
geodeFile.delete()
|
geodeFile.delete()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val renameSuccessful = runCatching {
|
||||||
outputFile.renameTo(geodeFile)
|
outputFile.renameTo(geodeFile)
|
||||||
|
}.getOrDefault(false)
|
||||||
|
|
||||||
|
if (!renameSuccessful) {
|
||||||
|
// attempt a manual copy if rename fails for whatever reason
|
||||||
|
DownloadUtils.copyFile(outputFile.inputStream(), geodeFile.outputStream())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
sendError(e)
|
sendError(e)
|
||||||
return
|
return
|
||||||
} finally {
|
} finally {
|
||||||
val tempFileClone = File(tempFilePath)
|
val tempFileClone = File(tempFilePath)
|
||||||
try {
|
runCatching {
|
||||||
if (tempFileClone.exists()) tempFileClone.delete()
|
if (tempFileClone.exists()) tempFileClone.delete()
|
||||||
} catch (_: Exception) { }
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// extraction performed
|
// extraction performed
|
||||||
|
|||||||
@@ -2,7 +2,9 @@ package com.geode.launcher.utils
|
|||||||
|
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.os.FileUtils
|
import android.os.FileUtils
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.runInterruptible
|
import kotlinx.coroutines.runInterruptible
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
import okhttp3.MediaType
|
import okhttp3.MediaType
|
||||||
import okhttp3.OkHttpClient
|
import okhttp3.OkHttpClient
|
||||||
import okhttp3.Request
|
import okhttp3.Request
|
||||||
@@ -12,11 +14,14 @@ import okhttp3.coroutines.executeAsync
|
|||||||
import okio.Buffer
|
import okio.Buffer
|
||||||
import okio.BufferedSink
|
import okio.BufferedSink
|
||||||
import okio.BufferedSource
|
import okio.BufferedSource
|
||||||
|
import okio.FileSystem
|
||||||
import okio.ForwardingSink
|
import okio.ForwardingSink
|
||||||
import okio.ForwardingSource
|
import okio.ForwardingSource
|
||||||
|
import okio.Path.Companion.toOkioPath
|
||||||
import okio.Sink
|
import okio.Sink
|
||||||
import okio.Source
|
import okio.Source
|
||||||
import okio.buffer
|
import okio.buffer
|
||||||
|
import java.io.File
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
import java.io.InputStream
|
import java.io.InputStream
|
||||||
import java.io.OutputStream
|
import java.io.OutputStream
|
||||||
@@ -30,8 +35,9 @@ object DownloadUtils {
|
|||||||
suspend fun downloadStream(
|
suspend fun downloadStream(
|
||||||
httpClient: OkHttpClient,
|
httpClient: OkHttpClient,
|
||||||
url: String,
|
url: String,
|
||||||
onProgress: ProgressCallback? = null
|
onProgress: ProgressCallback? = null,
|
||||||
): InputStream {
|
onResponse: suspend (ResponseBody) -> Unit,
|
||||||
|
) {
|
||||||
val request = Request.Builder()
|
val request = Request.Builder()
|
||||||
.url(url)
|
.url(url)
|
||||||
.build()
|
.build()
|
||||||
@@ -58,11 +64,19 @@ object DownloadUtils {
|
|||||||
val progressClient = progressClientBuilder.build()
|
val progressClient = progressClientBuilder.build()
|
||||||
|
|
||||||
val call = progressClient.newCall(request)
|
val call = progressClient.newCall(request)
|
||||||
val response = call.executeAsync()
|
call.executeAsync().use { response ->
|
||||||
|
when (response.code) {
|
||||||
return response.body.byteStream()
|
200 -> onResponse(response.body)
|
||||||
|
else -> throw IOException("unexpected response ${response.code}")
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extracts a file named by zipPath from inputStream and copies it to outputStream
|
||||||
|
*
|
||||||
|
* inputStream and outputStream are both closed after this function executes
|
||||||
|
*/
|
||||||
suspend fun extractFileFromZipStream(inputStream: InputStream, outputStream: OutputStream, zipPath: String) {
|
suspend fun extractFileFromZipStream(inputStream: InputStream, outputStream: OutputStream, zipPath: String) {
|
||||||
// note to self: ZipInputStreams are a little silly
|
// note to self: ZipInputStreams are a little silly
|
||||||
// (runInterruptible allows it to cancel, otherwise it waits for the stream to finish)
|
// (runInterruptible allows it to cancel, otherwise it waits for the stream to finish)
|
||||||
@@ -90,6 +104,22 @@ object DownloadUtils {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
suspend fun copyFile(from: File, to: File) {
|
||||||
|
val fs = FileSystem.SYSTEM
|
||||||
|
|
||||||
|
val fromPath = from.toOkioPath()
|
||||||
|
val toPath = to.toOkioPath()
|
||||||
|
|
||||||
|
withContext(Dispatchers.IO) {
|
||||||
|
fs.copy(fromPath, toPath)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Copies data from inputStream to outputStream
|
||||||
|
*
|
||||||
|
* Both streams are closed upon the conclusion of this function.
|
||||||
|
*/
|
||||||
fun copyFile(inputStream: InputStream, outputStream: OutputStream) {
|
fun copyFile(inputStream: InputStream, outputStream: OutputStream) {
|
||||||
// gotta love copying
|
// gotta love copying
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
||||||
|
|||||||
Reference in new issue
Block a user