fix(watch): corrige la remontee distance/calories sur seance neuve dans WatchHeartRateCollector
QA vert cible. Complete le correctif precedent fix/distance-calories-health-services, insuffisant sur le cas seance neuve. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@ -2,6 +2,8 @@ package com.gametime.watch.bridge
|
||||
|
||||
import android.content.Context
|
||||
import android.content.pm.ApplicationInfo
|
||||
import android.content.pm.PackageManager
|
||||
import android.location.LocationManager
|
||||
import android.os.Handler
|
||||
import android.os.Looper
|
||||
import android.util.Log
|
||||
@ -315,11 +317,12 @@ internal class WatchHeartRateCollector(
|
||||
val requestedTypeNames = (
|
||||
executionContext["healthServicesExerciseTypeStrategy"] as? List<*>
|
||||
)?.filterIsInstance<String>().orEmpty()
|
||||
val config = exerciseConfigFromCapabilities(
|
||||
val configs = exerciseConfigsFromCapabilities(
|
||||
capabilities,
|
||||
requestedTypeNames,
|
||||
canUseGps = canStartGpsExercise(context),
|
||||
)
|
||||
if (config == null) {
|
||||
if (configs.isEmpty()) {
|
||||
exerciseMetricsStartInFlight = false
|
||||
Log.w(TAG, "no exercise type supports usable live metrics sessionId=$sessionId")
|
||||
if (shouldAggregate) {
|
||||
@ -331,29 +334,11 @@ internal class WatchHeartRateCollector(
|
||||
exerciseMetricsStartInFlight = false
|
||||
return@addListener
|
||||
}
|
||||
exerciseHeartRateSupported = DataType.HEART_RATE_BPM in config.dataTypes
|
||||
exerciseClient.setUpdateCallback(context.mainExecutor, exerciseCallback)
|
||||
val startFuture = exerciseClient.startExerciseAsync(config)
|
||||
startFuture.addListener(
|
||||
{
|
||||
exerciseMetricsStartInFlight = false
|
||||
try {
|
||||
startFuture.get()
|
||||
exerciseMetricsStarted = true
|
||||
Log.d(
|
||||
TAG,
|
||||
"exercise metrics started sessionId=$sessionId type=${config.exerciseType} dataTypes=${config.dataTypes}",
|
||||
)
|
||||
} catch (error: Exception) {
|
||||
Log.w(TAG, "exercise metrics start failed", error)
|
||||
clearExerciseCallback(exerciseClient)
|
||||
exerciseHeartRateSupported = false
|
||||
if (shouldAggregate) {
|
||||
startMeasureHeartRateFallback(context)
|
||||
}
|
||||
}
|
||||
},
|
||||
context.mainExecutor,
|
||||
startExerciseMetricsConfig(
|
||||
context,
|
||||
exerciseClient,
|
||||
configs,
|
||||
index = 0,
|
||||
)
|
||||
} catch (error: Exception) {
|
||||
exerciseMetricsStartInFlight = false
|
||||
@ -364,73 +349,144 @@ internal class WatchHeartRateCollector(
|
||||
)
|
||||
}
|
||||
|
||||
private fun exerciseConfigFromCapabilities(
|
||||
private fun startExerciseMetricsConfig(
|
||||
context: Context,
|
||||
exerciseClient: ExerciseClient,
|
||||
configs: List<ExerciseConfig>,
|
||||
index: Int,
|
||||
) {
|
||||
if (!shouldAggregate) {
|
||||
exerciseMetricsStartInFlight = false
|
||||
return
|
||||
}
|
||||
val config = configs.getOrNull(index)
|
||||
if (config == null) {
|
||||
exerciseMetricsStartInFlight = false
|
||||
clearExerciseCallback(exerciseClient)
|
||||
exerciseHeartRateSupported = false
|
||||
Log.w(TAG, "all exercise metric configs failed sessionId=$sessionId")
|
||||
if (shouldAggregate) {
|
||||
startMeasureHeartRateFallback(context)
|
||||
}
|
||||
return
|
||||
}
|
||||
exerciseHeartRateSupported = DataType.HEART_RATE_BPM in config.dataTypes
|
||||
exerciseClient.setUpdateCallback(context.mainExecutor, exerciseCallback)
|
||||
val startFuture = exerciseClient.startExerciseAsync(config)
|
||||
startFuture.addListener(
|
||||
{
|
||||
try {
|
||||
startFuture.get()
|
||||
exerciseMetricsStartInFlight = false
|
||||
exerciseMetricsStarted = true
|
||||
Log.d(
|
||||
TAG,
|
||||
"exercise metrics started sessionId=$sessionId type=${config.exerciseType} dataTypes=${config.dataTypes}",
|
||||
)
|
||||
} catch (error: Exception) {
|
||||
Log.w(
|
||||
TAG,
|
||||
"exercise metrics start failed type=${config.exerciseType} dataTypes=${config.dataTypes}",
|
||||
error,
|
||||
)
|
||||
clearExerciseCallback(exerciseClient)
|
||||
exerciseHeartRateSupported = false
|
||||
startExerciseMetricsConfig(
|
||||
context,
|
||||
exerciseClient,
|
||||
configs,
|
||||
index + 1,
|
||||
)
|
||||
}
|
||||
},
|
||||
context.mainExecutor,
|
||||
)
|
||||
}
|
||||
|
||||
private fun exerciseConfigsFromCapabilities(
|
||||
capabilities: androidx.health.services.client.data.ExerciseCapabilities,
|
||||
requestedTypeNames: List<String>,
|
||||
): ExerciseConfig? {
|
||||
canUseGps: Boolean,
|
||||
): List<ExerciseConfig> {
|
||||
val requestedTypes = exerciseTypesFromNames(requestedTypeNames)
|
||||
var distanceOnlyConfig: ExerciseConfig? = null
|
||||
var caloriesOnlyConfig: ExerciseConfig? = null
|
||||
var heartRateOnlyConfig: ExerciseConfig? = null
|
||||
val distanceCaloriesConfigs = mutableListOf<ExerciseConfig>()
|
||||
val caloriesConfigs = mutableListOf<ExerciseConfig>()
|
||||
val heartRateConfigs = mutableListOf<ExerciseConfig>()
|
||||
for (exerciseType in requestedTypes) {
|
||||
if (exerciseType !in capabilities.supportedExerciseTypes) {
|
||||
continue
|
||||
}
|
||||
val supported = capabilities.getExerciseTypeCapabilities(exerciseType)
|
||||
.supportedDataTypes
|
||||
val dataTypes = mutableSetOf<androidx.health.services.client.data.DataType<*, *>>()
|
||||
if (DataType.DISTANCE !in supported) {
|
||||
if (DataType.CALORIES in supported) {
|
||||
dataTypes.add(DataType.CALORIES)
|
||||
}
|
||||
if (DataType.HEART_RATE_BPM in supported) {
|
||||
val supportsHeartRate = DataType.HEART_RATE_BPM in supported
|
||||
if (
|
||||
canUseGps &&
|
||||
DataType.DISTANCE in supported &&
|
||||
DataType.CALORIES in supported
|
||||
) {
|
||||
val dataTypes = mutableSetOf<androidx.health.services.client.data.DataType<*, *>>(
|
||||
DataType.DISTANCE,
|
||||
DataType.CALORIES,
|
||||
)
|
||||
if (supportsHeartRate) {
|
||||
dataTypes.add(DataType.HEART_RATE_BPM)
|
||||
}
|
||||
if (dataTypes.isEmpty()) {
|
||||
Log.w(
|
||||
TAG,
|
||||
"exercise type lacks usable metrics type=$exerciseType supported=$supported",
|
||||
)
|
||||
continue
|
||||
}
|
||||
if (
|
||||
DataType.CALORIES in dataTypes &&
|
||||
caloriesOnlyConfig == null
|
||||
) {
|
||||
caloriesOnlyConfig = ExerciseConfig.builder(exerciseType)
|
||||
distanceCaloriesConfigs.add(
|
||||
ExerciseConfig.builder(exerciseType)
|
||||
.setDataTypes(dataTypes)
|
||||
.setIsAutoPauseAndResumeEnabled(false)
|
||||
.setIsGpsEnabled(false)
|
||||
.build()
|
||||
} else if (heartRateOnlyConfig == null) {
|
||||
heartRateOnlyConfig = ExerciseConfig.builder(exerciseType)
|
||||
.setDataTypes(dataTypes)
|
||||
.setIsAutoPauseAndResumeEnabled(false)
|
||||
.setIsGpsEnabled(false)
|
||||
.build()
|
||||
}
|
||||
continue
|
||||
.setIsGpsEnabled(true)
|
||||
.build(),
|
||||
)
|
||||
}
|
||||
dataTypes.add(DataType.DISTANCE)
|
||||
if (DataType.CALORIES in supported) {
|
||||
dataTypes.add(DataType.CALORIES)
|
||||
}
|
||||
if (DataType.HEART_RATE_BPM in supported) {
|
||||
dataTypes.add(DataType.HEART_RATE_BPM)
|
||||
}
|
||||
val config = ExerciseConfig.builder(exerciseType)
|
||||
.setDataTypes(dataTypes)
|
||||
.setIsAutoPauseAndResumeEnabled(false)
|
||||
.setIsGpsEnabled(true)
|
||||
.build()
|
||||
if (DataType.CALORIES in dataTypes) {
|
||||
return config
|
||||
}
|
||||
if (distanceOnlyConfig == null) {
|
||||
distanceOnlyConfig = config
|
||||
val dataTypes = mutableSetOf<androidx.health.services.client.data.DataType<*, *>>(
|
||||
DataType.CALORIES,
|
||||
)
|
||||
if (supportsHeartRate) {
|
||||
dataTypes.add(DataType.HEART_RATE_BPM)
|
||||
}
|
||||
caloriesConfigs.add(
|
||||
ExerciseConfig.builder(exerciseType)
|
||||
.setDataTypes(dataTypes)
|
||||
.setIsAutoPauseAndResumeEnabled(false)
|
||||
.setIsGpsEnabled(false)
|
||||
.build(),
|
||||
)
|
||||
} else if (supportsHeartRate) {
|
||||
heartRateConfigs.add(
|
||||
ExerciseConfig.builder(exerciseType)
|
||||
.setDataTypes(setOf(DataType.HEART_RATE_BPM))
|
||||
.setIsAutoPauseAndResumeEnabled(false)
|
||||
.setIsGpsEnabled(false)
|
||||
.build(),
|
||||
)
|
||||
} else {
|
||||
Log.w(
|
||||
TAG,
|
||||
"exercise type lacks usable metrics type=$exerciseType supported=$supported",
|
||||
)
|
||||
}
|
||||
}
|
||||
return caloriesOnlyConfig ?: distanceOnlyConfig ?: heartRateOnlyConfig
|
||||
return distanceCaloriesConfigs + caloriesConfigs + heartRateConfigs
|
||||
}
|
||||
|
||||
private fun canStartGpsExercise(context: Context): Boolean {
|
||||
if (
|
||||
context.checkSelfPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) !=
|
||||
PackageManager.PERMISSION_GRANTED
|
||||
) {
|
||||
return false
|
||||
}
|
||||
return try {
|
||||
val locationManager = context.getSystemService(LocationManager::class.java)
|
||||
?: return false
|
||||
locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER) ||
|
||||
locationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER)
|
||||
} catch (error: RuntimeException) {
|
||||
Log.w(TAG, "location availability check failed", error)
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
private fun exerciseTypesFromNames(names: List<String>): List<ExerciseType> {
|
||||
|
||||
Reference in New Issue
Block a user