From 76962f965ef38b23f7823293724a1d163ee60b11 Mon Sep 17 00:00:00 2001 From: vvb2060 Date: Fri, 9 May 2025 09:22:43 +0000 Subject: [PATCH] app: use github api to check updates --- .../magisk/dialog/ManagerInstallDialog.kt | 3 +- .../magisk/ui/install/InstallViewModel.kt | 7 ++- .../java/com/topjohnwu/magisk/core/Const.kt | 3 +- .../magisk/core/data/NetworkServices.kt | 37 ++++++++------- .../magisk/core/di/ServiceLocator.kt | 2 +- .../topjohnwu/magisk/core/model/UpdateInfo.kt | 13 +++-- .../magisk/core/repository/NetworkService.kt | 47 +++++++++++++++---- app/stub/build.gradle.kts | 12 +++-- .../topjohnwu/magisk/DownloadActivity.java | 30 ++---------- 9 files changed, 88 insertions(+), 66 deletions(-) diff --git a/app/apk/src/main/java/com/topjohnwu/magisk/dialog/ManagerInstallDialog.kt b/app/apk/src/main/java/com/topjohnwu/magisk/dialog/ManagerInstallDialog.kt index ede61c0c5..a88b2784b 100644 --- a/app/apk/src/main/java/com/topjohnwu/magisk/dialog/ManagerInstallDialog.kt +++ b/app/apk/src/main/java/com/topjohnwu/magisk/dialog/ManagerInstallDialog.kt @@ -14,7 +14,8 @@ class ManagerInstallDialog : MarkDownDialog() { private val svc get() = ServiceLocator.networkService override suspend fun getMarkdownText(): String { - val text = svc.fetchString(Info.remote.magisk.note) + val str = Info.remote.magisk.note + val text = if (str.startsWith("http", true)) svc.fetchString(str) else str // Cache the changelog AppContext.cacheDir.listFiles { _, name -> name.endsWith(".md") }.orEmpty().forEach { it.delete() diff --git a/app/apk/src/main/java/com/topjohnwu/magisk/ui/install/InstallViewModel.kt b/app/apk/src/main/java/com/topjohnwu/magisk/ui/install/InstallViewModel.kt index 0c1ce01dc..b85d3b671 100644 --- a/app/apk/src/main/java/com/topjohnwu/magisk/ui/install/InstallViewModel.kt +++ b/app/apk/src/main/java/com/topjohnwu/magisk/ui/install/InstallViewModel.kt @@ -71,11 +71,14 @@ class InstallViewModel(svc: NetworkService, markwon: Markwon) : BaseViewModel() viewModelScope.launch(Dispatchers.IO) { try { val file = File(AppContext.cacheDir, "${BuildConfig.APP_VERSION_CODE}.md") + val note = Info.remote.magisk.note val text = when { file.exists() -> file.readText() - Const.Url.CHANGELOG_URL.isEmpty() -> "" + Const.APP_IS_CANARY && note.isEmpty() -> "" + Const.APP_IS_CANARY && !note.startsWith("http", true) -> note else -> { - val str = svc.fetchString(Const.Url.CHANGELOG_URL) + val url = if (Const.APP_IS_CANARY) note else Const.Url.CHANGELOG_URL + val str = svc.fetchString(url) file.writeText(str) str } diff --git a/app/core/src/main/java/com/topjohnwu/magisk/core/Const.kt b/app/core/src/main/java/com/topjohnwu/magisk/core/Const.kt index 92cf629e5..3c99504cd 100644 --- a/app/core/src/main/java/com/topjohnwu/magisk/core/Const.kt +++ b/app/core/src/main/java/com/topjohnwu/magisk/core/Const.kt @@ -43,8 +43,7 @@ object Const { const val PATREON_URL = "https://www.patreon.com/topjohnwu" const val SOURCE_CODE_URL = "https://github.com/topjohnwu/Magisk" - val CHANGELOG_URL = if (APP_IS_CANARY) Info.remote.magisk.note - else "https://topjohnwu.github.io/Magisk/releases/${BuildConfig.APP_VERSION_CODE}.md" + const val CHANGELOG_URL = "https://topjohnwu.github.io/Magisk/releases/${BuildConfig.APP_VERSION_CODE}.md" const val GITHUB_RAW_URL = "https://raw.githubusercontent.com/" const val GITHUB_API_URL = "https://api.github.com/" diff --git a/app/core/src/main/java/com/topjohnwu/magisk/core/data/NetworkServices.kt b/app/core/src/main/java/com/topjohnwu/magisk/core/data/NetworkServices.kt index be9913123..1420be317 100644 --- a/app/core/src/main/java/com/topjohnwu/magisk/core/data/NetworkServices.kt +++ b/app/core/src/main/java/com/topjohnwu/magisk/core/data/NetworkServices.kt @@ -1,25 +1,16 @@ package com.topjohnwu.magisk.core.data -import com.topjohnwu.magisk.core.model.BranchInfo import com.topjohnwu.magisk.core.model.ModuleJson +import com.topjohnwu.magisk.core.model.Release import com.topjohnwu.magisk.core.model.UpdateInfo import okhttp3.ResponseBody import retrofit2.http.GET import retrofit2.http.Headers import retrofit2.http.Path +import retrofit2.http.Query import retrofit2.http.Streaming import retrofit2.http.Url -private const val BRANCH = "branch" -private const val REPO = "repo" -private const val FILE = "file" - -interface GithubPageServices { - - @GET - suspend fun fetchUpdateJSON(@Url file: String): UpdateInfo -} - interface RawServices { @GET @@ -32,14 +23,26 @@ interface RawServices { @GET suspend fun fetchModuleJson(@Url url: String): ModuleJson + @GET + suspend fun fetchUpdateJSON(@Url url: String): UpdateInfo + } interface GithubApiServices { - @GET("repos/{$REPO}/branches/{$BRANCH}") - @Headers("Accept: application/vnd.github.v3+json") - suspend fun fetchBranch( - @Path(REPO, encoded = true) repo: String, - @Path(BRANCH) branch: String - ): BranchInfo + @GET("/repos/{owner}/{repo}/releases") + @Headers("Accept: application/vnd.github+json") + suspend fun fetchRelease( + @Path("owner") owner: String = "topjohnwu", + @Path("repo") repo: String = "Magisk", + @Query("per_page") per: Int = 10, + @Query("page") page: Int = 1, + ): List + + @GET("/repos/{owner}/{repo}/releases/latest") + @Headers("Accept: application/vnd.github+json") + suspend fun fetchLatestRelease( + @Path("owner") owner: String = "topjohnwu", + @Path("repo") repo: String = "Magisk", + ): Release } diff --git a/app/core/src/main/java/com/topjohnwu/magisk/core/di/ServiceLocator.kt b/app/core/src/main/java/com/topjohnwu/magisk/core/di/ServiceLocator.kt index e86bc1160..a621c8184 100644 --- a/app/core/src/main/java/com/topjohnwu/magisk/core/di/ServiceLocator.kt +++ b/app/core/src/main/java/com/topjohnwu/magisk/core/di/ServiceLocator.kt @@ -35,8 +35,8 @@ object ServiceLocator { val markwon by lazy { createMarkwon(AppContext) } val networkService by lazy { NetworkService( - createApiService(retrofit, Const.Url.GITHUB_PAGE_URL), createApiService(retrofit, Const.Url.GITHUB_RAW_URL), + createApiService(retrofit, Const.Url.GITHUB_API_URL), ) } } diff --git a/app/core/src/main/java/com/topjohnwu/magisk/core/model/UpdateInfo.kt b/app/core/src/main/java/com/topjohnwu/magisk/core/model/UpdateInfo.kt index 60b3a11d4..3f44647ec 100644 --- a/app/core/src/main/java/com/topjohnwu/magisk/core/model/UpdateInfo.kt +++ b/app/core/src/main/java/com/topjohnwu/magisk/core/model/UpdateInfo.kt @@ -27,11 +27,16 @@ data class ModuleJson( ) @JsonClass(generateAdapter = true) -data class CommitInfo( - val sha: String +data class ReleaseAssets( + val name: String, + val browser_download_url: String, ) @JsonClass(generateAdapter = true) -data class BranchInfo( - val commit: CommitInfo +data class Release( + val tag_name: String, + val name: String, + val prerelease: Boolean, + val assets: List, + val body: String, ) diff --git a/app/core/src/main/java/com/topjohnwu/magisk/core/repository/NetworkService.kt b/app/core/src/main/java/com/topjohnwu/magisk/core/repository/NetworkService.kt index 5f243dc00..1eec8299f 100644 --- a/app/core/src/main/java/com/topjohnwu/magisk/core/repository/NetworkService.kt +++ b/app/core/src/main/java/com/topjohnwu/magisk/core/repository/NetworkService.kt @@ -8,15 +8,18 @@ import com.topjohnwu.magisk.core.Config.Value.DEBUG_CHANNEL import com.topjohnwu.magisk.core.Config.Value.DEFAULT_CHANNEL import com.topjohnwu.magisk.core.Config.Value.STABLE_CHANNEL import com.topjohnwu.magisk.core.Info -import com.topjohnwu.magisk.core.data.GithubPageServices +import com.topjohnwu.magisk.core.data.GithubApiServices import com.topjohnwu.magisk.core.data.RawServices +import com.topjohnwu.magisk.core.model.MagiskJson +import com.topjohnwu.magisk.core.model.Release +import com.topjohnwu.magisk.core.model.UpdateInfo import retrofit2.HttpException import timber.log.Timber import java.io.IOException class NetworkService( - private val pages: GithubPageServices, - private val raw: RawServices + private val raw: RawServices, + private val api: GithubApiServices, ) { suspend fun fetchUpdate() = safe { var info = when (Config.updateChannel) { @@ -36,11 +39,39 @@ class NetworkService( } // UpdateInfo - private suspend fun fetchStableUpdate() = pages.fetchUpdateJSON("stable.json") - private suspend fun fetchBetaUpdate() = pages.fetchUpdateJSON("beta.json") - private suspend fun fetchCanaryUpdate() = pages.fetchUpdateJSON("canary.json") - private suspend fun fetchDebugUpdate() = pages.fetchUpdateJSON("debug.json") - private suspend fun fetchCustomUpdate(url: String) = pages.fetchUpdateJSON(url) + private suspend fun fetchStableUpdate(rel: Release? = null): UpdateInfo { + val release = rel ?: api.fetchLatestRelease() + val name = release.tag_name.drop(1) + val info = MagiskJson( + name, (name.toFloat() * 1000).toInt(), + release.assets[0].browser_download_url, release.body + ) + return UpdateInfo(info) + } + + private suspend fun fetchBetaUpdate(): UpdateInfo { + val release = api.fetchRelease().find { it.tag_name[0] == 'v' && it.prerelease } + return fetchStableUpdate(release) + } + + private suspend fun fetchCanaryUpdate(): UpdateInfo { + val release = api.fetchRelease().find { it.tag_name.startsWith("canary-") } + val info = MagiskJson( + release!!.name.substring(8, 16), + release.tag_name.drop(7).toInt(), + release.assets.find { it.name == "app-release.apk" }!!.browser_download_url, + release.body + ) + return UpdateInfo(info) + } + + private suspend fun fetchDebugUpdate(): UpdateInfo { + val release = fetchCanaryUpdate() + val link = release.magisk.link.replace("app-release.apk", "app-debug.apk") + return UpdateInfo(release.magisk.copy(link = link)) + } + + private suspend fun fetchCustomUpdate(url: String) = raw.fetchUpdateJSON(url) private inline fun safe(factory: () -> T): T? { return try { diff --git a/app/stub/build.gradle.kts b/app/stub/build.gradle.kts index be547dc02..154ec61cb 100644 --- a/app/stub/build.gradle.kts +++ b/app/stub/build.gradle.kts @@ -13,24 +13,28 @@ android { namespace = "com.topjohnwu.magisk" val canary = !Config.version.contains(".") - - val url = if (canary) null - else "https://github.com/topjohnwu/Magisk/releases/download/v${Config.version}/Magisk-v${Config.version}.apk" + val base = "https://github.com/topjohnwu/Magisk/releases/download/" + val url = base + "v${Config.version}/Magisk-v${Config.version}.apk" + val canaryUrl = base + "canary-${Config.versionCode}/" defaultConfig { applicationId = "com.topjohnwu.magisk" versionCode = 1 versionName = "1.0" - buildConfigField("String", "APK_URL", url?.let { "\"$it\"" } ?: "null" ) + buildConfigField("String", "APK_URL", "\"$url\"") buildConfigField("int", "STUB_VERSION", Config.stubVersion) } buildTypes { release { + if (canary) buildConfigField("String", "APK_URL", "\"${canaryUrl}app-release.apk\"") proguardFiles("proguard-rules.pro") isMinifyEnabled = true isShrinkResources = false } + debug { + if (canary) buildConfigField("String", "APK_URL", "\"${canaryUrl}app-debug.apk\"") + } } buildFeatures { diff --git a/app/stub/src/main/java/com/topjohnwu/magisk/DownloadActivity.java b/app/stub/src/main/java/com/topjohnwu/magisk/DownloadActivity.java index 979a20c94..ed619e571 100644 --- a/app/stub/src/main/java/com/topjohnwu/magisk/DownloadActivity.java +++ b/app/stub/src/main/java/com/topjohnwu/magisk/DownloadActivity.java @@ -27,8 +27,6 @@ import com.topjohnwu.magisk.net.Networking; import com.topjohnwu.magisk.net.Request; import com.topjohnwu.magisk.utils.APKInstall; -import org.json.JSONException; - import java.io.ByteArrayInputStream; import java.io.File; import java.io.FileOutputStream; @@ -48,13 +46,8 @@ import javax.crypto.spec.SecretKeySpec; public class DownloadActivity extends Activity { private static final String APP_NAME = "Magisk"; - private static final String JSON_URL = BuildConfig.DEBUG ? - "https://topjohnwu.github.io/magisk-files/debug.json" : - "https://topjohnwu.github.io/magisk-files/canary.json"; - private String apkLink = BuildConfig.APK_URL; private Context themed; - private ProgressDialog dialog; private boolean dynLoad; @Override @@ -75,11 +68,7 @@ public class DownloadActivity extends Activity { ProviderInstaller.install(this); if (Networking.checkNetworkStatus(this)) { - if (BuildConfig.APK_URL == null) { - fetchCanary(); - } else { - showDialog(); - } + showDialog(); } else { new AlertDialog.Builder(themed) .setCancelable(false) @@ -115,23 +104,10 @@ public class DownloadActivity extends Activity { .show(); } - private void fetchCanary() { - dialog = ProgressDialog.show(themed, "", "", true); - request(JSON_URL).getAsJSONObject(json -> { - dialog.dismiss(); - try { - apkLink = json.getJSONObject("magisk").getString("link"); - showDialog(); - } catch (JSONException e) { - error(e); - } - }); - } - private void dlAPK() { - dialog = ProgressDialog.show(themed, getString(dling), getString(dling) + " " + APP_NAME, true); + ProgressDialog.show(themed, getString(dling), getString(dling) + " " + APP_NAME, true); // Download and upgrade the app - var request = request(apkLink).setExecutor(AsyncTask.THREAD_POOL_EXECUTOR); + var request = request(BuildConfig.APK_URL).setExecutor(AsyncTask.THREAD_POOL_EXECUTOR); if (dynLoad) { request.getAsFile(StubApk.current(this), file -> StubApk.restartProcess(this)); } else {