My project is a app get data from a ble device and show this. Here is WorkoutSessionScreen:
@Composable
fun WorkoutSessionScreen(
viewModel: MainViewModel = hiltViewModel(),
onEndWorkout: () -> Unit = {}
) {
val state by viewModel.state.collectAsState()
val exercise by viewModel.exerciseState.collectAsState()
Scaffold { innerPadding ->
Column(
modifier = Modifier
.fillMaxSize()
.padding(innerPadding)
) {
// Stat Section (Hiển thị thông tin thống kê)
WorkoutStatsSection(
workoutStat = state.sessionStat,
modifier = Modifier
.padding(16.dp)
)
Spacer(modifier = Modifier.height(16.dp))
// Thông tin bà i táºp
StatCard(
title = "Bà i táºp",
exercise = exercise,
value = when (exercise.name) {
"Idle" -> "Äang chá»"
"Pushup" -> state.sessionStat.pushupReps.toString()
"Squat" -> state.sessionStat.squatReps.toString()
"Crunch" -> state.sessionStat.crunchReps.toString()
else -> "Không xác định"
},
modifier = Modifier
.fillMaxWidth()
.padding(16.dp)
)
Spacer(modifier = Modifier.height(32.dp))
// Các nút Ä‘iá»u khiển
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceEvenly
) {
Button(
onClick = {
},
modifier = Modifier.width(120.dp)
) {
Text("Tạm dừng")
}
Button(
onClick = {
onEndWorkout()
viewModel.finishSession()
},
modifier = Modifier.width(120.dp)
) {
Text("Kết thúc")
}
}
}
}
}
Here is MainViewModel:
@HiltViewModel
class MainViewModel @Inject constructor(
private val bleManager: BleManager,
private val bleGatt: BleGatt
) : ViewModel() {
private val _state = MutableStateFlow(UiState())
val state: StateFlow = _state.asStateFlow()
private val _exerciseState = MutableStateFlow(Exercise("Idle", R.drawable.idle))
val exerciseState: StateFlow = _exerciseState.asStateFlow()
init {
viewModelScope.launch {
bleGatt.exerciseState.collectLatest { newState ->
Log.d("ViewModel", "Get state from BLE: ${newState}k" )
when(newState) {
"1" -> {
_exerciseState.value = Exercise("Pushup", R.drawable.pushup)
}
"2" -> {
_exerciseState.value = Exercise("Squat", R.drawable.squat)
}
"3" -> {
_exerciseState.value = Exercise("Crunch", R.drawable.crunch)
}
else -> {
_exerciseState.value = Exercise("Idle", R.drawable.idle)
}
}
if (newState != "4") {
updateExerciseCount(newState)
}
}
}
}
fun updateExerciseCount(state: String) {
when (state) {
"1" -> {
countPushup()
Log.d("ViewModel", "Count pushup")
Log.d("ViewModel", "Current pushup: ${_state.value.sessionStat.pushupReps}")
}
"2" -> {
countSquat()
Log.d("ViewModel", "Count squat")
Log.d("ViewModel", "Current squat: ${_state.value.sessionStat.squatReps}")
Log.d("ViewModel", "Current state: ${this.state.value.sessionStat.squatReps}")
}
"3" -> {
countCrunch()
Log.d("ViewModel", "Count crunch")
Log.d("ViewModel", "Current crunch: ${_state.value.sessionStat.crunchReps}")
Log.d("ViewModel", "Current state: ${this.state.value.sessionStat.crunchReps}")
}
else -> {
Log.d("ViewModel", "Don't get any state")
}
}
}
///////
fun countPushup() {
_state.update { state ->
val newPushupReps = state.sessionStat.pushupReps + 1
val newCalories = calculateSessionCalories(newPushupReps, state.sessionStat.squatReps, state.sessionStat.crunchReps)
state.copy(
sessionStat = state.sessionStat.copy(
pushupReps = newPushupReps,
totalCalories = newCalories
)
)
}
}
fun countSquat() {
_state.update { state ->
val newSquatReps = state.sessionStat.squatReps + 1
val newCalories = calculateSessionCalories(state.sessionStat.pushupReps, newSquatReps, state.sessionStat.crunchReps)
state.copy(
sessionStat = state.sessionStat.copy(
squatReps = newSquatReps,
totalCalories = newCalories
)
)
}
}
fun countCrunch() {
_state.update { state ->
val newCrunchReps = state.sessionStat.crunchReps + 1
val newCalories = calculateSessionCalories(state.sessionStat.pushupReps, state.sessionStat.squatReps, newCrunchReps)
state.copy(
sessionStat = state.sessionStat.copy(
crunchReps = newCrunchReps,
totalCalories = newCalories
)
)
}
}
///// some other code
}
Here is some log from Logcat:
2025-03-26 10:51:28.203 26994-27033 BleGatt com.qtcoding.workouttracker D characteristic changed: android.bluetooth.BluetoothGattCharacteristic@aee9130
2025-03-26 10:51:28.204 26994-27033 BleGatt com.qtcoding.workouttracker D Exercise state change expected: 4
2025-03-26 10:51:28.207 26994-26994 ViewModel com.qtcoding.workouttracker D Get state from BLE: 4k
2025-03-26 10:51:30.236 26994-27033 BleGatt com.qtcoding.workouttracker D characteristic changed: android.bluetooth.BluetoothGattCharacteristic@aee9130
2025-03-26 10:51:30.238 26994-27033 BleGatt com.qtcoding.workouttracker D Exercise state change expected: 2
2025-03-26 10:51:30.243 26994-26994 ViewModel com.qtcoding.workouttracker D Get state from BLE: 2k
2025-03-26 10:51:30.244 26994-26994 ViewModel com.qtcoding.workouttracker D Count squat
2025-03-26 10:51:30.244 26994-26994 ViewModel com.qtcoding.workouttracker D Current squat: 1
2025-03-26 10:51:30.244 26994-26994 ViewModel com.qtcoding.workouttracker D Current state: 1
2025-03-26 10:51:32.267 26994-27033 BleGatt com.qtcoding.workouttracker D characteristic changed: android.bluetooth.BluetoothGattCharacteristic@aee9130
2025-03-26 10:51:32.270 26994-27033 BleGatt com.qtcoding.workouttracker D Exercise state change expected: 1
2025-03-26 10:51:32.276 26994-26994 ViewModel com.qtcoding.workouttracker D Get state from BLE: 1k
2025-03-26 10:51:32.277 26994-26994 ViewModel com.qtcoding.workouttracker D Count pushup
2025-03-26 10:51:32.277 26994-26994 ViewModel com.qtcoding.workouttracker D Current pushup: 1
2025-03-26 10:51:34.268 26994-27033 BleGatt com.qtcoding.workouttracker D characteristic changed: android.bluetooth.BluetoothGattCharacteristic@aee9130
2025-03-26 10:51:34.269 26994-27033 BleGatt com.qtcoding.workouttracker D Exercise state change expected: 2
2025-03-26 10:51:34.274 26994-26994 ViewModel com.qtcoding.workouttracker D Get state from BLE: 2k
2025-03-26 10:51:34.275 26994-26994 ViewModel com.qtcoding.workouttracker D Count squat
2025-03-26 10:51:34.275 26994-26994 ViewModel com.qtcoding.workouttracker D Current squat: 2
2025-03-26 10:51:34.275 26994-26994 ViewModel com.qtcoding.workouttracker D Current state: 2
2025-03-26 10:51:34.868 26994-27863 BluetoothAdapter com.qtcoding.workouttracker I STATE_ON
2025-03-26 10:51:34.869 26994-27863 BluetoothLeScanner com.qtcoding.workouttracker D could not find callback wrapper
2025-03-26 10:51:36.223 26994-27033 BleGatt com.qtcoding.workouttracker D characteristic changed: android.bluetooth.BluetoothGattCharacteristic@aee9130
2025-03-26 10:51:36.225 26994-27033 BleGatt com.qtcoding.workouttracker D Exercise state change expected: 3
2025-03-26 10:51:36.232 26994-26994 ViewModel com.qtcoding.workouttracker D Get state from BLE: 3k
2025-03-26 10:51:36.233 26994-26994 ViewModel com.qtcoding.workouttracker D Count crunch
2025-03-26 10:51:36.233 26994-26994 ViewModel com.qtcoding.workouttracker D Current crunch: 1
2025-03-26 10:51:36.233 26994-26994 ViewModel com.qtcoding.workouttracker D Current state: 1
2025-03-26 10:51:38.355 26994-27033 BleGatt com.qtcoding.workouttracker D characteristic changed: android.bluetooth.BluetoothGattCharacteristic@aee9130
2025-03-26 10:51:38.356 26994-27033 BleGatt com.qtcoding.workouttracker D Exercise state change expected: 2
2025-03-26 10:51:38.361 26994-26994 ViewModel com.qtcoding.workouttracker D Get state from BLE: 2k
2025-03-26 10:51:38.362 26994-26994 ViewModel com.qtcoding.workouttracker D Count squat
2025-03-26 10:51:38.362 26994-26994 ViewModel com.qtcoding.workouttracker D Current squat: 3
2025-03-26 10:51:38.362 26994-26994 ViewModel com.qtcoding.workouttracker D Current state: 3
Based on the log, i think the state and exerciseState is changing but i don't know why the UI not recompose
I want the screen will update UI based on the data from ble device