Fix update version check

This commit is contained in:
世界
2026-02-16 11:22:53 +08:00
parent 2ad11b7045
commit a7c18535e1
3 changed files with 64 additions and 32 deletions

View File

@@ -4,7 +4,6 @@ import android.os.Build
import io.nekohasekai.libbox.Libbox import io.nekohasekai.libbox.Libbox
import io.nekohasekai.sfa.BuildConfig import io.nekohasekai.sfa.BuildConfig
import io.nekohasekai.sfa.ktx.unwrap import io.nekohasekai.sfa.ktx.unwrap
import io.nekohasekai.sfa.update.UpdateCheckException
import io.nekohasekai.sfa.update.UpdateInfo import io.nekohasekai.sfa.update.UpdateInfo
import io.nekohasekai.sfa.update.UpdateTrack import io.nekohasekai.sfa.update.UpdateTrack
import io.nekohasekai.sfa.utils.HTTPClient import io.nekohasekai.sfa.utils.HTTPClient
@@ -27,18 +26,25 @@ class GitHubUpdateChecker : Closeable {
private val json = Json { ignoreUnknownKeys = true } private val json = Json { ignoreUnknownKeys = true }
fun checkUpdate(track: UpdateTrack): UpdateInfo? { fun checkUpdate(track: UpdateTrack): UpdateInfo? {
val includePrerelease = track == UpdateTrack.BETA val releases = getReleases()
val release = getLatestRelease(includePrerelease) ?: return null var selected: ReleaseCandidate? = null
if (!release.assets.any { it.name == METADATA_FILENAME }) { for (release in releases) {
throw UpdateCheckException.TrackNotSupported() if (!isReleaseInTrack(release, track)) {
continue
}
val metadata = runCatching { downloadMetadata(release) }.getOrNull() ?: continue
if (!isNewerThanCurrent(metadata.versionName)) {
continue
}
val currentBest = selected
if (currentBest == null || isBetterVersion(metadata, currentBest.metadata)) {
selected = ReleaseCandidate(release, metadata)
}
} }
val metadata = downloadMetadata(release)!! val release = selected?.release ?: return null
val metadata = selected.metadata
if (metadata.versionCode <= BuildConfig.VERSION_CODE) {
return null
}
val isLegacy = Build.VERSION.SDK_INT < Build.VERSION_CODES.M val isLegacy = Build.VERSION.SDK_INT < Build.VERSION_CODES.M
val apkAsset = release.assets.find { asset -> val apkAsset = release.assets.find { asset ->
@@ -58,7 +64,7 @@ class GitHubUpdateChecker : Closeable {
) )
} }
private fun getLatestRelease(includePrerelease: Boolean): GitHubRelease? { private fun getReleases(): List<GitHubRelease> {
val request = client.newRequest() val request = client.newRequest()
request.setURL(RELEASES_URL) request.setURL(RELEASES_URL)
request.setHeader("Accept", "application/vnd.github.v3+json") request.setHeader("Accept", "application/vnd.github.v3+json")
@@ -67,13 +73,31 @@ class GitHubUpdateChecker : Closeable {
val response = request.execute() val response = request.execute()
val content = response.content.unwrap val content = response.content.unwrap
val releases = json.decodeFromString<List<GitHubRelease>>(content) return json.decodeFromString(content)
}
return if (includePrerelease) { private fun isReleaseInTrack(release: GitHubRelease, track: UpdateTrack): Boolean {
releases.firstOrNull() if (release.draft) {
} else { return false
releases.firstOrNull { !it.prerelease && !it.draft }
} }
return when (track) {
UpdateTrack.STABLE -> !release.prerelease
UpdateTrack.BETA -> true
}
}
private fun isNewerThanCurrent(versionName: String): Boolean {
return Libbox.compareSemver(versionName, BuildConfig.VERSION_NAME)
}
private fun isBetterVersion(version: VersionMetadata, other: VersionMetadata): Boolean {
if (Libbox.compareSemver(version.versionName, other.versionName)) {
return true
}
if (Libbox.compareSemver(other.versionName, version.versionName)) {
return false
}
return version.versionCode > other.versionCode
} }
private fun downloadMetadata(release: GitHubRelease): VersionMetadata? { private fun downloadMetadata(release: GitHubRelease): VersionMetadata? {
@@ -117,4 +141,9 @@ class GitHubUpdateChecker : Closeable {
@SerialName("version_code") val versionCode: Int = 0, @SerialName("version_code") val versionCode: Int = 0,
@SerialName("version_name") val versionName: String = "", @SerialName("version_name") val versionName: String = "",
) )
private data class ReleaseCandidate(
val release: GitHubRelease,
val metadata: VersionMetadata,
)
} }

View File

@@ -198,6 +198,7 @@ class MainActivity :
val updateInfo = Vendor.checkUpdateAsync() val updateInfo = Vendor.checkUpdateAsync()
UpdateState.setUpdate(updateInfo) UpdateState.setUpdate(updateInfo)
} catch (_: Exception) { } catch (_: Exception) {
UpdateState.setUpdate(null)
} }
} }
} }
@@ -495,6 +496,7 @@ class MainActivity :
val result = Vendor.checkUpdateAsync() val result = Vendor.checkUpdateAsync()
UpdateState.setUpdate(result) UpdateState.setUpdate(result)
} catch (_: Exception) { } catch (_: Exception) {
UpdateState.setUpdate(null)
} }
} }
}) { }) {

View File

@@ -188,6 +188,7 @@ fun AppSettingsScreen(navController: NavController) {
currentTrack = currentTrack, currentTrack = currentTrack,
onTrackSelected = { track -> onTrackSelected = { track ->
currentTrack = track currentTrack = track
UpdateState.clear()
scope.launch(Dispatchers.IO) { scope.launch(Dispatchers.IO) {
Settings.updateTrack = track Settings.updateTrack = track
} }
@@ -932,25 +933,25 @@ fun AppSettingsScreen(navController: NavController) {
}, },
) )
.clickable(enabled = !isChecking) { .clickable(enabled = !isChecking) {
if (hasUpdate && updateInfo != null) { scope.launch {
showUpdateAvailableDialog = true UpdateState.isChecking.value = true
} else { withContext(Dispatchers.IO) {
scope.launch { try {
UpdateState.isChecking.value = true val result = Vendor.checkUpdateAsync()
withContext(Dispatchers.IO) { UpdateState.setUpdate(result)
try { if (result == null) {
val result = Vendor.checkUpdateAsync() showErrorDialog = R.string.no_updates_available
UpdateState.setUpdate(result) } else {
if (result == null) { showUpdateAvailableDialog = true
showErrorDialog = R.string.no_updates_available
}
} catch (_: UpdateCheckException.TrackNotSupported) {
showErrorDialog = R.string.update_track_not_supported
} catch (_: Exception) {
} }
} catch (_: UpdateCheckException.TrackNotSupported) {
UpdateState.setUpdate(null)
showErrorDialog = R.string.update_track_not_supported
} catch (_: Exception) {
UpdateState.setUpdate(null)
} }
UpdateState.isChecking.value = false
} }
UpdateState.isChecking.value = false
} }
}, },
colors = colors =