From cde9a342389a88672991a55b044af11437ebdb2c Mon Sep 17 00:00:00 2001 From: AntsyLich <59261191+AntsyLich@users.noreply.github.com> Date: Tue, 18 Mar 2025 19:36:16 +0600 Subject: [PATCH] Tweak the app updater logic and add FOSS build support (mihonapp/mihon#1843) (cherry picked from commit 28093935d867e5d1a95944e67c6d1eb46cab4a37) --- .../more/settings/screen/about/AboutScreen.kt | 6 +- .../data/updater/AppUpdateChecker.kt | 7 +- .../data/updater/AppUpdateNotifier.kt | 2 +- .../kanade/tachiyomi/ui/main/MainActivity.kt | 2 +- .../tachiyomi/util/system/BuildConfig.kt | 3 + .../tachiyomi/data/release/GithubRelease.kt | 80 ++++------------ .../data/release/ReleaseServiceImpl.kt | 93 +++++++++++++++++-- .../interactor/GetApplicationRelease.kt | 5 +- .../tachiyomi/domain/release/model/Release.kt | 28 +----- .../domain/release/service/ReleaseService.kt | 5 +- .../interactor/GetApplicationReleaseTest.kt | 12 ++- 11 files changed, 132 insertions(+), 111 deletions(-) diff --git a/app/src/main/java/eu/kanade/presentation/more/settings/screen/about/AboutScreen.kt b/app/src/main/java/eu/kanade/presentation/more/settings/screen/about/AboutScreen.kt index cf32759b9..bbe8e8b61 100644 --- a/app/src/main/java/eu/kanade/presentation/more/settings/screen/about/AboutScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/more/settings/screen/about/AboutScreen.kt @@ -134,7 +134,7 @@ object AboutScreen : Screen() { versionName = result.release.version, changelogInfo = result.release.info, releaseLink = result.release.releaseLink, - downloadLink = result.release.getDownloadLink(), + downloadLink = result.release.downloadLink, ) navigator.push(updateScreen) }, @@ -211,7 +211,7 @@ object AboutScreen : Screen() { versionName = result.release.version, changelogInfo = result.release.info, releaseLink = result.release.releaseLink, - downloadLink = result.release.getDownloadLink(), + downloadLink = result.release.downloadLink, ) navigator.push(updateScreen) }, @@ -352,7 +352,7 @@ object AboutScreen : Screen() { val updateChecker = AppUpdateChecker() withUIContext { try { - when (val result = withIOContext { updateChecker.getReleaseNotes(context) }) { + when (val result = withIOContext { updateChecker.getReleaseNotes() }) { is GetApplicationRelease.Result.NewUpdate -> { onAvailableUpdate(result) } diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/updater/AppUpdateChecker.kt b/app/src/main/java/eu/kanade/tachiyomi/data/updater/AppUpdateChecker.kt index e218989ae..c637fef09 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/updater/AppUpdateChecker.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/updater/AppUpdateChecker.kt @@ -3,6 +3,7 @@ package eu.kanade.tachiyomi.data.updater import android.content.Context import android.os.Build import eu.kanade.tachiyomi.BuildConfig +import eu.kanade.tachiyomi.util.system.isFossBuildType import eu.kanade.tachiyomi.util.system.isPreviewBuildType import tachiyomi.core.common.util.lang.withIOContext import tachiyomi.domain.UnsortedPreferences @@ -34,6 +35,7 @@ class AppUpdateChecker( return withIOContext { val result = getApplicationRelease.await( GetApplicationRelease.Arguments( + isFoss = isFossBuildType, isPreview = isPreviewBuildType || peekIntoPreview, commitCount = BuildConfig.COMMIT_COUNT.toInt(), versionName = BuildConfig.VERSION_NAME, @@ -61,7 +63,7 @@ class AppUpdateChecker( if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { AppUpdateDownloadJob.start( context = context, - url = result.release.getDownloadLink(), + url = result.release.downloadLink, title = result.release.version, scheduled = true, ) @@ -75,10 +77,11 @@ class AppUpdateChecker( } // KMK --> - suspend fun getReleaseNotes(context: Context): GetApplicationRelease.Result { + suspend fun getReleaseNotes(): GetApplicationRelease.Result { return withIOContext { getApplicationRelease.awaitReleaseNotes( GetApplicationRelease.Arguments( + isFoss = isFossBuildType, isPreview = isPreviewBuildType || peekIntoPreview, commitCount = BuildConfig.COMMIT_COUNT.toInt(), versionName = BuildConfig.VERSION_NAME, diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/updater/AppUpdateNotifier.kt b/app/src/main/java/eu/kanade/tachiyomi/data/updater/AppUpdateNotifier.kt index 4137ced66..85082e878 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/updater/AppUpdateNotifier.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/updater/AppUpdateNotifier.kt @@ -48,7 +48,7 @@ internal class AppUpdateNotifier(private val context: Context) { fun promptUpdate(release: Release) { val updateIntent = NotificationReceiver.downloadAppUpdatePendingBroadcast( context, - release.getDownloadLink(), + release.downloadLink, release.version, ) diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/main/MainActivity.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/main/MainActivity.kt index 7a7800b62..306002540 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/main/MainActivity.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/main/MainActivity.kt @@ -448,7 +448,7 @@ class MainActivity : BaseActivity() { versionName = result.release.version, changelogInfo = result.release.info, releaseLink = result.release.releaseLink, - downloadLink = result.release.getDownloadLink(), + downloadLink = result.release.downloadLink, ) navigator.push(updateScreen) } diff --git a/app/src/main/java/eu/kanade/tachiyomi/util/system/BuildConfig.kt b/app/src/main/java/eu/kanade/tachiyomi/util/system/BuildConfig.kt index 0470bc464..dfe8a4a9e 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/util/system/BuildConfig.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/util/system/BuildConfig.kt @@ -18,3 +18,6 @@ val isPreviewBuildType: Boolean val isReleaseBuildType: Boolean inline get() = BuildConfig.BUILD_TYPE == "release" + +val isFossBuildType: Boolean + inline get() = BuildConfig.BUILD_TYPE == "foss" diff --git a/data/src/main/java/tachiyomi/data/release/GithubRelease.kt b/data/src/main/java/tachiyomi/data/release/GithubRelease.kt index fd94ea2d1..c5ee9f708 100644 --- a/data/src/main/java/tachiyomi/data/release/GithubRelease.kt +++ b/data/src/main/java/tachiyomi/data/release/GithubRelease.kt @@ -2,78 +2,34 @@ package tachiyomi.data.release import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable -import tachiyomi.domain.release.model.Release /** * 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, + @SerialName("tag_name") + val version: String, + @SerialName("body") + val info: String, + @SerialName("html_url") + val releaseLink: String, + @SerialName("assets") + val assets: List, // KMK --> - @SerialName("prerelease") val preRelease: Boolean = false, - @SerialName("draft") val draft: Boolean = false, + @SerialName("prerelease") + val preRelease: Boolean = false, + @SerialName("draft") + val draft: Boolean = false, // KMK <-- ) /** - * Assets class containing download url. + * Asset class containing asset name and download url. */ @Serializable -data class GitHubAssets(@SerialName("browser_download_url") val downloadLink: String) - -/** - * Regular expression that matches a mention to a valid GitHub username, like it's - * done in GitHub Flavored Markdown. It follows these constraints: - * - * - Alphanumeric with single hyphens (no consecutive hyphens) - * - Cannot begin or end with a hyphen - * - Max length of 39 characters - * - * Convert '(@cuong-tran)' to '([@cuong-tran](https://github.com/cuong-tran))' - * - * Reference: https://stackoverflow.com/a/30281147 - */ -val gitHubUsernameMentionRegex = - """\B@([a-z0-9](?:-(?=[a-z0-9])|[a-z0-9]){0,38}(?<=[a-z0-9]))""".toRegex( - RegexOption.IGNORE_CASE, - ) - -// KMK --> -/** - * Convert from: https://github.com/komikku-app/komikku/compare/23d862d17...48fb4a2e6 - * to: [komikku-app/komikku@23d862d17...48fb4a2e6](https://github.com/komikku-app/komikku/compare/23d862d17...48fb4a2e6) - */ -val gitHubCommitsCompareRegex = - """(\[[^]]+]\()?https://github.com/(?[^/]+)/(?[^/]+)/compare/(?[0-9a-f.rv]+)\.\.\.(?[0-9a-f.rv]+)\)?""" - .toRegex(RegexOption.IGNORE_CASE) -// KMK <-- - -val releaseMapper: (GithubRelease) -> Release = { - Release( - it.version, - it.info - .replace(gitHubUsernameMentionRegex) { mention -> - "[${mention.value}](https://github.com/${mention.value.substring(1)})" - } - // KMK --> - .replace(gitHubCommitsCompareRegex) { matchResult -> - val owner = matchResult.groups["owner"]!!.value - val repo = matchResult.groups["repo"]!!.value - val from = matchResult.groups["from"]!!.value - val to = matchResult.groups["to"]!!.value - - "[$owner/$repo@$from...$to](https://github.com/$owner/$repo/compare/$from...$to)" - }, - // KMK <-- - it.releaseLink, - it.assets.map(GitHubAssets::downloadLink), - // KMK --> - preRelease = it.preRelease, - draft = it.draft, - // KMK <-- - ) -} +data class GitHubAsset( + val name: String, + @SerialName("browser_download_url") + val downloadLink: String, +) diff --git a/data/src/main/java/tachiyomi/data/release/ReleaseServiceImpl.kt b/data/src/main/java/tachiyomi/data/release/ReleaseServiceImpl.kt index 376f61386..c493b33a7 100644 --- a/data/src/main/java/tachiyomi/data/release/ReleaseServiceImpl.kt +++ b/data/src/main/java/tachiyomi/data/release/ReleaseServiceImpl.kt @@ -1,10 +1,12 @@ package tachiyomi.data.release +import android.os.Build import eu.kanade.tachiyomi.network.GET import eu.kanade.tachiyomi.network.NetworkHelper import eu.kanade.tachiyomi.network.awaitSuccess import eu.kanade.tachiyomi.network.parseAs import kotlinx.serialization.json.Json +import tachiyomi.domain.release.interactor.GetApplicationRelease import tachiyomi.domain.release.model.Release import tachiyomi.domain.release.service.ReleaseService @@ -13,26 +15,101 @@ class ReleaseServiceImpl( private val json: Json, ) : ReleaseService { - override suspend fun latest(repository: String): Release { - return with(json) { + override suspend fun latest(arguments: GetApplicationRelease.Arguments): Release? { + val release = with(json) { networkService.client - .newCall(GET("https://api.github.com/repos/$repository/releases/latest")) + .newCall(GET("https://api.github.com/repos/${arguments.repository}/releases/latest")) .awaitSuccess() .parseAs() - .let(releaseMapper) } + + val downloadLink = getDownloadLink(release = release, isFoss = arguments.isFoss) ?: return null + + return Release( + version = release.version, + info = release.info.replace(gitHubUsernameMentionRegex) { mention -> + "[${mention.value}](https://github.com/${mention.value.substring(1)})" + }, + releaseLink = release.releaseLink, + downloadLink = downloadLink, + ) } // KMK --> - override suspend fun releaseNotes(repository: String): List { - val releases = "https://api.github.com/repos/$repository/releases" + override suspend fun releaseNotes(arguments: GetApplicationRelease.Arguments): List { return with(json) { networkService.client - .newCall(GET(releases)) + .newCall(GET("https://api.github.com/repos/${arguments.repository}/releases/latest")) .awaitSuccess() .parseAs>() - .map(releaseMapper) + .mapNotNull { release -> + val downloadLink = getDownloadLink(release = release, isFoss = arguments.isFoss) ?: return@mapNotNull null + + Release( + version = release.version, + info = release.info.replace(gitHubUsernameMentionRegex) { mention -> + "[${mention.value}](https://github.com/${mention.value.substring(1)})" + } + // KMK --> + .replace(gitHubCommitsCompareRegex) { matchResult -> + val owner = matchResult.groups["owner"]!!.value + val repo = matchResult.groups["repo"]!!.value + val from = matchResult.groups["from"]!!.value + val to = matchResult.groups["to"]!!.value + + "[$owner/$repo@$from...$to](https://github.com/$owner/$repo/compare/$from...$to)" + }, + // KMK <-- + releaseLink = release.releaseLink, + downloadLink = downloadLink, + // KMK --> + preRelease = release.preRelease, + draft = release.draft, + // KMK <-- + ) + } } } // KMK <-- + + private fun getDownloadLink(release: GithubRelease, isFoss: Boolean): String? { + val map = release.assets.associate { asset -> + BUILD_TYPES.find { "-$it" in asset.name } to asset.downloadLink + } + + return if (!isFoss) { + map[Build.SUPPORTED_ABIS[0]] ?: map[null] + } else { + map[FOSS] + } + } + + companion object { + private const val FOSS = "foss" + private val BUILD_TYPES = listOf(FOSS, "arm64-v8a", "armeabi-v7a", "x86_64", "x86") + + /** + * Regular expression that matches a mention to a valid GitHub username, like it's + * done in GitHub Flavored Markdown. It follows these constraints: + * + * - Alphanumeric with single hyphens (no consecutive hyphens) + * - Cannot begin or end with a hyphen + * - Max length of 39 characters + * + * Convert '(@cuong-tran)' to '([@cuong-tran](https://github.com/cuong-tran))' + * + * Reference: https://stackoverflow.com/a/30281147 + */ + private val gitHubUsernameMentionRegex = """\B@([a-z0-9](?:-(?=[a-z0-9])|[a-z0-9]){0,38}(?<=[a-z0-9]))""" + .toRegex(RegexOption.IGNORE_CASE) + + // KMK --> + /** + * Convert from: https://github.com/komikku-app/komikku/compare/23d862d17...48fb4a2e6 + * to: [komikku-app/komikku@23d862d17...48fb4a2e6](https://github.com/komikku-app/komikku/compare/23d862d17...48fb4a2e6) + */ + private val gitHubCommitsCompareRegex = """(\[[^]]+]\()?https://github\.com/(?[^/]+)/(?[^/]+)/compare/(?[0-9a-f.rv]+)\.\.\.(?[0-9a-f.rv]+)\)?""" + .toRegex(RegexOption.IGNORE_CASE) + // KMK <-- + } } diff --git a/domain/src/main/java/tachiyomi/domain/release/interactor/GetApplicationRelease.kt b/domain/src/main/java/tachiyomi/domain/release/interactor/GetApplicationRelease.kt index c4d5e79da..4f32c46f7 100644 --- a/domain/src/main/java/tachiyomi/domain/release/interactor/GetApplicationRelease.kt +++ b/domain/src/main/java/tachiyomi/domain/release/interactor/GetApplicationRelease.kt @@ -26,7 +26,7 @@ class GetApplicationRelease( } // KMK --> - val releases = service.releaseNotes(arguments.repository) + val releases = service.releaseNotes(arguments) .filter { !it.preRelease && isNewVersion( @@ -57,7 +57,7 @@ class GetApplicationRelease( // KMK --> suspend fun awaitReleaseNotes(arguments: Arguments): Result { - val releases = service.releaseNotes(arguments.repository) + val releases = service.releaseNotes(arguments) .filter { !it.preRelease } val checksumRegex = """---(\R|.)*Checksums(\R|.)*""".toRegex() @@ -114,6 +114,7 @@ class GetApplicationRelease( } data class Arguments( + val isFoss: Boolean, /** If current version is Preview (beta) build */ val isPreview: Boolean, /** Commit count of current version */ diff --git a/domain/src/main/java/tachiyomi/domain/release/model/Release.kt b/domain/src/main/java/tachiyomi/domain/release/model/Release.kt index 54d3b62db..fed422037 100644 --- a/domain/src/main/java/tachiyomi/domain/release/model/Release.kt +++ b/domain/src/main/java/tachiyomi/domain/release/model/Release.kt @@ -1,7 +1,5 @@ package tachiyomi.domain.release.model -import android.os.Build - /** * Contains information about the latest release. */ @@ -9,31 +7,9 @@ data class Release( val version: String, val info: String, val releaseLink: String, - private val assets: List, + val downloadLink: String, // KMK --> val preRelease: Boolean = false, val draft: Boolean = false, // KMK <-- -) { - - /** - * Get download link of latest release from the assets. - * @return download link of latest release. - */ - fun getDownloadLink(): String { - val apkVariant = when (Build.SUPPORTED_ABIS[0]) { - "arm64-v8a" -> "-arm64-v8a" - "armeabi-v7a" -> "-armeabi-v7a" - "x86" -> "-x86" - "x86_64" -> "-x86_64" - else -> "" - } - - return assets.find { it.contains("Komikku$apkVariant-") } ?: assets[0] - } - - /** - * Assets class containing download url. - */ - data class Assets(val downloadLink: String) -} +) diff --git a/domain/src/main/java/tachiyomi/domain/release/service/ReleaseService.kt b/domain/src/main/java/tachiyomi/domain/release/service/ReleaseService.kt index 9856f8957..f775be49d 100644 --- a/domain/src/main/java/tachiyomi/domain/release/service/ReleaseService.kt +++ b/domain/src/main/java/tachiyomi/domain/release/service/ReleaseService.kt @@ -1,12 +1,13 @@ package tachiyomi.domain.release.service +import tachiyomi.domain.release.interactor.GetApplicationRelease import tachiyomi.domain.release.model.Release interface ReleaseService { - suspend fun latest(repository: String): Release + suspend fun latest(arguments: GetApplicationRelease.Arguments): Release? // KMK --> - suspend fun releaseNotes(repository: String): List + suspend fun releaseNotes(arguments: GetApplicationRelease.Arguments): List // KMK <-- } diff --git a/domain/src/test/java/tachiyomi/domain/release/interactor/GetApplicationReleaseTest.kt b/domain/src/test/java/tachiyomi/domain/release/interactor/GetApplicationReleaseTest.kt index c4b448afa..bddb163f9 100644 --- a/domain/src/test/java/tachiyomi/domain/release/interactor/GetApplicationReleaseTest.kt +++ b/domain/src/test/java/tachiyomi/domain/release/interactor/GetApplicationReleaseTest.kt @@ -40,7 +40,7 @@ class GetApplicationReleaseTest { "r2000", "info", "http://example.com/release_link", - listOf("http://example.com/assets"), + "http://example.com/release_link.apk", ), ) @@ -48,6 +48,7 @@ class GetApplicationReleaseTest { val result = getApplicationRelease.await( GetApplicationRelease.Arguments( + isFoss = false, isPreview = true, commitCount = 1000, versionName = "", @@ -70,7 +71,7 @@ class GetApplicationReleaseTest { "v2.0.0", "info", "http://example.com/release_link", - listOf("http://example.com/assets"), + "http://example.com/release_link.apk", ), ) @@ -78,6 +79,7 @@ class GetApplicationReleaseTest { val result = getApplicationRelease.await( GetApplicationRelease.Arguments( + isFoss = false, isPreview = false, commitCount = 0, versionName = "v1.0.0", @@ -99,7 +101,7 @@ class GetApplicationReleaseTest { "v1.0.0", "info", "http://example.com/release_link", - listOf("http://example.com/assets"), + "http://example.com/release_link.apk", ), ) @@ -107,6 +109,7 @@ class GetApplicationReleaseTest { val result = getApplicationRelease.await( GetApplicationRelease.Arguments( + isFoss = false, isPreview = false, commitCount = 0, versionName = "v2.0.0", @@ -127,7 +130,7 @@ class GetApplicationReleaseTest { "v2.0.0", "info", "http://example.com/release_link", - listOf("http://example.com/assets"), + "http://example.com/release_link.apk", ), ) @@ -135,6 +138,7 @@ class GetApplicationReleaseTest { val result = getApplicationRelease.await( GetApplicationRelease.Arguments( + isFoss = false, isPreview = false, commitCount = 0, versionName = "v1.0.0",