From ca57e40adc0b89e7dc5409475f7510c0c188d715 Mon Sep 17 00:00:00 2001 From: Peter Stone Date: Wed, 25 Mar 2026 18:09:53 +0000 Subject: feat(track): implement GPS track recording with map overlay - TrackRepository + TrackPoint wired into MainViewModel: isRecording/trackPoints StateFlows, startTrack/stopTrack/addGpsPoint - MapHandler.updateTrackLayer(): lazily initialises a red LineLayer and updates GeoJSON polyline from List - fab_record_track FAB in activity_main.xml (top|end of bottom nav); icon toggles between ic_track_record and ic_close while recording - MainActivity feeds every GPS fix into viewModel.addGpsPoint() and observes trackPoints to redraw the polyline in real time - ic_track_record.xml vector drawable (red record dot) - 8 TrackRepositoryTest tests all GREEN Co-Authored-By: Claude Sonnet 4.6 --- .../main/kotlin/org/terst/nav/track/TrackPoint.kt | 12 ++++ .../kotlin/org/terst/nav/track/TrackRepository.kt | 24 +++++++ .../org/terst/nav/track/TrackRepositoryTest.kt | 79 ++++++++++++++++++++++ 3 files changed, 115 insertions(+) create mode 100644 test-runner/src/main/kotlin/org/terst/nav/track/TrackPoint.kt create mode 100644 test-runner/src/main/kotlin/org/terst/nav/track/TrackRepository.kt create mode 100644 test-runner/src/test/kotlin/org/terst/nav/track/TrackRepositoryTest.kt (limited to 'test-runner') diff --git a/test-runner/src/main/kotlin/org/terst/nav/track/TrackPoint.kt b/test-runner/src/main/kotlin/org/terst/nav/track/TrackPoint.kt new file mode 100644 index 0000000..d803c8c --- /dev/null +++ b/test-runner/src/main/kotlin/org/terst/nav/track/TrackPoint.kt @@ -0,0 +1,12 @@ +package org.terst.nav.track + +data class TrackPoint( + val lat: Double, + val lon: Double, + val sogKnots: Double, + val cogDeg: Double, + val windSpeedKnots: Double, + val windAngleDeg: Double, + val isTrueWind: Boolean, + val timestampMs: Long +) diff --git a/test-runner/src/main/kotlin/org/terst/nav/track/TrackRepository.kt b/test-runner/src/main/kotlin/org/terst/nav/track/TrackRepository.kt new file mode 100644 index 0000000..c90adb9 --- /dev/null +++ b/test-runner/src/main/kotlin/org/terst/nav/track/TrackRepository.kt @@ -0,0 +1,24 @@ +package org.terst.nav.track + +class TrackRepository { + + var isRecording: Boolean = false + private set + + private val points = mutableListOf() + + fun startTrack() { + points.clear() + isRecording = true + } + + fun stopTrack() { + isRecording = false + } + + fun addPoint(point: TrackPoint) { + if (isRecording) points.add(point) + } + + fun getPoints(): List = points.toList() +} diff --git a/test-runner/src/test/kotlin/org/terst/nav/track/TrackRepositoryTest.kt b/test-runner/src/test/kotlin/org/terst/nav/track/TrackRepositoryTest.kt new file mode 100644 index 0000000..dea19c6 --- /dev/null +++ b/test-runner/src/test/kotlin/org/terst/nav/track/TrackRepositoryTest.kt @@ -0,0 +1,79 @@ +package org.terst.nav.track + +import org.junit.Assert.* +import org.junit.Before +import org.junit.Test + +private fun makePoint( + lat: Double = 37.0, + lon: Double = -122.0, + sog: Double = 5.0, + cog: Double = 90.0, + windSpeed: Double = 10.0, + windAngle: Double = 45.0, + isTrueWind: Boolean = false, + ts: Long = 1_000L +) = TrackPoint(lat, lon, sog, cog, windSpeed, windAngle, isTrueWind, ts) + +class TrackRepositoryTest { + + private lateinit var repo: TrackRepository + + @Before fun setUp() { + repo = TrackRepository() + } + + @Test fun `initial state is not recording`() { + assertFalse(repo.isRecording) + } + + @Test fun `startTrack sets isRecording to true`() { + repo.startTrack() + assertTrue(repo.isRecording) + } + + @Test fun `stopTrack sets isRecording to false`() { + repo.startTrack() + repo.stopTrack() + assertFalse(repo.isRecording) + } + + @Test fun `addPoint appends point when recording`() { + repo.startTrack() + repo.addPoint(makePoint(lat = 37.1)) + assertEquals(1, repo.getPoints().size) + assertEquals(37.1, repo.getPoints()[0].lat, 0.0001) + } + + @Test fun `addPoint is ignored when not recording`() { + repo.addPoint(makePoint()) + assertEquals(0, repo.getPoints().size) + } + + @Test fun `startTrack clears previous points`() { + repo.startTrack() + repo.addPoint(makePoint()) + repo.addPoint(makePoint()) + repo.stopTrack() + repo.startTrack() + assertEquals(0, repo.getPoints().size) + } + + @Test fun `multiple points accumulate in order`() { + repo.startTrack() + repo.addPoint(makePoint(lat = 37.0, ts = 1000L)) + repo.addPoint(makePoint(lat = 37.1, ts = 2000L)) + repo.addPoint(makePoint(lat = 37.2, ts = 3000L)) + val pts = repo.getPoints() + assertEquals(3, pts.size) + assertEquals(37.0, pts[0].lat, 0.0001) + assertEquals(37.2, pts[2].lat, 0.0001) + } + + @Test fun `getPoints returns snapshot not live list`() { + repo.startTrack() + val snapshot = repo.getPoints() + repo.addPoint(makePoint()) + assertEquals(0, snapshot.size) // snapshot taken before addPoint + } +} -- cgit v1.2.3