From 28093935d867e5d1a95944e67c6d1eb46cab4a37 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 (#1843) --- .../more/settings/screen/about/AboutScreen.kt | 2 +- .../data/updater/AppUpdateChecker.kt | 2 + .../data/updater/AppUpdateNotifier.kt | 2 +- .../kanade/tachiyomi/ui/main/MainActivity.kt | 2 +- .../tachiyomi/util/system/BuildConfig.kt | 3 ++ .../tachiyomi/data/release/GithubRelease.kt | 47 ++++++----------- .../data/release/ReleaseServiceImpl.kt | 50 +++++++++++++++++-- .../interactor/GetApplicationRelease.kt | 3 +- .../tachiyomi/domain/release/model/Release.kt | 28 +---------- .../domain/release/service/ReleaseService.kt | 3 +- .../interactor/GetApplicationReleaseTest.kt | 18 ++++--- 11 files changed, 85 insertions(+), 75 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 abf3cdd73..22edc809e 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 @@ -123,7 +123,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) }, 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 d9ce3b896..c6781277e 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 @@ -2,6 +2,7 @@ package eu.kanade.tachiyomi.data.updater import android.content.Context 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.release.interactor.GetApplicationRelease @@ -20,6 +21,7 @@ class AppUpdateChecker { return withIOContext { val result = getApplicationRelease.await( GetApplicationRelease.Arguments( + isFossBuildType, isPreviewBuildType, BuildConfig.COMMIT_COUNT.toInt(), 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 bd9bdbe30..466ba693e 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 @@ -38,7 +38,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 74e455a50..d46e400b0 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 @@ -312,7 +312,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 f7fa16923..9a08f7fc6 100644 --- a/data/src/main/java/tachiyomi/data/release/GithubRelease.kt +++ b/data/src/main/java/tachiyomi/data/release/GithubRelease.kt @@ -2,47 +2,28 @@ 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, ) /** - * 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 - * - * 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, - ) - -val releaseMapper: (GithubRelease) -> Release = { - Release( - it.version, - it.info.replace(gitHubUsernameMentionRegex) { mention -> - "[${mention.value}](https://github.com/${mention.value.substring(1)})" - }, - it.releaseLink, - it.assets.map(GitHubAssets::downloadLink), - ) -} +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 ea2363d53..d61c36934 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,13 +15,53 @@ 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, + ) + } + + 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 + * + * 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) + } } 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 bd6cdc794..b95173688 100644 --- a/domain/src/main/java/tachiyomi/domain/release/interactor/GetApplicationRelease.kt +++ b/domain/src/main/java/tachiyomi/domain/release/interactor/GetApplicationRelease.kt @@ -25,7 +25,7 @@ class GetApplicationRelease( return Result.NoNewUpdate } - val release = service.latest(arguments.repository) + val release = service.latest(arguments) ?: return Result.NoNewUpdate lastChecked.set(now.toEpochMilli()) @@ -73,6 +73,7 @@ class GetApplicationRelease( } data class Arguments( + val isFoss: Boolean, val isPreview: Boolean, val commitCount: Int, val versionName: String, 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 311d2f34f..d7001d952 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,27 +7,5 @@ data class Release( val version: String, val info: String, val releaseLink: String, - private val assets: List, -) { - - /** - * 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("mihon$apkVariant-") } ?: assets[0] - } - - /** - * Assets class containing download url. - */ - data class Assets(val downloadLink: String) -} + 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 61bbdb351..c4a64b0b0 100644 --- a/domain/src/main/java/tachiyomi/domain/release/service/ReleaseService.kt +++ b/domain/src/main/java/tachiyomi/domain/release/service/ReleaseService.kt @@ -1,8 +1,9 @@ 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? } 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 f6d28f734..757ed8e24 100644 --- a/domain/src/test/java/tachiyomi/domain/release/interactor/GetApplicationReleaseTest.kt +++ b/domain/src/test/java/tachiyomi/domain/release/interactor/GetApplicationReleaseTest.kt @@ -16,9 +16,9 @@ import java.time.Instant class GetApplicationReleaseTest { - lateinit var getApplicationRelease: GetApplicationRelease - lateinit var releaseService: ReleaseService - lateinit var preference: Preference + private lateinit var getApplicationRelease: GetApplicationRelease + private lateinit var releaseService: ReleaseService + private lateinit var preference: Preference @BeforeEach fun beforeEach() { @@ -39,13 +39,14 @@ class GetApplicationReleaseTest { "r2000", "info", "http://example.com/release_link", - listOf("http://example.com/assets"), + "http://example.com/release_link.apk", ) coEvery { releaseService.latest(any()) } returns release val result = getApplicationRelease.await( GetApplicationRelease.Arguments( + isFoss = false, isPreview = true, commitCount = 1000, versionName = "", @@ -67,13 +68,14 @@ class GetApplicationReleaseTest { "v2.0.0", "info", "http://example.com/release_link", - listOf("http://example.com/assets"), + "http://example.com/release_link.apk", ) coEvery { releaseService.latest(any()) } returns release val result = getApplicationRelease.await( GetApplicationRelease.Arguments( + isFoss = false, isPreview = false, commitCount = 0, versionName = "v1.0.0", @@ -95,13 +97,14 @@ class GetApplicationReleaseTest { "v1.0.0", "info", "http://example.com/release_link", - listOf("http://example.com/assets"), + "http://example.com/release_link.apk", ) coEvery { releaseService.latest(any()) } returns release val result = getApplicationRelease.await( GetApplicationRelease.Arguments( + isFoss = false, isPreview = false, commitCount = 0, versionName = "v2.0.0", @@ -121,13 +124,14 @@ class GetApplicationReleaseTest { "v1.0.0", "info", "http://example.com/release_link", - listOf("http://example.com/assets"), + "http://example.com/release_link.apk", ) coEvery { releaseService.latest(any()) } returns release val result = getApplicationRelease.await( GetApplicationRelease.Arguments( + isFoss = false, isPreview = false, commitCount = 0, versionName = "v2.0.0",