Commit graph

331 commits

Author SHA1 Message Date
torlando-tech
88f5966fbe fix: address PR #609 code review — volatile fields, dead code, UI dedup, overlap warning
- Add @Volatile to cross-thread power settings fields (KotlinBLEBridge,
  BleScanner, BleAdvertiser) for JMM visibility between Python/Kotlin threads
- Remove dead **power_kwargs from AndroidBLEDriver.start()
- Add ordering-safety comment in AndroidBLEInterface.__init__()
- Upgrade closeImmediate() off-thread log from Log.w to Log.e
- Deduplicate preset values in InterfaceConfigDialog using BlePowerPreset.getSettings()
- Add scan overlap warning when scan duration >= active scan interval

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 20:10:26 -05:00
torlando-tech
0afd5019b1 fix: BLE shutdown cleanup + battery-tunable power settings
Fix timing bug where BLE scanner/advertiser/GATT connections persist after
service restart because System.exit(0) kills the process before the async
Python shutdown chain can complete. Add synchronous stopImmediate() methods
that run directly on Main thread before process exit.

Add user-configurable BLE power settings (Performance/Balanced/Battery Saver/
Custom presets) with per-interface controls for scan interval, scan duration,
and advertising refresh interval. Settings flow through the full stack:
UI → Room DB → Python config → Kotlin BLE bridge → Scanner/Advertiser.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 15:09:44 -05:00
matthieu Texier
8d57749343 perf: use O(1) set membership for telemetry allowlist check
telemetry_allowed_requesters is already a set of lowercase strings;
remove redundant list comprehension that converted to O(n) scan.
2026-03-03 17:12:07 +01:00
matthieu Texier
da84a61351 Fix telemetry host allowlist canonicalization and map self-echo marker filtering 2026-03-03 14:09:39 +01:00
Torlando
4988751a44
Merge pull request #584 from torlando-tech/fix/sentry-triage-anr-oom-duplicates
fix: resolve 4 Sentry issues — ANRs, OOM, duplicate keys
2026-03-02 18:31:53 -05:00
torlando-tech
6900dc2a56 Serialize hot_add_interfaces and defer thread starts until after resource creation
Addresses two concurrency issues flagged in PR review:

1. Add _hot_add_lock to prevent concurrent hot_add_interfaces() calls from
   racing to adopt the same interface — the GIL releases during socket
   syscalls so two coroutines could both bind the same ports.

2. Reorder _add_interface so the UDP data server is created BEFORE any
   discovery threads start. Previously, if _IPv6UDPServer() raised after
   threads were already blocking on recvfrom(), socket cleanup would cause
   OSError in those threads.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 00:39:09 -05:00
torlando-tech
edd6781c85 Clean up sockets on partial failure in _add_interface
Wrap socket/thread/server setup in try/except so that already-created
sockets are closed if a later step fails, preventing EADDRINUSE on retry.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-01 23:31:51 -05:00
torlando-tech
1959dc992d Fix global UDPServer mutation and defer state registration in hot-add
Address Greptile review comments on PR #587:
- Replace global socketserver.UDPServer.address_family mutation with
  _IPv6UDPServer subclass to avoid affecting other UDPServer instances
- Defer adopted_interfaces/link_local_addresses/multicast_echoes
  registration until after socket and thread setup succeeds, preventing
  permanently stale state on partial failure

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-01 22:42:56 -05:00
torlando-tech
bdb6728aba Add Python tests for attachment staging to cover PR diff lines
Tests _write_attachment_staging() directly and exercises field 5/6/7
large-vs-small branching through _on_lxmf_delivery, covering all
19 new executable lines in reticulum_wrapper.py.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-01 19:01:22 -05:00
torlando-tech
9679368f69 test: add unit tests for AutoInterface hot-add and update NetworkChangeManager tests
- Add 23 Python tests for auto_interface_manager.py covering interface
  scanning, filtering (ignored/allowed/adopted), link-local detection,
  hot-add orchestration, and socket/thread setup
- Update 4 NetworkChangeManagerTest tests to match new behavior where
  first network connection triggers callback (needed for hot-add)
- Add 2 ReticulumServiceBinderTest tests for restartAutoInterface()

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-01 18:16:58 -05:00
torlando-tech
ced6fdd597 fix: hot-add network interfaces to AutoInterface on connectivity change
When Columba starts without WiFi (or with only cellular), AutoInterface
scans for network interfaces once during __init__ and never again. If
WiFi connects later, the new wlan0 interface is invisible — no multicast
discovery sockets are created, no peers are found.

Two bugs fixed:

1. NetworkChangeManager only fired on network *switches* (lastNetworkId
   != null), not on first connection. Changed condition to also fire
   when lastNetworkId is null, with the existing binder.isInitialized()
   guard preventing premature invocation during startup.

2. No mechanism existed to tell AutoInterface about new interfaces.
   Added auto_interface_manager.py which surgically hot-adds only NEW
   interfaces to the existing AutoInterface — creating multicast/unicast
   discovery sockets, joining the multicast group, starting discovery
   threads, and creating UDP data servers. This avoids tearing down
   existing peer connections on already-adopted interfaces.

The hot-add runs before the LXMF announce on network change, so the
announce goes out on the newly added interface.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-01 17:42:09 -05:00
torlando-tech
b363654af8 fix: resolve 4 Sentry issues — ANRs, OOM, duplicate keys
COLUMBA-16: Wrap probeLinkSpeed() IPC in withContext(Dispatchers.IO)
to prevent ANR when called from Main thread via MessagingScreen.

COLUMBA-4Q: Convert restoreAnnounceIdentities() to suspend fun and
wrap IPC call in withContext(Dispatchers.IO) to prevent ANR during
interface restart identity restoration.

COLUMBA-4R: Prevent OOM from 222MB+ JSON parse of large attachments.
Python now writes attachments >2MB to staging files instead of
hex-encoding inline. Kotlin adds 10MB size guard before JSONObject
parse and resolves staging files via _binary_ref for direct binary
loading without hex encode/decode overhead.

COLUMBA-3F: Add SELECT DISTINCT to getEnrichedContacts query to
prevent duplicate rows from multi-announce LEFT JOINs, which caused
"Key was already used" crashes in LazyColumn. Defense-in-depth
distinctBy added in ContactsViewModel.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-01 14:30:18 -05:00
matthieu Texier
43d1db8918 fix: include host device location in hosted group telemetry stream
Add 'Myself' as selectable group host and implement host self-telemetry storage so a hosting device includes its own coordinates in group telemetry behavior.

Unify local/remote collector send flow via TelemetryCollectorManager, store local telemetry through storeOwnTelemetry when collector=self, and re-sync Python host mode before local store to prevent intermittent 'Host mode not enabled'.

Also update UI/Settings behavior so host selection and host mode activation are explicit separate controls, and add/adjust tests across Kotlin and Python paths.
2026-02-26 16:13:01 +01:00
torlando-tech
14425c4fff fix: prevent delivered message status from regressing to sent/propagated
LXMF fires spurious failure/sent callbacks after a message is already
confirmed delivered. This triggered propagation retries that overwrote
'delivered' (double checkmark) with 'propagated' (single checkmark).

Python: add _successfully_delivered tracking set (mirrors existing
_successfully_propagated pattern) to guard _on_message_delivered,
_on_message_failed, and _on_message_sent from regressing state.

Kotlin: make 'delivered' truly terminal — block any status update that
would change it to a non-delivered state (defense-in-depth).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-23 16:55:05 -05:00
Torlando
450e8da030
Merge pull request #519 from torlando-tech/fix/announce-interface-lookup-main
fix: announce interface identification when transport disabled
2026-02-21 20:28:34 -05:00
torlando-tech
9b0d0715ca fix: resolve announce interface identification when transport is disabled
The announce_table in RNS is only populated when transport mode is enabled.
On non-transport nodes, interface extraction always returned None, causing
announces to show "Unknown" interface.

Extract interface lookup into python/interface_lookup.py module that checks
announce_table first, then falls back to path_table (which is always
populated regardless of transport mode).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-21 17:31:10 -05:00
Torlando
7748d060fc
Merge pull request #504 from torlando-tech/perf/reduce-battery-drain
perf: reduce battery drain by 88% fewer timer wake-ups
2026-02-21 17:08:52 -05:00
torlando-tech
aee0a642a5 style: use FIELD_FILE_ATTACHMENTS constant instead of magic number
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-20 23:41:21 -05:00
torlando-tech
255c148fec fix: send pending file notification eagerly on propagation fallback
When a large file attachment exceeds the recipient's size limit and
falls back to propagation, notify the recipient immediately rather
than waiting for propagation to succeed. The previous deferred
approach had three bugs that prevented the notification from ever
appearing:

1. Tracking entry was placed after the immediate-success return,
   so synchronous propagation success skipped notification entirely
2. If propagation failed (max_relay_retries_exceeded), the
   notification was never sent since it waited for success
3. Field 16 (APP_EXTENSIONS_FIELD) was not in the meaningful_fields
   set, so the recipient's _on_lxmf_delivery filtered the
   notification as an "empty probe message"

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-20 22:27:29 -05:00
torlando-tech
737b3a724f fix: increase heartbeat stop test timeout to match 5s idle sleep
The idle heartbeat interval was changed from 1s to 5s, but the test's
join(timeout=3) was not updated. The thread may be sleeping when
initialized is set to False and needs up to 5s to wake and check the
loop condition.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-20 18:04:23 -05:00
torlando-tech
79b53cbbf7 cleanup: fix stale comments/logs from heartbeat interval change
Update 4 Python docstrings and 1 log message that still referenced
the old 1s heartbeat interval after it was changed to 5s idle.
Remove dead WifiLock entries from detekt NoRelaxedMocksRule allowlist.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-20 10:19:18 -05:00
torlando-tech
6b3e697ec3 perf: reduce battery drain by 88% fewer timer wake-ups
Remove WiFi lock (WIFI_MODE_FULL_HIGH_PERF) — the battery whitelist already
ensures network access during Doze, and 802.11 power-save mode only adds
100-300ms latency without dropping the TCP connection. Briar operates
successfully without a WiFi lock. This alone reduces WiFi radio power by ~10x
during idle periods.

Increase health check interval from 5s to 30s (stale threshold 10s→60s),
lock refresh interval from 5min to 2h, Python maintenance loop from 1s to
30s, and Python heartbeat idle interval from 1s to 5s. These were all
over-provisioned relative to their actual detection/timeout requirements.

Net effect: timer-driven wake-ups drop from ~7,932/hour to ~960/hour.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-19 12:04:06 -05:00
torlando-tech
ac8e484ff1 Add SOCKS5 proxy UI fields to TCP Client interface config
Wire up the existing TorClientInterface backend to the UI by adding
SOCKS5 proxy toggle, host, and port fields under TCP Client advanced
options. Also fix socket resource leak during reconnect cycles.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-16 19:47:17 -05:00
torlando-tech
749124d2b2 Raise on TorClientInterface deployment failure instead of warning
When both deployment methods fail (filesystem copy and pkgutil),
raise FileNotFoundError so the caller's try/except and has_socks
check can properly fail-fast.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-16 18:53:40 -05:00
torlando-tech
1566e6759d Fail-fast when TorClientInterface deployment fails with SOCKS enabled
If any enabled interface uses SOCKS proxy and deployment fails, return
an error instead of letting RNS hit a confusing module-not-found error.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-16 18:53:40 -05:00
torlando-tech
e64026e01d Address review feedback: deployment robustness, premature online, .onion UX
- Use shutil.copy2 (sibling file via __file__) as primary deployment
  method for TorClientInterface.py, with pkgutil.get_data as fallback
- Remove premature self.online = True before timeout configuration
- Prevent disabling SOCKS proxy toggle when .onion address is entered

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-16 18:53:40 -05:00
torlando-tech
c500ad9096 Address Greptile review: unknown ATYP handling and hostname validation
- Add else clause for unsupported SOCKS5 ATYP values to prevent socket
  desynchronization from unconsumed bind address bytes
- Move hostname length validation into _socks5_handshake() where
  self.target_ip is used, rather than checking config value pre-init

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-16 18:53:40 -05:00
torlando-tech
892163a0cf Replace SOCKS5 monkey-patch with TorClientInterface external module
The previous socket.create_connection monkey-patch wasn't actually
intercepting Reticulum's TCP connections (RNS uses socket.connect()
directly, never create_connection). Replace with a proper RNS external
interface module that subclasses TCPClientInterface.

- Add TorClientInterface.py: overrides connect() with SOCKS5 handshake,
  uses Username/Password auth for Tor stream isolation (different
  circuits per interface), Tor-appropriate keepalive timeouts
- Remove ~175 lines of monkey-patch code from reticulum_wrapper.py
- Deploy interface to {configdir}/interfaces/ via pkgutil (RNS loads
  external interfaces from this directory)
- Config generation emits type=TorClientInterface with proxy_host/port
  when socks_proxy_enabled is set

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-16 18:53:40 -05:00
Claude
dc4946171c fix: use exact-read for SOCKS5 recv and suppress TooManyFunctions
- Replace bare sock.recv() calls with _recv_exact() helper that loops
  until all expected bytes arrive, preventing misparse when TCP delivers
  data in smaller chunks
- Parse SOCKS5 connect response in two stages: 4-byte header first,
  then variable-length bind address based on ATYP
- Add @Suppress("TooManyFunctions") to TcpClientWizardViewModel (17
  functions, threshold 15) since each function maps to a UI control

https://claude.ai/code/session_01DxBMKpvbEp6ckVEnLDb6Cv
2026-02-16 18:53:40 -05:00
Claude
2c2a7ddbac feat: add Tor/Orbot SOCKS5 proxy support for TCP connections (#392)
Enable TCP client interfaces to route connections through a SOCKS5
proxy (e.g., Orbot) for Tor .onion address support. When a user enters
a .onion hostname, the SOCKS proxy toggle auto-enables.

Changes across all layers of the stack:
- Data model: add socksProxyEnabled/Host/Port fields to TCPClient
- Serialization: JSON round-trip through DB and Python config builder
- Python wrapper: minimal SOCKS5 handshake implementation that
  monkey-patches socket.create_connection for proxied targets
- UI: "Connect via Tor (Orbot)" toggle with proxy host/port fields
  in the TCP client wizard review step
- Community servers: add interloper node .onion address

https://claude.ai/code/session_01DxBMKpvbEp6ckVEnLDb6Cv
2026-02-16 18:53:40 -05:00
torlando-tech
b292532d20 fix: batch TX audio frames to eliminate progressive call degradation
Voice calls degraded progressively from 60ms to 130ms per-frame arrival
rate over 20 seconds, even on fast 1-hop local WiFi links. Root cause:
each RNS.Packet.send() holds the Python GIL for encryption (AES-256-CBC
+ HMAC-SHA256) and transport dispatch, creating a feedback loop where
both devices' TX/RX paths compete for GIL time.

Batch 3 audio frames per RNS.Packet.send() call, reducing crypto
overhead from ~16.7 calls/sec to ~5.6 calls/sec (67% reduction).
The LXST wire format already supports frame lists ({0x01: [f1, f2, f3]})
and the receiver already handles both single frames and lists.

Results: packet arrival rate stable at ~60ms/frame for 40+ seconds,
zero silence callbacks, zero PLC, buffer steady at 6-9 frames.

Also updates LXST-kt submodule with adaptive playout drain for ring
buffer latency bounding during packet bursts and speaker toggles.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-16 14:45:06 -05:00
torlando-tech
ed635ac6d8 fix: prevent _call_handler_lock deadlock on call reconnect
__link_closed() was calling Kotlin callbacks (signal + onCallEnded)
while holding _call_handler_lock. The signal triggered Kotlin hangup()
synchronously, running NativePlaybackEngine.destroy() which blocks on
Oboe stream close — keeping the Python lock held for the entire
duration and preventing subsequent call() from acquiring it.

Fix: move Kotlin callbacks outside the lock (same pattern as hangup())
and dispatch hangup() async in Kotlin's STATUS_AVAILABLE handler.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-16 00:22:17 -05:00
Torlando
12aa765f5b
Merge pull request #426 from torlando-tech/claude/distinguish-tcp-announcements-0JUsT
Add filtering options for announce notifications
2026-02-15 15:24:06 -05:00
torlando-tech
30b61f673d fix(test): stabilize flaky establish_link tests with concrete status values
Use concrete string sentinels for Link.ACTIVE/CLOSED instead of
auto-created MagicMock attributes (Mock == Mock comparison is
nondeterministic across Python versions). Also provide extra
time.time() side_effect values and set Transport.active_links = []
to prevent StopIteration on unexpected calls.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-15 15:06:12 -05:00
Torlando
7ea41e0859
Merge pull request #458 from torlando-tech/claude/fix-icon-colors-3zIIT
fix: swap icon foreground/background color order to match LXMF standard
2026-02-14 17:44:59 -05:00
torlando-tech
f4d2076202 fix: voice call prebuffer + audio mode, quieter announce logs
- Update LXST-kt: defer playback stream start until prebuffer fills,
  set MODE_IN_COMMUNICATION for Oboe voice calls
- Reduce announce handler logging from INFO+separators to single DEBUG

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-14 13:12:49 -05:00
Claude
033cd88b86
fix: swap icon foreground/background color order to match LXMF standard
The LXMF icon appearance field (0x04) format used by Sideband and
MeshChat is [icon_name, fg_bytes, bg_bytes], but Columba had the
colors reversed as [icon_name, bg_bytes, fg_bytes]. This caused icons
to render with swapped colors in other LXMF clients while appearing
correct within Columba (which consistently used the wrong order for
both packing and unpacking).

Fixed all packing locations (send_lxmf_message, send_location_telemetry,
send_lxmf_message_with_method, appearance_from_marker_symbol) and all
unpacking locations (_on_lxmf_delivery, poll_received_messages,
unpack_telemetry_stream) to use the correct [name, fg, bg] order.

https://claude.ai/code/session_01XYEj7YKEJhABdHSUaXpiHy
2026-02-13 06:18:47 +00:00
torlando-tech
c92b8f6db9 fix: use correct LXMF error state threshold (0xf0, not 8)
LXMF error states are PR_NO_PATH=0xf0, PR_LINK_FAILED=0xf1,
PR_TRANSFER_FAILED=0xf2 — not sequential from 8. Use >= 0xf0 to match
the actual constant definitions from LXMRouter.py.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-11 22:40:54 -05:00
torlando-tech
5efd4f9b31 fix: prevent missed propagation sync completions on fast transports
On fast transports with cached paths/links, LXMF can complete sync within
milliseconds. The heartbeat loop was in 1-second idle mode and missed the
COMPLETE state entirely, leaving the UI stuck in "syncing" state.

Three coordinated fixes:
- Use -1 sentinel instead of None when resetting propagation state so the
  heartbeat immediately switches to 100ms fast-polling
- Add brief post-request polling loop (up to 2s) to catch fast completions
  before the heartbeat picks up
- Treat IDLE-during-active-sync as sync completion in PropagationNodeManager
  (handles case where COMPLETE was missed but LXMF returned to IDLE)
- Increase SharedFlow extraBufferCapacity from 1 to 5 to prevent dropping
  rapid state transitions

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-11 21:00:14 -05:00
Torlando
60da0fb6e9
Merge pull request #436 from torlando-tech/claude/fix-columba-sync-notification-awwYM
fix: resolve persistent "Syncing with relay..." notification that never dismisses
2026-02-10 20:38:21 -05:00
torlando-tech
3c75120443 fix: flaky Python test from global state leak + remove CI self-cancel
1. test_telemetry_host_mode.py: TestSendTelemetryRequestSuccess.setUp()
   was setting reticulum_wrapper.RETICULUM_AVAILABLE = True without
   restoring it in tearDown(), leaking into other test files on the
   same xdist worker. Now saves/restores all three globals.

2. test_wrapper_path.py: Added defensive @patch to mock-mode test so
   it's immune to global leaks from other test files.

3. ci.yml: Removed gh-run-cancel fail-fast from all jobs. The cancel
   API cancels the entire run including the calling job, making every
   job show "cancelled" with no way to identify which actually failed.
   The ci-passed gate job already handles overall status reporting.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-10 19:35:58 -05:00
torlando-tech
4e12e81b14 fix: align stream appearance JSON keys/format with Kotlin parser
- Change unpack_telemetry_stream appearance keys from name/fg/bg to
  icon_name/foreground_color/background_color to match parseAppearanceJson
- Remove # prefix from hex color strings (Kotlin expects raw RRGGBB)
- Cap _pending_location_events buffer at 100 to prevent unbounded growth
- Remove accidental spec docs from PR
- Update all test assertions to match new key names and format

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-09 23:40:47 -05:00
torlando-tech
71bf6b10bb fix: pass iconAppearance in collector telemetry and fix allowlist docs
- Add identityRepository to TelemetryCollectorManager so collector-bound
  telemetry includes FIELD_ICON_APPEARANCE (matching LocationSharingManager)
- Fix misleading log/comments: empty allowed_requesters set blocks all
  requests (not allows all) — code behavior was already correct
- Update TelemetryCollectorManagerTest with new constructor parameter

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-09 23:06:36 -05:00
torlando-tech
214ad7fd6d fix: address review feedback on tests, docs, and artifacts
- Replace runtime pip install with pytest.skip in test_pr422 (CI-safe)
- Use valid hex colors in MapViewModel appearance test
- Fix appearanceJson docstring to match actual stored schema
- Remove stray artifacts: =0.21.0, AUDIO_DEBUG_ANALYSIS.md,
  dispatcher-audit-report.txt

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-09 22:37:12 -05:00
torlando-tech
f1687eef74 test: add 41 unit tests for icon appearance and byte order changes
Covers accuracy clamping, FIELD_ICON_APPEARANCE byte order
(bg/fg) across all pack/unpack sites, icon name hyphen validation,
startup race buffering, and end-to-end send/receive consistency.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-09 22:04:50 -05:00
torlando-tech
e68cb96591 fix: buffer location events arriving before callback registration
Instead of dropping location events when kotlin_location_received_callback
isn't registered yet (startup race), buffer them and drain on registration.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-09 21:59:03 -05:00
torlando-tech
2917428901 fix: correct FIELD_ICON_APPEARANCE byte order and AIDL nullability
Align all pack/unpack sites with Sideband wire format [name, bg, fg].
Add @nullable to AIDL icon params since they're optional.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-09 21:36:39 -05:00
torlando-tech
750ed78874 fix: detect location-only messages before callback registration
Location detection was gated on kotlin_location_received_callback being
set. Messages arriving before LocationSharingManager registered the
callback (startup race) had is_location_only=False, causing them to
appear as empty chat bubbles.

Now: field detection (FIELD_TELEMETRY, FIELD_COLUMBA_META) runs
unconditionally, only the callback invocation checks registration.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-09 21:15:06 -05:00
torlando-tech
ff16eec6d4 fix: clamp accuracy to unsigned short range in pack_location_telemetry
struct.pack("!H") requires 0-65535 but coarsened locations with
approxRadius > 655m produce values exceeding this, crashing the send.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-09 21:01:16 -05:00
torlando-tech
6c8256481c fix: use appearance from telemetry message for map markers
Two bugs prevented icon rendering on Columba-to-Columba location sharing:

1. Python: location_event dict passed to kotlin_location_received_callback
   never included FIELD_ICON_APPEARANCE data — it was only parsed for the
   message callback path, not the location callback path.

2. Kotlin MapViewModel: marker icons were read exclusively from the RNS
   announce cache (announce?.iconName), ignoring the appearanceJson stored
   in ReceivedLocationEntity. Announce cache only updates on chat messages,
   so icon changes didn't propagate until a regular message was sent.

Fix: parse FIELD_ICON_APPEARANCE into location_event['appearance'] on
receive, and prefer telemetry appearance over announce data in MapViewModel.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-09 20:54:20 -05:00