| Age | Commit message (Collapse) | Author |
|
- 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
|
|
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
|
|
- Start LocationService at ECONOMY (5 s, PRIORITY_BALANCED_POWER_ACCURACY)
instead of FULL (1 Hz, PRIORITY_HIGH_ACCURACY); promote to FULL only
while a track is actively recording and demote back when recording stops
- Use PRIORITY_BALANCED_POWER_ACCURACY for ECONOMY and ANCHOR_WATCH modes
so the GPS chip idles and network/passive sources handle light-duty fixes
- Add ACTION_START_ECONOMY / ACTION_START_FULL intents so MainActivity can
drive mode transitions from lifecycle callbacks
- onPause: drop to ECONOMY if not recording or anchor-watching; onResume:
restore the appropriate mode so the first visible frame has a fresh rate
- Stop-anchor-watch now returns to ECONOMY instead of FULL (anchor drop
rarely coincides with active track recording)
- Remove ACCESS_BACKGROUND_LOCATION from manifest — the foreground service
with foregroundServiceType="location" already covers background access
- Add 5 m minimum-distance filter to DeviceGpsProvider so stationary devices
don't generate spurious location callbacks
https://claude.ai/code/session_01YUbuZNDAoLea4cf9UGQ9qn
|
|
Google Files uses application/octet-stream for .gpx because Android 16's
MimeTypeMap has no entry for that extension. The ExternalStorageProvider
content URI encodes the full file path, so pathSuffix=".gpx" keeps the
ACTION_VIEW filter targeted without matching all binary files.
ACTION_SEND (share sheet) filters added for typed MIME variants; code now
handles both actions — EXTRA_STREAM for SEND, intent.data for VIEW.
For octet-stream and generic XML types, OpenableColumns.DISPLAY_NAME is
checked before parsing (uri.lastPathSegment is an opaque ID for content
URIs, not the filename). All decisions logged to NavLogger.
https://claude.ai/code/session_01DNjbYxiC1cco83dArNGW3G
|
|
Android's MimeTypeMap does not include application/gpx+xml for .gpx on
all devices, so file managers fall back to text/xml or application/xml.
Add intent-filters for both XML MIME types and check OpenableColumns
DISPLAY_NAME in code before parsing — so Nav appears in the chooser for
XML files but only processes ones named *.gpx.
Remove the broken file:// pathSuffix filter (irrelevant on Android 16,
also syntactically invalid when combined with mimeType in a single <data>
element). Remove unnecessary BROWSABLE category from file-open filters.
https://claude.ai/code/session_01DNjbYxiC1cco83dArNGW3G
|
|
- AndroidManifest: add application/gpx and file:// intent-filters so Nav
appears in the chooser on devices that don't know application/gpx+xml
- MainActivity: check file extension as fallback when MIME type is generic
- TrackStorage: filter MediaStore fallback query by OWNER_PACKAGE_NAME so
the system grants access to our own files without READ_EXTERNAL_STORAGE,
which also survives app reinstall (same package name = same ownership)
- TrackRepository: add reloadTracks() to force a fresh load from storage
- MainViewModel: add reloadPastTracks() called after location permission
grant so the first-launch race condition can self-heal
https://claude.ai/code/session_01DNjbYxiC1cco83dArNGW3G
|
|
Saved tracks overlay was immediately hidden after showing because
bottomNav.selectedItemId = nav_map fires the nav item listener, which
calls hideOverlays(). Removed that line — the map tab is already
selected when the instrument sheet is open.
GPX file handler:
- Manifest: intent-filter for application/gpx+xml + singleTop launch
mode so onNewIntent is called when app is already running
- MainActivity: handleIncomingGpx() called from onCreate + onNewIntent;
parses the URI on IO dispatcher and hands points to ViewModel
- MainViewModel: addImportedTrack() prepends parsed points to pastTracks
and savedTracks state flows (displayed in session only, not persisted)
https://claude.ai/code/session_01DNjbYxiC1cco83dArNGW3G
|
|
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
|
|
Layout doesn't support landscape; rotating was causing a crash.
configChanges also prevents unnecessary activity recreation if the
system somehow delivers an orientation event anyway.
https://claude.ai/code/session_01HXPjBsogsJVRwCiekDGkJX
|
|
GpxSerializer/GpxParser: full round-trip of all TrackPoint fields via
GPX 1.1 + nav: extensions namespace. 13 unit tests.
TrackStorage: MediaStore on API 29+ (no permission needed), direct file
I/O on API 24-28 (WRITE_EXTERNAL_STORAGE maxSdkVersion=28).
TrackRepository: stopTrack() is now suspend, writes GPX and returns
TrackSummary (distance nm, duration, max/avg SOG, avg wind, avg wave).
getPastTracks() lazy-loads from Documents/Nav/ on first call.
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>
|
|
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>
|
|
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>
|
|
|
|
App Distribution
|
|
Implements the wind/current map overlay and 7-day weather forecast
display that loads on application launch:
Data layer:
- Open-Meteo API (free, no key): WeatherApiService + MarineApiService
- Moshi-annotated response models (WeatherResponse, MarineResponse)
- WeatherRepository: parallel async fetch, Result<T> error propagation
- Domain models: WindArrow (with beaufortScale/isCalm) + ForecastItem
(with weatherDescription/isRainy via WMO weather codes)
Presentation layer:
- MainViewModel: StateFlow<UiState> (Loading/Success/Error),
windArrow and forecast streams
- MainActivity: runtime location permission, FusedLocationProvider
GPS fetch on startup, falls back to SF Bay default
- MapFragment: MapLibre GL map with wind-arrow SymbolLayer;
icon rotated by wind direction, size scaled by speed in knots
- ForecastFragment + ForecastAdapter: RecyclerView ListAdapter
showing 7-day hourly forecast (time, description, wind, temp, precip)
Resources:
- ic_wind_arrow.xml vector drawable (north-pointing, rotated by MapLibre)
- BottomNavigationView: Map / Forecast tabs
- ViewBinding enabled throughout
Tests (TDD — written before implementation):
- WindArrowTest: calm detection, direction normalisation, Beaufort scale
- ForecastItemTest: weatherDescription, isRainy for WMO codes
- WeatherApiServiceTest: MockWebServer request params + response parsing
- WeatherRepositoryTest: MockK service mocks, data mapping, error paths
- MainViewModelTest: Turbine StateFlow assertions for all state transitions
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
|
|