@@ -156,6 +156,7 @@ public final class CastPlayer extends BasePlayer {
156
156
private final StateHolder <Boolean > playWhenReady ;
157
157
private final StateHolder <Integer > repeatMode ;
158
158
private final StateHolder <Float > volume ;
159
+ private final StateHolder <Boolean > deviceMuted ;
159
160
private final StateHolder <PlaybackParameters > playbackParameters ;
160
161
@ Nullable private RemoteMediaClient remoteMediaClient ;
161
162
private CastTimeline currentTimeline ;
@@ -269,6 +270,7 @@ public CastPlayer(
269
270
playWhenReady = new StateHolder <>(false );
270
271
repeatMode = new StateHolder <>(REPEAT_MODE_OFF );
271
272
volume = new StateHolder <>(1f );
273
+ deviceMuted = new StateHolder <>(false );
272
274
playbackParameters = new StateHolder <>(PlaybackParameters .DEFAULT );
273
275
playbackState = STATE_IDLE ;
274
276
currentTimeline = CastTimeline .EMPTY_CAST_TIMELINE ;
@@ -857,10 +859,9 @@ public int getDeviceVolume() {
857
859
return 0 ;
858
860
}
859
861
860
- /** This method is not supported and always returns {@code false}. */
861
862
@ Override
862
863
public boolean isDeviceMuted () {
863
- return false ;
864
+ return deviceMuted . value ;
864
865
}
865
866
866
867
/**
@@ -901,11 +902,33 @@ public void decreaseDeviceVolume(@C.VolumeFlags int flags) {}
901
902
*/
902
903
@ Deprecated
903
904
@ Override
904
- public void setDeviceMuted (boolean muted ) {}
905
+ public void setDeviceMuted (boolean muted ) {
906
+ setDeviceMuted (muted , 0 );
907
+ }
905
908
906
- /** This method is not supported and does nothing. */
907
909
@ 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
+ }
909
932
910
933
/** This method is not supported and does nothing. */
911
934
@ Override
@@ -930,6 +953,7 @@ private void updateInternalStateAndNotifyIfChanged() {
930
953
updatePlayerStateAndNotifyIfChanged (/* resultCallback= */ null );
931
954
updateRepeatModeAndNotifyIfChanged (/* resultCallback= */ null );
932
955
updateVolumeAndNotifyIfChanged (/* resultCallback= */ null );
956
+ updateDeviceMutedAndNotifyIfChanged (/* resultCallback= */ null );
933
957
updatePlaybackRateAndNotifyIfChanged (/* resultCallback= */ null );
934
958
boolean playingPeriodChangedByTimelineChange = updateTimelineAndNotifyIfChanged ();
935
959
Timeline currentTimeline = getCurrentTimeline ();
@@ -1053,6 +1077,14 @@ private void updateVolumeAndNotifyIfChanged(@Nullable ResultCallback<?> resultCa
1053
1077
}
1054
1078
}
1055
1079
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
+
1056
1088
/**
1057
1089
* Updates the timeline and notifies {@link Player.Listener event listeners} if required.
1058
1090
*
@@ -1192,6 +1224,8 @@ private void updateAvailableCommandsAndNotifyIfChanged() {
1192
1224
Util .getAvailableCommands (/* player= */ this , PERMANENT_AVAILABLE_COMMANDS )
1193
1225
.buildUpon ()
1194
1226
.addIf (COMMAND_SET_VOLUME , isSetVolumeCommandAvailable ())
1227
+ .addIf (COMMAND_ADJUST_DEVICE_VOLUME , isToggleMuteCommandAvailable ())
1228
+ .addIf (COMMAND_ADJUST_DEVICE_VOLUME_WITH_FLAGS , isToggleMuteCommandAvailable ())
1195
1229
.build ();
1196
1230
if (!availableCommands .equals (previousAvailableCommands )) {
1197
1231
listeners .queueEvent (
@@ -1210,6 +1244,16 @@ private boolean isSetVolumeCommandAvailable() {
1210
1244
return false ;
1211
1245
}
1212
1246
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
+
1213
1257
private void setMediaItemsInternal (
1214
1258
List <MediaItem > mediaItems ,
1215
1259
int startIndex ,
@@ -1318,6 +1362,16 @@ private void setVolumeAndNotifyIfChanged(float volume) {
1318
1362
}
1319
1363
}
1320
1364
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
+
1321
1375
private void setPlaybackParametersAndNotifyIfChanged (PlaybackParameters playbackParameters ) {
1322
1376
if (this .playbackParameters .value .equals (playbackParameters )) {
1323
1377
return ;
@@ -1441,6 +1495,15 @@ private static float fetchVolume(RemoteMediaClient remoteMediaClient) {
1441
1495
return (float ) mediaStatus .getStreamVolume ();
1442
1496
}
1443
1497
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
+
1444
1507
private static int fetchCurrentWindowIndex (
1445
1508
@ Nullable RemoteMediaClient remoteMediaClient , Timeline timeline ) {
1446
1509
if (remoteMediaClient == null ) {
@@ -1705,8 +1768,7 @@ public DeviceInfo fetchDeviceInfo() {
1705
1768
// There's only one remote routing controller. It's safe to assume it's the Cast routing
1706
1769
// controller.
1707
1770
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.
1710
1772
return new DeviceInfo .Builder (DeviceInfo .PLAYBACK_TYPE_REMOTE )
1711
1773
.setMaxVolume (remoteController .getVolumeMax ())
1712
1774
.setRoutingControllerId (remoteController .getId ())
0 commit comments