app/src/main/java/dev/equwal/inkupdate/Store.kt (4165 bytes)
1 // SPDX-License-Identifier: GPL-3.0-or-later 2 // Copyright (c) 2026 equwal 3 package dev.equwal.inkupdate 4 5 import android.content.Context 6 import org.json.JSONObject 7 8 /** 9 * What the app remembers between checks: the last result for each app, the 10 * time of the last check, the daily switch, and the version of the last 11 * notification. 12 * 13 * The results are one JSON object in the shared preferences. A failed check 14 * keeps the last good result, so a bad network does not hide a known update. 15 */ 16 class Store(context: Context) { 17 18 private val prefs = context.applicationContext 19 .getSharedPreferences("inkupdate", Context.MODE_PRIVATE) 20 21 // ---- the daily job ----------------------------------------------------- 22 23 /** True while the daily check is on. It is on until the person turns it off. */ 24 var daily: Boolean 25 get() = prefs.getBoolean(DAILY, true) 26 set(v) = prefs.edit().putBoolean(DAILY, v).apply() 27 28 /** True after the first start did its work. */ 29 var started: Boolean 30 get() = prefs.getBoolean(STARTED, false) 31 set(v) = prefs.edit().putBoolean(STARTED, v).apply() 32 33 // ---- the last check ---------------------------------------------------- 34 35 /** When the last check ran, in milliseconds. 0 means never. */ 36 val checkedAt: Long get() = prefs.getLong(CHECKED_AT, 0L) 37 38 /** True when every request of the last check gave an answer. */ 39 val lastOk: Boolean get() = prefs.getBoolean(LAST_OK, true) 40 41 fun checked(at: Long, ok: Boolean) { 42 prefs.edit().putLong(CHECKED_AT, at).putBoolean(LAST_OK, ok).apply() 43 } 44 45 // ---- the results ------------------------------------------------------- 46 47 /** 48 * Keeps the result of one app. A [Result.Unknown] does not replace a result 49 * that the app already has. 50 */ 51 fun put(pkg: String, r: Result) { 52 if (r is Result.Unknown && result(pkg) != null) return 53 val all = results() 54 all.put(pkg, write(r)) 55 prefs.edit().putString(RESULTS, all.toString()).apply() 56 } 57 58 /** The last result of one app, or null when there is none. */ 59 fun result(pkg: String): Result? { 60 val o = results().optJSONObject(pkg) ?: return null 61 return read(o) 62 } 63 64 // ---- what the app told the person -------------------------------------- 65 66 /** The version of the last notification for one app. */ 67 fun notified(pkg: String): String? = prefs.getString(NOTIFIED + pkg, null) 68 69 fun setNotified(pkg: String, versionName: String) { 70 prefs.edit().putString(NOTIFIED + pkg, versionName).apply() 71 } 72 73 // ---- JSON -------------------------------------------------------------- 74 75 private fun results(): JSONObject = try { 76 JSONObject(prefs.getString(RESULTS, "{}") ?: "{}") 77 } catch (e: Exception) { 78 JSONObject() 79 } 80 81 private fun write(r: Result): JSONObject = JSONObject().apply { 82 when (r) { 83 is Result.Update -> { 84 put(KIND, "update") 85 put("source", r.source) 86 put("version", r.versionName) 87 put("page", r.page) 88 if (r.apk != null) put("apk", r.apk) 89 } 90 is Result.UpToDate -> put(KIND, "ok") 91 is Result.LeftToPlay -> put(KIND, "play") 92 is Result.Unknown -> { 93 put(KIND, "unknown") 94 put("reason", r.reason) 95 } 96 } 97 } 98 99 private fun read(o: JSONObject): Result? = when (o.optString(KIND)) { 100 "update" -> Result.Update( 101 o.optString("source"), 102 o.optString("version"), 103 o.optString("page"), 104 if (o.has("apk")) o.optString("apk") else null 105 ) 106 "ok" -> Result.UpToDate 107 "play" -> Result.LeftToPlay 108 "unknown" -> Result.Unknown(o.optString("reason")) 109 else -> null 110 } 111 112 private companion object { 113 const val DAILY = "daily" 114 const val STARTED = "started" 115 const val CHECKED_AT = "checkedAt" 116 const val LAST_OK = "lastOk" 117 const val RESULTS = "results" 118 const val NOTIFIED = "notified." 119 const val KIND = "kind" 120 } 121 }