@@ -113,7 +113,8 @@ public final class CastPlayer extends BasePlayer {
113113 public static final DeviceInfo DEVICE_INFO_REMOTE_EMPTY =
114114 new DeviceInfo .Builder (DeviceInfo .PLAYBACK_TYPE_REMOTE ).setMaxVolume (MAX_VOLUME ).build ();
115115
116- private static final Range <Integer > VOLUME_RANGE = new Range <>(0 , MAX_VOLUME );
116+ private static final Range <Integer > RANGE_DEVICE_VOLUME = new Range <>(0 , MAX_VOLUME );
117+ private static final Range <Float > RANGE_VOLUME = new Range <>(0.f , 1.f );
117118
118119 static {
119120 MediaLibraryInfo .registerModule ("media3.cast" );
@@ -178,6 +179,7 @@ public final class CastPlayer extends BasePlayer {
178179 private final StateHolder <Integer > repeatMode ;
179180 private boolean isMuted ;
180181 private int deviceVolume ;
182+ private final StateHolder <Float > volume ;
181183 private final StateHolder <PlaybackParameters > playbackParameters ;
182184 @ Nullable private CastSession castSession ;
183185 @ Nullable private RemoteMediaClient remoteMediaClient ;
@@ -293,6 +295,7 @@ public CastPlayer(
293295 playWhenReady = new StateHolder <>(false );
294296 repeatMode = new StateHolder <>(REPEAT_MODE_OFF );
295297 deviceVolume = MAX_VOLUME ;
298+ volume = new StateHolder <>(1f );
296299 playbackParameters = new StateHolder <>(PlaybackParameters .DEFAULT );
297300 playbackState = STATE_IDLE ;
298301 currentTimeline = CastTimeline .EMPTY_CAST_TIMELINE ;
@@ -781,14 +784,34 @@ public AudioAttributes getAudioAttributes() {
781784 return AudioAttributes .DEFAULT ;
782785 }
783786
784- /** This method is not supported and does nothing. */
785787 @ Override
786- public void setVolume (float volume ) {}
788+ public void setVolume (float volume ) {
789+ if (remoteMediaClient == null ) {
790+ return ;
791+ }
792+ // We update the local state and send the message to the receiver app, which will cause the
793+ // operation to be perceived as synchronous by the user. When the operation reports a result,
794+ // the local state will be updated to reflect the state reported by the Cast SDK.
795+ volume = RANGE_VOLUME .clamp (volume );
796+ setVolumeAndNotifyIfChanged (volume );
797+ listeners .flushEvents ();
798+ PendingResult <MediaChannelResult > pendingResult = remoteMediaClient .setStreamVolume (volume );
799+ this .volume .pendingResultCallback =
800+ new ResultCallback <MediaChannelResult >() {
801+ @ Override
802+ public void onResult (MediaChannelResult result ) {
803+ if (remoteMediaClient != null ) {
804+ updateVolumeAndNotifyIfChanged (this );
805+ listeners .flushEvents ();
806+ }
807+ }
808+ };
809+ pendingResult .setResultCallback (this .volume .pendingResultCallback );
810+ }
787811
788- /** This method is not supported and returns 1. */
789812 @ Override
790813 public float getVolume () {
791- return 1 ;
814+ return volume . value ;
792815 }
793816
794817 /** This method is not supported and does nothing. */
@@ -880,7 +903,7 @@ public void setDeviceVolume(@IntRange(from = 0) int volume, @C.VolumeFlags int f
880903 if (castSession == null ) {
881904 return ;
882905 }
883- volume = VOLUME_RANGE .clamp (volume );
906+ volume = RANGE_DEVICE_VOLUME .clamp (volume );
884907 try {
885908 // See [Internal ref: b/399691860] for context on why we don't use
886909 // RemoteMediaClient.setStreamVolume.
@@ -969,8 +992,9 @@ private void updateInternalStateAndNotifyIfChanged() {
969992 ? getCurrentTimeline ().getPeriod (oldWindowIndex , period , /* setIds= */ true ).uid
970993 : null ;
971994 updatePlayerStateAndNotifyIfChanged (/* resultCallback= */ null );
972- updateVolumeAndNotifyIfChanged ();
995+ updateDeviceVolumeAndNotifyIfChanged ();
973996 updateRepeatModeAndNotifyIfChanged (/* resultCallback= */ null );
997+ updateVolumeAndNotifyIfChanged (/* resultCallback= */ null );
974998 updatePlaybackRateAndNotifyIfChanged (/* resultCallback= */ null );
975999 boolean playingPeriodChangedByTimelineChange = updateTimelineAndNotifyIfChanged ();
9761000 Timeline currentTimeline = getCurrentTimeline ();
@@ -1079,13 +1103,23 @@ private void updatePlaybackRateAndNotifyIfChanged(@Nullable ResultCallback<?> re
10791103 }
10801104
10811105 @ RequiresNonNull ("castSession" )
1082- private void updateVolumeAndNotifyIfChanged () {
1106+ private void updateDeviceVolumeAndNotifyIfChanged () {
10831107 if (castSession != null ) {
1084- int deviceVolume = VOLUME_RANGE .clamp ((int ) Math .round (castSession .getVolume () * MAX_VOLUME ));
1108+ int deviceVolume =
1109+ RANGE_DEVICE_VOLUME .clamp ((int ) Math .round (castSession .getVolume () * MAX_VOLUME ));
10851110 setDeviceVolumeAndNotifyIfChanged (deviceVolume , castSession .isMute ());
10861111 }
10871112 }
10881113
1114+ @ RequiresNonNull ("remoteMediaClient" )
1115+ private void updateVolumeAndNotifyIfChanged (@ Nullable ResultCallback <?> resultCallback ) {
1116+ if (volume .acceptsUpdate (resultCallback )) {
1117+ float remoteVolume = RANGE_VOLUME .clamp (fetchVolume (remoteMediaClient ));
1118+ setVolumeAndNotifyIfChanged (remoteVolume );
1119+ volume .clearPendingResultCallback ();
1120+ }
1121+ }
1122+
10891123 @ RequiresNonNull ("remoteMediaClient" )
10901124 private void updateRepeatModeAndNotifyIfChanged (@ Nullable ResultCallback <?> resultCallback ) {
10911125 if (repeatMode .acceptsUpdate (resultCallback )) {
@@ -1229,14 +1263,29 @@ private boolean updateTracksAndSelectionsAndNotifyIfChanged() {
12291263
12301264 private void updateAvailableCommandsAndNotifyIfChanged () {
12311265 Commands previousAvailableCommands = availableCommands ;
1232- availableCommands = Util .getAvailableCommands (/* player= */ this , PERMANENT_AVAILABLE_COMMANDS );
1266+ availableCommands =
1267+ Util .getAvailableCommands (/* player= */ this , PERMANENT_AVAILABLE_COMMANDS )
1268+ .buildUpon ()
1269+ .addIf (COMMAND_GET_VOLUME , isSetVolumeCommandAvailable ())
1270+ .addIf (COMMAND_SET_VOLUME , isSetVolumeCommandAvailable ())
1271+ .build ();
12331272 if (!availableCommands .equals (previousAvailableCommands )) {
12341273 listeners .queueEvent (
12351274 Player .EVENT_AVAILABLE_COMMANDS_CHANGED ,
12361275 listener -> listener .onAvailableCommandsChanged (availableCommands ));
12371276 }
12381277 }
12391278
1279+ private boolean isSetVolumeCommandAvailable () {
1280+ if (remoteMediaClient != null ) {
1281+ MediaStatus mediaStatus = remoteMediaClient .getMediaStatus ();
1282+ if (mediaStatus != null ) {
1283+ return mediaStatus .isMediaCommandSupported (MediaStatus .COMMAND_SET_VOLUME );
1284+ }
1285+ }
1286+ return false ;
1287+ }
1288+
12401289 private void setMediaItemsInternal (
12411290 List <MediaItem > mediaItems ,
12421291 int startIndex ,
@@ -1347,6 +1396,15 @@ private void setRepeatModeAndNotifyIfChanged(@Player.RepeatMode int repeatMode)
13471396 }
13481397 }
13491398
1399+ private void setVolumeAndNotifyIfChanged (float volume ) {
1400+ if (this .volume .value != volume ) {
1401+ this .volume .value = volume ;
1402+ listeners .queueEvent (
1403+ Player .EVENT_VOLUME_CHANGED , listener -> listener .onVolumeChanged (volume ));
1404+ updateAvailableCommandsAndNotifyIfChanged ();
1405+ }
1406+ }
1407+
13501408 private void setPlaybackParametersAndNotifyIfChanged (PlaybackParameters playbackParameters ) {
13511409 if (this .playbackParameters .value .equals (playbackParameters )) {
13521410 return ;
@@ -1470,6 +1528,14 @@ private static int fetchPlaybackState(RemoteMediaClient remoteMediaClient) {
14701528 }
14711529 }
14721530
1531+ private static float fetchVolume (RemoteMediaClient remoteMediaClient ) {
1532+ MediaStatus mediaStatus = remoteMediaClient .getMediaStatus ();
1533+ if (mediaStatus == null ) {
1534+ return 1f ;
1535+ }
1536+ return (float ) mediaStatus .getStreamVolume ();
1537+ }
1538+
14731539 private static int fetchCurrentWindowIndex (
14741540 @ Nullable RemoteMediaClient remoteMediaClient , Timeline timeline ) {
14751541 if (remoteMediaClient == null ) {
@@ -1734,8 +1800,6 @@ public DeviceInfo fetchDeviceInfo() {
17341800 // There's only one remote routing controller. It's safe to assume it's the Cast routing
17351801 // controller.
17361802 RoutingController remoteController = controllers .get (1 );
1737- // TODO b/364580007 - Populate volume information, and implement Player volume-related
1738- // methods.
17391803 return new DeviceInfo .Builder (DeviceInfo .PLAYBACK_TYPE_REMOTE )
17401804 .setMaxVolume (MAX_VOLUME )
17411805 .setRoutingControllerId (remoteController .getId ())
@@ -1774,7 +1838,7 @@ private final class CastListener extends Cast.Listener {
17741838
17751839 @ Override
17761840 public void onVolumeChanged () {
1777- updateVolumeAndNotifyIfChanged ();
1841+ updateDeviceVolumeAndNotifyIfChanged ();
17781842 listeners .flushEvents ();
17791843 }
17801844 }
0 commit comments