summaryrefslogtreecommitdiff
path: root/android-app/app/src/main/kotlin
diff options
context:
space:
mode:
authorPeter Stone <thepeterstone@gmail.com>2026-07-01 06:24:45 +0000
committerPeter Stone <thepeterstone@gmail.com>2026-07-01 06:24:45 +0000
commitc92dcd0d6407e506c84ca8bb79ab395257d7c392 (patch)
tree594ba09d274f094bd5aac967d72d46193b40048b /android-app/app/src/main/kotlin
parent36d94937a561300197ef53aa9096defadfda3e72 (diff)
fix(aton): switch to org.json, add stale-cache fallback
Replace Gson dependency in AtonData with org.json.JSONObject (Android SDK built-in) and add testImplementation for org.json to support unit tests. AtonRepository now falls back to stale cache data when the network is unavailable rather than returning an empty list.
Diffstat (limited to 'android-app/app/src/main/kotlin')
-rw-r--r--android-app/app/src/main/kotlin/org/terst/nav/ui/aton/AtonData.kt29
-rw-r--r--android-app/app/src/main/kotlin/org/terst/nav/ui/aton/AtonRepository.kt24
2 files changed, 26 insertions, 27 deletions
diff --git a/android-app/app/src/main/kotlin/org/terst/nav/ui/aton/AtonData.kt b/android-app/app/src/main/kotlin/org/terst/nav/ui/aton/AtonData.kt
index 2ff59cd..d3a6b6b 100644
--- a/android-app/app/src/main/kotlin/org/terst/nav/ui/aton/AtonData.kt
+++ b/android-app/app/src/main/kotlin/org/terst/nav/ui/aton/AtonData.kt
@@ -1,9 +1,8 @@
package org.terst.nav.ui.aton
-import com.google.gson.JsonObject
-import com.google.gson.JsonParser
import org.maplibre.geojson.Feature
import org.maplibre.geojson.Point
+import org.json.JSONObject
object AtonData {
fun colorFor(type: String): String = when (type) {
@@ -14,24 +13,20 @@ object AtonData {
}
fun parseStation(json: String): Feature? = try {
- val obj = JsonParser.parseString(json).asJsonObject
- val lat = obj.get("lat").asDouble
- val lng = obj.get("lng").asDouble
- val type = obj.get("type")?.asString ?: ""
- val id = obj.get("id")?.asString ?: ""
- val name = obj.get("name")?.asString ?: ""
- val props = JsonObject().apply {
- addProperty("id", id)
- addProperty("name", name)
- addProperty("type", type)
- addProperty("color", colorFor(type))
+ val obj = JSONObject(json)
+ val lat = obj.getDouble("lat")
+ val lng = obj.getDouble("lng")
+ val type = obj.optString("type", "")
+ Feature.fromGeometry(Point.fromLngLat(lng, lat)).apply {
+ addStringProperty("id", obj.optString("id"))
+ addStringProperty("name", obj.optString("name"))
+ addStringProperty("type", type)
+ addStringProperty("color", colorFor(type))
}
- Feature.fromGeometry(Point.fromLngLat(lng, lat), props)
} catch (_: Exception) { null }
fun parseResponse(json: String): List<Feature> = try {
- val arr = JsonParser.parseString(json).asJsonObject
- .getAsJsonArray("stations")
- arr.mapNotNull { parseStation(it.toString()) }
+ val arr = JSONObject(json).getJSONArray("stations")
+ (0 until arr.length()).mapNotNull { parseStation(arr.get(it).toString()) }
} catch (_: Exception) { emptyList() }
}
diff --git a/android-app/app/src/main/kotlin/org/terst/nav/ui/aton/AtonRepository.kt b/android-app/app/src/main/kotlin/org/terst/nav/ui/aton/AtonRepository.kt
index b5472b3..249fdac 100644
--- a/android-app/app/src/main/kotlin/org/terst/nav/ui/aton/AtonRepository.kt
+++ b/android-app/app/src/main/kotlin/org/terst/nav/ui/aton/AtonRepository.kt
@@ -16,18 +16,22 @@ class AtonRepository(private val context: Context) {
"https://api.tidesandcurrents.noaa.gov/mdapi/prod/webapi/stations.json?type=aton&units=english"
suspend fun getFeatures(): List<Feature> = withContext(Dispatchers.IO) {
- val cached = readCache()
- if (cached != null) return@withContext AtonData.parseResponse(cached)
- val fresh = fetchFromApi() ?: return@withContext emptyList()
- cacheFile.writeText(fresh)
- AtonData.parseResponse(fresh)
+ val staleJson = readCacheRaw()
+ if (staleJson != null && isCacheFresh()) return@withContext AtonData.parseResponse(staleJson)
+ val fresh = fetchFromApi()
+ if (fresh != null) {
+ cacheFile.writeText(fresh)
+ return@withContext AtonData.parseResponse(fresh)
+ }
+ // Network unavailable — serve stale data if any, otherwise empty
+ if (staleJson != null) AtonData.parseResponse(staleJson) else emptyList()
}
- private fun readCache(): String? {
- if (!cacheFile.exists()) return null
- if (System.currentTimeMillis() - cacheFile.lastModified() > maxAgeMs) return null
- return cacheFile.readText()
- }
+ private fun isCacheFresh(): Boolean =
+ cacheFile.exists() && System.currentTimeMillis() - cacheFile.lastModified() <= maxAgeMs
+
+ private fun readCacheRaw(): String? =
+ if (cacheFile.exists()) runCatching { cacheFile.readText() }.getOrNull() else null
private fun fetchFromApi(): String? = try {
URL(apiUrl).openStream().bufferedReader().readText()