feat(wearos): track swimming stroke count during workouts
This commit is contained in:
@@ -20,12 +20,14 @@ class HealthServicesManager(context: Context) {
|
|||||||
|
|
||||||
private val hrChannel = Channel<Double>(Channel.CONFLATED)
|
private val hrChannel = Channel<Double>(Channel.CONFLATED)
|
||||||
private val calChannel = Channel<Double>(Channel.CONFLATED)
|
private val calChannel = Channel<Double>(Channel.CONFLATED)
|
||||||
|
private val strokeCountChannel = Channel<Long>(Channel.CONFLATED)
|
||||||
private var callbackRegistered = false
|
private var callbackRegistered = false
|
||||||
|
|
||||||
private val combinedCallback = object : androidx.health.services.client.ExerciseUpdateCallback {
|
private val combinedCallback = object : androidx.health.services.client.ExerciseUpdateCallback {
|
||||||
override fun onNewUpdate(update: ExerciseUpdate) {
|
override fun onNewUpdate(update: ExerciseUpdate) {
|
||||||
update.getData(DataType.HEART_RATE_BPM)?.toDouble()?.let { hrChannel.trySend(it) }
|
update.getData(DataType.HEART_RATE_BPM)?.toDouble()?.let { hrChannel.trySend(it) }
|
||||||
update.getData(DataType.CALORIES)?.toDouble()?.let { calChannel.trySend(it) }
|
update.getData(DataType.CALORIES)?.toDouble()?.let { calChannel.trySend(it) }
|
||||||
|
update.getData(DataType.SWIMMING_STROKES)?.let { strokeCountChannel.trySend(it) }
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onAvailabilityChanged(dataType: DataType, availability: DataPointAvailability) {}
|
override fun onAvailabilityChanged(dataType: DataType, availability: DataPointAvailability) {}
|
||||||
@@ -46,6 +48,7 @@ class HealthServicesManager(context: Context) {
|
|||||||
|
|
||||||
val config = ExerciseConfig.builder(ExerciseType.SWIMMING_POOL)
|
val config = ExerciseConfig.builder(ExerciseType.SWIMMING_POOL)
|
||||||
.setDataTypes(poolCapabilities.supportedDataTypes)
|
.setDataTypes(poolCapabilities.supportedDataTypes)
|
||||||
|
.setSwimmingPoolLengthMeters(poolLengthMeters)
|
||||||
.build()
|
.build()
|
||||||
|
|
||||||
exerciseClient.startExerciseAsync(config).await()
|
exerciseClient.startExerciseAsync(config).await()
|
||||||
@@ -73,6 +76,11 @@ class HealthServicesManager(context: Context) {
|
|||||||
return calChannel.receiveAsFlow()
|
return calChannel.receiveAsFlow()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun strokeCountFlow(): Flow<Long> {
|
||||||
|
ensureCallbackRegistered()
|
||||||
|
return strokeCountChannel.receiveAsFlow()
|
||||||
|
}
|
||||||
|
|
||||||
private fun ensureCallbackRegistered() {
|
private fun ensureCallbackRegistered() {
|
||||||
if (!callbackRegistered) {
|
if (!callbackRegistered) {
|
||||||
exerciseClient.setUpdateCallback(combinedCallback)
|
exerciseClient.setUpdateCallback(combinedCallback)
|
||||||
|
|||||||
@@ -70,6 +70,7 @@ class WorkoutViewModel(application: Application) : AndroidViewModel(application)
|
|||||||
private var startTime = 0L
|
private var startTime = 0L
|
||||||
private var lapStartTime = 0L
|
private var lapStartTime = 0L
|
||||||
private var lapDetector: LapDetector? = null
|
private var lapDetector: LapDetector? = null
|
||||||
|
private var currentStrokeCount = 0L
|
||||||
|
|
||||||
private val workoutReceiver = object : BroadcastReceiver() {
|
private val workoutReceiver = object : BroadcastReceiver() {
|
||||||
override fun onReceive(context: Context?, intent: Intent?) {
|
override fun onReceive(context: Context?, intent: Intent?) {
|
||||||
@@ -141,10 +142,12 @@ class WorkoutViewModel(application: Application) : AndroidViewModel(application)
|
|||||||
_currentSetIndex.value = 0
|
_currentSetIndex.value = 0
|
||||||
_currentRep.value = 0
|
_currentRep.value = 0
|
||||||
_completedLaps.value = emptyList()
|
_completedLaps.value = emptyList()
|
||||||
|
currentStrokeCount = 0L
|
||||||
|
|
||||||
startElapsedTimeTracking()
|
startElapsedTimeTracking()
|
||||||
startHeartRateTracking()
|
startHeartRateTracking()
|
||||||
startCaloriesTracking()
|
startCaloriesTracking()
|
||||||
|
startStrokeCountTracking()
|
||||||
startLapDetection()
|
startLapDetection()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -186,6 +189,14 @@ class WorkoutViewModel(application: Application) : AndroidViewModel(application)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun startStrokeCountTracking() {
|
||||||
|
viewModelScope.launch {
|
||||||
|
healthManager.strokeCountFlow().collect { delta ->
|
||||||
|
currentStrokeCount += delta
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun completeLap() {
|
fun completeLap() {
|
||||||
if (!_isWorkoutActive.value || _isResting.value) return
|
if (!_isWorkoutActive.value || _isResting.value) return
|
||||||
val workout = _selectedWorkout.value ?: return
|
val workout = _selectedWorkout.value ?: return
|
||||||
@@ -202,10 +213,12 @@ class WorkoutViewModel(application: Application) : AndroidViewModel(application)
|
|||||||
repNumber = _currentRep.value + 1,
|
repNumber = _currentRep.value + 1,
|
||||||
distance = currentSet.distancePerRep,
|
distance = currentSet.distancePerRep,
|
||||||
duration = lapDuration,
|
duration = lapDuration,
|
||||||
heartRate = _heartRate.value
|
heartRate = _heartRate.value,
|
||||||
|
strokeCount = currentStrokeCount.toInt()
|
||||||
)
|
)
|
||||||
|
|
||||||
_completedLaps.value = _completedLaps.value + lap
|
_completedLaps.value = _completedLaps.value + lap
|
||||||
|
currentStrokeCount = 0L
|
||||||
|
|
||||||
if (_currentRep.value < currentSet.repCount - 1) {
|
if (_currentRep.value < currentSet.repCount - 1) {
|
||||||
_currentRep.value++
|
_currentRep.value++
|
||||||
|
|||||||
@@ -21,7 +21,8 @@ data class CompletedLap(
|
|||||||
val repNumber: Int,
|
val repNumber: Int,
|
||||||
val distance: Int,
|
val distance: Int,
|
||||||
val duration: Float,
|
val duration: Float,
|
||||||
val heartRate: Int?
|
val heartRate: Int?,
|
||||||
|
val strokeCount: Int? = null
|
||||||
)
|
)
|
||||||
|
|
||||||
data class WorkoutSummary(
|
data class WorkoutSummary(
|
||||||
|
|||||||
Reference in New Issue
Block a user