summaryrefslogtreecommitdiff
path: root/android-app/app/src/main/res/layout
AgeCommit message (Collapse)Author
2026-05-27Embed anchor watch form inline in Safety DashboardClaude
Replaces the "Configure Anchor Watch" button with depth and rode EditTexts directly in the anchor card; radius updates live via TextWatcher. Removes the separate AnchorWatchHandler overlay flow and onConfigureAnchor() from SafetyListener. https://claude.ai/code/session_01KXYBuAHZkvv63DeUG6XaZD
2026-05-27Add pirate mode Easter egg to trip reportClaude
Long-press the "Trip Report" header flips the narrative into pirate dialect (☠ VOYAGE header, "Made X nm by the log", "Avast!" log-entry prefixes, closing "Fair winds and following seas. Arr."). Long-press again returns to standard nav voice. Seamanlike output remains the default; pirate mode is purely opt-in. https://claude.ai/code/session_01KXYBuAHZkvv63DeUG6XaZD
2026-05-26Add notes to finished tracks, single Nav voice, fix speed coloringClaude
- TrackDetailSheet: + Add note button opens a dialog with text, camera, and gallery. Entry is saved with timestampMs=track.endMs so it lands inside the track's time window and appears automatically in the event log. logEventAdapter.update() refreshes the list after save. - TripReportGenerator: removed NarrativeStyle enum and 4-style branch. generateNarrative() now emits a concise PASSAGE header (date, time range, duration), stats line (nm / avg kt / max kt), optional conditions (seas, temp), and a timestamped deck log. Fragment and layout updated to remove the ChipGroup style picker. - TrackColors: replaced Expression.get("color") — which silently fails for lineColor on LineLayer in MapLibre Android 11.x — with Expression.step(Expression.get("speed"), ...) that maps the numeric "speed" property directly to color literals. "color" string property removed from speedSegments() features. https://claude.ai/code/session_01YUbuZNDAoLea4cf9UGQ9qn
2026-05-26Fix LocationService current TODO; show log entries in track detail viewClaude
LocationService: - Add _currentSpeedKt/_currentDirectionDeg to companion state - Add setCurrentConditions() companion function - snapshot() now returns real current data instead of null - MainActivity.applyConditions() calls setCurrentConditions() on each marine conditions update TrackDetailSheet: - buildLogEvents() filters NavApplication.logbookRepository entries by track time window (startMs..endMs); entries without GPS snap to nearest track point by timestamp - Note/photo events added to the event log list with 📝/📷 icons - drawNotes() adds a green CircleLayer for note/photo markers on the map - LogEvent extended with photoPath field - LogEventAdapter shows a downsampled photo thumbnail when present item_log_entry.xml: add iv_log_thumb ImageView (hidden by default) https://claude.ai/code/session_01YUbuZNDAoLea4cf9UGQ9qn
2026-05-25Persist log entries and photos; stamp with GPS locationClaude
- LogbookStorage: Moshi-backed JSON persistence to getExternalFilesDir, photos saved to Pictures/Nav/ instead of cache - LogbookRepository: file-backed replacement for in-memory store, loads saved entries on init so they survive app restarts - VoiceLogViewModel: expose entries StateFlow, accept lat/lon in save() - VoiceLogFragment: switch camera to TakePicture(Uri) via FileProvider for full-res photos; gallery copies content URI to persistent storage; passes current GPS position to save(); shows scrollable entries list - MainViewModel: expose currentPosition StateFlow updated on each GPS fix - AndroidManifest: add FileProvider for Pictures/Nav/ directory - TripReportViewModel: updated to use LogbookRepository https://claude.ai/code/session_01YUbuZNDAoLea4cf9UGQ9qn
2026-05-20Merge main: preserve embedded track detail map, tack detection fixes, 0.12 ↵Claude
past track opacity - Tack detector: STABILITY_MAX=30, START_SKIP_MS=120s cold-start filter (feature branch) - TrackDetailSheet: embedded Fragment with MapView, back button pops stack (feature branch) - layout_track_detail_sheet: MapView top half, detail panel bottom half (feature branch) - MapHandler past track dim opacity: 0.12 (down from 0.22) - SavedTracksFragment: fragment transaction to TrackDetailSheet + SAF setup (both) - VoiceLogFragment: inline track list, logViewModel refs fixed (main)
2026-05-20Fix XML conflict markers left in layout_track_detail_sheet.xmlClaude
https://claude.ai/code/session_01YUbuZNDAoLea4cf9UGQ9qn
2026-05-20Fix tack detection and convert track detail to embedded-map fragmentClaude
TackDetector: revert STABILITY_MAX to 30° (was tightened too far to 20°), add 120s start-skip to filter GPS cold-start false positives, keep 45s minimum gap between tacks. TrackDetailSheet: convert from BottomSheetDialogFragment to a regular Fragment with an embedded MapLibreMap in the top half. This fixes the map centering issue (the embedded map is always sized to its own frame), eliminates accidental dismissal when dragging the map, and makes the view self-contained — back navigation restores the track list via the fragment back stack. Tack markers rendered on the embedded map; tapping a log event pans the embedded map directly. https://claude.ai/code/session_01YUbuZNDAoLea4cf9UGQ9qn
2026-05-20Inline saved tracks on Log tab; collapse log entry when not recordingClaude
Removed the 'Saved Tracks' button that opened a separate overlay. Tracks now appear directly on the Log screen in a RecyclerView below the Generate Trip Report button. Tapping a track hides the overlay and opens the existing TrackDetailSheet bottom sheet over the map. The log entry section (text field, mic, camera, save/clear) is hidden with visibility=gone when not recording and shown when a recording is in progress. The storage-setup prompt is now surfaced here too. SavedTrackAdapter and DATE_FMT widened to internal so VoiceLogFragment can reuse them directly. SavedTracksFragment kept for the GPX import flow. https://claude.ai/code/session_01YUbuZNDAoLea4cf9UGQ9qn
2026-05-20Fix Recenter bleed-through; drop noisy speed events; track detail as bottom ↵Claude
sheet Recenter button was reappearing over overlays because the isFollowing flow kept calling fadeIn(fabRecenter) even when an overlay was visible. Added overlayShowing flag — the flow skips FAB updates while an overlay is shown. hideOverlays() now correctly restores fabRecenter visibility based on the actual following state instead of just resetting alpha. Speed up/Slow down events fired on every 1.5 kt GPS jitter, flooding the log with noise. Removed the detection loop entirely — no reliable motor on/off signal is available from GPS alone. TrackDetailSheet converted from a full-screen Fragment to a BottomSheetDialogFragment: opens at half-screen height so the track route is visible on the map behind it, draggable to full height, 20% background dim. SavedTracksFragment now calls hideOverlays() then sheet.show() instead of fragment-replacing the overlay container. https://claude.ai/code/session_01YUbuZNDAoLea4cf9UGQ9qn
2026-05-20Fix gap between wave animation and ocean conditions panelClaude
Removed the spurious constraintBottom_toBottomOf="parent" from forecast_row: ConstraintLayout was vertically centering it between the wave and the sheet bottom (default bias 0.5) instead of placing it flush below the wave. Also aligned light-mode wave_sea_bottom (#074B68 → #0D2137) with the forecast_row background so the wave tail blends seamlessly in light theme. https://claude.ai/code/session_01YUbuZNDAoLea4cf9UGQ9qn
2026-05-20Move Saved Tracks to Log tab; restore conditions sheet on backClaude
- hideOverlays() now resets bottomSheetBehavior to COLLAPSED/not-hideable, so the conditions sheet reappears correctly when returning from any overlay (previously it stayed hidden after navigating away via the track browser) - Remove btn_saved_tracks from the conditions sheet (layout + click handler) - Add btn_saved_tracks to the Log screen layout and wire it in VoiceLogFragment via MainActivity.showSavedTracks(); tracks live with the log, not with weather https://claude.ai/code/session_01YUbuZNDAoLea4cf9UGQ9qn
2026-05-20Fix GPX file opening, share sheet visibility, and add SAF track storageClaude
Intent filter fixes: - Remove pathSuffix=".gpx" + scheme restriction from the application/octet-stream ACTION_VIEW filter; pathSuffix matches the URI path, not the filename, so it never fired against file manager content URIs (opaque paths like /provider/uuid). The existing display-name check in handleIncomingGpx() is the correct gate. - Add ACTION_SEND filter for application/octet-stream; Nav was invisible in share sheets because most file managers send this type for .gpx files. SAF-based track storage (API 29+): - Replace fragile SharedPrefs URI cache + OWNER_PACKAGE_NAME MediaStore query with Storage Access Framework as the primary write/read path. The user grants access to their tracks folder once via ACTION_OPEN_DOCUMENT_TREE; DocumentFile.listFiles() finds all GPX files without relying on MediaStore ownership (which Android clears on uninstall). After reinstall, a one-tap "Restore track access" button in Saved Tracks re-authorizes the same folder and all tracks immediately reappear. - MediaStore path (existing) is kept as fallback for tracks saved before this change - Add SafState enum (CONFIGURED / PERMISSION_LOST / NOT_CONFIGURED) in TrackStorage - TrackRepository exposes safState() and initSafDirectory() thin wrappers - MainViewModel exposes safState StateFlow and onSafDirectorySelected() - SavedTracksFragment observes safState and shows contextual setup / re-auth button - MainActivity registers safPickerLauncher and exposes launchSafPicker() - Add androidx.documentfile:documentfile:1.0.1 dependency https://claude.ai/code/session_01YUbuZNDAoLea4cf9UGQ9qn
2026-05-15fix: saved tracks browser, GPX import persistenceClaude
Saved tracks browser: - TrackDetailSheet converted from BottomSheetDialogFragment to Fragment so it fills the overlay instead of floating over the map - Adapter tap pushes detail fragment onto the back stack; back button returns to the list naturally via popBackStack() - SavedTracksFragment back button calls hideOverlays() (made internal) to close the overlay and return to the map - Layout updated to full-screen: header with back button, RecyclerView fills remaining space via layout_weight instead of fixed 300dp GPX import persistence: - TrackRepository.importTrack() saves parsed points to Documents/Nav/ via TrackStorage (URI is persisted to SharedPreferences) and adds to in-memory list - MainViewModel.addImportedTrack() now calls importTrack() so files opened via the GPX handler load on future launches https://claude.ai/code/session_01DNjbYxiC1cco83dArNGW3G
2026-05-12Add saved tracks browser, tack detection, and map zoom-to-boundsPeter Stone
- TackDetector: sliding-window COG algorithm (60-140deg delta = tack/jibe) with stability check to prevent mixed-window false positives; 13 tests green - TackEvent, SavedTrack: new models wrapping points + summary + detected tacks - TrackRepository.getSavedTracks(): returns List<SavedTrack> with lazy load - MapHandler: zoomToTrackBounds (LatLngBounds fit with padding), panToPoint (log step-through), updateTackLayer (yellow circle markers) - MainViewModel: savedTracks + selectedTrack StateFlows, panToPosition SharedFlow - SavedTracksFragment: RecyclerView list with date/distance/duration/tack count - TrackDetailSheet: bottom sheet with stats + event log (tacks, speed changes, start/end); tapping an entry pans the map to that position - MainActivity: zoom map on track complete, wire Saved Tracks button in instrument sheet, observe panToPosition to drive map, show tack markers when a saved track is selected Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-05fix: keep bottom nav visible when panning; expand drag handle touch targetPeter Stone
Bottom nav: remove it from the following/panning fade cycle — only fabRecenter and fabRecordTrack swap; nav stays visible throughout. Drag handle: 4dp × 36dp was too small and competed with BottomSheet drag gestures. Now 40dp tall via inset drawable (visual bar unchanged, touch area is a usable 40dp × 36dp). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-04feat: past tracks layer toggle + persist vessel/crew registryPeter Stone
Layers menu: - Add "Past tracks" switch to layer picker sheet — hides/shows previous voyage polylines on the map, persisted across sessions via SharedPrefs Vessel/crew registry: - VesselRepository now takes a Context and persists all vessels and crew to SharedPreferences as JSON (Moshi, same pattern as BoatProfileRepository) - Wire VesselRepository as a singleton in NavApplication so the Fragment and ViewModel share one instance — previously each created its own empty repo Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-04feat: move Layers from bottom nav to dedicated FABPeter Stone
Replaces the nav_layers bottom nav item with a mini FloatingActionButton anchored below the HUD, giving layers access without occupying a nav slot. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-23Merge origin/main — unite all work onto one branchClaude
Brings in dev log (NavLogger), UnitPrefs, MapLayerManager, HUD views, conditions throttling, track save/load pipeline, improved ParticleWindView (antimeridian-aware, dynamic particle count), Snackbar error surfacing, and all other main-branch work from the prior session. Combined with master's hardware source flags, vessel registry, crew management, thermal alarm, CPA collision alerts, and track stats. Also documents primary branch policy in CLAUDE.md and .agent/config.md: all work merges to main, never master. https://claude.ai/code/session_011h2dXbgXg3PesQMmQUNTCW
2026-04-22fix+feat: CI build fixes and hardware source feature flagsClaude
CI fixes: - ParticleWindView: bounds.lonEast → bounds.northEast.longitude (MapLibre 13.x API; lonEast not available as property) - VesselRegistryFragment: Vessel(id="") and CrewMember(id="") → include required name="" so copy() compiles Hardware source feature flags: - HardwareSource enum: AIS_RECEIVER, NMEA_INSTRUMENTS - FeatureFlags: SharedPreferences-backed; all sources default OFF (app works phone-only out of the box) - NavApplication: exposes featureFlags singleton - SafetyFragment: "HARDWARE DATA SOURCES" card with two MaterialSwitch toggles; reads initial state from FeatureFlags; calls SafetyListener.onHardwareSourceChanged on change - AIS_RECEIVER flag: - MainViewModel.processAisSentence() / refreshAisFromInternet() skip when disabled; onHardwareSourceChanged() clears targets + cpaAlerts - NMEA_INSTRUMENTS flag: - LocationService skips nmeaStreamManager.start() on service launch when disabled - New ACTION_START_NMEA / ACTION_STOP_NMEA intents allow live toggle - MainActivity.onHardwareSourceChanged() sends the appropriate intent Tests: 102 total, all GREEN (+7 FeatureFlagsTest) https://claude.ai/code/session_011h2dXbgXg3PesQMmQUNTCW
2026-04-22feat: CPA/TCPA collision alerts and live track statsClaude
CPA/TCPA alerts: - CpaAlert data class with severity (CAUTION/WARNING/DANGER) and label() - CpaThresholds: 0.5/1.0/2.0 nm, max TCPA 30 min - MainViewModel.updateCpaAlerts() runs on every GPS update and AIS sentence; skips stationary contacts; filters TCPA ≤ 0 or > 30 min; sorts by TCPA ascending - SafetyFragment: new COLLISION AVOIDANCE card shows live alert list, color-coded by severity; clears to "No traffic alerts" when safe - MainActivity: observes cpaAlerts → safetyFragment.updateCpaAlerts() Track stats: - TrackStats data class: distanceNm, durationMs, avgSogKnots, durationFormatted (m:ss / h:mm:ss) - TrackRepository.haversineNm() + computeStats() using point timestamps - MainViewModel exposes trackStats StateFlow, cleared on stopTrack() - activity_main: dark pill overlay bar (tv_track_stats) above map - MainActivity: shows "● REC 0.00nm m:ss x.xkt avg" while recording Tests: 95 total, all GREEN (+16 new — 9 TrackStats, 7 CpaAlert) https://claude.ai/code/session_011h2dXbgXg3PesQMmQUNTCW
2026-04-22feat: vessel registry, crew management, and thermal alarmClaude
- Vessel/CrewMember data classes with full profile fields (MMSI, callsign, flag, home port, hull type, dimensions, roles) - VesselRepository: in-memory CRUD for own vessel, fleet, and crew; getOwnVessel(), getSkipper(), getCrewByRole() helpers - VesselRegistryFragment: own-vessel card + crew list + known-vessels list; AlertDialog editors for add/edit; long-press to delete - ic_vessel.xml sailboat icon; 5th bottom-nav tab "Vessel" - ThermalMonitor: thermalFlow() using Intent.ACTION_BATTERY_CHANGED (battery °C) + PowerManager.OnThermalStatusChangedListener (API 29+); ThermalState OK/WARM/HOT/CRITICAL with distinct-until-changed filter - MainActivity: startThermalMonitoring() sounds mob_alarm + Toast on HOT (≥45°C) and CRITICAL (≥50°C); auto-silences on OK/WARM - MainViewModel: thermalState and thermalReading StateFlows - 18 VesselRepositoryTest cases — all GREEN (test-runner) - design.md: updated competitive matrix + feature implementation status https://claude.ai/code/session_011h2dXbgXg3PesQMmQUNTCW
2026-04-11Add hidden dev log (long-press drag handle)Claude
NavLogger is a 200-entry in-memory ring buffer with format: HH:mm:ss.SSS LEVEL tag: message Entries are written at: - App start - First GPS fix (lat/lon/sog/cog) - fetchCurrentConditions: request params + full success summary (wind, temp, waves, swell, current) or exception class+message - fetchForecastItems: request params + item count or error DevLogSheet (BottomSheetDialogFragment) shows the log in a monospace ScrollView with Copy and Clear buttons. Trigger: long-press the drag handle bar on the instrument bottom sheet. Primary use: copy the log and paste it to Claude to diagnose conditions API failures without needing adb. https://claude.ai/code/session_01HXPjBsogsJVRwCiekDGkJX
2026-04-11Fix: use INVISIBLE instead of GONE for condition spinnersClaude
GONE collapses the spinner's space, causing the value/unit TextViews to reflow and bounce on every refresh. INVISIBLE keeps the 10dp width reserved so the surrounding text never moves. https://claude.ai/code/session_01HXPjBsogsJVRwCiekDGkJX
2026-04-11WaveView adapts sky/sea colors to light/dark modeClaude
Day (light mode): vivid sky blue (#2B8FC4 → #87C8DF) fading into tropical deep-sea blue (#0D7A9A → #074B68) — evokes a clear Kona afternoon with deep Pacific below. Shimmer is bright white sunlight on water (#50FFFFFF). Night (dark mode): retains the existing dark charcoal sky (#1C1B1F → #162433) and midnight navy sea (#0B3050 → #0D2137) with blue shimmer. Colors are defined as adaptive color resources (values/ and values-night/) and read in WaveView.onSizeChanged() and paint init instead of hardcoded Color.parseColor() calls. https://claude.ai/code/session_01HXPjBsogsJVRwCiekDGkJX
2026-04-11Fix bottom sheet corners; add full light/dark mode paletteClaude
Rounded corners: - Replace androidx CardView with MaterialCardView + ShapeAppearance.Nav.BottomSheet so only the top-left and top-right corners are rounded (24dp); bottom corners are square flush with the screen edge Light/dark mode: - Introduce values-night/colors.xml with the existing dark M3 palette (moved from values/colors.xml); instrument values stay dark (#E6E1E5 text, #1C1B1F bg) - values/colors.xml now carries the light M3 palette: surface #FFFBFE, onSurface #1C1B1F, surfaceVariant #E7E0EB etc.; instrument text flips to dark (#1C1B1F normal, #6F6878 secondary) for readability on light backgrounds - Fix InstrumentUnit hardcoded #6B6070 → ?attr/colorOnSurfaceVariant so the unit labels adapt automatically in both modes - Mark the forecast row (ocean navy #0D2137) and WaveView with android:forceDarkAllowed="false" so the system never inverts them — the ocean section is intentionally always dark regardless of system theme https://claude.ai/code/session_01HXPjBsogsJVRwCiekDGkJX
2026-04-11Show loading spinners next to each instrument value during conditions fetchClaude
- Add _conditionsLoading StateFlow to MainViewModel; set true when loadConditions() fires, false when the fetch completes (success or fail) - Cancel any in-flight conditions job when a new loadConditions() call arrives (e.g. rapid map pans) so only the latest fetch drives the state - Add a 10dp ProgressBar after each of the six data values in layout_instruments_sheet.xml (wind, temp, baro, current, waves, swell); visibility GONE by default, alpha 0.5; header section tinted colorOnSurfaceVariant, forecast section tinted #5B9EC2 to match its labels - Observe conditionsLoading in MainActivity and toggle all six spinners together (all values come from one API call so they load in sync) https://claude.ai/code/session_01HXPjBsogsJVRwCiekDGkJX
2026-04-11Remove bad ASA external links; fix conditions not loading on startupClaude
- Strip all three unverified external link cards (ASA courses, ASA online, Quizlet flashcards) from the Learn tab — asa.com is a Brazilian financial site, not the American Sailing Association - Remove dead openUrl() click handlers and unused Intent/Uri imports from LearnFragment - Parallelize the two sequential API calls in fetchCurrentConditions so the HUD populates in ~half the time (weather + marine fetched concurrently via async/await) - Add onFailure logging in loadConditions so errors are visible in logcat instead of silently swallowed - Eagerly call loadConditions in the setStyle callback as a startup backstop: the camera-idle listener fires before GPS fixes and the initial position may be (0,0), so this guarantees a real fetch once the style is ready with a valid camera position https://claude.ai/code/session_01HXPjBsogsJVRwCiekDGkJX
2026-04-11Four features: outbound link markers, offline content, log text/photo, ↵Claude
departure picker Learn tab - ic_open_in_new.xml: external link icon (box + arrow) applied to all browser-opening cards - Migration guide cards retain internal › chevron; ASA/Flashcards cards show ↗ icon - New REFERENCE section (offline, works without connectivity): - ColRegs Rules of the Road → colregs_reference.md (rules 1–38, lights table, sound signals, day shapes, memory aids) - Sailing Quick Reference → sailing_reference.md (points of sail, Beaufort scale, nav lights, knots, buoyage IALA-B, VHF channels, distress signals, tide rule of 12) - ColRegs card moved from external ASA section to offline REFERENCE section Log entry - LogEntry: add photoPath field (absolute file path or content URI string) - VoiceLogViewModel: replace confirmAndSave() with save(text, photoPath?) so the fragment controls text (user may edit recognized speech before saving) - VoiceLogFragment: redesigned layout with EditText (editable, voice fills it), camera button (TakePicturePreview → JPEG in cacheDir), gallery button (GetContent), photo thumbnail with remove button, Save / Clear row - Manifest: add android.hardware.camera uses-feature (required=false) Departure date/time picker (trip planning) - ic_calendar.xml: calendar icon for the picker button - PreTripReportViewModel: _departureMs StateFlow (default = now), setDeparture(ms) - PreTripReportGenerator.generateReport(): departureDateTimeMs param; findDepartureSlot() matches nearest UTC forecast item; condition window labels show actual local times (e.g. "2 PM") when departure is not near-now; buildWatchList uses departure hour for Kona trades warning instead of system clock - fragment_pretrip_report.xml: DEPART card with label + calendar button above generate - PreTripReportFragment: MaterialDatePicker (future dates only) → MaterialTimePicker chain; auto-regenerates after picker confirms https://claude.ai/code/session_01HXPjBsogsJVRwCiekDGkJX
2026-04-10Add Learn tab; move quit to Safety; remove persistent MOB FABClaude
UI cleanup: - Remove always-visible MOB FAB — Safety screen button is sufficient - Remove floating quit button; move to bottom of Safety screen - fragment_container is now clickable/focusable, blocking map touch-through for all overlays (trip reports, log, safety) - Record Track FAB hidden while any overlay is shown, visible on map tab New Learn tab (5th nav item): - Migration guides for Navionics and Sea People (local markdown via DocFragment) - ASA Course Catalog, ASA Online Learning, ColRegs, Sailing Flashcards (open in browser) - Remove blanket assets/ .gitignore so markdown docs are tracked https://claude.ai/code/session_01HXPjBsogsJVRwCiekDGkJX
2026-04-10Fix invalid colorBackground theme attr in pretrip layoutClaude
colorBackground is not a Material theme attribute; replace with colorSurface which is the correct equivalent. https://claude.ai/code/session_01HXPjBsogsJVRwCiekDGkJX
2026-04-10Add boat profiles and overhaul pre-trip departure briefingClaude
- BoatProfileRepository: Moshi+SharedPrefs persistence, pre-seeds Erickson 23 (155%/100%/65% headsails, 2 reefs) and Cal 20 (1 reef) - PreTripModels: expand with SailConfig, ConditionSlice, RouteProjection, WatchItem, SimilarTripSummary; full BoatProfile with sail inventory - PreTripReportGenerator: full rewrite — 4-hour conditions window, Honokohau candidate-heading route projection with TWA scoring and current-component calc, boat-specific sail plan from inventory, hazard watchlist, similar-trip comparison via past GPX tracks - PreTripReportViewModel: inject BoatProfileRepository, fix GPS to LocationService.bestPosition (Honokohau fallback), pass full forecastItems list and pastTracks to generator - PreTripReportFragment: boat selector ChipGroup, auto-generate on open, render all sections (conditions table, route, sail plan, watchlist, similar trips) - fragment_pretrip_report.xml: redesign with all report cards - item_condition_column.xml: new layout for conditions-window columns - NavApplication: instantiate BoatProfileRepository as app singleton https://claude.ai/code/session_01HXPjBsogsJVRwCiekDGkJX
2026-04-10Add unit settings, wind particles toggle, and recenter-to-topClaude
- New UnitPrefs: persisted C/F, ft/m, hPa/inHg, kt/mph/kph — defaults to °F - InstrumentHandler now accepts raw values (knots, metres, hPa, °C) and formats via UnitPrefs on every update - LayerPickerSheet expanded with wind particles toggle and unit chip selectors; wrapped in ScrollView to handle the additional height - MapLayerManager tracks particlesEnabled (persisted); setParticlesEnabled() added - MainActivity: caches last raw values so refreshUnits() can reformat everything instantly when the user changes a unit without waiting for new sensor data - Recenter button moved to top (below HUD strip) so it's never obscured by the bottom sheet or bottom nav - Unit label TextViews given IDs in HUD and instrument sheet for live updates https://claude.ai/code/session_01HXPjBsogsJVRwCiekDGkJX
2026-04-10Area conditions HUD redesign: map-center conditions refresh + boat HUD stripClaude
- Persistent top-of-map HUD strip (SOG/COG/BSP/Depth) replaces instrument grid in bottom sheet - Bottom sheet now shows area conditions only (Wind/Temp/Baro + wave forecast) — always visible - loadConditions() fires on every camera idle event so panning the map refreshes conditions - Crosshair appears at map center while panning; hides when following GPS - Added tempC field to MarineConditions (already fetched from Open-Meteo hourly) - InstrumentHandler slimmed to area conditions only; updateDisplay() removed - LocationService pipes nmeaBoatSpeedData from NmeaStreamManager to companion flow https://claude.ai/code/session_01HXPjBsogsJVRwCiekDGkJX
2026-04-09merge: reconcile master particle-wind work with origin/main redesignClaude
Merged divergent branches (common ancestor a9d87b6). Took origin/main as base (layer manager, recenter, quit button, GPX tracks, marine conditions, dark theme, user position layer, active/past track layers) and layered in particle wind simulation work: - MapHandler: added setupWindLayer, updateWindLayer, updateWindGridLayer; kept their user-pos layer, follow/recenter state, active+past tracks - WeatherRepository: kept their fetchCurrentConditions, added fetchWindGrid - MainViewModel: kept their track/marine/summary changes, added windGrid StateFlow, loadWindGrid, buildGrid - MainActivity: kept their full UI (layer picker, quit, recenter, fade animations), added particleWindView wiring, weatherLoaded guard, windArrow/windGrid observers - activity_main.xml: took their LinearLayout redesign, added ParticleWindView overlay inside ConstraintLayout https://claude.ai/code/session_01HXPjBsogsJVRwCiekDGkJX
2026-04-08feat(wind): Level 2 — canvas particle simulation (uniform field)Claude
- ParticleWindView: transparent View overlay on the map; tracks 300 particles as three FloatArrays (lat, lon, age) — no per-frame allocation; projects lat/lon → screen via MapLibreMap.projection each frame; draws a 14px tail segment in the wind travel direction with alpha fading by age; respawns out-of-bounds/aged particles randomly within visible bounds; speed scaled to viewport so animation is consistent at any zoom level; animates via postInvalidateOnAnimation() - activity_main.xml: ParticleWindView added above MapView, match_parent, non-interactive (clickable/focusable=false) - MainActivity: finds particle_wind_view; calls attachMap after getMapAsync; feeds directionDeg/speedKt from windArrow StateFlow https://claude.ai/code/session_01HXPjBsogsJVRwCiekDGkJX
2026-04-07fix(track): fix silent GPX save failure + add stop friction + quit buttonPeter Stone
TrackStorage: openOutputStream null returned true (file never written). Added IS_PENDING flag to fix Android 10-11 race where insert succeeds but file isn't physically created yet. Added storage-mounted guard. TrackRepository now logs save failures. Stop tracking now requires a long press (haptic feedback) — prevents accidental mid-sail stops from a single tap. Quit button (top-right, tonal X) stops LocationService and calls finishAffinity(). Prompts if a track is in progress. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-06feat(map): layer manager — satellite/charts/hybrid + wind toggle, ↵Peter Stone
long-press picker MapLayerManager: all raster sources registered at style-build time, visibility toggled on demand. Persists base preset and wind state to SharedPreferences. Sources: Google satellite, NOAA RNC charts (tileservice.charts.noaa.gov), OWM wind, OpenSeaMap seamarks. LayerPickerSheet: bottom sheet with chip group (Satellite/Charts/Hybrid) and wind toggle, launched from map long-press. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-06feat(tracks): show summary sheet on track stop; 2-min minimumPeter Stone
TrackSummarySheet: bottom sheet showing distance (nm), duration, max/avg speed, avg wind and waves (when available, waves in ft). Only shown for tracks ≥ 2 minutes — shorter tracks are discarded silently. MainViewModel: exposes trackSummary SharedFlow (replay=0) and trackStartMs. MainActivity: observes flow, shows sheet after stopTrack completes. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-06feat(ui): restructure instrument sheet layout — inline arrows, WaveView, ↵Peter Stone
ocean forecast section Full layout rewrite: 3×2 GridLayout instrument grid with inline DirectionArrowView for AWS/TWS/HDG/COG, depth+baro row, animated WaveView divider, and ocean-blue forecast section for Current/Waves/Swell. Stubs valueCurrDir/valueWaveDir as nullable in InstrumentHandler to compile; full handler rewrite follows in Task 6. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-05feat(ui): remove report section from instrument sheet, fix touch-throughPeter Stone
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-04feat(ui): surface trip planning and reports in instrument sheetPeter Stone
2026-04-04refactor(ui): stabilize layout by moving BottomNav outside CoordinatorLayoutPeter Stone
2026-04-04feat(tripreport): add pre-trip planning and past track visualizationPeter Stone
- Fix compilation errors (missing imports for PropertyFactory and MaterialButton) - Implement Pre-Trip Report with weather summary, boat profile, and sail suggestions - Differentiate between active track (solid red) and past tracks (dotted red) on map - Add navigation to Pre-Trip Report from Safety Dashboard - Robustify track storage to preserve multiple tracks in session Co-Authored-By: Gemini CLI <gemini-cli@google.com>
2026-04-04feat(tripreport): add AI trip narrative generator with multiple stylesPeter Stone
- Consolidate track data, weather, and log entries into a TripSummary - Implement TripReportGenerator with Professional, Adventurous, Journal, and Pirate styles - Add TripReportFragment and ViewModel for UI interaction - Share TrackRepository and LogbookRepository via NavApplication singleton - Fix compilation error in MainViewModel rich metadata recording 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>