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>
This commit is contained in:
parent
d3494acc0d
commit
025d3007db
9 changed files with 332 additions and 17 deletions
60
source/GpsService.mc
Normal file
60
source/GpsService.mc
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
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);
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue