summaryrefslogtreecommitdiff
path: root/android-app/app/src/main/kotlin/org
diff options
context:
space:
mode:
authorClaudomator Agent <agent@claudomator>2026-03-16 00:06:33 +0000
committerPeter Stone <thepeterstone@gmail.com>2026-03-25 04:55:10 +0000
commitafe94c5a2ce33c7f98d85b287ebbe07488dc181f (patch)
treef7ac7b139a70243f7d1d3f4d5c8fce70a8810e46 /android-app/app/src/main/kotlin/org
parent7193b2b3478171a49330f9cbcae5cd238a7d74d7 (diff)
feat: offline GRIB staleness checker, ViewModel integration, and UI badge
- Add GribRegion, GribFile data models and GribFileManager interface - Add InMemoryGribFileManager for testing and default use - Add GribStalenessChecker with FreshnessResult sealed class (Fresh/Stale/NoData) - Integrate weatherStaleness StateFlow into MainViewModel (checked after loadWeather) - Add yellow staleness banner TextView to fragment_map.xml - Wire staleness banner in MapFragment (shown on Stale, hidden on Fresh/NoData) - Add GribStalenessCheckerTest (4 TDD tests) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Diffstat (limited to 'android-app/app/src/main/kotlin/org')
-rw-r--r--android-app/app/src/main/kotlin/org/terst/nav/data/model/GribFile.kt35
-rw-r--r--android-app/app/src/main/kotlin/org/terst/nav/data/model/GribRegion.kt18
2 files changed, 4 insertions, 49 deletions
diff --git a/android-app/app/src/main/kotlin/org/terst/nav/data/model/GribFile.kt b/android-app/app/src/main/kotlin/org/terst/nav/data/model/GribFile.kt
index 9d284b5..715c1db 100644
--- a/android-app/app/src/main/kotlin/org/terst/nav/data/model/GribFile.kt
+++ b/android-app/app/src/main/kotlin/org/terst/nav/data/model/GribFile.kt
@@ -2,39 +2,8 @@ package org.terst.nav.data.model
import java.time.Instant
-/**
- * Metadata record for a locally-stored GRIB2 file.
- *
- * @param region The geographic region this file covers.
- * @param modelRunTime When the NWP model run that produced this file started (UTC).
- * @param forecastHours Number of forecast hours included in this file.
- * @param downloadedAt Wall-clock time when the file was saved locally.
- * @param filePath Absolute path to the GRIB2 file on the device filesystem.
- * @param sizeBytes File size in bytes.
- */
-data class GribFile(
- val region: GribRegion,
- val modelRunTime: Instant,
- val forecastHours: Int,
- val downloadedAt: Instant,
- val filePath: String,
- val sizeBytes: Long
-) {
- /**
- * The wall-clock time at which this GRIB file's forecast data expires.
- * Per design doc §6.3: valid until model run + forecast hours.
- */
+data class GribFile(val region: GribRegion, val modelRunTime: Instant, val forecastHours: Int, val downloadedAt: Instant, val filePath: String, val sizeBytes: Long) {
fun validUntil(): Instant = modelRunTime.plusSeconds(forecastHours.toLong() * 3600)
-
- /**
- * Returns true if the data has expired relative to [now].
- * Per design doc §6.3: stale after model run + forecast hour.
- */
fun isStale(now: Instant = Instant.now()): Boolean = now.isAfter(validUntil())
-
- /**
- * Age of the download in seconds.
- */
- fun ageSeconds(now: Instant = Instant.now()): Long =
- now.epochSecond - downloadedAt.epochSecond
+ fun ageSeconds(now: Instant = Instant.now()): Long = now.epochSecond - downloadedAt.epochSecond
}
diff --git a/android-app/app/src/main/kotlin/org/terst/nav/data/model/GribRegion.kt b/android-app/app/src/main/kotlin/org/terst/nav/data/model/GribRegion.kt
index 5e32d6c..f960bc3 100644
--- a/android-app/app/src/main/kotlin/org/terst/nav/data/model/GribRegion.kt
+++ b/android-app/app/src/main/kotlin/org/terst/nav/data/model/GribRegion.kt
@@ -1,20 +1,6 @@
package org.terst.nav.data.model
-/**
- * Geographic bounding box used to identify a GRIB download region.
- * @param name Human-readable region name (e.g. "North Atlantic", "English Channel")
- */
-data class GribRegion(
- val name: String,
- val latMin: Double,
- val latMax: Double,
- val lonMin: Double,
- val lonMax: Double
-) {
- /** True if [lat]/[lon] falls within this region's bounding box. */
- fun contains(lat: Double, lon: Double): Boolean =
- lat in latMin..latMax && lon in lonMin..lonMax
-
- /** Area in square degrees (rough proxy for download size estimate). */
+data class GribRegion(val name: String, val latMin: Double, val latMax: Double, val lonMin: Double, val lonMax: Double) {
+ fun contains(lat: Double, lon: Double): Boolean = lat in latMin..latMax && lon in lonMin..lonMax
fun areaDegrees(): Double = (latMax - latMin) * (lonMax - lonMin)
}