switch to okio official coroutines

This commit is contained in:
Chloe committed 2026-02-01 01:28:07 -07:00
1 parent d0715c1bd8
commit 1f1e0de523
3 files changed
+131 -115

No files matched your search

+3
View File
@@ -100,8 +100,11 @@ dependencies {
implementation ("androidx.activity:activity-ktx:1.12.3") implementation ("androidx.activity:activity-ktx:1.12.3")
implementation ("androidx.appcompat:appcompat:1.7.1") implementation ("androidx.appcompat:appcompat:1.7.1")
implementation ("androidx.documentfile:documentfile:1.1.0") implementation ("androidx.documentfile:documentfile:1.1.0")
implementation ("com.squareup.okio:okio:3.16.4") implementation ("com.squareup.okio:okio:3.16.4")
implementation ("com.squareup.okhttp3:okhttp:5.3.2") implementation ("com.squareup.okhttp3:okhttp:5.3.2")
implementation ("com.squareup.okhttp3:okhttp-coroutines:5.3.2")
implementation ("org.jetbrains.kotlinx:kotlinx-serialization-json:1.10.0") implementation ("org.jetbrains.kotlinx:kotlinx-serialization-json:1.10.0")
implementation ("org.jetbrains.kotlinx:kotlinx-serialization-json-okio:1.10.0") implementation ("org.jetbrains.kotlinx:kotlinx-serialization-json-okio:1.10.0")
implementation ("org.jetbrains.kotlinx:kotlinx-datetime:0.7.1") implementation ("org.jetbrains.kotlinx:kotlinx-datetime:0.7.1")
@@ -1,6 +1,5 @@
package com.geode.launcher.updater package com.geode.launcher.updater
import com.geode.launcher.utils.DownloadUtils.executeCoroutine
import kotlin.time.Clock import kotlin.time.Clock
import kotlinx.datetime.DateTimeUnit import kotlinx.datetime.DateTimeUnit
import kotlin.time.Instant import kotlin.time.Instant
@@ -12,6 +11,7 @@ import kotlinx.serialization.json.okio.decodeFromBufferedSource
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
import okhttp3.OkHttpClient import okhttp3.OkHttpClient
import okhttp3.Request import okhttp3.Request
import okhttp3.coroutines.executeAsync
import java.io.IOException import java.io.IOException
import java.net.URL import java.net.URL
import kotlin.time.ExperimentalTime import kotlin.time.ExperimentalTime
@@ -62,45 +62,46 @@ class ReleaseRepository(private val httpClient: OkHttpClient) {
.build() .build()
val call = httpClient.newCall(request) val call = httpClient.newCall(request)
val response = call.executeCoroutine()
return when (response.code) { return call.executeAsync().use { response ->
200 -> { when (response.code) {
val format = Json { 200 -> {
namingStrategy = JsonNamingStrategy.SnakeCase val format = Json {
ignoreUnknownKeys = true namingStrategy = JsonNamingStrategy.SnakeCase
ignoreUnknownKeys = true
}
val release = format.decodeFromBufferedSource<List<Release>>(
response.body.source()
)
(release.find { it.tagName != "nightly" })
?.let(::DownloadableGitHubLoaderRelease)
} }
403 -> {
// determine if the error code is a ratelimit
// (github docs say it sends 429 too, but haven't seen that)
val release = format.decodeFromBufferedSource<List<Release>>( val limitRemaining = response.headers.get(GITHUB_RATELIMIT_REMAINING)?.toInt()
response.body!!.source() val limitReset = response.headers.get(GITHUB_RATELIMIT_RESET)?.toLong()
)
(release.find { it.tagName != "nightly" }) if (limitRemaining == 0 && limitReset != null) {
?.let(::DownloadableGitHubLoaderRelease) // handle ratelimit with a custom error
} // there's also a retry-after header but again, haven't seen
403 -> { val resetTime = Instant.fromEpochSeconds(limitReset, 0L)
// determine if the error code is a ratelimit val msg = generateRateLimitMessage(resetTime)
// (github docs say it sends 429 too, but haven't seen that)
val limitRemaining = response.headers.get(GITHUB_RATELIMIT_REMAINING)?.toInt() throw IOException(msg)
val limitReset = response.headers.get(GITHUB_RATELIMIT_RESET)?.toLong() }
if (limitRemaining == 0 && limitReset != null) { throw IOException("response 403: ${response.body.string()}")
// handle ratelimit with a custom error }
// there's also a retry-after header but again, haven't seen 404 -> {
val resetTime = Instant.fromEpochSeconds(limitReset, 0L) null
val msg = generateRateLimitMessage(resetTime) }
else -> {
throw IOException(msg) throw IOException("unknown response ${response.code}")
} }
throw IOException("response 403: ${response.body!!.string()}")
}
404 -> {
null
}
else -> {
throw IOException("unknown response ${response.code}")
} }
} }
} }
@@ -124,44 +125,44 @@ class ReleaseRepository(private val httpClient: OkHttpClient) {
.build() .build()
val call = httpClient.newCall(request) val call = httpClient.newCall(request)
val response = call.executeCoroutine() return call.executeAsync().use { response ->
when (response.code) {
200 -> {
val format = Json {
namingStrategy = JsonNamingStrategy.SnakeCase
ignoreUnknownKeys = true
}
return when (response.code) { val release = format.decodeFromBufferedSource<Release>(
200 -> { response.body.source()
val format = Json { )
namingStrategy = JsonNamingStrategy.SnakeCase
ignoreUnknownKeys = true release
} }
403 -> {
// determine if the error code is a ratelimit
// (github docs say it sends 429 too, but haven't seen that)
val release = format.decodeFromBufferedSource<Release>( val limitRemaining = response.headers[GITHUB_RATELIMIT_REMAINING]?.toInt()
response.body!!.source() val limitReset = response.headers[GITHUB_RATELIMIT_RESET]?.toLong()
)
release if (limitRemaining == 0 && limitReset != null) {
} // handle ratelimit with a custom error
403 -> { // there's also a retry-after header but again, haven't seen
// determine if the error code is a ratelimit val resetTime = Instant.fromEpochSeconds(limitReset, 0L)
// (github docs say it sends 429 too, but haven't seen that) val msg = generateRateLimitMessage(resetTime)
val limitRemaining = response.headers[GITHUB_RATELIMIT_REMAINING]?.toInt() throw IOException(msg)
val limitReset = response.headers[GITHUB_RATELIMIT_RESET]?.toLong() }
if (limitRemaining == 0 && limitReset != null) { throw IOException("response 403: ${response.body.string()}")
// handle ratelimit with a custom error }
// there's also a retry-after header but again, haven't seen 404 -> {
val resetTime = Instant.fromEpochSeconds(limitReset, 0L) null
val msg = generateRateLimitMessage(resetTime) }
else -> {
throw IOException(msg) throw IOException("unknown response ${response.code}")
} }
throw IOException("response 403: ${response.body!!.string()}")
}
404 -> {
null
}
else -> {
throw IOException("unknown response ${response.code}")
} }
} }
} }
@@ -181,28 +182,28 @@ class ReleaseRepository(private val httpClient: OkHttpClient) {
.build() .build()
val call = httpClient.newCall(request) val call = httpClient.newCall(request)
val response = call.executeCoroutine() return call.executeAsync().use { response ->
when (response.code) {
200 -> {
val format = Json {
namingStrategy = JsonNamingStrategy.SnakeCase
ignoreUnknownKeys = true
}
return when (response.code) { val body = format.decodeFromBufferedSource<LoaderPayload<LoaderVersion>>(
200 -> { response.body.source()
val format = Json { )
namingStrategy = JsonNamingStrategy.SnakeCase
ignoreUnknownKeys = true val release = body.payload ?: throw IOException("index error: ${body.error}")
DownloadableLoaderRelease(release)
}
404 -> {
null
}
else -> {
throw IOException("unknown response ${response.code}")
} }
val body = format.decodeFromBufferedSource<LoaderPayload<LoaderVersion>>(
response.body!!.source()
)
val release = body.payload ?: throw IOException("index error: ${body.error}")
DownloadableLoaderRelease(release)
}
404 -> {
null
}
else -> {
throw IOException("unknown response ${response.code}")
} }
} }
} }
@@ -3,17 +3,18 @@ package com.geode.launcher.utils
import android.os.Build import android.os.Build
import android.os.FileUtils import android.os.FileUtils
import kotlinx.coroutines.runInterruptible import kotlinx.coroutines.runInterruptible
import kotlinx.coroutines.suspendCancellableCoroutine
import okhttp3.Call
import okhttp3.Callback
import okhttp3.MediaType import okhttp3.MediaType
import okhttp3.OkHttpClient import okhttp3.OkHttpClient
import okhttp3.Request import okhttp3.Request
import okhttp3.Response import okhttp3.RequestBody
import okhttp3.ResponseBody import okhttp3.ResponseBody
import okhttp3.coroutines.executeAsync
import okio.Buffer import okio.Buffer
import okio.BufferedSink
import okio.BufferedSource import okio.BufferedSource
import okio.ForwardingSink
import okio.ForwardingSource import okio.ForwardingSource
import okio.Sink
import okio.Source import okio.Source
import okio.buffer import okio.buffer
import java.io.IOException import java.io.IOException
@@ -21,8 +22,6 @@ import java.io.InputStream
import java.io.OutputStream import java.io.OutputStream
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
import java.util.zip.ZipInputStream import java.util.zip.ZipInputStream
import kotlin.coroutines.resume
import kotlin.coroutines.resumeWithException
typealias ProgressCallback = (progress: Long, outOf: Long) -> Unit typealias ProgressCallback = (progress: Long, outOf: Long) -> Unit
@@ -50,7 +49,7 @@ object DownloadUtils {
val originalResponse = chain.proceed(chain.request()) val originalResponse = chain.proceed(chain.request())
originalResponse.newBuilder() originalResponse.newBuilder()
.body(ProgressResponseBody( .body(ProgressResponseBody(
originalResponse.body!!, onProgress originalResponse.body, onProgress
)) ))
.build() .build()
} }
@@ -59,30 +58,8 @@ object DownloadUtils {
val progressClient = progressClientBuilder.build() val progressClient = progressClientBuilder.build()
val call = progressClient.newCall(request) val call = progressClient.newCall(request)
val response = call.executeCoroutine() return call.executeAsync().use { response ->
response.body.byteStream()
return response.body!!.byteStream()
}
suspend fun Call.executeCoroutine(): Response {
return suspendCancellableCoroutine { continuation ->
this.enqueue(object : Callback {
override fun onFailure(call: Call, e: IOException) {
if (continuation.isCancelled) {
return
}
continuation.resumeWithException(e)
}
override fun onResponse(call: Call, response: Response) {
continuation.resume(response)
}
})
continuation.invokeOnCancellation {
this.cancel()
}
} }
} }
@@ -167,3 +144,38 @@ private class ProgressResponseBody (
} }
} }
} }
// now based on <https://github.com/square/okhttp/blob/master/samples/guide/src/main/java/okhttp3/recipes/UploadProgress.java#L84>
class ProgressRequestBody(
private val requestBody: RequestBody,
private val progressCallback: ProgressCallback
) : RequestBody() {
override fun contentType(): MediaType? {
return requestBody.contentType()
}
override fun contentLength(): Long {
return requestBody.contentLength()
}
override fun writeTo(sink: BufferedSink) {
val bufferedSink = this.sink(sink).buffer()
requestBody.writeTo(bufferedSink)
bufferedSink.flush()
}
private fun sink(sink: Sink): Sink {
return object : ForwardingSink(sink) {
var totalBytesWritten = 0L
override fun write(source: Buffer, byteCount: Long) {
super.write(source, byteCount)
totalBytesWritten += byteCount
progressCallback(
totalBytesWritten,
contentLength()
)
}
}
}
}