[Fix] Scanner item flicker

This commit is contained in:
2026-06-27 14:14:44 +07:00
parent 7ef4b7d1cf
commit 9ed28e11d9
@@ -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<String, Job>()
private val advertisementMap = mutableMapOf<String, Pair<Advertisement, TimeSource.Monotonic.ValueTimeMark>>()
private val advertisementMap = mutableMapOf<String, AdvertisementMark>()
/** Bonded device */
private val bondedIds = mutableSetOf<String>() // 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() {