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); } }