From ca21b5e713a4c5149991d8b4d65e9c46c7445318 Mon Sep 17 00:00:00 2001 From: FooIbar <118464521+FooIbar@users.noreply.github.com> Date: Thu, 6 Jul 2023 01:53:41 +0800 Subject: [PATCH] Introduce Request.executeAndParseAs() --- .../hippo/ehviewer/client/EhRequestBuilder.kt | 8 +++++ .../hippo/ehviewer/ui/settings/AboutScreen.kt | 35 ++++++++----------- .../tachiyomi/data/release/GithubRelease.kt | 27 ++++++++++++++ .../tachiyomi/data/release/GithubWorkflow.kt | 33 +++++++++++++++++ 4 files changed, 83 insertions(+), 20 deletions(-) create mode 100644 app/src/main/java/tachiyomi/data/release/GithubRelease.kt create mode 100644 app/src/main/java/tachiyomi/data/release/GithubWorkflow.kt diff --git a/app/src/main/java/com/hippo/ehviewer/client/EhRequestBuilder.kt b/app/src/main/java/com/hippo/ehviewer/client/EhRequestBuilder.kt index 79eea838f..3de2abc95 100644 --- a/app/src/main/java/com/hippo/ehviewer/client/EhRequestBuilder.kt +++ b/app/src/main/java/com/hippo/ehviewer/client/EhRequestBuilder.kt @@ -21,10 +21,12 @@ import com.hippo.ehviewer.EhApplication.Companion.okHttpClient import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.launch import kotlinx.coroutines.suspendCancellableCoroutine +import kotlinx.serialization.json.Json import kotlinx.serialization.json.JsonArrayBuilder import kotlinx.serialization.json.JsonObjectBuilder import kotlinx.serialization.json.buildJsonArray import kotlinx.serialization.json.buildJsonObject +import kotlinx.serialization.json.okio.decodeFromBufferedSource import okhttp3.Call import okhttp3.FormBody import okhttp3.MediaType @@ -103,6 +105,12 @@ suspend inline fun Call.usingCancellable(crossinline block: suspend Response suspend inline fun Request.execute(block: Response.() -> R) = okHttpClient.newCall(this).executeAsync().use(block) suspend inline fun Request.executeNonCache(crossinline block: suspend Response.() -> R) = nonCacheOkHttpClient.newCall(this).usingCancellable(block) +suspend inline fun Request.executeAndParseAs() = execute { parseAs() } + +inline fun Response.parseAs(): T = json.decodeFromBufferedSource(body.source()) + +val json = Json { ignoreUnknownKeys = true } + const val CHROME_USER_AGENT = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36" const val CHROME_ACCEPT = "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9" const val CHROME_ACCEPT_LANGUAGE = "en-US,en;q=0.5" diff --git a/app/src/main/java/com/hippo/ehviewer/ui/settings/AboutScreen.kt b/app/src/main/java/com/hippo/ehviewer/ui/settings/AboutScreen.kt index 68a783010..fe78f1cf1 100644 --- a/app/src/main/java/com/hippo/ehviewer/ui/settings/AboutScreen.kt +++ b/app/src/main/java/com/hippo/ehviewer/ui/settings/AboutScreen.kt @@ -30,6 +30,7 @@ import com.hippo.ehviewer.BuildConfig import com.hippo.ehviewer.R import com.hippo.ehviewer.Settings import com.hippo.ehviewer.client.execute +import com.hippo.ehviewer.client.executeAndParseAs import com.hippo.ehviewer.ui.LICENSE_SCREEN import com.hippo.ehviewer.ui.LocalNavController import com.hippo.ehviewer.ui.tools.DialogState @@ -37,7 +38,6 @@ import com.hippo.ehviewer.ui.tools.rememberDialogState import com.hippo.ehviewer.ui.tools.toAnnotatedString import com.hippo.ehviewer.util.ExceptionUtils import com.hippo.ehviewer.util.installPackage -import com.hippo.ehviewer.util.iter import eu.kanade.tachiyomi.util.lang.withUIContext import eu.kanade.tachiyomi.util.system.logcat import io.ktor.util.encodeBase64 @@ -47,6 +47,9 @@ import moe.tarsin.coroutines.runSuspendCatching import okhttp3.Request import okio.sink import org.json.JSONObject +import tachiyomi.data.release.GithubArtifacts +import tachiyomi.data.release.GithubRelease +import tachiyomi.data.release.GithubWorkflowRuns import java.io.File import java.util.zip.ZipInputStream @@ -146,21 +149,15 @@ fun AboutScreen() { val branch = ghRequest(API_URL).execute { JSONObject(body.string()).getString("default_branch") } - val branchUrl = "$API_URL/branches/$branch" - val commitSha = ghRequest(branchUrl).execute { - JSONObject(body.string()).getJSONObject("commit").getString("sha") - } - val shortSha = commitSha.take(7) + val workflowRunsUrl = "$API_URL/actions/workflows/ci.yml/runs?branch=$branch&per_page=1" + val workflowRun = ghRequest(workflowRunsUrl).executeAndParseAs().workflowRuns[0] + val shortSha = workflowRun.headSha.take(7) if (shortSha != curSha) { - val archiveUrl = ghRequest("$API_URL/actions/artifacts").execute { - JSONObject(body.string()).getJSONArray("artifacts").iter().find { - val wf = it.getJSONObject("workflow_run") - it.getString("name").startsWith("arm64-v8a") && wf.getString("head_sha") == commitSha - } - }?.getString("archive_download_url") - if (archiveUrl != null) { + val artifacts = ghRequest(workflowRun.artifactsUrl).executeAndParseAs() + if (artifacts.artifacts.isNotEmpty()) { + val archiveUrl = artifacts.getDownloadLink() // TODO: Show changelog - dialogState.showNewVersion(shortSha, "") { file -> + dialogState.showNewVersion(shortSha, workflowRun.title) { file -> ghRequest(archiveUrl).execute { ZipInputStream(body.byteStream()).use { zip -> zip.nextEntry @@ -179,12 +176,10 @@ fun AboutScreen() { } else { val curVersion = BuildConfig.VERSION_NAME val releaseUrl = "$API_URL/releases/latest" - val release = ghRequest(releaseUrl).execute { - JSONObject(body.string()) - } - val latestVersion = release.getString("name") - val description = release.getString("body") - val downloadUrl = (release.getJSONArray("assets")[0] as JSONObject).getString("browser_download_url") + val release = ghRequest(releaseUrl).executeAndParseAs() + val latestVersion = release.version + val description = release.info + val downloadUrl = release.getDownloadLink() if (latestVersion != curVersion) { dialogState.showNewVersion(latestVersion, description) { file -> ghRequest(downloadUrl).execute { diff --git a/app/src/main/java/tachiyomi/data/release/GithubRelease.kt b/app/src/main/java/tachiyomi/data/release/GithubRelease.kt new file mode 100644 index 000000000..eaf6b729b --- /dev/null +++ b/app/src/main/java/tachiyomi/data/release/GithubRelease.kt @@ -0,0 +1,27 @@ +package tachiyomi.data.release + +import android.os.Build +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +/** + * Contains information about the latest release from GitHub. + */ +@Serializable +data class GithubRelease( + @SerialName("tag_name") val version: String, + @SerialName("body") val info: String, + @SerialName("html_url") val releaseLink: String, + @SerialName("assets") val assets: List, +) { + fun getDownloadLink(): String { + val apkVariant = Build.SUPPORTED_ABIS[0] + return (assets.find { it.downloadLink.contains(apkVariant) } ?: assets.find { it.downloadLink.contains("universal") } ?: assets[0]).downloadLink + } +} + +/** + * Assets class containing download url. + */ +@Serializable +data class GitHubAssets(@SerialName("browser_download_url") val downloadLink: String) diff --git a/app/src/main/java/tachiyomi/data/release/GithubWorkflow.kt b/app/src/main/java/tachiyomi/data/release/GithubWorkflow.kt new file mode 100644 index 000000000..d3af7b802 --- /dev/null +++ b/app/src/main/java/tachiyomi/data/release/GithubWorkflow.kt @@ -0,0 +1,33 @@ +package tachiyomi.data.release + +import android.os.Build +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +@Serializable +data class GithubWorkflowRuns( + @SerialName("workflow_runs") val workflowRuns: List, +) + +@Serializable +data class GithubWorkflowRun( + @SerialName("head_sha") val headSha: String, + @SerialName("display_title") val title: String, + @SerialName("artifacts_url") val artifactsUrl: String, +) + +@Serializable +data class GithubArtifacts( + @SerialName("artifacts") val artifacts: List, +) { + fun getDownloadLink(): String { + val apkVariant = Build.SUPPORTED_ABIS[0] + return (artifacts.find { it.name.contains(apkVariant) } ?: artifacts[0]).downloadLink + } +} + +@Serializable +data class GithubArtifact( + val name: String, + @SerialName("archive_download_url") val downloadLink: String, +)