Skip to content

feat(connections): list only BLE devices visible via scan#5877

Merged
jamesarich merged 1 commit into
mainfrom
claude/serene-pasteur-efa8e1
Jun 20, 2026
Merged

feat(connections): list only BLE devices visible via scan#5877
jamesarich merged 1 commit into
mainfrom
claude/serene-pasteur-efa8e1

Conversation

@jamesarich

Copy link
Copy Markdown
Collaborator

The Connections BLE picker listed every system-bonded device returned by BluetoothAdapter.getBondedDevices() unconditionally — even with no scan running and even when the radio wasn't nearby — so previously paired devices cluttered the list. This restricts the BLE list to what's actually visible via the current scan.

🌟 Changes

  • Scan-only BLE list: ScannerViewModel.bleDevicesForUi now surfaces a bonded device only when the active scan currently sees it (advertising). Stale system-bonded peripherals that aren't nearby are hidden.
  • Bonded routing preserved: a bonded device that is picked up by the scan still appears flagged bonded, so tapping it connects directly instead of re-running the pairing dialog. Unbonded scanned devices are unchanged (shown, routed through bonding on tap).
  • Active connection stays visible: the currently-selected device is kept regardless of scan visibility. A connected radio stops advertising and would otherwise drop out of the list mid-session — which would also strip the connected card's live RSSI readout. The selected address was added to the combine for this.
  • Sort order is unchanged: scanned-bonded first (by name), then unbonded scanned in discovery order; RSSI updates don't re-sort.

🛠️ Notes for reviewers

  • Android-only in effect. The desktop/JVM CommonGetDiscoveredDevicesUseCase never populates bleDevices, so its picker was already scan-driven — the common-code change filters an already-empty list there.
  • Empty-state implication. With BLE auto-scan off and nothing selected, the BLE section now shows its existing empty state until the user scans, rather than listing stale bonded devices. bleAutoScan (auto-starts the scan on screen open) governs whether the list populates immediately.

Testing Performed

feature:connections ScannerViewModelTest (commonTest; runs on both JVM and AndroidHostTest):

  • Rewrote bleDevicesForUi shows bonded devices only once they are visible via scan (was ... sorts by bonded then discovery order, which asserted the now-removed "bonded always shows pre-scan" behavior). Verifies a bonded device stays hidden until the scan sees it, then appears bonded and sorted first, and that RSSI updates don't re-sort.
  • Added bleDevicesForUi keeps the selected device visible even when not seen via scan.
  • Harness now exposes currentDeviceAddressFlow to drive the selected-device path.

Verified locally: ./gradlew spotlessApply spotlessCheck detekt pass; :feature:connections:allTests --tests "*ScannerViewModelTest*" — all 12 pass.

The Connections BLE list merged system-bonded devices (from
BluetoothAdapter.getBondedDevices()) into the picker unconditionally —
even with no scan running and even when the radio wasn't nearby — so
previously paired devices cluttered the list.

Restrict bleDevicesForUi to devices the active scan currently sees. A
bonded device still appears (and keeps its direct-connect routing)
once it's picked up by the scan; bonded devices that aren't advertising
are hidden. The currently-selected device is kept regardless of scan
visibility so the active connection stays visible and disconnectable —
a connected radio stops advertising and would otherwise drop out,
taking the connected card's live RSSI with it.

Android-only in effect: the desktop/JVM use case never populated the
bonded list, so its picker was already scan-driven.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@github-actions github-actions Bot added the enhancement New feature or request label Jun 20, 2026
@codecov

codecov Bot commented Jun 20, 2026

Copy link
Copy Markdown

❌ 1 Tests Failed:

Tests completed Failed Passed Skipped
2932 1 2931 0
View the top 1 failed test(s) by shortest run time
org.meshtastic.core.data.repository.DeviceLinkRepositoryImplTest::reconcilePrunesShortCodesNoLongerInCatalog()[jvm]
Stack Traces | 0.141s run time
org.opentest4j.AssertionFailedError: expected: <[a]> but was: <[a, b]>
	at kotlin.test.junit5.JUnit5Asserter.assertEquals(JUnitSupport.kt:32)
	at kotlin.test.AssertionsKt__AssertionsKt.assertEquals(Assertions.kt:63)
	at kotlin.test.AssertionsKt.assertEquals(Unknown Source)
	at kotlin.test.AssertionsKt__AssertionsKt.assertEquals$default(Assertions.kt:62)
	at kotlin.test.AssertionsKt.assertEquals$default(Unknown Source)
	at org.meshtastic.core.data.repository.DeviceLinkRepositoryImplTest$reconcilePrunesShortCodesNoLongerInCatalog$1.invokeSuspend(DeviceLinkRepositoryImplTest.kt:172)
	at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:34)
	at kotlinx.coroutines.UndispatchedCoroutine.afterResume(CoroutineContext.kt:278)
	at kotlinx.coroutines.AbstractCoroutine.resumeWith(AbstractCoroutine.kt:101)
	at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:47)
	at kotlinx.coroutines.internal.DispatchedContinuationKt.resumeCancellableWithInternal(DispatchedContinuation.kt:384)
	at kotlinx.coroutines.DispatchedCoroutine.afterResume(Builders.common.kt:588)
	at kotlinx.coroutines.AbstractCoroutine.resumeWith(AbstractCoroutine.kt:101)
	at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:47)
	at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:100)
	at kotlinx.coroutines.internal.LimitedDispatcher$Worker.run(LimitedDispatcher.kt:124)
	at kotlinx.coroutines.scheduling.TaskImpl.run(Tasks.kt:89)
	at kotlinx.coroutines.scheduling.CoroutineScheduler.runSafely(CoroutineScheduler.kt:586)
	at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.executeTask(CoroutineScheduler.kt:798)
	at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.runWorker(CoroutineScheduler.kt:717)
	at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(CoroutineScheduler.kt:704)

To view more test analytics, go to the Test Analytics Dashboard
📋 Got 3 mins? Take this short survey to help us improve Test Analytics.

@jamesarich jamesarich added this pull request to the merge queue Jun 20, 2026
Merged via the queue into main with commit 53cb156 Jun 20, 2026
19 checks passed
@jamesarich jamesarich deleted the claude/serene-pasteur-efa8e1 branch June 20, 2026 13:00
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant