summaryrefslogtreecommitdiff
path: root/android-app/app/src/main
AgeCommit message (Collapse)Author
2026-04-04fix(ui): resolve MainActivity crash in smoke tests by reordering ↵Peter Stone
initialization and guarding MapLibre - Ensure lateinit UI properties are assigned before setupMap() and setupHandlers() - Skip MapLibre initialization and lifecycle calls when NavApplication.isTesting is true to avoid emulator Vulkan crashes - Guard MapView lifecycle calls in onResume, onStart, etc. Co-Authored-By: Gemini CLI <gemini-cli@google.com>
2026-04-03feat(instruments): add forecast wind, wave, swell and current from Open-MeteoPeter Stone
- Add swell params to MarineApiService request - Add swell fields to MarineHourly model - Add MarineConditions snapshot model - Add WeatherRepository.fetchCurrentConditions() (first forecast hour) - Add MainViewModel.loadConditions() + marineConditions StateFlow - Add Forecast section to instrument sheet: Curr / Wave / Swell - Populate TWS from forecast wind speed on first GPS fix - Trigger loadConditions() once on first GPS position received Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-03feat(instruments): replace simulation with real GPS and barometer dataPeter Stone
- Drop VMG, Polar %, and PolarDiagramView — no NMEA source on boat - Shrink grid to 3×2 (AWS/HDG/BSP / TWS/COG/SOG) - Move Depth + Baro to expanded section side by side - Initialize all instruments to "—" on startup - Wire LocationService.locationFlow → SOG + COG display (real GPS) - Wire LocationService.barometerStatus → Baro display (device sensor) - Delete startInstrumentSimulation() fake loop entirely Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-02feat(map): interactive map with auto-follow, recenter button, and UI ↵Peter Stone
immersive mode (#2) * docs: add map interaction design spec Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * docs: add map interaction implementation plan Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * feat(map): add isFollowing state and gesture-driven manual mode to MapHandler * feat(ui): add fab_recenter pill button to map layout * feat(map): wire UI fade-out and recenter button to MapHandler.isFollowing * fix(map): prevent fadeIn flash on cold start; consolidate fab_mob listener * fix(map): preserve user zoom level on recenter Capture the current camera zoom when the user gestures (entering manual mode) and pass it back to centerOnLocation in recenter(), so tapping Recenter returns to the user's chosen zoom rather than always snapping to the default 14. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(map): capture lastZoom on camera idle, not gesture start OnCameraMoveStartedListener fires before the gesture completes, so it captured the pre-gesture zoom. OnCameraIdleListener fires after the camera settles, giving the user's final intended zoom level. Only update lastZoom while in manual mode (isFollowing=false). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(map): guard recenter against null island and add KDoc - Skip recenter() if no GPS fix received (lastLat/lastLon still 0.0) to avoid animating to 0°N, 0°E - Add KDoc comment to recenter() consistent with other public methods Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-03fix(ui): raise FAB elevation above CardView sheet to fix z-orderPeter Stone
The instrument sheet CardView has cardElevation=16dp which was rendering on top of the FABs (default ~6dp elevation). Set app:elevation=20dp on both FABs so they always appear above the sheet. Also reverts oversized marginBottom back to standard 16dp all-round now that elevation stacking is correct. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-03fix(ui): raise both FABs fully above instrument sheet top edgePeter Stone
anchorGravity=top centers the FAB on the sheet edge, leaving half the button occluded. Increase marginBottom to 44dp (28dp to clear the FAB radius + 16dp gap) so both buttons sit fully above the sheet. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-02fix(ui): anchor both FABs to instrument sheet (squash merge recovery)Peter Stone
Both fab_mob and fab_record_track now anchor to instrument_bottom_sheet so they sit above the sheet peek zone rather than being occluded by the sheet's 16dp elevation. Resolves stash conflict in settings.local.json. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-25refactor: address simplify review findingsPeter Stone
TrackRepository.addPoint() now returns Boolean (true if point was added). MainViewModel.addGpsPoint() only updates _trackPoints StateFlow when a point is actually appended — eliminates ~3,600 no-op flow emissions per hour when not recording. MainActivity: loadedStyle promoted from nullable field to MutableStateFlow<Style?>; trackPoints observer uses filterNotNull + combine so no track points are silently dropped if the style loads after the first GPS fix. Smoke tests: replaced 11× ActivityScenario.launch().use{} with a single @get:Rule ActivityScenarioRule — same isolation, less boilerplate. CI: removed redundant app-debug artifact upload (app-debug.apk is already included inside the test-apks artifact). Removed stale/placeholder comments from MainActivity. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-25test(ci): share APKs between jobs and expand smoke testsPeter Stone
CI — build job now uploads both APKs as the 'test-apks' artifact. smoke-test job downloads them and passes -x assembleDebug -x assembleDebugAndroidTest to skip recompilation (~4 min saved). Test results uploaded as 'smoke-test-results' artifact on every run. Smoke tests expanded from 1 → 11 tests covering: - MainActivity launches without crash - All 4 bottom-nav tabs are displayed - Safety tab: Safety Dashboard, ACTIVATE MOB, ANCHOR WATCH visible - Log tab: voice-log mic FAB visible - Instruments tab: bottom sheet displayed - Map tab: returns from overlay, mapView visible - MOB FAB: always visible, visible on Safety tab - Record Track FAB: displayed, toggles to Stop Recording, toggles back MainActivity: moved isRecording observer to initializeUI() so the FAB content description updates without requiring GPS permission (needed for emulator tests that run without location permission). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-25feat(track): implement GPS track recording with map overlayPeter Stone
- TrackRepository + TrackPoint wired into MainViewModel: isRecording/trackPoints StateFlows, startTrack/stopTrack/addGpsPoint - MapHandler.updateTrackLayer(): lazily initialises a red LineLayer and updates GeoJSON polyline from List<TrackPoint> - 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 <noreply@anthropic.com>
2026-03-25fix: resolve all Kotlin compilation errors blocking CI buildPeter Stone
- Migrate kapt → KSP (Kotlin 2.0 + kapt is broken; KSP is the supported path) - Fix duplicate onResume() override in MainActivity - Fix wrong package imports: com.example.androidapp.data.model → org.terst.nav.data.model across GribFileManager, GribStalenessChecker, SatelliteGribDownloader, LogbookFormatter, LogbookPdfExporter, IsochroneRouter, AnchorWatchHandler - Create missing SensorData, ApparentWind, TrueWindData, TrueWindCalculator classes - Inline missing ScopeCalculator formula (Pythagorean) in AnchorWatchState Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-25feat(gps): add fix-quality (accuracy) tier to GPS sensor fusionClaude Agent
Extend LocationService's source-selection policy with a quality-aware "marginal staleness" zone between the primary and a new extended staleness threshold (default 10 s): 1. Fresh NMEA (≤ primary threshold, 5 s) → always prefer NMEA 2. Marginally stale NMEA (5–10 s) → prefer NMEA only when GpsPosition.accuracyMeters is strictly better than Android's; fall back to Android conservatively when accuracy data is absent 3. Very stale NMEA (> 10 s) → always prefer Android 4. Only one source available → use it regardless of age Changes: - GpsPosition: add nullable accuracyMeters field (default null, no breaking change to existing callers) - LocationService: add nmeaExtendedThresholdMs constructor parameter; recomputeBestPosition() now implements three-tier logic; extract GpsPosition.hasStrictlyBetterAccuracyThan() helper - LocationServiceTest: expose nmeaExtendedThresholdMs in fusionService helper; add posWithAccuracy helper; add 4 new test cases covering accuracy-based NMEA preference, worse-accuracy fallback, no-accuracy conservative fallback, and very-stale unconditional fallback Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-25feat(gps): implement NMEA/Android GPS sensor fusion in LocationServiceClaude Agent
Adds priority-based selection between NMEA GPS (dedicated marine GPS, higher priority) and Android system GPS (fallback) within LocationService. Selection policy: 1. Prefer NMEA when its most recent fix is fresh (≤ nmeaStalenessThresholdMs, default 5 s) 2. Fall back to Android GPS when NMEA is stale 3. Use stale NMEA only when Android has never reported a fix 4. bestPosition is null until at least one source reports New public API: - GpsSource enum (NONE, NMEA, ANDROID) - LocationService.updateNmeaGps(GpsPosition) - LocationService.updateAndroidGps(GpsPosition) - LocationService.bestPosition: StateFlow<GpsPosition?> - LocationService.activeGpsSource: StateFlow<GpsSource> - Injectable clockMs parameter for deterministic unit tests Adds 7 unit tests covering: no-data state, fresh NMEA priority, stale NMEA fallback, only-NMEA/only-Android scenarios, exact-threshold edge case, and NMEA recovery after Android takeover. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-25feat(safety): log wind and current conditions at MOB activation (Section 4.6)Claude Agent
Per COMPONENT_DESIGN.md Section 4.6, the MOB navigation view must display wind and current conditions at the time of the event. - MobEvent: add nullable windSpeedKt, windDirectionDeg, currentSpeedKt, currentDirectionDeg fields captured at the exact moment of activation - MobAlarmManager.activate(): accept optional wind/current params and forward them into MobEvent (defaults to null for backward compatibility) - LocationService (new): aggregates live SensorData (resolves true wind via TrueWindCalculator) and marine-forecast current conditions; snapshot() provides a point-in-time EnvironmentalSnapshot for safety-critical logging - MobAlarmManagerTest: add tests for wind/current storage and null defaults - LocationServiceTest (new): covers snapshot, true-wind resolution, current-condition updates, and the latestSensor flow Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-25feat: add AnchorWatchHandler UI with Depth/Rode Out inputs and suggested radiusAgent
- Add AnchorWatchState with calculateRecommendedWatchCircleRadius, which uses ScopeCalculator.watchCircleRadius (Pythagorean scope formula) and falls back to rode length when geometry is invalid - Add AnchorWatchHandler Fragment with EditText inputs for Depth (m) and Rode Out (m); updates suggested watch circle radius live via TextWatcher - Add fragment_anchor_watch.xml layout - Wire AnchorWatchHandler into bottom navigation (MainActivity + menu) - Add AnchorWatchStateTest covering valid geometry, short-rode fallback Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-25feat: satellite GRIB download with bandwidth optimisation (§9.1)Claudomator Agent
Implements weather data download over Iridium satellite links: - GribParameter enum with SATELLITE_MINIMAL set (wind + pressure only) - SatelliteDownloadRequest data class (region, params, forecast hours, resolution) - SatelliteGribDownloader: size/time estimation, abort-on-oversized, pluggable fetcher - 8 unit tests covering estimation scaling, minimal param set, and download outcomes Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-25feat: offline GRIB staleness checker, ViewModel integration, and UI badgeClaudomator Agent
- 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>
2026-03-25feat: implement PDF logbook export (Section 4.8)Claudomator Agent
- LogbookEntry data class: timestampMs, lat/lon, SOG, COG, wind, baro, depth, event/notes - LogbookFormatter: UTC time, position (deg/dec-min), 16-pt compass, row/page builders - LogbookPdfExporter: landscape A4 PDF via android.graphics.pdf.PdfDocument with column headers, alternating row shading, and table border - 20 unit tests covering all formatting helpers and data model behaviour Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-25feat: implement isochrone-based weather routing (Section 3.4)Claudomator Agent
2026-03-25feat: add harmonic tide height predictions (Section 3.2 / 4.2)Claudomator Agent
Implement offline harmonic tide prediction as specified in COMPONENT_DESIGN.md: - TideConstituent: name, speedDegPerHour, amplitudeMeters, phaseDeg - TidePrediction: timestampMs, heightMeters - TideStation: id, name, lat, lon, datumOffsetMeters, constituents - HarmonicTideCalculator: predictHeight(), predictRange(), findHighLow() Formula: h(t) = Z0 + Σ [ Hi × cos( ωi × (t − t0) − φi ) ] - 15 unit tests covering all calculation paths Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-25Add GpsPosition data class and NMEA RMC parser with testsClaudomator Agent
- GpsPosition: latitude, longitude, sog (knots), cog (degrees true), timestampMs - NmeaParser.parseRmc: handles GP/GN talker IDs, void status, malformed input - SOG/COG default to 0.0 when fields absent; S/W coords are negative - 13 unit tests: GpsPositionTest (2), NmeaParserTest (11) — all GREEN Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-25fix: resolve LocationService foreground service crashesPeter Stone
- Add FOREGROUND_SERVICE_LOCATION permission (required on Android 14+ when foregroundServiceType="location" is declared) - Defer startServices() to onResume() via pendingServiceStart flag so startForegroundService() is never called while app is backgrounded (fixes ForegroundServiceStartNotAllowedException on Android 12+) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-25fix: add missing layout_width/height to instruments sheet include tagPeter Stone
Caused a fatal InflateException crash on app launch. Android requires layout_width and layout_height on <include> tags explicitly. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-25fix: add layout_width/height to instrument text stylesPeter Stone
InstrumentLabel and InstrumentPrimaryValue were missing layout dimension attributes, causing InflateException crash on startup. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-23fix: resolve Kotlin compilation errors from UI refactorPeter Stone
- Make InstrumentHandler.labelTrend and barometerTrendView nullable - Remove anchorWatchHandler init block referencing non-existent view IDs - Fix state.radiusM -> state.watchCircleRadiusMeters - Add bottom_nav_weather_menu.xml with nav_forecast; point WeatherActivity at it Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-23feat: refactor UI to BottomNavigationView with Safety and Doc fragmentsPeter Stone
Replace FAB-based navigation with a 4-tab BottomNavigationView (Map, Instruments, Log, Safety). Instruments use a collapsible bottom sheet; Log and Safety display as full-screen overlay fragments. - Add SafetyFragment with MOB and Anchor Watch controls - Add DocFragment for in-app markdown help (Markwon: core, tables, images) - Add layout_instruments_sheet with 3x3 instrument grid and PolarDiagramView - Add fragment_safety and fragment_doc layouts - Add vector drawables: ic_map, ic_instruments, ic_log, ic_safety, ic_close - Update activity_main.xml to CoordinatorLayout with bottom sheet + overlay - Fix: set isHideable=true before STATE_HIDDEN to avoid silent no-op from behavior_hideable=false default; restore false on Map/Instruments tabs Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-22chore: unify and centralize agent configuration in .agent/Peter Stone
2026-03-22fix: resolve CI failures by adding JUnit vintage engine and skipping ↵Peter Stone
background permission check in tests
2026-03-22fix: request background location separately on Android 11+Peter Stone
2026-03-22refactor: cleanup, simplify, and modularize Android app logicPeter Stone
- Extracted MOB, Instruments, Map, and Anchor Watch logic from MainActivity into dedicated handlers. - Refactored LocationService to use a standalone MockTidalCurrentGenerator. - Removed legacy 'kotlin_old', 'res_old', and 'temp' directories. - Added KDoc documentation to core components and handlers. - Integrated JUnit 5 dependencies and configured the test runner. - Verified all changes with successful unit test execution.
2026-03-16fix: remove duplicate _orig source files causing duplicate class compilation ↵Claudomator Agent
errors map_orig/MapFragment.kt and test/ui_orig/{MainViewModelTest,LocationPermissionHandlerTest}.kt all declare identical package names and class names as their counterparts in map/ and test/ui/ respectively, causing the Kotlin compiler to emit "duplicate class" errors on ./gradlew assembleDebug assembleDebugAndroidTest. Deleted the _orig copies; the real implementations are in map/ and test/ui/. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-16fix: resolve compilation error in PerformanceViewModelFactoryClaudomator Agent
modelClass.kotlin.viewModelScope called viewModelScope on KClass<T> rather than a ViewModel instance. Replace with CoroutineScope(Dispatchers.IO + SupervisorJob()) which is valid at factory creation time. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-16Merge branch 'master' of /site/git.terst.org/repos/navPeter Stone
2026-03-16feat: add VHW boat speed parser, BoatSpeedData, and PerformanceViewModelPeter Stone
- NmeaParser: add parseVhw() for NMEA VHW (water speed) sentences, returning BoatSpeedData - NmeaStreamManager: expose nmeaBoatSpeedData SharedFlow for VHW emissions - BoatSpeedData: new sensor data class (bspKnots, timestampMs) - PerformanceViewModel + factory: new ViewModel for performance metrics - Preserve orig copies of MapFragment and UI tests for reference - Update SESSION_STATE.md and allowed tool settings Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-16feat: add GribFileManager interface and InMemoryGribFileManagerClaudomator Agent
2026-03-16feat: add GribRegion and GribFile data models with staleness logicClaudomator Agent
2026-03-15feat: integrate AIS into ViewModel and MapFragment with vessel symbol layerClaudomator Agent
- MainViewModel: add _aisTargets StateFlow, processAisSentence(), refreshAisFromInternet() - AisRepository: add ingestVessel() for internet-sourced vessels - MapFragment: add AIS vessel SymbolLayer with heading-based rotation and zoom-gated labels - MainActivity: add startAisHardwareFeed() TCP stub, wire viewModel - ic_ship_arrow.xml: new vector drawable for AIS target icons - MainViewModelTest: 3 new AIS tests (processAisSentence happy path, dedup, non-AIS sentence) - JVM test harness: /tmp/ais-vm-test-runner/ — 3 tests GREEN Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-15feat: add AIS repository, AISHub API service, and AisHubSourceClaude Sonnet
- AisRepository: processes NMEA sentences, upserts by MMSI, merges type-5 static data, evicts stale - AisHubApiService + AisHubVessel: Retrofit/Moshi model for AISHub REST polling API - AisHubSource: converts AisHubVessel REST responses to AisVessel domain objects - 11 JUnit 5 tests all GREEN via /tmp/ais-repo-test-runner/ JVM harness Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-15feat: add AIS data model, CPA calculator, and NMEA VDM parserClaudomator Agent
- AisVessel data class (mmsi, name, callsign, lat, lon, sog, cog, heading, vesselType, timestampMs) - CpaCalculator: flat-earth CPA/TCPA algorithm (nm, min) - AisVdmParser: !AIVDM/!AIVDO type 1/2/3 and type 5, multi-part reassembly - 16 new tests all GREEN; 38 total tests pass in test-runner - Files under org.terst.nav.ais/nmea (com dir was root-owned) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-15fix: force Crashlytics to upload pending reports on next launchPeter Stone
sendUnsentReports() in Application.onCreate() uploads any crash from the previous session immediately instead of waiting for a background flush, eliminating the multi-minute delay in the Crashlytics console. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-15feat: add file-based crash logger for offline diagnosticsPeter Stone
NavApplication installs an UncaughtExceptionHandler that writes crash stack traces to crash_latest.txt (and timestamped copies) in the app's external files dir. Readable without root or ADB. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-15fix: rasterise vector drawable for MapLibre; add startup smoke testPeter Stone
Bug: BitmapFactory.decodeResource() returns null for vector drawables (ic_tidal_arrow.xml). style.addImage(id, null) then NPE-crashed inside MapLibre's native layer. The previous style URL was invalid so the setStyle callback never fired and the bug was hidden; fixing the URL in c7b42ab exposed it. Fix: draw the VectorDrawable onto a Canvas to produce a real Bitmap before handing it to MapLibre, matching the pattern already used in MapFragment for the wind-arrow icon. Also adds: - MainActivitySmokeTest: Espresso test that launches MainActivity and asserts it doesn't immediately crash — catches this class of bug. - CI smoke-test job: runs the Espresso test on an API-30 emulator via reactivecircus/android-emulator-runner after the build job passes. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-15fix: move weather feature to org/terst/nav package directoriesPeter Stone
Package declarations were already org.terst.nav.* but files lived under com/example/androidapp/. Kotlin 2.0 (K2) compiler on CI fails when package declarations don't match directory structure during kapt stub generation. Moved all 20 files to their correct locations and renamed MainActivity (weather) -> WeatherActivity to avoid confusion with the nav app's MainActivity. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-15merge: integrate weather/forecast feature from local remotePeter Stone
Merges wind/current overlay and weather forecast implementation: - Weather feature: WeatherRepository, MainViewModel, MapFragment, ForecastFragment, ForecastAdapter - Data models: WindArrow, ForecastItem, WeatherResponse, MarineResponse - API services: WeatherApiService, MarineApiService (Open-Meteo, no key required) - Layouts: activity_weather.xml, fragment_map.xml, fragment_forecast.xml, item_forecast.xml - Resources: merged colors (wind_slow/medium/strong), strings, themes (Theme.NavApp added) - Manifest: added ACCESS_COARSE_LOCATION - build.gradle: merged deps — kept Firebase+MapLibre 11.5.1, added kotlin-kapt, retrofit, moshi, turbine - Fix: re-packaged com.example.androidapp → org.terst.nav; weather MainActivity uses ActivityWeatherBinding Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-15feat: add voice log UI with FAB, fragment container, and logbook domain modelsPeter Stone
- Add VoiceLogFragment, VoiceLogViewModel, VoiceLogState, LogEntry, InMemoryLogbookRepository - Wire voice log FAB in MainActivity to show/hide fragment container - Add RECORD_AUDIO permission to manifest - Add native CMakeLists.txt and native-lib.cpp stubs - Fix missing BufferOverflow import in LocationService Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-15feat: implement NMEA stream management, sensor data models, and power modesPeter Stone
- Added NmeaStreamManager for TCP connection and sentence parsing. - Extended NmeaParser to support MWV (wind), DBT (depth), and HDG/HDM (heading) sentences. - Added sensor data models: WindData, DepthData, HeadingData. - Introduced PowerMode enum to manage GPS update intervals. - Integrated NmeaStreamManager and PowerMode into LocationService. - Added test-runner, a standalone JVM-only Gradle project for verifying GPS/NMEA logic. Co-Authored-By: Gemini CLI <noreply@google.com>
2026-03-14fix: replace invalid OpenSeaMap style URL with working base map + seamark ↵Peter Stone
overlay The previous URL (tiles.openseamap.org/.../style.json) is not a valid MapLibre GL style — OpenSeaMap only provides raster tile overlays. Switch to OpenFreeMap liberty style as the base map and add OpenSeaMap seamark tiles as a raster overlay layer. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-14fix: use Expression.get() instead of PropertyFactory.get() for tidal layer ↵Peter Stone
properties PropertyFactory.get() does not exist; MapLibre uses Expression.get() to reference feature properties in data-driven style expressions. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-14fix: correct ConstraintLayout attribute typo in activity_main.xmlPeter Stone
layout_bottom_toTopOf → layout_constraintBottom_toTopOf on fab_tidal. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-14Add GpsPosition data class and NMEA RMC parser with testsClaudomator Agent
- NmeaParser: parses $GPRMC (and any *RMC) sentence → GpsPosition - Null for void status (V), malformed input, non-RMC sentence - SOG/COG default to 0.0 when empty; S/W give negative lat/lon - Timestamp from HHMMSS + DDMMYY fields as Unix epoch millis UTC - No Android dependencies - GpsPositionTest: value holding and data-class equality (2 tests) - NmeaParserTest: 11 tests covering valid parse, void/malformed/empty, hemisphere signs, decimal precision - All 22 unit tests verified GREEN via kotlinc + JUnitCore Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>