Files
Escape-penacony-standalone/app/src/main/java/com/catlas/escapepenacony/data/AppUpdateManager.kt
T

164 lines
6.2 KiB
Kotlin

package com.catlas.escapepenacony.data
import android.content.Context
import android.content.Intent
import android.net.Uri
import android.os.Build
import android.os.Environment
import android.provider.Settings
import androidx.core.content.FileProvider
import com.catlas.escapepenacony.BuildConfig
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import org.json.JSONObject
import java.io.File
import java.io.FileOutputStream
import java.net.HttpURLConnection
import java.net.URL
data class AppReleaseInfo(
val tagName: String,
val releaseTitle: String,
val changelog: String,
val apkDownloadUrl: String,
val isNewer: Boolean
)
object AppUpdateManager {
private val GITHUB_ENDPOINTS = listOf(
"https://api.github.com/repos/CAtlas0784/Escape-penacony-standalone/releases/latest",
"https://api.github.com/repos/CAtlas0784/Escape-penacony/releases/latest"
)
suspend fun checkForAppUpdate(currentVersion: String = BuildConfig.VERSION_NAME): Result<AppReleaseInfo?> =
withContext(Dispatchers.IO) {
for (endpoint in GITHUB_ENDPOINTS) {
try {
val url = URL(endpoint)
val conn = (url.openConnection() as HttpURLConnection).apply {
connectTimeout = 8000
readTimeout = 8000
setRequestProperty("Accept", "application/vnd.github.v3+json")
setRequestProperty("User-Agent", "Escape-Penacony-Android-App")
}
if (conn.responseCode == 200) {
val body = conn.inputStream.bufferedReader().use { it.readText() }
val json = JSONObject(body)
val tagName = json.getString("tag_name")
val title = json.optString("name", tagName)
val changelog = json.optString("body", "")
var downloadUrl = ""
val assets = json.optJSONArray("assets")
if (assets != null) {
for (i in 0 until assets.length()) {
val asset = assets.getJSONObject(i)
val name = asset.optString("name", "")
if (name.endsWith(".apk", ignoreCase = true)) {
downloadUrl = asset.getString("browser_download_url")
break
}
}
}
val isNewer = isNewerVersion(currentVersion, tagName)
return@withContext Result.success(
AppReleaseInfo(
tagName = tagName,
releaseTitle = title,
changelog = changelog,
apkDownloadUrl = downloadUrl,
isNewer = isNewer
)
)
}
} catch (e: Exception) {
// Try next endpoint
}
}
Result.success(null)
}
suspend fun downloadApk(
context: Context,
downloadUrl: String,
onProgress: (Int) -> Unit
): Result<File> = withContext(Dispatchers.IO) {
try {
val url = URL(downloadUrl)
val conn = (url.openConnection() as HttpURLConnection).apply {
connectTimeout = 15000
readTimeout = 15000
setRequestProperty("User-Agent", "Escape-Penacony-Android-App")
}
val fileLength = conn.contentLength
val dir = context.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS) ?: context.filesDir
val apkFile = File(dir, "Escape-penacony-update.apk")
conn.inputStream.use { input ->
FileOutputStream(apkFile).use { output ->
val buffer = ByteArray(8 * 1024)
var total: Long = 0
var count: Int
while (input.read(buffer).also { count = it } != -1) {
total += count
if (fileLength > 0) {
val progress = ((total * 100) / fileLength).toInt()
withContext(Dispatchers.Main) {
onProgress(progress)
}
}
output.write(buffer, 0, count)
}
}
}
Result.success(apkFile)
} catch (e: Exception) {
Result.failure(e)
}
}
fun promptInstallApk(context: Context, apkFile: File) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
if (!context.packageManager.canRequestPackageInstalls()) {
val settingsIntent = Intent(Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES).apply {
data = Uri.parse("package:${context.packageName}")
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
}
context.startActivity(settingsIntent)
return
}
}
val apkUri: Uri = FileProvider.getUriForFile(
context,
"${context.packageName}.fileprovider",
apkFile
)
val installIntent = Intent(Intent.ACTION_VIEW).apply {
setDataAndType(apkUri, "application/vnd.android.package-archive")
addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
}
context.startActivity(installIntent)
}
private fun isNewerVersion(current: String, latest: String): Boolean {
val c = current.removePrefix("v").split(".").map { it.toIntOrNull() ?: 0 }
val l = latest.removePrefix("v").split(".").map { it.toIntOrNull() ?: 0 }
val maxLen = maxOf(c.size, l.size)
for (i in 0 until maxLen) {
val cv = c.getOrElse(i) { 0 }
val lv = l.getOrElse(i) { 0 }
if (lv > cv) return true
if (lv < cv) return false
}
return false
}
}