feat(watch): clôture lot #91 - fréquence cardiaque live, notifications de séance et finitions montre

Consolide le lot applicatif watch companion validé :
- télémétrie fréquence cardiaque live remontée montre -> téléphone
  (collecteur watch, adapter Wear Data Layer, persistance Drift,
  propagation aux écrans historique/programme/profil/exécution)
- notifications de séance en arrière-plan côté téléphone (service
  foreground de statut + passerelle applicative)
- finitions montre : chrono d'étape, score d'étape, retrait du bouton
  "lancer une séance", thème, icônes et polices watch_app

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-28 05:56:12 +02:00
parent c65a5a76a9
commit 65d43b9768
80 changed files with 12292 additions and 892 deletions

View File

@ -30,6 +30,8 @@ final class WatchCommandAckEvent {
abstract interface class NativeWatchBridgeClient {
Stream<WatchSessionProjection> get projections;
Stream<WatchSensorSample> get sensorSamples;
Stream<WatchCommandAckEvent> get acks;
Stream<WatchBridgeConnectionEvent> get connectionEvents;
@ -45,25 +47,28 @@ final class MethodChannelNativeWatchBridgeClient
implements NativeWatchBridgeClient {
const MethodChannelNativeWatchBridgeClient({
MethodChannel methodChannel = const MethodChannel(_methodChannelName),
EventChannel projectionChannel = const EventChannel(
_projectionChannelName,
EventChannel projectionChannel = const EventChannel(_projectionChannelName),
EventChannel sensorSampleChannel = const EventChannel(
_sensorSampleChannelName,
),
EventChannel ackChannel = const EventChannel(_ackChannelName),
EventChannel connectionChannel = const EventChannel(
_connectionChannelName,
),
EventChannel connectionChannel = const EventChannel(_connectionChannelName),
}) : _methodChannel = methodChannel,
_projectionChannel = projectionChannel,
_sensorSampleChannel = sensorSampleChannel,
_ackChannel = ackChannel,
_connectionChannel = connectionChannel;
static const _methodChannelName = 'gametime.watch_bridge/methods';
static const _projectionChannelName = 'gametime.watch_bridge/projections';
static const _sensorSampleChannelName =
'gametime.watch_bridge/sensor_samples';
static const _ackChannelName = 'gametime.watch_bridge/acks';
static const _connectionChannelName = 'gametime.watch_bridge/connection';
final MethodChannel _methodChannel;
final EventChannel _projectionChannel;
final EventChannel _sensorSampleChannel;
final EventChannel _ackChannel;
final EventChannel _connectionChannel;
@ -77,6 +82,16 @@ final class MethodChannelNativeWatchBridgeClient
});
}
@override
Stream<WatchSensorSample> get sensorSamples {
return _sensorSampleChannel
.receiveBroadcastStream()
.where((event) => event is Map)
.map((event) {
return WatchSensorSample.fromJson(_stringObjectMap(event));
});
}
@override
Stream<WatchCommandAckEvent> get acks {
return _ackChannel
@ -144,7 +159,11 @@ String? _nullableStringFromJson(Object? value) {
}
int? _nullableIntFromJson(Object? value) {
return value is int ? value : value is num ? value.toInt() : null;
return value is int
? value
: value is num
? value.toInt()
: null;
}
T _enumFromJson<T extends Enum>(Object? value, List<T> values, T fallback) {