Add launch stats reporting per callsign
При старте приложения обновляет запись своего позывного во внешнем документе: версия ПО, время последнего запуска и число запусков. Записи других позывных не изменяются.
This commit is contained in:
99
app/src/main/java/ru/ua1zbe/svxremote/StatsReporter.kt
Normal file
99
app/src/main/java/ru/ua1zbe/svxremote/StatsReporter.kt
Normal file
@@ -0,0 +1,99 @@
|
||||
package ru.ua1zbe.svxremote
|
||||
|
||||
import android.content.Context
|
||||
import android.util.Base64
|
||||
import android.util.Log
|
||||
import org.json.JSONObject
|
||||
import ru.ua1zbe.svxremote.prefs.Prefs
|
||||
import java.net.HttpURLConnection
|
||||
import java.net.URL
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.Date
|
||||
import java.util.Locale
|
||||
import java.util.TimeZone
|
||||
|
||||
/**
|
||||
* Учёт запусков по позывному.
|
||||
*
|
||||
* При старте приложения обновляет во внешнем документе запись своего позывного
|
||||
* (версия ПО, время последнего запуска, число запусков). Записи других
|
||||
* позывных не изменяются.
|
||||
*/
|
||||
object StatsReporter {
|
||||
|
||||
private const val TAG = "SVXRemote"
|
||||
private const val API =
|
||||
"https://git2.ua1zbe.ru/api/v1/repos/svxhomyak/svxx/contents/.svxstats.json"
|
||||
private const val TOKEN = "d1ef9a06c6471f009aefd3097772d263c267b008"
|
||||
private const val BRANCH = "main"
|
||||
|
||||
/** Запускает фоновое обновление статистики (не блокирует старт приложения). */
|
||||
fun report(context: Context) {
|
||||
val callsign = Prefs.load(context).callsign.trim().uppercase()
|
||||
if (callsign.isEmpty()) return
|
||||
Thread({
|
||||
repeat(3) { attempt ->
|
||||
if (runCatching { update(callsign) }.getOrDefault(false)) return@Thread
|
||||
Thread.sleep(1000L * (attempt + 1))
|
||||
}
|
||||
}, "svx-stats").apply { isDaemon = true }.start()
|
||||
}
|
||||
|
||||
private fun update(callsign: String): Boolean {
|
||||
var sha: String? = null
|
||||
val root = JSONObject()
|
||||
|
||||
val get = URL(API).openConnection() as HttpURLConnection
|
||||
try {
|
||||
get.setRequestProperty("Authorization", "token $TOKEN")
|
||||
get.connectTimeout = 10_000
|
||||
get.readTimeout = 10_000
|
||||
if (get.responseCode == 200) {
|
||||
val obj = JSONObject(get.inputStream.bufferedReader().readText())
|
||||
sha = obj.optString("sha").ifEmpty { null }
|
||||
val encoded = obj.optString("content", "").replace("\n", "")
|
||||
if (encoded.isNotEmpty()) {
|
||||
val decoded = String(Base64.decode(encoded, Base64.DEFAULT), Charsets.UTF_8)
|
||||
runCatching { JSONObject(decoded) }.getOrNull()?.let { parsed ->
|
||||
for (key in parsed.keys()) root.put(key, parsed.get(key))
|
||||
}
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
get.disconnect()
|
||||
}
|
||||
|
||||
val now = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'", Locale.US).apply {
|
||||
timeZone = TimeZone.getTimeZone("UTC")
|
||||
}.format(Date())
|
||||
val launches = (root.optJSONObject(callsign)?.optInt("launches", 0) ?: 0) + 1
|
||||
root.put(callsign, JSONObject().apply {
|
||||
put("callsign", callsign)
|
||||
put("version", BuildConfig.VERSION_NAME)
|
||||
put("lastLaunch", now)
|
||||
put("launches", launches)
|
||||
})
|
||||
|
||||
val body = JSONObject().apply {
|
||||
put("content", Base64.encodeToString(root.toString().toByteArray(), Base64.NO_WRAP))
|
||||
put("message", "stats")
|
||||
put("branch", BRANCH)
|
||||
if (sha != null) put("sha", sha)
|
||||
}
|
||||
val put = URL(API).openConnection() as HttpURLConnection
|
||||
return try {
|
||||
put.requestMethod = "PUT"
|
||||
put.doOutput = true
|
||||
put.setRequestProperty("Authorization", "token $TOKEN")
|
||||
put.setRequestProperty("Content-Type", "application/json")
|
||||
put.connectTimeout = 10_000
|
||||
put.readTimeout = 10_000
|
||||
put.outputStream.use { it.write(body.toString().toByteArray()) }
|
||||
val code = put.responseCode
|
||||
Log.i(TAG, "stats $callsign launches=$launches http=$code")
|
||||
code in 200..299
|
||||
} finally {
|
||||
put.disconnect()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -17,6 +17,7 @@ class SvxApp : Application() {
|
||||
super.onCreate()
|
||||
createPushChannel()
|
||||
initRuStorePush()
|
||||
StatsReporter.report(this)
|
||||
}
|
||||
|
||||
private fun createPushChannel() {
|
||||
|
||||
Reference in New Issue
Block a user