@@ -156,6 +156,7 @@ public final class CastPlayer extends BasePlayer {
156156 private final StateHolder <Boolean > playWhenReady ;
157157 private final StateHolder <Integer > repeatMode ;
158158 private final StateHolder <Float > volume ;
159+ private final StateHolder <Boolean > deviceMuted ;
159160 private final StateHolder <PlaybackParameters > playbackParameters ;
160161 @ Nullable private RemoteMediaClient remoteMediaClient ;
161162 private CastTimeline currentTimeline ;
@@ -269,6 +270,7 @@ public CastPlayer(
269270 playWhenReady = new StateHolder <>(false );
270271 repeatMode = new StateHolder <>(REPEAT_MODE_OFF );
271272 volume = new StateHolder <>(1f );
273+ deviceMuted = new StateHolder <>(false );
272274 playbackParameters = new StateHolder <>(PlaybackParameters .DEFAULT );
273275 playbackState = STATE_IDLE ;
274276 currentTimeline = CastTimeline .EMPTY_CAST_TIMELINE ;
@@ -857,10 +859,9 @@ public int getDeviceVolume() {
857859 return 0 ;
858860 }
859861
860- /** This method is not supported and always returns {@code false}. */
861862 @ Override
862863 public boolean isDeviceMuted () {
863- return false ;
864+ return deviceMuted . value ;
864865 }
865866
866867 /**
@@ -901,11 +902,33 @@ public void decreaseDeviceVolume(@C.VolumeFlags int flags) {}
901902 */
902903 @ Deprecated
903904 @ Override
904- public void setDeviceMuted (boolean muted ) {}
905+ public void setDeviceMuted (boolean muted ) {
906+ setDeviceMuted (muted , 0 );
907+ }
905908
906- /** This method is not supported and does nothing. */
907909 @ Override
908- public void setDeviceMuted (boolean muted , @ C .VolumeFlags int flags ) {}
910+ public void setDeviceMuted (boolean muted , @ C .VolumeFlags int flags ) {
911+ if (remoteMediaClient == null ) {
912+ return ;
913+ }
914+ // We update the local state and send the message to the receiver app, which will cause the
915+ // operation to be perceived as synchronous by the user. When the operation reports a result,
916+ // the local state will be updated to reflect the state reported by the Cast SDK.
917+ setDeviceMutedAndNotifyIfChanged (muted );
918+ listeners .flushEvents ();
919+ PendingResult <MediaChannelResult > pendingResult = remoteMediaClient .setStreamMute (muted );
920+ this .deviceMuted .pendingResultCallback =
921+ new ResultCallback <MediaChannelResult >() {
922+ @ Override
923+ public void onResult (@ NonNull MediaChannelResult result ) {
924+ if (remoteMediaClient != null ) {
925+ updateDeviceMutedAndNotifyIfChanged (this );
926+ listeners .flushEvents ();
927+ }
928+ }
929+ };
930+ pendingResult .setResultCallback (this .deviceMuted .pendingResultCallback );
931+ }
909932
910933 /** This method is not supported and does nothing. */
911934 @ Override
@@ -930,6 +953,7 @@ private void updateInternalStateAndNotifyIfChanged() {
930953 updatePlayerStateAndNotifyIfChanged (/* resultCallback= */ null );
931954 updateRepeatModeAndNotifyIfChanged (/* resultCallback= */ null );
932955 updateVolumeAndNotifyIfChanged (/* resultCallback= */ null );
956+ updateDeviceMutedAndNotifyIfChanged (/* resultCallback= */ null );
933957 updatePlaybackRateAndNotifyIfChanged (/* resultCallback= */ null );
934958 boolean playingPeriodChangedByTimelineChange = updateTimelineAndNotifyIfChanged ();
935959 Timeline currentTimeline = getCurrentTimeline ();
@@ -1053,6 +1077,14 @@ private void updateVolumeAndNotifyIfChanged(@Nullable ResultCallback<?> resultCa
10531077 }
10541078 }
10551079
1080+ @ RequiresNonNull ("remoteMediaClient" )
1081+ private void updateDeviceMutedAndNotifyIfChanged (@ Nullable ResultCallback <?> resultCallback ) {
1082+ if (deviceMuted .acceptsUpdate (resultCallback )) {
1083+ setDeviceMutedAndNotifyIfChanged (fetchDeviceMuted (remoteMediaClient ));
1084+ deviceMuted .clearPendingResultCallback ();
1085+ }
1086+ }
1087+
10561088 /**
10571089 * Updates the timeline and notifies {@link Player.Listener event listeners} if required.
10581090 *
@@ -1192,6 +1224,8 @@ private void updateAvailableCommandsAndNotifyIfChanged() {
11921224 Util .getAvailableCommands (/* player= */ this , PERMANENT_AVAILABLE_COMMANDS )
11931225 .buildUpon ()
11941226 .addIf (COMMAND_SET_VOLUME , isSetVolumeCommandAvailable ())
1227+ .addIf (COMMAND_ADJUST_DEVICE_VOLUME , isToggleMuteCommandAvailable ())
1228+ .addIf (COMMAND_ADJUST_DEVICE_VOLUME_WITH_FLAGS , isToggleMuteCommandAvailable ())
11951229 .build ();
11961230 if (!availableCommands .equals (previousAvailableCommands )) {
11971231 listeners .queueEvent (
@@ -1210,6 +1244,16 @@ private boolean isSetVolumeCommandAvailable() {
12101244 return false ;
12111245 }
12121246
1247+ private boolean isToggleMuteCommandAvailable () {
1248+ if (remoteMediaClient != null ) {
1249+ MediaStatus mediaStatus = remoteMediaClient .getMediaStatus ();
1250+ if (mediaStatus != null ) {
1251+ return mediaStatus .isMediaCommandSupported (MediaStatus .COMMAND_TOGGLE_MUTE );
1252+ }
1253+ }
1254+ return false ;
1255+ }
1256+
12131257 private void setMediaItemsInternal (
12141258 List <MediaItem > mediaItems ,
12151259 int startIndex ,
@@ -1318,6 +1362,16 @@ private void setVolumeAndNotifyIfChanged(float volume) {
13181362 }
13191363 }
13201364
1365+ private void setDeviceMutedAndNotifyIfChanged (boolean muted ) {
1366+ if (this .deviceMuted .value != muted ) {
1367+ this .deviceMuted .value = muted ;
1368+ listeners .queueEvent (
1369+ Player .EVENT_DEVICE_VOLUME_CHANGED ,
1370+ listener -> listener .onDeviceVolumeChanged (getDeviceVolume (), muted ));
1371+ updateAvailableCommandsAndNotifyIfChanged ();
1372+ }
1373+ }
1374+
13211375 private void setPlaybackParametersAndNotifyIfChanged (PlaybackParameters playbackParameters ) {
13221376 if (this .playbackParameters .value .equals (playbackParameters )) {
13231377 return ;
@@ -1441,6 +1495,15 @@ private static float fetchVolume(RemoteMediaClient remoteMediaClient) {
14411495 return (float ) mediaStatus .getStreamVolume ();
14421496 }
14431497
1498+ private static boolean fetchDeviceMuted (RemoteMediaClient remoteMediaClient ) {
1499+ MediaStatus mediaStatus = remoteMediaClient .getMediaStatus ();
1500+ if (mediaStatus == null ) {
1501+ // No media session active, yet.
1502+ return false ;
1503+ }
1504+ return mediaStatus .isMute ();
1505+ }
1506+
14441507 private static int fetchCurrentWindowIndex (
14451508 @ Nullable RemoteMediaClient remoteMediaClient , Timeline timeline ) {
14461509 if (remoteMediaClient == null ) {
@@ -1705,8 +1768,7 @@ public DeviceInfo fetchDeviceInfo() {
17051768 // There's only one remote routing controller. It's safe to assume it's the Cast routing
17061769 // controller.
17071770 RoutingController remoteController = controllers .get (1 );
1708- // TODO b/364580007 - Populate volume information, and implement Player volume-related
1709- // methods.
1771+ // TODO b/364580007 - Populate min volume information.
17101772 return new DeviceInfo .Builder (DeviceInfo .PLAYBACK_TYPE_REMOTE )
17111773 .setMaxVolume (remoteController .getVolumeMax ())
17121774 .setRoutingControllerId (remoteController .getId ())
0 commit comments