diff --git a/app/src/androidTest/java/io/legado/app/UpdateTest.kt b/app/src/androidTest/java/io/legado/app/UpdateTest.kt index 60d3163c3..23a82ae8c 100644 --- a/app/src/androidTest/java/io/legado/app/UpdateTest.kt +++ b/app/src/androidTest/java/io/legado/app/UpdateTest.kt @@ -1,51 +1,34 @@ package io.legado.app +import com.google.gson.Gson import io.legado.app.exception.NoStackTraceException import io.legado.app.help.http.okHttpClient -import io.legado.app.utils.channel -import io.legado.app.utils.jsonPath +import io.legado.app.model.GithubRelease +import io.legado.app.utils.fromJsonArray import okhttp3.Request +import org.junit.Assert.assertTrue import org.junit.Test -import splitties.init.appCtx class UpdateTest { - private val lastReleaseUrl = "https://api.github.com/repos/gedoor/legado/releases/latest" + private val lastReleaseUrl = + "https://api.github.com/repos/gedoor/legado/releases?page=1?per_page=1" @Test fun updateApp() { val body = okHttpClient.newCall(Request.Builder().url(lastReleaseUrl).build()).execute() .body!!.string() - val rootDoc = jsonPath.parse(body) - val downloadUrl = - rootDoc.read>("\$.assets[?(@.name =~ /legado_app_.*?apk\$/)].browser_download_url") - print(downloadUrl) - } - @Test - fun updateLollipop() { - val body = okHttpClient.newCall(Request.Builder().url(lastReleaseUrl).build()).execute() - .body!!.string() - val rootDoc = jsonPath.parse(body) - val downloadUrl = - rootDoc.read>("\$.assets[?(@.name =~ /legado_lollipop_.*?apk\$/)].browser_download_url") - print(downloadUrl) - } + val releaseList = Gson().fromJsonArray(body) + .getOrElse { + throw NoStackTraceException("获取新版本出错 " + it.localizedMessage) + } + .flatMap { it.gitReleaseToAppReleaseInfo() } + .sortedByDescending { it.createdAt } - @Test - fun updateChannel() { - val body = okHttpClient.newCall(Request.Builder().url(lastReleaseUrl).build()).execute() - .body!!.string() - val rootDoc = jsonPath.parse(body) - val path = "\$.assets[?(@.name =~ /legado_${appCtx.channel}_.*?apk\$/)]" - val downloadUrl = rootDoc.read>("${path}.browser_download_url") - .firstOrNull() - ?: throw NoStackTraceException("获取新版本出错") - val fileName = rootDoc.read>("${path}.name") - .firstOrNull() - ?: throw NoStackTraceException("获取新版本出错") - print(downloadUrl) - print(fileName) + assertTrue(releaseList.isNotEmpty()) + assertTrue(releaseList.all { it.downloadUrl.isNotBlank() }) + assertTrue(releaseList.all { it.versionName.isNotBlank() }) } } \ No newline at end of file diff --git a/app/src/main/java/io/legado/app/constant/AppConst.kt b/app/src/main/java/io/legado/app/constant/AppConst.kt index fb020a164..0bd021452 100644 --- a/app/src/main/java/io/legado/app/constant/AppConst.kt +++ b/app/src/main/java/io/legado/app/constant/AppConst.kt @@ -5,6 +5,7 @@ import android.content.pm.PackageManager import android.provider.Settings import androidx.annotation.Keep import io.legado.app.BuildConfig +import io.legado.app.model.AppVariant import splitties.init.appCtx import java.text.SimpleDateFormat @@ -58,6 +59,11 @@ object AppConst { appCtx.packageManager.getPackageInfo(appCtx.packageName, PackageManager.GET_ACTIVITIES) ?.let { appInfo.versionName = it.versionName + // TODO: 增加测试版还是正式版的检查 + if (it.packageName.contains("releaseA")) { + appInfo.appVariant = AppVariant.COMPATIBLE + } + if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.P) { appInfo.versionCode = it.longVersionCode } else { @@ -74,7 +80,8 @@ object AppConst { @Keep data class AppInfo( var versionCode: Long = 0L, - var versionName: String = "" + var versionName: String = "", + var appVariant: AppVariant = AppVariant.UNKNOWN ) /** diff --git a/app/src/main/java/io/legado/app/help/AppUpdateGitHub.kt b/app/src/main/java/io/legado/app/help/AppUpdateGitHub.kt index 0fa0ee2a1..65c90fdb9 100644 --- a/app/src/main/java/io/legado/app/help/AppUpdateGitHub.kt +++ b/app/src/main/java/io/legado/app/help/AppUpdateGitHub.kt @@ -1,49 +1,55 @@ package io.legado.app.help import androidx.annotation.Keep +import com.google.gson.Gson import io.legado.app.constant.AppConst import io.legado.app.exception.NoStackTraceException import io.legado.app.help.coroutine.Coroutine import io.legado.app.help.http.newCallStrResponse import io.legado.app.help.http.okHttpClient -import io.legado.app.utils.channel -import io.legado.app.utils.jsonPath -import io.legado.app.utils.readString +import io.legado.app.model.AppReleaseInfo +import io.legado.app.model.GithubRelease +import io.legado.app.utils.fromJsonArray import kotlinx.coroutines.CoroutineScope -import splitties.init.appCtx @Keep @Suppress("unused") object AppUpdateGitHub : AppUpdate.AppUpdateInterface { + // TODO:在关于->其他,添加其他版本选项,使用该函数 + private suspend fun getRecentReleases(): List { + val lastReleaseUrl = + "https://api.github.com/repos/gedoor/legado/releases?page=1?per_page=1" + val body = okHttpClient.newCallStrResponse { + url(lastReleaseUrl) + }.body + if (body.isNullOrBlank()) { + throw NoStackTraceException("获取新版本出错") + } + return Gson().fromJsonArray(body) + .getOrElse { + throw NoStackTraceException("获取新版本出错 " + it.localizedMessage) + } + .flatMap { it.gitReleaseToAppReleaseInfo() } + .sortedByDescending { it.createdAt } + } + override fun check( scope: CoroutineScope, ): Coroutine { return Coroutine.async(scope) { - val lastReleaseUrl = "https://api.github.com/repos/gedoor/legado/releases/latest" - val body = okHttpClient.newCallStrResponse { - url(lastReleaseUrl) - }.body - if (body.isNullOrBlank()) { - throw NoStackTraceException("获取新版本出错") - } - val rootDoc = jsonPath.parse(body) - val tagName = rootDoc.readString("$.tag_name") - ?: throw NoStackTraceException("获取新版本出错") - if (tagName > AppConst.appInfo.versionName) { - val updateBody = rootDoc.readString("$.body") - ?: throw NoStackTraceException("获取新版本出错") - val path = "\$.assets[?(@.name =~ /legado_${appCtx.channel}_.*?apk\$/)]" - val downloadUrl = rootDoc.read>("${path}.browser_download_url") - .firstOrNull() - ?: throw NoStackTraceException("获取新版本出错") - val fileName = rootDoc.read>("${path}.name") - .firstOrNull() - ?: throw NoStackTraceException("获取新版本出错") - return@async AppUpdate.UpdateInfo(tagName, updateBody, downloadUrl, fileName) - } else { - throw NoStackTraceException("已是最新版本") - } + val recentReleases = getRecentReleases() + recentReleases + .filter { it.appVariant == AppConst.appInfo.appVariant } + .firstOrNull { it.versionName > AppConst.appInfo.versionName } + ?.let { + return@async AppUpdate.UpdateInfo( + it.versionName, + it.note, + it.downloadUrl, + it.name + ) + } ?: throw NoStackTraceException("已是最新版本") }.timeout(10000) } diff --git a/app/src/main/java/io/legado/app/model/AppReleaseInfo.kt b/app/src/main/java/io/legado/app/model/AppReleaseInfo.kt new file mode 100644 index 000000000..c1ea2dafb --- /dev/null +++ b/app/src/main/java/io/legado/app/model/AppReleaseInfo.kt @@ -0,0 +1,101 @@ +package io.legado.app.model + +import com.google.gson.annotations.SerializedName +import java.time.Instant + +data class AppReleaseInfo( + val appVariant: AppVariant, + val createdAt: Long, + val note: String, + val name: String, + val downloadUrl: String, + val assetUrl: String +) { + val versionName: String = name.split("_").getOrNull(2)?.removeSuffix(".apk") ?: "" +} + +enum class AppVariant { + OFFICIAL, + COMPATIBLE, + BETA_RELEASE, + UNKNOWN +} + +data class GithubRelease( + val assets: List, + val body: String, + @SerializedName("prerelease") + val isPreLease: Boolean, +) { + fun gitReleaseToAppReleaseInfo(): List { + return assets + .filter { it.isValid } + .map { it.assetToAppReleaseInfo(isPreLease, body) } + } +} + +data class Asset( + @SerializedName("browser_download_url") + val apkUrl: String, + @SerializedName("content_type") + val contentType: String, + @SerializedName("created_at") + val createdAt: String, + @SerializedName("download_count") + val downloadCount: Int, + val id: Int, + val name: String, + val state: String, + val url: String +) { + val isValid: Boolean + get() = (contentType == "application/vnd.android.package-archive") && (state == "uploaded") + + fun assetToAppReleaseInfo(preRelease: Boolean, note: String): AppReleaseInfo { + val instant = Instant.parse(createdAt) + val timestamp: Long = instant.toEpochMilli() + + return when { + preRelease && name.contains("releaseA") -> + AppReleaseInfo( + AppVariant.COMPATIBLE, + timestamp, + note, + name, + apkUrl, + url + ) + + preRelease && name.contains("release") -> + AppReleaseInfo( + AppVariant.BETA_RELEASE, + timestamp, + note, + name, + apkUrl, + url + ) + + !preRelease && name.contains("release") -> + AppReleaseInfo( + AppVariant.OFFICIAL, + timestamp, + note, + name, + apkUrl, + url + ) + + else -> + AppReleaseInfo( + AppVariant.UNKNOWN, + timestamp, + note, + name, + apkUrl, + url + ) + + } + } +}