feat(companion): live location sharing via [LOC] messages

Share and track positions over the mesh through chat messages, separate
from (and parallel to) the 0-hop auto-advert.

Receive / tracking:
- geo::parseLocShare + [LOC] tag (sibling to [WAY]); LiveTrackStore (16
  slots, DM=pubkey/verified, channel=name/best-effort, 20-min expiry).
- MyMesh parses [LOC] on DM + channel receive → UITask live-track table.
- NearbyScreen overlays shares onto contacts (fresher pin) and lists
  non-contact senders; markers (*=DM, ~=chan) + "Loc:" age/verified line.

Send:
- Map menu "Share my pos" (one-shot) → Messages recipient picker.
- New Tools > Live Share tool: Receive (Track loc) + Send (Auto, Target,
  Move/Min gap/Heartbeat, Share now). Target chosen via the Messages
  picker (channel/DM). Movement-gated auto-send engine in UITask loop.

Map / navigation:
- Trail map shows tracked contacts as ◆ markers (ICON_MAP_CONTACT).
- Home "Map" page: mini-map preview + status line; ENTER opens the tool,
  Cancel returns Home.

Prefs: track_shared_loc + loc_share_* (NodePrefs schema 0xC0DE0012).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
MarekZegare4
2026-06-22 10:13:09 +02:00
co-authored by Claude Opus 4.8
parent 29deaaad44
commit d78e74d4cd
14 changed files with 816 additions and 21 deletions
+19
View File
@@ -109,4 +109,23 @@ static inline bool parseLatLon(const char* text, int32_t& lat_1e6, int32_t& lon_
return false;
}
// Tag marking a live position share inside a message: "[LOC]<lat>,<lon>".
// Distinct from WAYPOINT_MSG_TAG: a waypoint is a static point-of-interest to
// save, whereas this announces the *sender's own* current position so the
// receiver can update its bearing/track on that node. Both reuse parseLatLon
// for the coordinate, and both stay readable on firmware/apps that don't know
// the tag (it just looks like a coordinate).
#define LOCATION_MSG_TAG "[LOC]"
// True if `text` carries a LOCATION_MSG_TAG share; fills lat/lon (1e6-scaled)
// from the coordinate that follows it. Returns false for plain text, for a
// bare coordinate, or for a [WAY] share — only an explicit [LOC] tag counts,
// so ordinary messages that happen to contain digits don't move anyone's pin.
static inline bool parseLocShare(const char* text, int32_t& lat_1e6, int32_t& lon_1e6) {
if (!text) return false;
const char* tag = strstr(text, LOCATION_MSG_TAG);
if (!tag) return false;
return parseLatLon(tag + strlen(LOCATION_MSG_TAG), lat_1e6, lon_1e6);
}
} // namespace geo