fix(train): a recording followed whichever starts first, the app listening or the service
On a fresh install (GrapheneOS) a walk recorded no GPS on the screen, which waited for GPS throughout though the file was whole. The app listens to the recorder's lines as soon as it asked for the start, and Android starts the service a moment later: listening first, it got nothing of the file, and the service then wrote the header straight to the file, past what hands each line on. The reader takes the first line it gets as the header, found none, and dropped every line after. The service now writes its header through write() as every line, and the app takes the header from the file's first line where lines came without one (RecordingReader.recover, on the next second), reading the lines kept since after it, so either order and either recorder version is followed. The fake recorder can now begin late, as the service does. On foot, auto-pause by steps paused after 5 s and went on only with a step where the phone has no step detector or one switched off (GrapheneOS' Sensors): GPS speed decides there, and until the first step and once the detector has been silent 30 s. A minute without any fix, the recording screen asks whether location is on and precise. ADR 0030, D65. Whats-New: A recording follows its GPS again on a phone where it waited for GPS throughout. Whats-New: Auto-pause on foot goes by GPS speed where the phone counts no steps. Whats-New: After a minute without GPS, the recording screen asks whether location is on and precise. Whats-New-De: Eine Aufzeichnung folgt ihrem GPS wieder auf Handys, auf denen sie die ganze Zeit auf GPS wartete. Whats-New-De: Die automatische Pause zu Fuß richtet sich nach der GPS-Geschwindigkeit, wo das Handy keine Schritte zählt. Whats-New-De: Nach einer Minute ohne GPS fragt die Aufzeichnung, ob der Standort an und genau ist. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
This commit is contained in:
parent
bcbefe38a3
commit
d305725bb5
3 changed files with 252 additions and 21 deletions
|
|
@ -36,6 +36,24 @@ void main() {
|
|||
expect(live.pace, isNull);
|
||||
});
|
||||
|
||||
test('a minute without any fix, from Start or since the last, says GPS is '
|
||||
'silent (D65); a row never', () {
|
||||
Recording run(List<Fix> fixes) =>
|
||||
Recording(activity: Activity.walking, startedAt: start, fixes: fixes);
|
||||
|
||||
expect(liveOf(run([]), at(59)).gpsSilent, isFalse);
|
||||
expect(liveOf(run([]), at(60)).gpsSilent, isTrue);
|
||||
expect(liveOf(run([north(30, 0)]), at(89)).gpsSilent, isFalse);
|
||||
expect(liveOf(run([north(30, 0)]), at(90)).gpsSilent, isTrue);
|
||||
expect(
|
||||
liveOf(
|
||||
Recording(activity: Activity.indoorRowing, startedAt: start),
|
||||
at(600),
|
||||
).gpsSilent,
|
||||
isFalse,
|
||||
);
|
||||
});
|
||||
|
||||
test('moving, it shows the distance and the pace of the last 30 seconds', () {
|
||||
final live = liveOf(
|
||||
Recording(
|
||||
|
|
|
|||
|
|
@ -202,6 +202,38 @@ void main() {
|
|||
expect(platform.started, hasLength(1));
|
||||
});
|
||||
|
||||
test('a service that began after the app listened, its header written to '
|
||||
'the file alone, is followed all the same (D65)', () async {
|
||||
platform.beginsLate = true;
|
||||
var now = start;
|
||||
await withClock(Clock(() => now), () async {
|
||||
final bloc = await open();
|
||||
bloc.add(const RecordingEventStartRequested(Activity.walking));
|
||||
await bloc.stream.firstWhere((s) => s.phase == RecordingPhase.recording);
|
||||
await pumpEventQueue();
|
||||
|
||||
platform
|
||||
..begin()
|
||||
..now = start.add(const Duration(seconds: 1))
|
||||
..write('p @ 0 0 500 -')
|
||||
..now = start.add(const Duration(seconds: 30))
|
||||
..write('p @ ${(100 * 89.932).round()} 0 500 -');
|
||||
now = start.add(const Duration(seconds: 30));
|
||||
final live = (await tick(bloc)).live!;
|
||||
|
||||
expect(live.distanceMetres, 100);
|
||||
expect(live.movingTime, const Duration(seconds: 30));
|
||||
expect(platform.notices.last.text, '100 m');
|
||||
|
||||
// What comes after is taken in once, as it comes.
|
||||
platform
|
||||
..now = start.add(const Duration(seconds: 60))
|
||||
..write('p @ ${(200 * 89.932).round()} 0 500 -');
|
||||
now = start.add(const Duration(seconds: 60));
|
||||
expect((await tick(bloc)).live!.distanceMetres, 200);
|
||||
});
|
||||
});
|
||||
|
||||
test('it follows the service: distance, an automatic pause, a pause by '
|
||||
'hand, and shows each change in the notification', () async {
|
||||
var now = start;
|
||||
|
|
|
|||
|
|
@ -59,8 +59,14 @@ class FakeRecorderPlatform({
|
|||
/// Pauses and resumes by hand, in order.
|
||||
final byHand = <bool>[];
|
||||
|
||||
/// Whether the service begins only when told ([begin]), after the app
|
||||
/// listened, and then writes its header to the file alone, unstreamed:
|
||||
/// what Train up to 0.3.0 did on a fresh install (ADR 0030, D65).
|
||||
bool beginsLate = false;
|
||||
|
||||
File? _file;
|
||||
StreamController<String>? _lines;
|
||||
String? _header;
|
||||
|
||||
/// The time the next line is written at.
|
||||
DateTime now = DateTime.utc(2026, 10, 28, 7, 30);
|
||||
|
|
@ -114,13 +120,18 @@ class FakeRecorderPlatform({
|
|||
wheelMm: wheelMm,
|
||||
));
|
||||
notices.add(notice);
|
||||
_file = File(path)
|
||||
..writeAsStringSync(
|
||||
_header =
|
||||
'halkyon-train-recording 1 ${activity.name} '
|
||||
'${now.millisecondsSinceEpoch}\n',
|
||||
);
|
||||
'${now.millisecondsSinceEpoch}';
|
||||
_file = File(path);
|
||||
if (!beginsLate) {
|
||||
begin();
|
||||
}
|
||||
}
|
||||
|
||||
/// The service begins: its header written to the file, not to the lines.
|
||||
void begin() => _file!.writeAsStringSync('$_header\n');
|
||||
|
||||
@override
|
||||
Future<void> pause() async {
|
||||
byHand.add(true);
|
||||
|
|
@ -180,7 +191,10 @@ class FakeRecorderPlatform({
|
|||
@override
|
||||
Stream<String> lines() {
|
||||
final controller = _lines = StreamController<String>();
|
||||
_file!.readAsLinesSync().forEach(controller.add);
|
||||
final file = _file!;
|
||||
if (file.existsSync()) {
|
||||
file.readAsLinesSync().forEach(controller.add);
|
||||
}
|
||||
return controller.stream;
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue