Search code examples
androidkotlinandroid-jetpack-composereal-time

Realtime Graph in Jetpack Compose


Trying to implement a real time graph for sensor data in Jetpack Compose, but I am struggling with the correct way to pass the sensor data to the graph.

The app is receiving new sensor data over BLE every 40ms in a Kotlin class, and I would like to pass these to a custom Compose Line Chart. I tried implementing it with a Stateflow, which is then collected in the Composable to fill a list, but this is too slow:

DataReceiver:

private val _liveData = MutableStateFlow(0)
// The UI collects from this StateFlow to get its state updates
val liveData: StateFlow<Int> = _liveData

override fun onCharacteristicChanged(
    characteristic: BluetoothGattCharacteristic,
    value: ByteArray,
) {
    _liveData.value = value[0].toInt()
}

This part works fine and the data is set to the liveData value. But when I try to collect this from the composable e.g.:

LaunchedEffect(DataReceiver.liveData) {
    DataReceiver.liveData
        .collect {
            Log.d("BLE", "Value: " + it.value.toString())
        }
}

I am missing some values, like this is not fast enough to capture all the data from the flow?

How can I improve this or what is the best way to receive these values from an event and add it to a list in a composable?


Solution

  • Based on the suggestions by @Leviathan I came up with this solution, in case anyone looks for something similar:

    override fun dataFlow() = callbackFlow<ByteArray> {
        val characteristicChangedListener = object: ICharacteristicChangedListener {
            override fun onCharacteristicChanged(
                deviceAddress: String,
                characteristic: BluetoothGattCharacteristic,
                value: ByteArray
            ) {
                val uuid = characteristic.uuid.toString().uppercase()
                trySend(it)
            }
        }
    
        AndroidBLEDeviceHandler.registerCharacteristicChangedListener(characteristicChangedListener);
    
        awaitClose {
            AndroidBLEDeviceHandler.unregisterCharacteristicChangedListener(characteristicChangedListener);
        }
    }
    

    Then I created a viewmodel for my graph composable and there I collect the flow and put the data in a list:

        dataList = mutableListOf()
        ...
        streamEMGJob = viewModelScope.launch {
            chartDataManager.dataFlow().collect {
                //Process the data
                dataList.add(it)
            }
        }
    

    Not sure if this is the best solution but it works fast enough and does not lose any datapoints