75 lines
4.6 KiB
Markdown
75 lines
4.6 KiB
Markdown
|
|
# ADR 0037: Train's reminders to move, workouts suggested, and the Loop's own detection
|
|||
|
|
|
|||
|
|
- **Status:** decided 2 Nov 2026, by the developer (D1–D3, each the agent's
|
|||
|
|
recommendation), the rest proposed by the agent and flagged where it chose.
|
|||
|
|
- **Builds on:** ADR 0030 (Train: D19 no background location, D21 the recorder, D37 to
|
|||
|
|
D39 the Loop's days and the sessions it detects).
|
|||
|
|
|
|||
|
|
## Context
|
|||
|
|
|
|||
|
|
The developer asked for two things around the Polar Loop: to be reminded to get up when
|
|||
|
|
idle for some 30 minutes (not while driving, not at lunch), and for Train to start or at
|
|||
|
|
least suggest a workout from movement, such as a walk when one is on a stroll.
|
|||
|
|
|
|||
|
|
Polar's SDK (8.3.0) was read for both. The band's own inactivity alert is an on/off flag
|
|||
|
|
in its general settings, which the SDK's public API doesn't set (`PolarUserDeviceSettings`
|
|||
|
|
lacks it; only a low-level write of the settings file would); the band tells a phone of
|
|||
|
|
an alert as a device-to-host notification (`INACTIVITY_ALERT`), over a live connection
|
|||
|
|
alone, which would mean a foreground service holding the band all day. The SDK does set
|
|||
|
|
the band's automatic training detection (`setAutomaticTrainingDetectionSettings`: on or
|
|||
|
|
off, a sensitivity, a minimum duration in seconds) and reads it back
|
|||
|
|
(`getUserDeviceSettings`); the sensitivity's range is documented nowhere (Polar Flow
|
|||
|
|
offers low, medium and high).
|
|||
|
|
|
|||
|
|
D19 rules out background location, and Train uses no Play services, so Android's
|
|||
|
|
activity recognition (a Play service) is out too.
|
|||
|
|
|
|||
|
|
## Decision
|
|||
|
|
|
|||
|
|
1. **The reminder counts the phone's steps** (D1, the recommendation taken). A
|
|||
|
|
WorkManager job every 15 minutes (its shortest period; `MovementWorker.kt`), while
|
|||
|
|
reminders or suggestions are on, reads Android's step counter (`TYPE_STEP_COUNTER`,
|
|||
|
|
kept by the phone's own low-power hardware, under the activity permission the
|
|||
|
|
recorder asks for already) and keeps the last two hours of readings in
|
|||
|
|
`movement_state.json` (the app's files, never synced, never backed up, D53). It
|
|||
|
|
reminds where fewer than 50 steps were counted over the idle time the person chose
|
|||
|
|
(30 minutes by default), all of it within their hours and outside their break, on
|
|||
|
|
their days, at most once each idle time, never while Train records, and never while
|
|||
|
|
the phone is in a car: Android's car mode (Android Auto) or a car's Bluetooth audio or
|
|||
|
|
hands-free connected (`CarCheck`; nothing else tells a car without location). A
|
|||
|
|
reminder can come up to 15 minutes late. *Alternatives:* the band's own alert (a
|
|||
|
|
foreground service connected all day, its setting written below the SDK's API, no car
|
|||
|
|
detection), or both.
|
|||
|
|
|
|||
|
|
2. **A workout is suggested from the steps, and the band detects its own** (D2, the
|
|||
|
|
recommendation taken). The same job: where the last reading's span (10 to 25
|
|||
|
|
minutes) averaged a walking cadence (80 steps a minute or more) or a running one (140
|
|||
|
|
or more) while nothing records, a notification asks "Walking?" or "Running?", at most
|
|||
|
|
once an hour; tapped, it opens the Record sheet for that activity (the minutes before
|
|||
|
|
aren't recorded: the band's detection catches those). The Loop's own automatic
|
|||
|
|
training detection is switched and tuned on the band's screen (on or off, and the
|
|||
|
|
shortest session it keeps: 10, 15, 20 or 30 minutes), so a session nobody recorded
|
|||
|
|
still comes in afterwards as D39's. Its sensitivity stays as the band has it (agent's
|
|||
|
|
choice): the SDK documents no range, and a guessed value could set the band wrong.
|
|||
|
|
*Alternatives:* the phone's suggestion alone, or the band's detection alone.
|
|||
|
|
|
|||
|
|
3. **Off until turned on; then every 30 minutes, weekdays 9:00 to 18:00, not 12:00 to
|
|||
|
|
13:00** (D3, the recommendation taken). Settings' *Moving more* screen holds both
|
|||
|
|
switches (the suggestion its own, also off) and every value: the idle time (15 to 120
|
|||
|
|
minutes), the hours, the break, the days. Turning either on asks for the activity
|
|||
|
|
and notification permissions; refused, it stays off. The settings are the person's,
|
|||
|
|
on this device alone (`movement.json` in their folder, never synced), and hold for
|
|||
|
|
the person whose app is open: the job follows whoever was last open.
|
|||
|
|
|
|||
|
|
4. **The job's words come from the app** (agent's choice): the app writes the
|
|||
|
|
notifications' titles and texts in the person's language into the job's
|
|||
|
|
configuration whenever it configures it, since the job runs while the app is closed
|
|||
|
|
and Android's resources hold none of Train's texts.
|
|||
|
|
|
|||
|
|
## Consequences
|
|||
|
|
|
|||
|
|
- No server change and no migration.
|
|||
|
|
- A phone without a step counter (rare; most since Android 4.4) offers neither: the
|
|||
|
|
screen says so.
|
|||
|
|
- The logic deciding a reminder or a suggestion is pure Kotlin (`Movement.kt`), tested
|
|||
|
|
on the JVM as the recorder's.
|