| Age | Commit message (Collapse) | Author |
|
- 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>
|
|
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>
|
|
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>
|
|
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>
|
|
- 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>
|
|
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>
|
|
- 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>
|
|
- 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>
|
|
|
|
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>
|
|
- 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>
|
|
- 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>
|
|
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>
|
|
InstrumentLabel and InstrumentPrimaryValue were missing layout dimension
attributes, causing InflateException crash on startup.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
|
- 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>
|
|
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>
|
|
|
|
background permission check in tests
|
|
|
|
- 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.
|
|
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>
|
|
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>
|
|
|
|
- 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>
|
|
|
|
|
|
- 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>
|
|
- 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>
|
|
- 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>
|
|
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>
|
|
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>
|
|
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>
|
|
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>
|
|
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>
|
|
- 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>
|
|
- 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>
|
|
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>
|
|
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>
|
|
layout_bottom_toTopOf → layout_constraintBottom_toTopOf on fab_tidal.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
|
- 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>
|
|
- 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>
|
|
|
|
Extract location permission decision logic from MainActivity into a
testable LocationPermissionHandler class. Covers: permission already
granted, needs request, fine-only granted, coarse-only granted, both
granted, both denied, and never-ask-again (empty grants) scenarios.
All permissions (INTERNET, ACCESS_FINE_LOCATION, ACCESS_COARSE_LOCATION)
were already declared in AndroidManifest.xml; no manifest changes needed.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
|
|
|
|
|
|
|
|
|
App Distribution
|
|
|
|
This commit introduces the core functionality for the Man Overboard (MOB) alarm.
Key changes include:
- Added a persistent, high-contrast red MOB Floating Action Button to the UI.
- Implemented dynamic location permission requests and initialization of LocationService.
- Created a MobWaypoint data class to store MOB location and timestamp.
- Developed the activateMob() function to:
- Capture current GPS coordinates.
- Set the active MOB waypoint and mark MOB as activated.
- Switch to a dedicated MOB navigation view, hiding other UI elements.
- Start a continuous, looping audible alarm (assumes R.raw.mob_alarm exists).
- Log the MOB event to the console (placeholder for future logbook integration).
- Implemented a MOB navigation view (ConstraintLayout) with real-time distance to MOB and elapsed time display.
- Added a recoverMob() function, triggered by a 'Recovered' button, to:
- Deactivate MOB mode.
- Stop and release the audible alarm.
- Restore the main UI visibility.
- Location updates are observed to continuously update the MOB navigation display.
- Ensured MediaPlayer resources are properly released on activity destruction.
Future enhancements (not part of this commit) include:
- Implementing a bearing arrow in the MOB navigation view.
- Integrating with a persistent logbook system.
|