'How to create a list from scanned BLE results
as invers to the question asked here How to convert Flow<List<Object>> to Flow<Object> I want to convert my Flow<Object> to Flow<List<Object>>.
At least I think I want that, so I try to explain what I want to achieve and give some background. I am working on an Android application that uses bluetooth to scan and connect to BLE devices. I'm fairly new to the Android platform and kotlin so I haven't quite grasped all the details despite all the many things I've already learnt.
My repository has a method which returns a Flow of ScanResults from the bluetooth adapter:
fun bluetoothScan(): Flow<ScanResult> {
return bluetoothStack.bluetoothScan()
}
My ViewModel consumes that function, maps the data to my BleScanResult and returns it as LiveData.
val scanResults: LiveData<BleScanResult> =
scanEnabled.flatMapLatest { doScan ->
if (doScan) {
repository.bluetoothScan().map { BleScanResult(it.device.name, it.device.address) }
} else {
emptyFlow()
}
}.asLiveData()
In my activity I want to observer on that data and display it in a RecyclerView:
val adapter = ScanResultListAdapter()
binding.rcBleScanResults.adapter = adapter
viewModel.scanResults.observe(this) { result ->
//result.let { adapter.submitList(it) }
}
The problem is that scanResults is from type Flow<BleScanResult> and not Flow<List<BleScanResult>>, so the call to adapter.submitList(it) throws an error as it is expected to be a list.
So, how do I convert Flow to Flow<List> (with additional filtering of duplicates)? Or is there something I miss about the conception of Flow/LiveData?
Solution 1:[1]
You can try to use a MutableList and fill it with the data you get form a Flow, something like the following:
val results: MutableList<BleScanResult> = mutableListOf()
val scanResults: LiveData<List<BleScanResult>> =
scanEnabled.flatMapLatest { doScan ->
if (doScan) {
repository.bluetoothScan().map {
results.apply {
add(BleScanResult(it.device.name, it.device.address))
}
}
} else {
emptyFlow()
}
}.asLiveData()
You can also use a MutableSet instead of MutableList if you want to have a unique list of items (assuming BleScanResult is a data class).
Solution 2:[2]
You could use the liveData builder to collect the Flow's values into a MutableList.
Here I copy the MutableList using toList() before emitting it since RecyclerView Adapters don't play well with mutable data sources.
val scanResults: LiveData<List<BleScanResult>> = liveData {
val cumulativeResults = mutableListOf<BleScanResult>()
scanEnabled.flatMapLatest { doScan ->
if (doScan) {
repository.bluetoothScan().map { BleScanResult(it.device.name, it.device.address) }
} else {
emptyFlow()
}
}.collect {
cumulativeResults += it
emit(cumulativeResults.toList())
}
}
If you want to avoid duplicate entries and reordering of entries, you can use a set like this:
val scanResults: LiveData<List<BleScanResult>> = liveData {
val cumulativeResults = mutableSetOf<BleScanResult>()
scanEnabled.flatMapLatest { doScan ->
if (doScan) {
repository.bluetoothScan().map { BleScanResult(it.device.name, it.device.address) }
} else {
emptyFlow()
}
}.collect {
if (it !in cumulativeResults) {
cumulativeResults += it
emit(cumulativeResults.toList())
}
}
}
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | |
| Solution 2 |
