I need to update UI composable from bluetooth characteristic change.
Incoming data is:
@RequiresApi(Build.VERSION_CODES.TIRAMISU)
data class OrganConfig(
var numberOfSetzers:Int=99,
var manuals : MutableList<ManualConfig> = mutableListOf( ManualConfig(keys = 32, name = "Pedal", valid=true),
ManualConfig(keys = 64, name = "Positif", valid=true)
),
var valid:Boolean=false)
I have a modelView:
class OrganModel : ViewModel() {
...
@RequiresApi(Build.VERSION_CODES.TIRAMISU)
private val _organConfig = MutableStateFlow(OrganConfig())
@RequiresApi(Build.VERSION_CODES.TIRAMISU)
val organConfig : StateFlow<OrganConfig> = _organConfig.asStateFlow()
...
@RequiresApi(Build.VERSION_CODES.TIRAMISU)
private suspend fun parseData(dataInputStream: DataInputStream) : Optional<AbstractAnswer> =
AbstractAnswer.checkHeader(dataInputStream)
@RequiresApi(Build.VERSION_CODES.TIRAMISU)
fun incomingAnswer(characteristic:BluetoothGattCharacteristic, value:ByteArray) {
val ds = DataInputStream(ByteArrayInputStream(value))
viewModelScope.launch(Dispatchers.IO) {
val a = parseData(ds)
if (a.isPresent) {
val answer=a.get()
withContext(Dispatchers.Main) {
when(answer) {
is ConfigAnswer -> {
_organConfig.update { cfg ->
cfg.copy(
numberOfSetzers = answer.setzers.toInt(),
manuals = answer.manuals,
valid = true
)
}
}
...
else -> Timber.w("Unknown answer $answer")
}
}
}
}
}
...
@RequiresApi(Build.VERSION_CODES.TIRAMISU)
fun refresh() {
_organConfig.update { cfg->
cfg.copy(numberOfSetzers = 66)
}
}
}
I have an incoming event, when bluetooth characteristic changed like this:
private val connectionEventListener by lazy {
ConnectionEventListener().apply {
...
onCharacteristicChanged={ _, characteristic, value ->
organModel.incomingAnswer(characteristic, value)
}
}
Everything seems work, I see incoming values in logcat.
In my Composable:
@RequiresApi(Build.VERSION_CODES.TIRAMISU)
@SuppressLint("StateFlowValueCalledInComposition")
@Composable
fun OrganConfiguration(organModel: OrganModel, refreshClicked: () -> Unit) {
val cfg by organModel.organConfig.collectAsState()
Column(Modifier.fillMaxWidth()) {
Row(
modifier = Modifier
.fillMaxWidth()
.padding(10.dp),
horizontalArrangement = Arrangement.Center
) {
Text(text = "Orgona konfiguráció")
}
Row(
modifier = Modifier
.fillMaxWidth()
.padding(all = 8.dp),
horizontalArrangement = Arrangement.Center
) {
Box(
Modifier
.weight(1f)
.padding(10.dp),
Alignment.CenterStart
) {
VerticalItem(
label = "Manuals",
value = cfg.manuals.size.toString()
)
}
Box(
Modifier
.weight(1f)
.padding(10.dp),
Alignment.Center
) {
VerticalItem(
label = "Registers",
value = cfg.numberOfSetzers.toString()
)
}
Box(
Modifier
.weight(1f)
.padding(10.dp),
Alignment.CenterEnd
) {
IconButton(
onClick = { refreshClicked() }) {
Icon(
Icons.Filled.Refresh, contentDescription = "refresh", tint = Color.Gray,
modifier = Modifier.size(48.dp)
)
}
}
}
}
}
I never see changes. But when I click refresh button to force changes in viewmodel I see the new values.
Bluetooth events are in background threads, it may be that should be the reason of not emitting statechage? How can I make update from bluetooth event thread?
thx,
Zamek