diff --git a/android/app/build.gradle.kts b/android/app/build.gradle.kts
index 9eddf4c..0abb597 100644
--- a/android/app/build.gradle.kts
+++ b/android/app/build.gradle.kts
@@ -13,8 +13,8 @@ android {
applicationId = "org.maturita.maturita"
minSdk = 26
targetSdk = 35
- versionCode = 4
- versionName = "android-4"
+ versionCode = 5
+ versionName = "android-5"
buildConfigField("String", "COMMIT", "\"$commit\"")
buildConfigField("String", "UPDATE_REPO", "\"pepaskopec-blip/maturita.c\"")
buildConfigField("String", "UPDATE_BRANCH", "\"builds\"")
diff --git a/android/app/src/main/java/org/maturita/maturita/update/Updater.kt b/android/app/src/main/java/org/maturita/maturita/update/Updater.kt
index dad9196..74292ef 100644
--- a/android/app/src/main/java/org/maturita/maturita/update/Updater.kt
+++ b/android/app/src/main/java/org/maturita/maturita/update/Updater.kt
@@ -2,8 +2,11 @@ package org.maturita.maturita.update
import android.app.Application
import android.content.Intent
+import android.net.Uri
+import android.os.Build
import android.os.Handler
import android.os.Looper
+import android.provider.Settings
import androidx.core.content.FileProvider
import org.maturita.maturita.BuildConfig
import java.io.File
@@ -22,6 +25,8 @@ data class UpdateState(
class Updater(private val app: Application) {
private val io = Executors.newSingleThreadExecutor()
private val main = Handler(Looper.getMainLooper())
+ private val repo = BuildConfig.UPDATE_REPO
+ private val branch = BuildConfig.UPDATE_BRANCH
private fun emit(cb: (UpdateState) -> Unit, state: UpdateState) {
main.post { cb(state) }
@@ -35,18 +40,17 @@ class Updater(private val app: Application) {
emit(cb, UpdateState(status = "checking", messageKey = "update_checking", messageArg = null))
io.execute {
try {
- val atom = get("https://github.com/${BuildConfig.UPDATE_REPO}/commits/${BuildConfig.UPDATE_BRANCH}.atom")
- val sha = Regex(""".*?/commit/([0-9a-f]{7,40})""", RegexOption.IGNORE_CASE)
- .find(atom)?.groupValues?.get(1)
- ?: throw IllegalStateException("sha")
- val version = get(
- "https://raw.githubusercontent.com/${BuildConfig.UPDATE_REPO}/$sha/VERSION",
- ).trim()
+ val atom = get("https://github.com/$repo/commits/$branch.atom")
+ val remote = atomMainSha(atom)
+ ?: run {
+ val tip = atomTip(atom) ?: error("sha")
+ get("https://raw.githubusercontent.com/$repo/$tip/VERSION").trim()
+ }
val local = BuildConfig.COMMIT
- if (version.startsWith(local) || local.startsWith(version)) {
- emit(cb, UpdateState("ok", version, "update_uptodate", version.take(7)))
+ if (sameCommit(remote, local)) {
+ emit(cb, UpdateState("ok", remote, "update_uptodate", remote.take(7)))
} else {
- emit(cb, UpdateState("available", version, "update_available", version.take(7), true))
+ emit(cb, UpdateState("available", remote, "update_available", remote.take(7), true))
}
} catch (_: Exception) {
emit(cb, UpdateState("error", messageKey = "update_err_network", messageArg = null))
@@ -58,11 +62,20 @@ class Updater(private val app: Application) {
emit(cb, UpdateState(status = "downloading", messageKey = "update_downloading", messageArg = null))
io.execute {
try {
- val atom = get("https://github.com/${BuildConfig.UPDATE_REPO}/commits/${BuildConfig.UPDATE_BRANCH}.atom")
- val sha = Regex(""".*?/commit/([0-9a-f]{7,40})""", RegexOption.IGNORE_CASE)
- .find(atom)?.groupValues?.get(1)
- ?: throw IllegalStateException("sha")
- val url = "https://raw.githubusercontent.com/${BuildConfig.UPDATE_REPO}/$sha/maturita-android.apk"
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O &&
+ !app.packageManager.canRequestPackageInstalls()
+ ) {
+ val settings = Intent(Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES).apply {
+ data = Uri.parse("package:${app.packageName}")
+ flags = Intent.FLAG_ACTIVITY_NEW_TASK
+ }
+ main.post { app.startActivity(settings) }
+ emit(cb, UpdateState("error", messageKey = "update_err_download", messageArg = null))
+ return@execute
+ }
+ val atom = get("https://github.com/$repo/commits/$branch.atom")
+ val tip = atomTip(atom) ?: error("sha")
+ val url = "https://raw.githubusercontent.com/$repo/$tip/maturita-android.apk"
val dir = File(app.cacheDir, "updates").apply { mkdirs() }
val apk = File(dir, "maturita.apk")
download(url, apk)
@@ -77,13 +90,25 @@ class Updater(private val app: Application) {
flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_GRANT_READ_URI_PERMISSION
}
main.post { app.startActivity(intent) }
- emit(cb, UpdateState("staged", sha, "update_staged", sha.take(7)))
+ emit(cb, UpdateState("staged", tip, "update_staged", tip.take(7)))
} catch (_: Exception) {
emit(cb, UpdateState("error", messageKey = "update_err_download", messageArg = null))
}
}
}
+ private fun atomTip(atom: String): String? =
+ Regex("""Commit/([0-9a-f]{40})""", RegexOption.IGNORE_CASE).find(atom)?.groupValues?.get(1)
+ ?: Regex("""/commit/([0-9a-f]{7,40})""", RegexOption.IGNORE_CASE).find(atom)?.groupValues?.get(1)
+
+ private fun atomMainSha(atom: String): String? =
+ Regex("""Build from ([0-9a-f]{7,40})""", RegexOption.IGNORE_CASE).find(atom)?.groupValues?.get(1)
+
+ private fun sameCommit(a: String, b: String): Boolean {
+ val n = minOf(a.length, b.length)
+ return n >= 7 && a.take(n).equals(b.take(n), ignoreCase = true)
+ }
+
private fun isApk(file: File): Boolean {
if (file.length() < 1_000_000L) return false
file.inputStream().use { input ->
@@ -93,12 +118,18 @@ class Updater(private val app: Application) {
}
}
- private fun get(url: String): String {
- val c = (URL(url).openConnection() as HttpURLConnection).apply {
+ private fun open(url: String, readMs: Int): HttpURLConnection {
+ return (URL(url).openConnection() as HttpURLConnection).apply {
connectTimeout = 15000
- readTimeout = 15000
+ readTimeout = readMs
instanceFollowRedirects = true
+ setRequestProperty("User-Agent", "maturita.c-android")
+ setRequestProperty("Accept", "*/*")
}
+ }
+
+ private fun get(url: String): String {
+ val c = open(url, 15000)
return try {
if (c.responseCode !in 200..299) error("http ${c.responseCode}")
c.inputStream.bufferedReader().use { it.readText() }
@@ -108,11 +139,7 @@ class Updater(private val app: Application) {
}
private fun download(url: String, dest: File) {
- val c = (URL(url).openConnection() as HttpURLConnection).apply {
- connectTimeout = 20000
- readTimeout = 60000
- instanceFollowRedirects = true
- }
+ val c = open(url, 60000)
try {
if (c.responseCode !in 200..299) error("http ${c.responseCode}")
c.inputStream.use { input -> dest.outputStream().use { input.copyTo(it) } }