Misc fixes for GitHub update check
This commit is contained in:
@@ -5,11 +5,10 @@ import android.net.Uri
|
|||||||
import androidx.core.content.edit
|
import androidx.core.content.edit
|
||||||
import be.mygod.vpnhotspot.App.Companion.app
|
import be.mygod.vpnhotspot.App.Companion.app
|
||||||
import be.mygod.vpnhotspot.BuildConfig
|
import be.mygod.vpnhotspot.BuildConfig
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.*
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.flow.cancellable
|
||||||
import kotlinx.coroutines.flow.flow
|
import kotlinx.coroutines.flow.flow
|
||||||
import kotlinx.coroutines.withContext
|
import org.json.JSONArray
|
||||||
import org.json.JSONObject
|
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import java.net.HttpURLConnection
|
import java.net.HttpURLConnection
|
||||||
import java.net.URL
|
import java.net.URL
|
||||||
@@ -24,7 +23,7 @@ object UpdateChecker {
|
|||||||
private const val KEY_PUBLISHED = "update.published"
|
private const val KEY_PUBLISHED = "update.published"
|
||||||
private const val UPDATE_INTERVAL = 1000 * 60 * 60 * 6
|
private const val UPDATE_INTERVAL = 1000 * 60 * 60 * 6
|
||||||
|
|
||||||
private class GitHubUpdate(override val message: String, private val published: Long) : AppUpdate {
|
private data class GitHubUpdate(override val message: String, val published: Long) : AppUpdate {
|
||||||
override val stalenessDays get() = max(0,
|
override val stalenessDays get() = max(0,
|
||||||
TimeUnit.DAYS.convert(System.currentTimeMillis() - published, TimeUnit.MILLISECONDS)).toInt()
|
TimeUnit.DAYS.convert(System.currentTimeMillis() - published, TimeUnit.MILLISECONDS)).toInt()
|
||||||
|
|
||||||
@@ -33,6 +32,36 @@ object UpdateChecker {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private data class SemVer(val major: Int, val minor: Int, val revision: Int) : Comparable<SemVer> {
|
||||||
|
override fun compareTo(other: SemVer): Int {
|
||||||
|
var result = major - other.major
|
||||||
|
if (result != 0) return result
|
||||||
|
result = minor - other.minor
|
||||||
|
if (result != 0) return result
|
||||||
|
return revision - other.revision
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private val semverParser = "^v?(\\d+)\\.(\\d+)\\.(\\d+)(?:-|$)".toPattern()
|
||||||
|
private fun CharSequence.toSemVer() = semverParser.matcher(this).let { matcher ->
|
||||||
|
require(matcher.find()) { "Unrecognized version $this" }
|
||||||
|
SemVer(matcher.group(1)!!.toInt(), matcher.group(2)!!.toInt(), matcher.group(3)!!.toInt())
|
||||||
|
}
|
||||||
|
private val myVer by lazy { BuildConfig.VERSION_NAME.toSemVer() }
|
||||||
|
|
||||||
|
private fun findUpdate(response: JSONArray): GitHubUpdate? {
|
||||||
|
for (i in 0 until response.length()) {
|
||||||
|
val obj = response.getJSONObject(i)
|
||||||
|
val name = obj.getString("name")
|
||||||
|
val isNew = try {
|
||||||
|
name.toSemVer() > myVer
|
||||||
|
} catch (e: IllegalArgumentException) {
|
||||||
|
Timber.w(e)
|
||||||
|
false
|
||||||
|
}
|
||||||
|
if (isNew) return GitHubUpdate(name, Instant.parse(obj.getString("published_at")).toEpochMilli())
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
fun check() = flow<AppUpdate?> {
|
fun check() = flow<AppUpdate?> {
|
||||||
val myVersion = "v${BuildConfig.VERSION_NAME}"
|
val myVersion = "v${BuildConfig.VERSION_NAME}"
|
||||||
emit(app.pref.getString(KEY_VERSION, null)?.let {
|
emit(app.pref.getString(KEY_VERSION, null)?.let {
|
||||||
@@ -42,28 +71,30 @@ object UpdateChecker {
|
|||||||
val now = System.currentTimeMillis()
|
val now = System.currentTimeMillis()
|
||||||
val lastFetched = app.pref.getLong(KEY_LAST_FETCHED, -1)
|
val lastFetched = app.pref.getLong(KEY_LAST_FETCHED, -1)
|
||||||
if (lastFetched in 0..now) delay(lastFetched + UPDATE_INTERVAL - now)
|
if (lastFetched in 0..now) delay(lastFetched + UPDATE_INTERVAL - now)
|
||||||
val conn = URL("https://api.github.com/repos/Mygod/VPNHotspot/releases/latest")
|
currentCoroutineContext().ensureActive()
|
||||||
|
val conn = URL("https://api.github.com/repos/Mygod/VPNHotspot/releases?per_page=100")
|
||||||
.openConnection() as HttpURLConnection
|
.openConnection() as HttpURLConnection
|
||||||
|
app.pref.edit {
|
||||||
try {
|
try {
|
||||||
conn.setRequestProperty("Accept", "application/vnd.github.v3+json")
|
conn.setRequestProperty("Accept", "application/vnd.github.v3+json")
|
||||||
val response = JSONObject(withContext(Dispatchers.IO) {
|
val update = findUpdate(JSONArray(withContext(Dispatchers.IO) {
|
||||||
conn.inputStream.bufferedReader().readText()
|
conn.inputStream.bufferedReader().readText()
|
||||||
|
}))
|
||||||
|
putLong(KEY_PUBLISHED, if (update == null) -1 else {
|
||||||
|
putString(KEY_VERSION, update.message)
|
||||||
|
update.published
|
||||||
})
|
})
|
||||||
val version = response.getString("tag_name")
|
emit(update)
|
||||||
val published = Instant.parse(response.getString("published_at")).toEpochMilli()
|
|
||||||
app.pref.edit {
|
|
||||||
putLong(KEY_LAST_FETCHED, System.currentTimeMillis())
|
|
||||||
putString(KEY_VERSION, version)
|
|
||||||
putLong(KEY_PUBLISHED, published)
|
|
||||||
}
|
|
||||||
emit(if (myVersion == version) null else GitHubUpdate(version, published))
|
|
||||||
} catch (_: CancellationException) {
|
} catch (_: CancellationException) {
|
||||||
return@flow
|
return@flow
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
Timber.w(e)
|
Timber.w(e)
|
||||||
} finally {
|
} finally {
|
||||||
conn.disconnectCompat()
|
conn.disconnectCompat()
|
||||||
|
putLong(KEY_LAST_FETCHED, System.currentTimeMillis())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
delay(System.currentTimeMillis() - (conn.getHeaderField("X-RateLimit-Reset")?.toLongOrNull() ?: 0) * 1000)
|
||||||
}
|
}
|
||||||
|
}.cancellable()
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user