11#nullable enable
22
33using System ;
4+ using System . ComponentModel ;
45using CommunityToolkit . Mvvm . ComponentModel ;
56using CommunityToolkit . Mvvm . Messaging ;
67using CommunityToolkit . Mvvm . Messaging . Messages ;
@@ -26,11 +27,20 @@ public sealed partial class VolumeViewModel : ObservableRecipient,
2627 private readonly DispatcherQueue _dispatcherQueue ;
2728 private readonly ISettingsService _settingsService ;
2829 private readonly PlayerContext _playerContext ;
30+ private readonly CastContext _castContext ;
31+ private readonly ICastService _castService ;
2932
30- public VolumeViewModel ( ISettingsService settingsService , PlayerContext playerContext )
33+ // Guards against a re-entrant SetVolumeAsync/SetMuteAsync call when Volume or IsMute
34+ // is updated programmatically from a Chromecast status event.
35+ private bool _updatingFromCast ;
36+
37+ public VolumeViewModel ( ISettingsService settingsService , PlayerContext playerContext ,
38+ CastContext castContext , ICastService castService )
3139 {
3240 _settingsService = settingsService ;
3341 _playerContext = playerContext ;
42+ _castContext = castContext ;
43+ _castService = castService ;
3444 _volume = settingsService . PersistentVolume ;
3545 _maxVolume = settingsService . MaxVolume ;
3646 _isMute = _volume == 0 ;
@@ -42,6 +52,8 @@ public VolumeViewModel(ISettingsService settingsService, PlayerContext playerCon
4252 MediaPlayer . IsMutedChanged += OnIsMutedChanged ;
4353 }
4454
55+ _castContext . PropertyChanged += OnCastContextPropertyChanged ;
56+
4557 // View model doesn't receive any messages
4658 IsActive = true ;
4759 }
@@ -77,6 +89,17 @@ public void Receive(ChangeVolumeRequestMessage message)
7789
7890 partial void OnVolumeChanged ( int value )
7991 {
92+ // While casting, proxy the volume change to the Chromecast receiver.
93+ if ( _castContext . IsCasting && _castContext . Client is { } castClient )
94+ {
95+ if ( ! _updatingFromCast )
96+ {
97+ _ = _castService . SetVolumeAsync ( castClient , value / 100.0 ) ;
98+ }
99+
100+ return ;
101+ }
102+
80103 if ( MediaPlayer == null ) return ;
81104 double newValue = value / 100d ;
82105 // bool stayMute = IsMute && newValue - MediaPlayer.Volume < 0.005;
@@ -87,6 +110,17 @@ partial void OnVolumeChanged(int value)
87110
88111 partial void OnIsMuteChanged ( bool value )
89112 {
113+ // While casting, proxy the mute change to the Chromecast receiver.
114+ if ( _castContext . IsCasting && _castContext . Client is { } castClient )
115+ {
116+ if ( ! _updatingFromCast )
117+ {
118+ _ = _castService . SetMuteAsync ( castClient , value ) ;
119+ }
120+
121+ return ;
122+ }
123+
90124 if ( MediaPlayer == null ) return ;
91125 MediaPlayer . IsMuted = value ;
92126 }
@@ -108,6 +142,49 @@ private void OnIsMutedChanged(IMediaPlayer sender, object? args)
108142 }
109143 }
110144
145+ /// <summary>
146+ /// Handles changes to the <see cref="CastContext"/> so that the volume control reflects
147+ /// the Chromecast device's receiver volume while casting and reverts to the local player
148+ /// once the session ends.
149+ /// </summary>
150+ private void OnCastContextPropertyChanged ( object sender , PropertyChangedEventArgs e )
151+ {
152+ switch ( e . PropertyName )
153+ {
154+ case nameof ( CastContext . IsCasting ) :
155+ _updatingFromCast = true ;
156+ if ( _castContext . IsCasting )
157+ {
158+ // Casting just started — seed Volume and IsMute from the receiver's
159+ // last known state. The receiver will push a status update shortly
160+ // with the authoritative values.
161+ Volume = ( int ) Math . Round ( _castContext . CastVolume * 100 ) ;
162+ IsMute = _castContext . CastIsMuted ;
163+ }
164+ else
165+ {
166+ // Casting ended — restore volume from the local player / persisted setting.
167+ Volume = _settingsService . PersistentVolume ;
168+ IsMute = Volume == 0 ;
169+ }
170+
171+ _updatingFromCast = false ;
172+ break ;
173+
174+ case nameof ( CastContext . CastVolume ) when _castContext . IsCasting :
175+ _updatingFromCast = true ;
176+ Volume = ( int ) Math . Round ( _castContext . CastVolume * 100 ) ;
177+ _updatingFromCast = false ;
178+ break ;
179+
180+ case nameof ( CastContext . CastIsMuted ) when _castContext . IsCasting :
181+ _updatingFromCast = true ;
182+ IsMute = _castContext . CastIsMuted ;
183+ _updatingFromCast = false ;
184+ break ;
185+ }
186+ }
187+
111188 /// <summary>
112189 /// Sets the volume to a specified value or adjusts it by a given amount.
113190 /// </summary>
0 commit comments