Home > front end >  How to create a list from scanned BLE results
How to create a list from scanned BLE results

Time:04-29

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?

CodePudding user response:

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).

CodePudding user response:

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 list = mutableListOf<BleScanResult>()

    scanEnabled.flatMapLatest { doScan ->
        if (doScan) {
            repository.bluetoothScan().map { BleScanResult(it.device.name, it.device.address) }
        } else {
            emptyFlow()
        }
    }.collect {
        list  = it
        emit(list.toList())
    }
}

  • Related