From 9ed28e11d9571c6c8b1d94bfe778a0dd56310a98 Mon Sep 17 00:00:00 2001 From: lamtruong28 Date: Sat, 27 Jun 2026 14:14:44 +0700 Subject: [PATCH] [Fix] Scanner item flicker --- .../screens/scanner/ScannerViewModel.kt | 26 ++++++++++++++----- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/composeApp/src/commonMain/kotlin/com/digitoolsolutions/app/torquevaultkmp/screens/scanner/ScannerViewModel.kt b/composeApp/src/commonMain/kotlin/com/digitoolsolutions/app/torquevaultkmp/screens/scanner/ScannerViewModel.kt index 04c16b4..cdeb465 100644 --- a/composeApp/src/commonMain/kotlin/com/digitoolsolutions/app/torquevaultkmp/screens/scanner/ScannerViewModel.kt +++ b/composeApp/src/commonMain/kotlin/com/digitoolsolutions/app/torquevaultkmp/screens/scanner/ScannerViewModel.kt @@ -37,6 +37,7 @@ import kotlinx.coroutines.withTimeout import kotlinx.coroutines.TimeoutCancellationException import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.map +import kotlin.time.Duration.Companion.milliseconds import kotlin.time.Duration.Companion.seconds import kotlin.time.TimeSource import kotlin.uuid.ExperimentalUuidApi @@ -45,6 +46,10 @@ data class ConnectedDevice( val advertisement: Advertisement, val state: State ) +data class AdvertisementMark( + val advertisement: Advertisement, + val timeMark: TimeSource.Monotonic.ValueTimeMark +) @OptIn(ExperimentalUuidApi::class) class ScannerViewModel( private val bleManager: BleManager, @@ -54,7 +59,7 @@ class ScannerViewModel( private val scannerRepository: ScannerRepository ) : ViewModel() { companion object { - private val SCAN_TIMEOUT = 1.seconds + private val SCAN_TIMEOUT = 5.seconds private val CONNECTION_TIMEOUT = 5.seconds private const val MAX_LOG_ENTRIES = 50 } @@ -79,7 +84,7 @@ class ScannerViewModel( private var cleanupJob: Job? = null private val connectionJobs = mutableMapOf() - private val advertisementMap = mutableMapOf>() + private val advertisementMap = mutableMapOf() /** Bonded device */ private val bondedIds = mutableSetOf() // Cache @@ -118,7 +123,12 @@ class ScannerViewModel( connect(advertisement) } else { val now = TimeSource.Monotonic.markNow() - advertisementMap[advertisement.identifier.toString()] = advertisement to now + val entry = advertisementMap[bleId] + if (entry != null) { + advertisementMap[bleId] = entry.copy(advertisement = advertisement, timeMark = now) + } else { + advertisementMap[bleId] = AdvertisementMark(advertisement, now) + } startCleanupJob() updateDeviceList() } @@ -131,8 +141,8 @@ class ScannerViewModel( if (cleanupJob?.isActive == true) return cleanupJob = viewModelScope.launch { while (advertisementMap.isNotEmpty() && isActive) { - delay(1000) - val keysToRemove = advertisementMap.filter { it.value.second.elapsedNow() > SCAN_TIMEOUT }.keys + delay(5000) + val keysToRemove = advertisementMap.filter { it.value.timeMark.elapsedNow() > SCAN_TIMEOUT }.keys if (keysToRemove.isNotEmpty()) { keysToRemove.forEach { advertisementMap.remove(it) } updateDeviceList() @@ -147,8 +157,10 @@ class ScannerViewModel( } private fun updateDeviceList() { - _devices.value = advertisementMap.values.map { it.first } -// .sortedByDescending { it.rssi } // Fix flicker + val newList = advertisementMap.values.map { it.advertisement } + if (newList != _devices.value) { + _devices.value = newList + } } fun stopScan() {