summaryrefslogtreecommitdiff
path: root/android-app/app/src/main
diff options
context:
space:
mode:
authorClaudomator Agent <agent@claudomator>2026-03-14 01:47:20 +0000
committerClaudomator Agent <agent@claudomator>2026-03-14 01:47:20 +0000
commit010d25c3e7e37ba109117a93e4d1c0f8802b01a9 (patch)
tree13240873666277df79eab89d71643ee9f00d7158 /android-app/app/src/main
parent3f18f770e9d33c5e5d0657c6160fa8f30b21831f (diff)
Add GpsProvider interface and DeviceGpsProvider (FusedLocation)
- GpsPosition: lat/lon/sog (knots)/cog (degrees true)/timestampMs - GpsProvider + GpsListener interfaces for provider abstraction - DeviceGpsProvider wraps LocationManager GPS_PROVIDER (1 Hz default) SOG: m/s × 1.94384 knots; fix-lost timeout 10s - FakeGpsProvider + 9 passing JVM unit tests (no Android deps) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Diffstat (limited to 'android-app/app/src/main')
-rw-r--r--android-app/app/src/main/kotlin/org/terst/nav/gps/DeviceGpsProvider.kt87
-rw-r--r--android-app/app/src/main/kotlin/org/terst/nav/gps/GpsPosition.kt9
-rw-r--r--android-app/app/src/main/kotlin/org/terst/nav/gps/GpsProvider.kt14
3 files changed, 110 insertions, 0 deletions
diff --git a/android-app/app/src/main/kotlin/org/terst/nav/gps/DeviceGpsProvider.kt b/android-app/app/src/main/kotlin/org/terst/nav/gps/DeviceGpsProvider.kt
new file mode 100644
index 0000000..f2a4e59
--- /dev/null
+++ b/android-app/app/src/main/kotlin/org/terst/nav/gps/DeviceGpsProvider.kt
@@ -0,0 +1,87 @@
+package org.terst.nav.gps
+
+import android.annotation.SuppressLint
+import android.content.Context
+import android.location.Location
+import android.location.LocationListener
+import android.location.LocationManager
+import android.os.Handler
+import android.os.Looper
+
+/**
+ * GPS provider backed by Android's LocationManager with GPS_PROVIDER.
+ *
+ * @param context Android context (application or activity)
+ * @param updateIntervalMs Location update interval in ms (default 1000 = 1 Hz)
+ */
+class DeviceGpsProvider(
+ private val context: Context,
+ private val updateIntervalMs: Long = 1000L
+) : GpsProvider {
+
+ private val locationManager: LocationManager =
+ context.getSystemService(Context.LOCATION_SERVICE) as LocationManager
+
+ private val listeners = mutableListOf<GpsListener>()
+ private val lock = Any()
+
+ @Volatile override var position: GpsPosition? = null
+ private set
+
+ private val fixLostHandler = Handler(Looper.getMainLooper())
+ private val fixLostRunnable = Runnable {
+ synchronized(lock) { listeners.toList() }.forEach { it.onFixLost() }
+ }
+
+ private val locationListener = object : LocationListener {
+ override fun onLocationChanged(location: Location) {
+ val pos = GpsPosition(
+ latitude = location.latitude,
+ longitude = location.longitude,
+ sog = location.speed * 1.94384, // m/s → knots
+ cog = location.bearing.toDouble(), // degrees true
+ timestampMs = location.time
+ )
+ position = pos
+ rescheduleFixLostTimer()
+ synchronized(lock) { listeners.toList() }.forEach { it.onPositionUpdate(pos) }
+ }
+
+ @Deprecated("Deprecated in API level 29")
+ override fun onStatusChanged(provider: String?, status: Int, extras: android.os.Bundle?) = Unit
+ }
+
+ @SuppressLint("MissingPermission")
+ override fun start() {
+ locationManager.requestLocationUpdates(
+ LocationManager.GPS_PROVIDER,
+ updateIntervalMs,
+ 0f,
+ locationListener,
+ Looper.getMainLooper()
+ )
+ rescheduleFixLostTimer()
+ }
+
+ override fun stop() {
+ locationManager.removeUpdates(locationListener)
+ fixLostHandler.removeCallbacks(fixLostRunnable)
+ }
+
+ override fun addListener(listener: GpsListener) {
+ synchronized(lock) { listeners.add(listener) }
+ }
+
+ override fun removeListener(listener: GpsListener) {
+ synchronized(lock) { listeners.remove(listener) }
+ }
+
+ private fun rescheduleFixLostTimer() {
+ fixLostHandler.removeCallbacks(fixLostRunnable)
+ fixLostHandler.postDelayed(fixLostRunnable, FIX_LOST_TIMEOUT_MS)
+ }
+
+ companion object {
+ private const val FIX_LOST_TIMEOUT_MS = 10_000L
+ }
+}
diff --git a/android-app/app/src/main/kotlin/org/terst/nav/gps/GpsPosition.kt b/android-app/app/src/main/kotlin/org/terst/nav/gps/GpsPosition.kt
new file mode 100644
index 0000000..5faf30c
--- /dev/null
+++ b/android-app/app/src/main/kotlin/org/terst/nav/gps/GpsPosition.kt
@@ -0,0 +1,9 @@
+package org.terst.nav.gps
+
+data class GpsPosition(
+ val latitude: Double,
+ val longitude: Double,
+ val sog: Double, // knots
+ val cog: Double, // degrees true
+ val timestampMs: Long
+)
diff --git a/android-app/app/src/main/kotlin/org/terst/nav/gps/GpsProvider.kt b/android-app/app/src/main/kotlin/org/terst/nav/gps/GpsProvider.kt
new file mode 100644
index 0000000..3c3d634
--- /dev/null
+++ b/android-app/app/src/main/kotlin/org/terst/nav/gps/GpsProvider.kt
@@ -0,0 +1,14 @@
+package org.terst.nav.gps
+
+interface GpsProvider {
+ fun start()
+ fun stop()
+ val position: GpsPosition?
+ fun addListener(listener: GpsListener)
+ fun removeListener(listener: GpsListener)
+}
+
+interface GpsListener {
+ fun onPositionUpdate(position: GpsPosition)
+ fun onFixLost()
+}