Recently Written · git

ink-update

Ink Update: tells you when Rebind, Ink Recents or Ink Dim has a new version. F-Droid, then Google Play, then GitHub.

git clone https://github.com/equwal/ink-update

Log | Files | Refs


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 }