App and track lifecycle¶
| Garmin | Wear OS | Apple Watch | |
|---|---|---|---|
| Status | ✅ | 🟡 | ⏳ |
| Code | source/SailEverApp.mc, source/views/SplashView.mc, source/StartMenu.mc, source/TripContext.mc, source/TripStore.mc, source/ActionMenu.mc (finishTrip), source/sync/TrackSession.mc, source/Config.mc |
wear/…/presentation/SailEverApp.kt, wear/…/presentation/MainActivity.kt, wear/…/presentation/views/OtherViews.kt (SplashView), wear/…/SailEverApplication.kt, wear/…/recording/TripEngine.kt, wear/…/recording/TrackingService.kt, wear/…/data/TripStore.kt |
— |
Verified against code: 2026-10-19
Purpose¶
This file describes how the WatchApp starts, how a sailor starts, pauses, resumes, stops and discards a track, how tracks group into a trip, and what happens to totals and local files at each step. It also covers recovery after a crash and how the app ends.
Related: menus and dialogs in menus.md, buttons and gestures in input.md, the pause screen in pause-view.md, uploads and pairing in sync-and-pairing.md, what is recorded each second in recording.md, vessel states in ../shared/vessel-state.md.
Overview¶
Launch ─► Splash (2 s) ─► Start menu ─┬─ Start new trip ──► vessel state picker ─► Recording
├─ Continue trip ───► vessel state picker ─► Recording
├─ Resume track ─────────────────────────► Recording
├─ Upload tracks / Pair / Unpair / Settings
└─ BACK / swipe right ─► app ends
Recording ─ Pause ─► Pause view (Garmin) ─ resume ─► Recording
Recording ─ Pause ─► app in the background (Wear OS) ─ Resume track ─► Recording
Recording ─ Stop ──► save view (if something to upload) ─► app ends
Recording ─ Discard (confirm) ─► start menu
A trip is identified by a trip token on the watch: 20 random hex digits, created when the sailor picks a vessel state after Start new trip. Every track recorded while that token is stored belongs to the trip. The token stays until the next Start new trip. There is no "end trip" action.
A track runs from the moment a vessel state is picked (or Resume track is chosen) until Stop Track or Discard Track. A pause stays inside the track.
Launch¶
- The app checks for a crashed track and ends it (Crash and process-death recovery).
- Upgrade rule: if a device token is stored and no consent, the app stores the consent (a watch paired before the consent existed counts as consented; sync-and-pairing.md).
- Only with consent: the app registers the device when this version is not registered yet (sync-and-pairing.md). Without consent the app sends nothing.
- The splash shows for 2 s, then the start menu opens.
Garmin¶
- Steps 2 and 3 run in
onStart. onStartturns on continuous location events right away, so the GPS can find a fix while the sailor is still in the menus. It stays on until the app ends or a track is paused.- A debug-only end-to-end autopilot can replace the splash (simulator testing; compiled out of release builds). It is not a user feature.
Wear OS¶
- Step 2 runs when the process starts (
SailEverApplication.onCreate), before any request is possible; step 3 inMainActivity.onCreate. - If a track is recording in the process (the activity was closed but the recording service kept running), the app skips the splash and opens straight on the recording screen. A paused track opens on the splash and the start menu.
- The Android system splash (app icon) shows first, then the SailEver splash.
- The start menu asks for the location and notification permissions the first time it is shown. Without location permission a track still records time and vessel state, but no positions.
Apple Watch¶
⏳ Same flow. Crash recovery uses HKHealthStore.recoverActiveWorkoutSession (port plan).
Splash¶
The splash is a navy screen with the name, the website and the app version. It closes after 2 s or on the first input, whichever comes first, and opens the start menu.
| Element | Position | Font / size | Colour | Content & format | Constraints |
|---|---|---|---|---|---|
| Background | full screen | — | Navy 0x0B1B2B |
— | — |
| Name | x 0.50, y 0.42, centred | FONT_LARGE |
Brand orange 0xED6017 |
SailEver |
— |
| Website | x 0.50, y 0.58, centred | FONT_XTINY |
Slate 0x94A3B8 |
sailever.com |
— |
| Version | x 0.50, y 0.86, centred | FONT_XTINY |
Slate 0x94A3B8 |
v + version, e.g. v1.0.0 |
Inside the round safe area |
There is no button-hint screen. The splash is the only screen before the start menu.
Garmin¶
- The version is the
AppVersionstring resource, set by the release script. - Any key, tap, START or BACK closes the splash early.
Wear OS¶
- The version is
BuildConfig.VERSION_NAME. - A tap closes the splash early. Stem buttons do not.
Start menu¶
The start menu is the app's home while no track records. Its items, order and sub-labels are in menus.md. In short:
| Item | Does |
|---|---|
Upload tracks |
Opens the upload screen, or pairing when not paired (the privacy consent first while there is none; sync-and-pairing.md) |
Resume track |
Resumes the paused track at once, no picker |
Continue trip |
Vessel state picker, then a new track in the same trip |
Start new trip |
Vessel state picker, then a new trip and its first track |
Pair device / Unpair device |
sync-and-pairing.md |
Settings |
Alerts and auto-stats (alerts-and-auto-stats.md); on Garmin also the activity type |
Resume track and Continue trip never show together.
Garmin: Sailing and Boating trips¶
Every trip is a Sailing trip or a Boating trip. Start new trip gives the new trip the type set under Settings → Activity Type (alerts-and-auto-stats.md) and stores it with the trip (trip_activity_type, "sailing" or "boating"). The trip keeps it until a new trip starts: changing the setting never changes the open trip. A trip without a stored type (started before 1.1.2) is a Sailing trip.
In a Boating trip:
- every Garmin activity of the trip is a Boating activity (recording.md): the first track, each
Continue tripand each new run afterResume track; - the vessel state picker leaves out
SAILandMOTORSAIL(menus.md), at the start of a track and while recording; - the start menu labels say so (
Continue boating,New boating trip; menus.md).
Everything else (instruments, stats, SLTB upload) is the same as in a Sailing trip; sailed and motorsailed distance stay 0.
Start a track¶
Start new trip¶
- The vessel state picker opens with ENGINE preselected (menus.md).
- Leaving the picker without a choice returns to the start menu. The current trip is kept; nothing is created.
- Picking a state creates the new trip: a new trip token, trip totals and track count reset to zero, the stored last vessel state and any paused track cleared. On Garmin the trip also stores its type, Sailing or Boating (Sailing and Boating trips).
- The track starts with the chosen state (Track start).
Continue trip¶
- The vessel state picker opens with the previous track's state preselected (ENGINE when none is stored).
- Leaving the picker returns to the start menu.
- Picking a state starts a new track in the stored trip.
Track start¶
On every new track (new trip or continued trip):
- A paused track's recording that was never resumed is ended first as a stale track (FINAL segment with END reason 2, queued for upload; see ../shared/track-sync.md).
- A new SLTB session begins: its track id is the start time in Unix seconds.
- All track totals, the state period and the position buffer start at zero.
- The 1 Hz tick starts (recording.md).
- The recording screen opens on the Dashboard.
Garmin¶
A FIT activity (sport Sailing, or Boating in a Boating trip) is created and started with the SailEver developer fields (recording.md).
Wear OS¶
A foreground service of type location starts (TrackingService) with an Ongoing Activity, 1 Hz GPS, the barometer, the battery level and a partial wake lock. It keeps the track recording with the screen off or the app in the background (live-surfaces.md). The chosen vessel state is stored as the last vessel state at once.
Pause¶
Pause Track (action menu / Controls page) pauses the current track:
- The tick stops counting: track time, state times and auto-stats intervals do not advance.
- The GPS is turned off.
- The open SLTB segment is closed and queued. Nothing is uploaded.
- A snapshot of the track is stored, so the track survives the app being closed: all track distances and times, max speed, the state period (distance, speed sum, sample count), the position buffer, the pause start time and the current vessel state (stored as the last vessel state).
- Trip totals are not touched; the track is not booked yet.
- What the sailor sees next is platform-specific (below).
Resuming continues the same track: the snapshot is cleared, the GPS comes back on, the tick restarts, and the recording screen returns to the instrument page shown before.
Garmin¶
The pause view opens. It is a standby screen, not the start menu. The FIT session is stopped in memory, not saved (session.stop()); resume starts it again, so the FIT activity continues.
Wear OS¶
There is no pause view. The foreground service stops (GPS off, wake lock released), the navigation returns to the start menu and the app goes to the background: the watch face shows. The Tile shows the paused track (live-surfaces.md).
Opening the app again shows the splash and the start menu with Resume track (below). While the paused track is still in the process memory, Resume track carries on with it in place (speed chart and position buffer included). If Android ended the process during the pause, it is rebuilt from the stored snapshot. On resume the odometer restarts from the next fix, so the distance does not jump across the pause.
Apple Watch¶
⏳ Pausing pauses the workout session. The pause view uses the reduced-luminance style (port plan).
Resume a paused track from the start menu¶
When the app was closed while a track was paused (on Wear OS: whenever a track is paused), the next launch shows Resume track at the top of the start menu (below Upload tracks when files wait). There is no automatic resume.
Choosing it:
- Rebuilds the track from the snapshot: totals, max speed, state period, position buffer.
- Uses the stored last vessel state (MOORING when none is stored).
- Clears the snapshot and continues the same SLTB session (same track id, next segment number).
- Starts recording immediately: no vessel state picker.
Garmin¶
A new FIT activity starts, because the old one was saved when the app closed (App end). A track paused across an app close therefore shows as two activities in Garmin Connect. SailEver itself sees one track.
Stop¶
Stop Track ends the track (on Wear OS after the stop confirmation):
- The tick stops.
- The track is booked into the trip totals: track count + 1, distance, sailed distance, and motored distance (engine + motorsail).
- The current vessel state is stored as the last vessel state (preselected by the next Continue trip).
- The paused snapshot is cleared.
- The SLTB session closes with a FINAL segment carrying END reason 0 and all track totals (../shared/track-sync.md).
- When the device is paired and this track has queued files, the save view makes one upload attempt for them (sync-and-pairing.md). Then the app ends.
- Otherwise the app ends at once.
The app never returns to the start menu after Stop Track.
Garmin¶
The FIT activity is saved to Garmin Connect (session.save()). The app ends with System.exit(), back to the watch face.
Wear OS¶
The foreground service stops and the activity finishes.
Apple Watch¶
⏳ watchOS apps cannot exit. After the save view the app returns to the start list (port plan).
Discard¶
Discard Track asks for confirmation first (menus.md). On Yes, Discard:
- The tick stops and the paused snapshot is cleared.
- All queued files of this track are deleted.
- Today's totals lose what this track had added.
- If any segment of this track was closed or uploaded, one discard file (FINAL segment with END reason 1) is queued and sent in one silent attempt, so the server deletes the track (sync-and-pairing.md).
- Trip totals and the last vessel state are unchanged; the track was never booked.
- The start menu opens. The trip token is kept, so
Continue tripis offered.
Garmin¶
The FIT recording is discarded (session.discard()): nothing reaches Garmin Connect.
Wear OS¶
The foreground service stops.
What happens to totals and files¶
| Step | Track totals | Trip totals | Last vessel state | Paused snapshot | Local files |
|---|---|---|---|---|---|
| Start new trip (state picked) | reset | reset to 0, count 0 | cleared | cleared | a stale paused track is closed (END reason 2) |
| Continue trip (state picked) | reset | unchanged | unchanged (Wear OS: set to the picked state) | — | — |
| First fix + 15 s, every hour | — | — | — | — | segment closed and queued, silent upload |
| Pause | frozen | unchanged | set | written | open segment closed and queued, no upload |
| Resume | continue | unchanged | — | cleared | — |
| Stop | final | booked (+1 track) | set | cleared | FINAL segment queued, save view |
| Discard | dropped | unchanged | unchanged | cleared | track's files deleted, discard file queued |
| Crash while recording | lost after the last checkpoint | unchanged | unchanged | — | checkpoint closed as FINAL (END reason 2), queued |
Segment rules, checkpoints and the queue budget: ../shared/track-sync.md.
Crash and process-death recovery¶
On launch the app looks for a stored SLTB session that is not paused. If it finds one, the app died while recording. The app:
- Restores what was checkpointed (at most the last 5 minutes are lost).
- Closes it as a FINAL segment with END reason 2 (totals unknown).
- Queues it. It appears under
Upload tracks; there is no automatic upload.
The crashed track is not booked into the trip totals, and the trip token is kept. A paused track is never treated as crashed.
Garmin¶
The check runs in getInitialView, before the splash. A FIT activity in progress at the crash is lost.
Wear OS¶
The check runs when the app opens without a live track in the process. Installing a new APK while recording kills the process and leads here.
App end¶
| Platform | How the app ends | What happens to a track |
|---|---|---|
| Garmin | BACK on the start menu; System.exit() after Stop; the system closing the app |
Recording: the track is stopped and booked like Stop Track, the FIT activity saved, the FINAL segment queued, no upload. Paused: the FIT activity is saved, the paused snapshot and SLTB session stay for Resume track. |
| Wear OS | Swipe right on the start menu; finish() after Stop |
Closing the activity never ends a track: the recording service keeps running. Window swipe-to-dismiss is off on all screens (input.md). The Ongoing Activity or the launcher brings the sailor back. |
| Apple Watch | ⏳ No exit; the system suspends the app | ⏳ The workout session keeps the track alive. |
States and edge cases¶
- No fix for a whole track: nothing is queued, not even a FINAL segment. Stop then ends the app without a save view.
- Stop within the first 15 s: the open segment becomes the FINAL segment and goes through the save view.
- First track of a new trip discarded: the trip token stays with zero tracks, so the start menu shows
Continue tripwith0 tracks, 0.0nm. - Start new trip while a track is paused: the paused track is ended as a stale track (END reason 2) and is not booked into the old trip's totals.
- Settings and pairing during a track: pairing is not reachable (the start menu is only shown while no track records). Settings are reachable on Wear OS from the Controls page, not on Garmin.
Known deviations¶
- Garmin, app closed while recording: the track is booked and ended with END reason 0, but nothing is uploaded and no screen tells the sailor. The files wait under
Upload tracks. - Discarding the first track of a trip leaves
Continue trip — 0 tracks, 0.0nm(see edge cases). Probably unintended.