feat(core): consolide le chainage type metier d'exercice -> strategie Health Services -> payload montre -> consommation Kotlin
Finalise #183 : modele/persistance des types metier d'exercice, mapping vers la strategie Health Services, propagation du payload dans le contrat watch bridge et consommation cote Kotlin montre. Perimetre complet QA vert. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@ -597,6 +597,8 @@ object WatchBridgePlugin {
|
||||
"setIndex" to projection["setIndex"],
|
||||
"passageIndex" to zeroBasedNullableIndex(projection["passageIndex"]),
|
||||
"stepIndex" to zeroBasedNullableIndex(projection["stepIndex"]),
|
||||
"healthServicesExerciseTypeStrategy" to
|
||||
projection["healthServicesExerciseTypeStrategy"],
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@ -312,7 +312,13 @@ internal class WatchHeartRateCollector(
|
||||
{
|
||||
try {
|
||||
val capabilities = capabilitiesFuture.get()
|
||||
val config = exerciseConfigFromCapabilities(capabilities)
|
||||
val requestedTypeNames = (
|
||||
executionContext["healthServicesExerciseTypeStrategy"] as? List<*>
|
||||
)?.filterIsInstance<String>().orEmpty()
|
||||
val config = exerciseConfigFromCapabilities(
|
||||
capabilities,
|
||||
requestedTypeNames,
|
||||
)
|
||||
if (config == null) {
|
||||
exerciseMetricsStartInFlight = false
|
||||
Log.w(TAG, "no exercise type supports heart rate or distance sessionId=$sessionId")
|
||||
@ -360,13 +366,11 @@ internal class WatchHeartRateCollector(
|
||||
|
||||
private fun exerciseConfigFromCapabilities(
|
||||
capabilities: androidx.health.services.client.data.ExerciseCapabilities,
|
||||
requestedTypeNames: List<String>,
|
||||
): ExerciseConfig? {
|
||||
val requestedTypes = listOf(
|
||||
ExerciseType.RUNNING,
|
||||
ExerciseType.WALKING,
|
||||
ExerciseType.HIGH_INTENSITY_INTERVAL_TRAINING,
|
||||
ExerciseType.WORKOUT,
|
||||
)
|
||||
val requestedTypes = exerciseTypesFromNames(requestedTypeNames)
|
||||
var distanceOnlyConfig: ExerciseConfig? = null
|
||||
var caloriesOnlyConfig: ExerciseConfig? = null
|
||||
var heartRateOnlyConfig: ExerciseConfig? = null
|
||||
for (exerciseType in requestedTypes) {
|
||||
if (exerciseType !in capabilities.supportedExerciseTypes) {
|
||||
@ -376,15 +380,29 @@ internal class WatchHeartRateCollector(
|
||||
.supportedDataTypes
|
||||
val dataTypes = mutableSetOf<androidx.health.services.client.data.DataType<*, *>>()
|
||||
if (DataType.DISTANCE !in supported) {
|
||||
if (DataType.HEART_RATE_BPM !in supported) {
|
||||
if (DataType.CALORIES in supported) {
|
||||
dataTypes.add(DataType.CALORIES)
|
||||
}
|
||||
if (DataType.HEART_RATE_BPM in supported) {
|
||||
dataTypes.add(DataType.HEART_RATE_BPM)
|
||||
}
|
||||
if (dataTypes.isEmpty()) {
|
||||
Log.w(
|
||||
TAG,
|
||||
"exercise type lacks distance and heart rate type=$exerciseType supported=$supported",
|
||||
"exercise type lacks usable metrics type=$exerciseType supported=$supported",
|
||||
)
|
||||
continue
|
||||
}
|
||||
dataTypes.add(DataType.HEART_RATE_BPM)
|
||||
if (heartRateOnlyConfig == null) {
|
||||
if (
|
||||
DataType.CALORIES in dataTypes &&
|
||||
caloriesOnlyConfig == null
|
||||
) {
|
||||
caloriesOnlyConfig = ExerciseConfig.builder(exerciseType)
|
||||
.setDataTypes(dataTypes)
|
||||
.setIsAutoPauseAndResumeEnabled(false)
|
||||
.setIsGpsEnabled(false)
|
||||
.build()
|
||||
} else if (heartRateOnlyConfig == null) {
|
||||
heartRateOnlyConfig = ExerciseConfig.builder(exerciseType)
|
||||
.setDataTypes(dataTypes)
|
||||
.setIsAutoPauseAndResumeEnabled(false)
|
||||
@ -400,13 +418,44 @@ internal class WatchHeartRateCollector(
|
||||
if (DataType.HEART_RATE_BPM in supported) {
|
||||
dataTypes.add(DataType.HEART_RATE_BPM)
|
||||
}
|
||||
return ExerciseConfig.builder(exerciseType)
|
||||
val config = ExerciseConfig.builder(exerciseType)
|
||||
.setDataTypes(dataTypes)
|
||||
.setIsAutoPauseAndResumeEnabled(false)
|
||||
.setIsGpsEnabled(true)
|
||||
.build()
|
||||
if (DataType.CALORIES in dataTypes) {
|
||||
return config
|
||||
}
|
||||
if (distanceOnlyConfig == null) {
|
||||
distanceOnlyConfig = config
|
||||
}
|
||||
}
|
||||
return heartRateOnlyConfig
|
||||
return distanceOnlyConfig ?: caloriesOnlyConfig ?: heartRateOnlyConfig
|
||||
}
|
||||
|
||||
private fun exerciseTypesFromNames(names: List<String>): List<ExerciseType> {
|
||||
val mapped = names.mapNotNull { name ->
|
||||
when (name) {
|
||||
"RUNNING" -> ExerciseType.RUNNING
|
||||
"WALKING" -> ExerciseType.WALKING
|
||||
"HIGH_INTENSITY_INTERVAL_TRAINING" ->
|
||||
ExerciseType.HIGH_INTENSITY_INTERVAL_TRAINING
|
||||
"WORKOUT" -> ExerciseType.WORKOUT
|
||||
else -> null
|
||||
}
|
||||
}.toMutableList()
|
||||
val fallback = listOf(
|
||||
ExerciseType.RUNNING,
|
||||
ExerciseType.WALKING,
|
||||
ExerciseType.HIGH_INTENSITY_INTERVAL_TRAINING,
|
||||
ExerciseType.WORKOUT,
|
||||
)
|
||||
for (exerciseType in fallback) {
|
||||
if (exerciseType !in mapped) {
|
||||
mapped.add(exerciseType)
|
||||
}
|
||||
}
|
||||
return mapped.distinct()
|
||||
}
|
||||
|
||||
private fun stopExerciseMetrics(context: Context) {
|
||||
|
||||
Reference in New Issue
Block a user