Skip to content

Fix: Show device settings immediately after opening the app#485

Merged
d4rken merged 1 commit into
mainfrom
fix/cold-start-aap-only-devices
Apr 7, 2026
Merged

Fix: Show device settings immediately after opening the app#485
d4rken merged 1 commit into
mainfrom
fix/cold-start-aap-only-devices

Conversation

@d4rken

@d4rken d4rken commented Apr 7, 2026

Copy link
Copy Markdown
Member

What changed

Device Settings for a paired AirPods profile could briefly appear blank right after opening CAPod if the app was launched before the AirPods had a chance to report their first battery update. The screen now populates immediately, with controls showing up as soon as the AirPods reconnect. Similarly, the Overview card and home-screen widget for that profile now show up during the same window instead of being temporarily absent.

Also fixes a subtle correctness issue: after a model correction on a paired device, a stale cached value could keep shadowing the updated profile model until the next battery message arrived. Profiles now win over cache for model and bonded address lookups, so corrections take effect right away.

Technical Context

Follow-up to #484, which fixed the mid-session case (BLE goes stale while AAP stays alive, profile has a cache). This PR addresses the deferred cold-start case:

  • Reachable window: AapAutoConnect.initialConnect() is keyed on combine(profilesRepo.profiles, bluetoothManager.connectedDevices) — entirely independent of BLE detection. aapManager.allStates[address] populates as soon as connect() is called (state = CONNECTING), before any handshake or battery message. toCachedState() returns null until at least one battery value arrives, so the cache stays empty until the first AAP battery notification (~5-30 s post-READY). Opening DeviceSettings during that window previously hit devices.firstOrNull { it.address == address } == null and rendered a blank screen.
  • Fix shape:
    • PodDevice gained two optional internal fields profileAddress / profileModel and its address / model getters now fall through to them. Call sites are all named-arg so this is additive.
    • Getter precedence is now ble → profile → cache → fallback (was ble → cache → fallback). The profile is the user-authoritative source after a model correction or explicit edit; cache is a stale snapshot. No regression expected for normal flow because cache is re-written on every battery message.
    • DeviceMonitor.devices collapsed the previous two-branch merge (live + cached-only) into a single non-live pass that covers cache-only, AAP-only, and cache+AAP in one mapNotNull. PodDevice's getters already fall through cached and aap independently, so the branches had no reason to be separate.
    • getDeviceForProfile (suspend fallback used by the widget) now looks up AAP state in addition to cache, and plumbs profileAddress / profileModel through.
  • Best-effort de-dupe: if a synthesized non-live device shares its model with an anonymous (no-profile-match) BLE pod, the anonymous pod is hidden. Catches the rare case where a profile has address + identityKey set but BLE detection can't IRK-match (e.g. wrong keys) — we'd otherwise show two cards for the same physical pods. Model match only, since BLE addresses are RPAs and can't be compared directly; at most one anonymous pod per matching model is hidden so a legitimate second physical device of the same model stays visible.
  • Regression tests: 7 new DeviceMonitorTest cases — AAP-only synthesis (CONNECTING + READY), negative case, getDeviceForProfile × 2, stale-cache vs updated profile precedence × 2, de-dupe positive + negative.
  • Out of scope: during CONNECTING/HANDSHAKING the signal badge shows ~5% via computeAapBoost. Minor cosmetic, deferred — fixing it would mean hiding the badge when ble == null or adding a separate AAP-connecting indicator.

Review guidance

  • The precedence change (ble → profile → cache) is intentional and widens beyond the cold-start case. Worth double-checking the two stale-cache tests (stale cache vs updated profile model / address) match your expectation.
  • The de-dupe is a heuristic — it deliberately only hides one anonymous BLE pod per matching model. If you'd prefer to be more conservative (skip de-dupe entirely and live with duplicates in the rare misconfiguration case), the dedupedLiveDevices block can be removed.
  • getDeviceForProfile is hit by BatteryGlanceWidget, so the widget benefits from the same cold-start improvement automatically.

@d4rken d4rken added the bug Something isn't working label Apr 7, 2026
@d4rken d4rken merged commit f67336d into main Apr 7, 2026
10 checks passed
@d4rken d4rken deleted the fix/cold-start-aap-only-devices branch April 7, 2026 13:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant