fd7d6dac4c
android / Build + lint + test (push) Successful in 3m53s
Three related improvements to UPnP/Sonos session handling, on top of the WiFi-lock + drop-suppression fixes. 1. Adopt a running session instead of clear+reload (the headline). Selecting a renderer always did removeAllTracksFromQueue + full reload -- a jarring restart if the speaker was already playing our queue (e.g. after the phone got disconnected but the autonomous Sonos kept going). selectUpnp now probes the renderer first; if it's mid-playback on the same track id at the same queue index, we ATTACH in place: sync the local cursor to its position, wire ActiveUpnpHolder, start polling -- no clear, no reload, and skip/seek immediately drive its live queue. Falls through to clear+reload when it isn't our queue. New PlayerController.moveCursorTo aligns the local cursor without auto-playing. 2. Discovery expiry + selection revert (anti-stickiness). upsertRoute only ever added, so a powered-off renderer lingered in the picker forever and could pin a stale selection. Stamp lastSeen per route; after the picker's active M-SEARCH scan, prune routes that didn't re-announce. A collector reverts the selection to the phone when the selected route leaves discovery while we're not actively casting -- so a later play never targets a ghost. Pruning is tied to picker-open scans only (no background timer -> no row flicker). 3. Reconcile immediately on network recovery. When NetworkStatus flips back to Healthy while a route is active, nudge an immediate poll instead of waiting up to POLL_INTERVAL_MS -- the held session re-confirms the renderer in one round-trip. Verified (read-only): the tap-play-onto-dead-route fallback still fires when the phone's network is Healthy (the poll-loop drop path is unchanged for that case); the drop-suppression gate only holds during phone-side outages, where a local fallback couldn't play either. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>