einsatzprotokoll/source/GpsService.mc
EiSiMo 025d3007db Phase 3: event creation flow (GPS + success / error)
GpsService runs a single-shot position request with a hard timeout
and early-exit when Config.GPS_TARGET_ACCURACY_M is reached.
LoadingView renders a circular progress bar around the edge plus the
"Standort wird bestimmt" prompt; on callback it persists a new Event
via EventStore.add and transitions to SuccessView (green checkmark,
short vibration, auto-close) or ErrorView (red alert, 3× vibration,
German message, longer hold). TextUtils extracts the shared
multi-line centered text rendering so MenuView, LoadingView and
ErrorView all render wrapped German text consistently. Positioning
permission added to manifest.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-11 20:36:22 +02:00

60 lines
1.9 KiB
MonkeyC

import Toybox.Lang;
import Toybox.Position;
import Toybox.Timer;
// Single-shot GPS acquisition with a hard timeout. Caller supplies
// a callback that fires exactly once with either a position fix or
// null (if nothing usable was received before the timeout). Accuracy
// shortcut: if the fix already hits Config.GPS_TARGET_ACCURACY_M we
// stop waiting even if the timeout hasn't expired.
class GpsService {
private var _callback as Method(result as Dictionary or Null) as Void;
private var _timer as Timer.Timer;
private var _finished as Boolean = false;
private var _bestFix as Dictionary or Null = null;
function initialize(callback as Method(result as Dictionary or Null) as Void) {
_callback = callback;
_timer = new Timer.Timer();
}
function start() as Void {
Position.enableLocationEvents(
Position.LOCATION_CONTINUOUS,
method(:_onPosition)
);
_timer.start(method(:_onTimeout), Config.GPS_TIMEOUT_MS, false);
}
function _onPosition(info as Position.Info) as Void {
if (_finished) { return; }
if (info == null || info.position == null) { return; }
var degrees = info.position.toDegrees();
var acc = (info.accuracy != null) ? info.accuracy.toFloat() : null;
_bestFix = {
"lat" => degrees[0].toFloat(),
"lon" => degrees[1].toFloat(),
"acc" => acc
};
// Good enough → stop early.
if (acc != null && acc <= Config.GPS_TARGET_ACCURACY_M) {
_finish(_bestFix);
}
}
function _onTimeout() as Void {
_finish(_bestFix);
}
function _finish(result as Dictionary or Null) as Void {
if (_finished) { return; }
_finished = true;
_timer.stop();
Position.enableLocationEvents(Position.LOCATION_DISABLE, method(:_onPosition));
_callback.invoke(result);
}
}