Phase 4: history view with reverse geocoding
3-section layout (15/70/15) for browsing recorded events. Reverse-geocodes coordinates via Photon API with Haversine distance check and address caching. Also adds simulator GPS polling fallback (Position.getInfo) since the simulator does not fire location event callbacks. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
025d3007db
commit
902121bd42
7 changed files with 368 additions and 11 deletions
|
|
@ -2,21 +2,22 @@ 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.
|
||||
// Single-shot GPS acquisition with a hard timeout. Uses both
|
||||
// enableLocationEvents (fires on real hardware) and a polling
|
||||
// fallback via Position.getInfo() (works in the simulator where
|
||||
// "Set Position" doesn't trigger location events).
|
||||
class GpsService {
|
||||
|
||||
private var _callback as Method(result as Dictionary or Null) as Void;
|
||||
private var _timer as Timer.Timer;
|
||||
private var _pollTimer 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();
|
||||
_pollTimer = new Timer.Timer();
|
||||
}
|
||||
|
||||
function start() as Void {
|
||||
|
|
@ -24,20 +25,34 @@ class GpsService {
|
|||
Position.LOCATION_CONTINUOUS,
|
||||
method(:_onPosition)
|
||||
);
|
||||
// Poll Position.getInfo() every 500ms as fallback for simulator.
|
||||
_pollTimer.start(method(:_poll), 500, true);
|
||||
_timer.start(method(:_onTimeout), Config.GPS_TIMEOUT_MS, false);
|
||||
}
|
||||
|
||||
function _poll() as Void {
|
||||
if (_finished) { return; }
|
||||
var info = Position.getInfo();
|
||||
if (info != null && info.position != null) {
|
||||
_processInfo(info);
|
||||
}
|
||||
}
|
||||
|
||||
function _onPosition(info as Position.Info) as Void {
|
||||
if (_finished) { return; }
|
||||
_processInfo(info);
|
||||
}
|
||||
|
||||
private function _processInfo(info as Position.Info) as Void {
|
||||
if (info == null || info.position == null) { return; }
|
||||
|
||||
var degrees = info.position.toDegrees();
|
||||
var acc = (info.accuracy != null) ? info.accuracy.toFloat() : null;
|
||||
var acc = (info.accuracy != null) ? info.accuracy : null;
|
||||
|
||||
_bestFix = {
|
||||
"lat" => degrees[0].toFloat(),
|
||||
"lon" => degrees[1].toFloat(),
|
||||
"acc" => acc
|
||||
"acc" => (acc != null) ? acc.toFloat() : null
|
||||
};
|
||||
|
||||
// Good enough → stop early.
|
||||
|
|
@ -54,6 +69,7 @@ class GpsService {
|
|||
if (_finished) { return; }
|
||||
_finished = true;
|
||||
_timer.stop();
|
||||
_pollTimer.stop();
|
||||
Position.enableLocationEvents(Position.LOCATION_DISABLE, method(:_onPosition));
|
||||
_callback.invoke(result);
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue