diff --git a/app/src/google/java/ani/dantotsu/others/AppUpdater.kt b/app/src/google/java/ani/dantotsu/others/AppUpdater.kt index 36b4c35d..e42e4b79 100644 --- a/app/src/google/java/ani/dantotsu/others/AppUpdater.kt +++ b/app/src/google/java/ani/dantotsu/others/AppUpdater.kt @@ -19,6 +19,7 @@ import ani.dantotsu.R import ani.dantotsu.buildMarkwon import ani.dantotsu.client import ani.dantotsu.currContext +import ani.dantotsu.decodeBase64ToString import ani.dantotsu.logError import ani.dantotsu.openLinkInBrowser import ani.dantotsu.settings.saving.PrefManager @@ -37,26 +38,88 @@ import java.text.SimpleDateFormat import java.util.Locale object AppUpdater { + private val fallbackStableUrl: String + get() = "aHR0cHM6Ly9hcGkuZGFudG90c3UuYXBwL3VwZGF0ZXMvc3RhYmxl".decodeBase64ToString() + private val fallbackBetaUrl: String + get() = "aHR0cHM6Ly9hcGkuZGFudG90c3UuYXBwL3VwZGF0ZXMvYmV0YQ==".decodeBase64ToString() + + @Serializable + data class FallbackResponse( + val version: String, + val changelog: String, + val downloadUrl: String? = null + ) + + private suspend fun fetchUpdateInfo(repo: String, isDebug: Boolean): Pair? { + return try { + fetchFromGithub(repo, isDebug) + } catch (e: Exception) { + Logger.log("Github fetch failed, trying fallback: ${e.message}") + try { + fetchFromFallback(isDebug) + } catch (e: Exception) { + Logger.log("Fallback fetch failed: ${e.message}") + null + } + } + } + + private suspend fun fetchFromGithub(repo: String, isDebug: Boolean): Pair { + return if (isDebug) { + val res = client.get("https://api.github.com/repos/$repo/releases") + .parsed().map { + Mapper.json.decodeFromJsonElement(it) + } + val r = res.filter { it.prerelease }.filter { !it.tagName.contains("fdroid") } + .maxByOrNull { + it.timeStamp() + } ?: throw Exception("No Pre Release Found") + val v = r.tagName.substringAfter("v", "") + (r.body ?: "") to v.ifEmpty { throw Exception("Weird Version : ${r.tagName}") } + } else { + val res = client.get("https://raw.githubusercontent.com/$repo/main/stable.md").text + res to res.substringAfter("# ").substringBefore("\n") + } + } + + private suspend fun fetchFromFallback(isDebug: Boolean): Pair { + val url = if (isDebug) fallbackBetaUrl else fallbackStableUrl + val response = client.get(url).parsed() + return response.changelog to response.version + } + + private suspend fun fetchApkUrl(repo: String, version: String, isDebug: Boolean): String? { + return try { + fetchApkUrlFromGithub(repo, version) + } catch (e: Exception) { + Logger.log("Github APK fetch failed, trying fallback: ${e.message}") + try { + fetchApkUrlFromFallback(version, isDebug) + } catch (e: Exception) { + Logger.log("Fallback APK fetch failed: ${e.message}") + null + } + } + } + + private suspend fun fetchApkUrlFromGithub(repo: String, version: String): String? { + val apks = client.get("https://api.github.com/repos/$repo/releases/tags/v$version") + .parsed().assets?.filter { + it.browserDownloadURL.endsWith(".apk") + } + return apks?.firstOrNull()?.browserDownloadURL + } + + private suspend fun fetchApkUrlFromFallback(version: String, isDebug: Boolean): String? { + val url = if (isDebug) fallbackBetaUrl else fallbackStableUrl + return client.get("$url/$version").parsed().downloadUrl + } + suspend fun check(activity: FragmentActivity, post: Boolean = false) { if (post) snackString(currContext()?.getString(R.string.checking_for_update)) val repo = activity.getString(R.string.repo) tryWithSuspend { - val (md, version) = if (BuildConfig.DEBUG) { - val res = client.get("https://api.github.com/repos/$repo/releases") - .parsed().map { - Mapper.json.decodeFromJsonElement(it) - } - val r = res.filter { it.prerelease }.filter { !it.tagName.contains("fdroid") } - .maxByOrNull { - it.timeStamp() - } ?: throw Exception("No Pre Release Found") - val v = r.tagName.substringAfter("v", "") - (r.body ?: "") to v.ifEmpty { throw Exception("Weird Version : ${r.tagName}") } - } else { - val res = - client.get("https://raw.githubusercontent.com/$repo/main/stable.md").text - res to res.substringAfter("# ").substringBefore("\n") - } + val (md, version) = fetchUpdateInfo(repo, BuildConfig.DEBUG) ?: return@tryWithSuspend Logger.log("Git Version : $version") val dontShow = PrefManager.getCustomVal("dont_ask_for_update_$version", false) @@ -69,7 +132,7 @@ object AppUpdater { ) addView( TextView(activity).apply { - val markWon = try { //slower phones can destroy the activity before this is done + val markWon = try { buildMarkwon(activity, false) } catch (e: IllegalArgumentException) { return@runOnUiThread @@ -89,17 +152,11 @@ object AppUpdater { setPositiveButton(currContext()!!.getString(R.string.lets_go)) { MainScope().launch(Dispatchers.IO) { try { - val apks = - client.get("https://api.github.com/repos/$repo/releases/tags/v$version") - .parsed().assets?.filter { - it.browserDownloadURL.endsWith( - ".apk" - ) - } - val apkToDownload = apks?.first() - apkToDownload?.browserDownloadURL.apply { - if (this != null) activity.downloadUpdate(version, this) - else openLinkInBrowser("https://github.com/repos/$repo/releases/tag/v$version") + val apkUrl = fetchApkUrl(repo, version, BuildConfig.DEBUG) + if (apkUrl != null) { + activity.downloadUpdate(version, apkUrl) + } else { + openLinkInBrowser("https://github.com/repos/$repo/releases/tag/v$version") } } catch (e: Exception) { logError(e) @@ -112,8 +169,7 @@ object AppUpdater { } show(activity.supportFragmentManager, "dialog") } - } - else { + } else { if (post) snackString(currContext()?.getString(R.string.no_update_found)) } } diff --git a/app/src/main/java/ani/dantotsu/Functions.kt b/app/src/main/java/ani/dantotsu/Functions.kt index e4e18ed9..4e53801b 100644 --- a/app/src/main/java/ani/dantotsu/Functions.kt +++ b/app/src/main/java/ani/dantotsu/Functions.kt @@ -157,6 +157,8 @@ import java.util.TimeZone import java.util.Timer import java.util.TimerTask import kotlin.collections.set +import kotlin.io.encoding.Base64 +import kotlin.io.encoding.ExperimentalEncodingApi import kotlin.math.log2 import kotlin.math.max import kotlin.math.min @@ -1524,3 +1526,13 @@ fun getLanguageName(language: String): String? { } return null } + +@OptIn(ExperimentalEncodingApi::class) +fun String.decodeBase64ToString(): String { + return try { + String(Base64.decode(this), Charsets.UTF_8) + } catch (e: Exception) { + Logger.log(e) + "" + } +} \ No newline at end of file