fix: Allow sharing when the GitHub release lags the installed version

Upstream bumps versionName in main before tagging the GitHub release, so
there is a recurring window where the installed app is newer than the
latest published universal APK. The hard version guard disabled the whole
sharing feature during that window (including for reviewers building this
branch at 1.7.5 while GitHub's latest is 1.7.4).

An older release is still a genuine, signed, checksum-verified universal
artifact, and Android already refuses downgrade installs on receivers, so:

- checkForUpdate now logs (instead of erroring) when the latest release is
  older than the installed app and proceeds normally.
- downloadUniversalApk no longer fails for an older-than-installed release.
- A cached artifact stays shareable regardless of the installed version.

The cached-artifact preference (never replace a newer cached APK with an
older one) and all signature/checksum verification are unchanged.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Moe Hamade 2026-07-24 19:46:23 +03:00
parent f180ef5fb3
commit 9700e9cc92
2 changed files with 13 additions and 26 deletions

View File

@ -309,8 +309,10 @@ class ApkDownloadViewModel(application: Application) : AndroidViewModel(applicat
)
}
is UniversalApkManager.UpdateStatus.Error -> {
// A cached artifact stays shareable even when the update
// check fails or the release lags the installed version.
val info = apkManager.getCachedApkInfo()
if (info != null && apkManager.isCompatibleWithInstalledVersion(info.version)) {
if (info != null) {
ApkPreparationStatus.Ready(
version = info.version,
sizeMB = (info.size / 1024 / 1024).toInt(),

View File

@ -129,23 +129,21 @@ class UniversalApkManager(private val context: Context) {
}
val cachedInfo = getCachedApkInfo()
val cachedApkIsOlder = cachedInfo?.let {
isOlderThanInstalledVersion(it.version)
} == true
val latestRelease = GitHubReleaseClient.fetchLatestRelease().getOrElse { error ->
return@withContext UpdateStatus.Error(
if (cachedApkIsOlder) {
"Cached sharing APK ${cachedInfo?.version} is older than installed app " +
"${installedVersionName()}, and a newer GitHub release could not be checked."
} else {
error.message ?: "Failed to fetch latest release from GitHub"
}
error.message ?: "Failed to fetch latest release from GitHub"
)
}
// The GitHub release may briefly lag behind the installed version
// (upstream bumps versionName in main before tagging the release).
// An older release is still a genuine, signed, universal artifact —
// recipients with a newer install can't be downgraded by Android
// anyway — so share it rather than disabling the feature.
if (isOlderThanInstalledVersion(latestRelease.versionName)) {
return@withContext UpdateStatus.Error(
"GitHub universal APK ${latestRelease.versionName} is older than installed app " +
"${installedVersionName()}. Wait for the matching GitHub release."
Log.i(
TAG,
"GitHub universal APK ${latestRelease.versionName} is older than installed " +
"app ${installedVersionName()}; sharing it until the matching release ships"
)
}
@ -208,15 +206,6 @@ class UniversalApkManager(private val context: Context) {
val release = GitHubReleaseClient.fetchLatestRelease().getOrElse { error ->
return@withContext Result.failure(error)
}
if (isOlderThanInstalledVersion(release.versionName)) {
return@withContext Result.failure(
GitHubReleaseClient.ReleaseFetchException(
message = "GitHub universal APK ${release.versionName} is older than " +
"installed app ${installedVersionName()}",
retryable = false
)
)
}
if (!GitHubReleaseClient.awaitSelectedNetworkRoute()) {
return@withContext Result.failure(
@ -494,10 +483,6 @@ class UniversalApkManager(private val context: Context) {
return GitHubReleaseClient.isNewerVersion(candidateVersion, installedVersionName())
}
fun isCompatibleWithInstalledVersion(candidateVersion: String): Boolean {
return !isOlderThanInstalledVersion(candidateVersion)
}
/**
* Verify the downloaded APK against either the running app's signing lineage
* or the pinned GitHub release certificate. The latter supports Play installs