feat(train): the heart-rate strap, recorded by the service itself (ADR 0030, D25-D27)
The Record sheet gains a Heart rate row: a sheet looks for straps advertising the Heart Rate Service, the nearest first, once nearby devices are allowed, and the one chosen is remembered in the person's folder; it records with every workout unless switched off for one. The recorder service connects to it with Android's own Bluetooth API, no package (D26), parses Heart Rate Measurement in Kotlin (tested on the JVM), writes each rate and the strap's state to the recording file, and asks for a lost strap again for as long as it records. The recording screen shows the heart rate, and "Strap lost" while it is gone; a workout's page shows the heart's average and highest rate, each split's average and its chart. iOS's recording waits for a Mac (D25), after the rower. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
This commit is contained in:
parent
405577050d
commit
de5fd43e06
5 changed files with 1782 additions and 42 deletions
|
|
@ -82,5 +82,23 @@
|
|||
"workoutMapSource": "Karte von {name}: sieht die Adresse deines Geräts und den gezeigten Ausschnitt, nicht deine Strecke.",
|
||||
"workoutMapSourceAbout": "Was {name} speichert",
|
||||
"workoutMapUnavailable": "Die Karte kann gerade nicht geladen werden.",
|
||||
"workoutNotFound": "Dieses Training gibt es nicht mehr."
|
||||
"workoutNotFound": "Dieses Training gibt es nicht mehr.",
|
||||
|
||||
"recordHeartRate": "Herzfrequenz",
|
||||
"recordNoStrap": "Kein Brustgurt",
|
||||
"strapSheetTitle": "Brustgurt",
|
||||
"strapSearching": "Suche Brustgurte in der Nähe …",
|
||||
"strapSearchingHint": "Leg den Gurt an: Die meisten wachen erst auf, wenn sie die Haut berühren.",
|
||||
"strapNear": "ganz nah",
|
||||
"strapFar": "weiter weg",
|
||||
"strapUnnamed": "Brustgurt ohne Namen",
|
||||
"strapForget": "Brustgurt vergessen",
|
||||
"strapRefused": "Train braucht Geräte in der Nähe, um deinen Brustgurt zu finden. Du kannst das in den Systemeinstellungen erlauben.",
|
||||
"strapBluetoothOff": "Bluetooth ist aus. Schalte es ein, um deinen Brustgurt zu finden.",
|
||||
"recordingHeartRate": "Herzfrequenz",
|
||||
"recordingStrapLost": "Brustgurt nicht verbunden",
|
||||
"workoutAverageHeartRate": "mittlere Herzfrequenz",
|
||||
"workoutMaxHeartRate": "höchste Herzfrequenz",
|
||||
"workoutBpmValue": "{bpm} bpm",
|
||||
"workoutHeartRateChart": "Herzfrequenz"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -104,5 +104,32 @@
|
|||
"workoutMapSourceAbout": "What {name} keeps",
|
||||
"@workoutMapSourceAbout": {"description": "A link to the map source's page about what it keeps.", "placeholders": {"name": {"type": "String", "example": "OpenFreeMap"}}},
|
||||
"workoutMapUnavailable": "The map can't be loaded now.",
|
||||
"workoutNotFound": "This workout is gone."
|
||||
"workoutNotFound": "This workout is gone.",
|
||||
|
||||
"recordHeartRate": "Heart rate",
|
||||
"recordNoStrap": "No strap",
|
||||
"@recordNoStrap": {"description": "On the Record sheet, under Heart rate, where no strap is chosen."},
|
||||
"strapSheetTitle": "Heart-rate strap",
|
||||
"strapSearching": "Looking for straps nearby…",
|
||||
"strapSearchingHint": "Put the strap on: most wake up only once they touch skin.",
|
||||
"strapNear": "close by",
|
||||
"@strapNear": {"description": "Under a strap found: its signal is strong."},
|
||||
"strapFar": "further off",
|
||||
"@strapFar": {"description": "Under a strap found: its signal is weak."},
|
||||
"strapUnnamed": "Unnamed strap",
|
||||
"@strapUnnamed": {"description": "A strap that sends no name."},
|
||||
"strapForget": "Forget strap",
|
||||
"strapRefused": "Train needs nearby devices to find your strap. You can allow it in the system's settings.",
|
||||
"strapBluetoothOff": "Bluetooth is off. Turn it on to find your strap.",
|
||||
"recordingHeartRate": "heart rate",
|
||||
"@recordingHeartRate": {"description": "Under the heart rate, lower case."},
|
||||
"recordingStrapLost": "Strap lost",
|
||||
"@recordingStrapLost": {"description": "While recording: the strap is out of reach or off; Train keeps asking for it."},
|
||||
"workoutAverageHeartRate": "average heart rate",
|
||||
"@workoutAverageHeartRate": {"description": "Lower case, under a figure."},
|
||||
"workoutMaxHeartRate": "highest heart rate",
|
||||
"@workoutMaxHeartRate": {"description": "Lower case, under a figure."},
|
||||
"workoutBpmValue": "{bpm} bpm",
|
||||
"@workoutBpmValue": {"placeholders": {"bpm": {"type": "int"}}},
|
||||
"workoutHeartRateChart": "Heart rate"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -555,6 +555,108 @@ abstract class AppLocalizations {
|
|||
/// In en, this message translates to:
|
||||
/// **'This workout is gone.'**
|
||||
String get workoutNotFound;
|
||||
|
||||
/// No description provided for @recordHeartRate.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Heart rate'**
|
||||
String get recordHeartRate;
|
||||
|
||||
/// On the Record sheet, under Heart rate, where no strap is chosen.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'No strap'**
|
||||
String get recordNoStrap;
|
||||
|
||||
/// No description provided for @strapSheetTitle.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Heart-rate strap'**
|
||||
String get strapSheetTitle;
|
||||
|
||||
/// No description provided for @strapSearching.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Looking for straps nearby…'**
|
||||
String get strapSearching;
|
||||
|
||||
/// No description provided for @strapSearchingHint.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Put the strap on: most wake up only once they touch skin.'**
|
||||
String get strapSearchingHint;
|
||||
|
||||
/// Under a strap found: its signal is strong.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'close by'**
|
||||
String get strapNear;
|
||||
|
||||
/// Under a strap found: its signal is weak.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'further off'**
|
||||
String get strapFar;
|
||||
|
||||
/// A strap that sends no name.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Unnamed strap'**
|
||||
String get strapUnnamed;
|
||||
|
||||
/// No description provided for @strapForget.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Forget strap'**
|
||||
String get strapForget;
|
||||
|
||||
/// No description provided for @strapRefused.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Train needs nearby devices to find your strap. You can allow it in the system\'s settings.'**
|
||||
String get strapRefused;
|
||||
|
||||
/// No description provided for @strapBluetoothOff.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Bluetooth is off. Turn it on to find your strap.'**
|
||||
String get strapBluetoothOff;
|
||||
|
||||
/// Under the heart rate, lower case.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'heart rate'**
|
||||
String get recordingHeartRate;
|
||||
|
||||
/// While recording: the strap is out of reach or off; Train keeps asking for it.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Strap lost'**
|
||||
String get recordingStrapLost;
|
||||
|
||||
/// Lower case, under a figure.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'average heart rate'**
|
||||
String get workoutAverageHeartRate;
|
||||
|
||||
/// Lower case, under a figure.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'highest heart rate'**
|
||||
String get workoutMaxHeartRate;
|
||||
|
||||
/// No description provided for @workoutBpmValue.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'{bpm} bpm'**
|
||||
String workoutBpmValue(int bpm);
|
||||
|
||||
/// No description provided for @workoutHeartRateChart.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Heart rate'**
|
||||
String get workoutHeartRateChart;
|
||||
}
|
||||
|
||||
class _AppLocalizationsDelegate
|
||||
|
|
|
|||
|
|
@ -298,4 +298,60 @@ class AppLocalizationsDe extends AppLocalizations {
|
|||
|
||||
@override
|
||||
String get workoutNotFound => 'Dieses Training gibt es nicht mehr.';
|
||||
|
||||
@override
|
||||
String get recordHeartRate => 'Herzfrequenz';
|
||||
|
||||
@override
|
||||
String get recordNoStrap => 'Kein Brustgurt';
|
||||
|
||||
@override
|
||||
String get strapSheetTitle => 'Brustgurt';
|
||||
|
||||
@override
|
||||
String get strapSearching => 'Suche Brustgurte in der Nähe …';
|
||||
|
||||
@override
|
||||
String get strapSearchingHint =>
|
||||
'Leg den Gurt an: Die meisten wachen erst auf, wenn sie die Haut berühren.';
|
||||
|
||||
@override
|
||||
String get strapNear => 'ganz nah';
|
||||
|
||||
@override
|
||||
String get strapFar => 'weiter weg';
|
||||
|
||||
@override
|
||||
String get strapUnnamed => 'Brustgurt ohne Namen';
|
||||
|
||||
@override
|
||||
String get strapForget => 'Brustgurt vergessen';
|
||||
|
||||
@override
|
||||
String get strapRefused =>
|
||||
'Train braucht Geräte in der Nähe, um deinen Brustgurt zu finden. Du kannst das in den Systemeinstellungen erlauben.';
|
||||
|
||||
@override
|
||||
String get strapBluetoothOff =>
|
||||
'Bluetooth ist aus. Schalte es ein, um deinen Brustgurt zu finden.';
|
||||
|
||||
@override
|
||||
String get recordingHeartRate => 'Herzfrequenz';
|
||||
|
||||
@override
|
||||
String get recordingStrapLost => 'Brustgurt nicht verbunden';
|
||||
|
||||
@override
|
||||
String get workoutAverageHeartRate => 'mittlere Herzfrequenz';
|
||||
|
||||
@override
|
||||
String get workoutMaxHeartRate => 'höchste Herzfrequenz';
|
||||
|
||||
@override
|
||||
String workoutBpmValue(int bpm) {
|
||||
return '$bpm bpm';
|
||||
}
|
||||
|
||||
@override
|
||||
String get workoutHeartRateChart => 'Herzfrequenz';
|
||||
}
|
||||
|
|
|
|||
|
|
@ -295,6 +295,62 @@ class AppLocalizationsEn extends AppLocalizations {
|
|||
|
||||
@override
|
||||
String get workoutNotFound => 'This workout is gone.';
|
||||
|
||||
@override
|
||||
String get recordHeartRate => 'Heart rate';
|
||||
|
||||
@override
|
||||
String get recordNoStrap => 'No strap';
|
||||
|
||||
@override
|
||||
String get strapSheetTitle => 'Heart-rate strap';
|
||||
|
||||
@override
|
||||
String get strapSearching => 'Looking for straps nearby…';
|
||||
|
||||
@override
|
||||
String get strapSearchingHint =>
|
||||
'Put the strap on: most wake up only once they touch skin.';
|
||||
|
||||
@override
|
||||
String get strapNear => 'close by';
|
||||
|
||||
@override
|
||||
String get strapFar => 'further off';
|
||||
|
||||
@override
|
||||
String get strapUnnamed => 'Unnamed strap';
|
||||
|
||||
@override
|
||||
String get strapForget => 'Forget strap';
|
||||
|
||||
@override
|
||||
String get strapRefused =>
|
||||
'Train needs nearby devices to find your strap. You can allow it in the system\'s settings.';
|
||||
|
||||
@override
|
||||
String get strapBluetoothOff =>
|
||||
'Bluetooth is off. Turn it on to find your strap.';
|
||||
|
||||
@override
|
||||
String get recordingHeartRate => 'heart rate';
|
||||
|
||||
@override
|
||||
String get recordingStrapLost => 'Strap lost';
|
||||
|
||||
@override
|
||||
String get workoutAverageHeartRate => 'average heart rate';
|
||||
|
||||
@override
|
||||
String get workoutMaxHeartRate => 'highest heart rate';
|
||||
|
||||
@override
|
||||
String workoutBpmValue(int bpm) {
|
||||
return '$bpm bpm';
|
||||
}
|
||||
|
||||
@override
|
||||
String get workoutHeartRateChart => 'Heart rate';
|
||||
}
|
||||
|
||||
/// The translations for English, as used in the United Kingdom (`en_GB`).
|
||||
|
|
|
|||
Loading…
Reference in a new issue