fix(watch): retablit la remontee distance/calories Health Services

Corrige l'absence de distance/calories alors que la FC fonctionnait. Perimetre QA vert cible, sans changement UI.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-30 18:29:19 +02:00
parent 3db37fff33
commit 7ce40ebf36
4 changed files with 29 additions and 7 deletions

View File

@ -554,8 +554,12 @@ final class WatchSensorSummary {
minHeartRateBpm: _nullableIntFromJson(json['minHeartRateBpm']), minHeartRateBpm: _nullableIntFromJson(json['minHeartRateBpm']),
averageHeartRateBpm: _nullableDoubleFromJson(json['averageHeartRateBpm']), averageHeartRateBpm: _nullableDoubleFromJson(json['averageHeartRateBpm']),
maxHeartRateBpm: _nullableIntFromJson(json['maxHeartRateBpm']), maxHeartRateBpm: _nullableIntFromJson(json['maxHeartRateBpm']),
totalDistanceMeters: _nullableDoubleFromJson(json['totalDistanceMeters']), totalDistanceMeters:
totalCaloriesKcal: _nullableDoubleFromJson(json['totalCaloriesKcal']), _nullableDoubleFromJson(json['totalDistanceMeters']) ??
_nullableDoubleFromJson(json['distanceMeters']),
totalCaloriesKcal:
_nullableDoubleFromJson(json['totalCaloriesKcal']) ??
_nullableDoubleFromJson(json['caloriesKcal']),
); );
} }

View File

@ -132,6 +132,21 @@ void main() {
expect(summary.totalDistanceMeters, isNull); expect(summary.totalDistanceMeters, isNull);
expect(summary.totalCaloriesKcal, isNull); expect(summary.totalCaloriesKcal, isNull);
}); });
test('accepts legacy live metric keys for summary totals', () {
final summary = WatchSensorSummary.fromJson({
'sessionId': 'session-1',
'sampleCount': 4,
'minHeartRateBpm': 91,
'averageHeartRateBpm': 128.5,
'maxHeartRateBpm': 174,
'distanceMeters': 842.4,
'caloriesKcal': 186.2,
});
expect(summary.totalDistanceMeters, 842.4);
expect(summary.totalCaloriesKcal, 186.2);
});
}); });
group('WatchSensorSample', () { group('WatchSensorSample', () {

View File

@ -312,7 +312,10 @@ void main() {
); );
await Future<void>.delayed(Duration.zero); await Future<void>.delayed(Duration.zero);
expect(telemetryRepository.samples.single.id, 'sample-1'); expect(
telemetryRepository.samples.single.id,
'telemetry:session-1:1784980800000',
);
final aggregate = telemetryRepository.aggregates.singleWhere( final aggregate = telemetryRepository.aggregates.singleWhere(
(aggregate) => aggregate.scope == WorkoutTelemetryAggregateScope.session, (aggregate) => aggregate.scope == WorkoutTelemetryAggregateScope.session,
); );

View File

@ -252,8 +252,8 @@ internal class WatchHeartRateCollector(
"minHeartRateBpm" to min, "minHeartRateBpm" to min,
"averageHeartRateBpm" to sampleSum / sampleCount, "averageHeartRateBpm" to sampleSum / sampleCount,
"maxHeartRateBpm" to max, "maxHeartRateBpm" to max,
"distanceMeters" to distanceMeters, "totalDistanceMeters" to distanceMeters,
"caloriesKcal" to caloriesKcal, "totalCaloriesKcal" to caloriesKcal,
), ),
).toString().toByteArray(StandardCharsets.UTF_8) ).toString().toByteArray(StandardCharsets.UTF_8)
Wearable.getCapabilityClient(context) Wearable.getCapabilityClient(context)
@ -321,7 +321,7 @@ internal class WatchHeartRateCollector(
) )
if (config == null) { if (config == null) {
exerciseMetricsStartInFlight = false exerciseMetricsStartInFlight = false
Log.w(TAG, "no exercise type supports heart rate or distance sessionId=$sessionId") Log.w(TAG, "no exercise type supports usable live metrics sessionId=$sessionId")
if (shouldAggregate) { if (shouldAggregate) {
startMeasureHeartRateFallback(context) startMeasureHeartRateFallback(context)
} }
@ -430,7 +430,7 @@ internal class WatchHeartRateCollector(
distanceOnlyConfig = config distanceOnlyConfig = config
} }
} }
return distanceOnlyConfig ?: caloriesOnlyConfig ?: heartRateOnlyConfig return caloriesOnlyConfig ?: distanceOnlyConfig ?: heartRateOnlyConfig
} }
private fun exerciseTypesFromNames(names: List<String>): List<ExerciseType> { private fun exerciseTypesFromNames(names: List<String>): List<ExerciseType> {