app: use github api to check updates

This commit is contained in:
vvb2060 2025-05-09 09:22:43 +00:00 committed by John Wu
parent a4b8c5e46b
commit 76962f965e
9 changed files with 88 additions and 66 deletions

View File

@ -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()

View File

@ -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
}

View File

@ -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/"

View File

@ -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<Release>
@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
}

View File

@ -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),
)
}
}

View File

@ -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<ReleaseAssets>,
val body: String,
)

View File

@ -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 <T> safe(factory: () -> T): T? {
return try {

View File

@ -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 {

View File

@ -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 {