Merge pull request #1730 from eagleeyetom/feature/cast-volume-fix

Android: fix Cast volume to control receiver
This commit is contained in:
advplyr
2025-11-23 14:31:15 -06:00
committed by GitHub
2 changed files with 74 additions and 7 deletions
@@ -76,7 +76,10 @@ class CastPlayer(var castContext: CastContext) : BasePlayer() {
COMMAND_GET_MEDIA_ITEMS_METADATA,
COMMAND_SET_MEDIA_ITEMS_METADATA,
COMMAND_CHANGE_MEDIA_ITEMS,
COMMAND_GET_TRACKS)
COMMAND_GET_TRACKS,
COMMAND_GET_DEVICE_VOLUME,
COMMAND_SET_DEVICE_VOLUME,
COMMAND_ADJUST_DEVICE_VOLUME)
.build()
var currentPlaybackState = Player.STATE_IDLE
@@ -861,27 +864,48 @@ class CastPlayer(var castContext: CastContext) : BasePlayer() {
}
override fun getDeviceVolume(): Int {
return 0
val session = castContext.sessionManager.currentCastSession
return if (session != null && !session.isMute) {
(session.volume * 100).toInt()
} else {
0
}
}
override fun isDeviceMuted(): Boolean {
return false
val session = castContext.sessionManager.currentCastSession
return session?.isMute ?: false
}
override fun setDeviceVolume(volume: Int) {
val session = castContext.sessionManager.currentCastSession
try {
val clamped = volume.coerceIn(0, 100)
session?.volume = clamped / 100.0
} catch (e: Exception) {
Log.e(tag, "Failed to set cast device volume", e)
}
}
override fun increaseDeviceVolume() {
val currentVolume = getDeviceVolume()
val newVolume = (currentVolume + 1).coerceAtMost(100)
setDeviceVolume(newVolume)
}
override fun decreaseDeviceVolume() {
val currentVolume = getDeviceVolume()
val newVolume = (currentVolume - 1).coerceAtLeast(0)
setDeviceVolume(newVolume)
}
override fun setDeviceMuted(muted: Boolean) {
val session = castContext.sessionManager.currentCastSession
try {
session?.isMute = muted
} catch (e: Exception) {
Log.e(tag, "Failed to set cast device mute state", e)
}
}
inner class StatusListener() : RemoteMediaClient.Callback(), SessionManagerListener<CastSession>, RemoteMediaClient.ProgressListener {
@@ -18,6 +18,8 @@ import android.support.v4.media.MediaDescriptionCompat
import android.support.v4.media.MediaMetadataCompat
import android.support.v4.media.session.MediaControllerCompat
import android.support.v4.media.session.MediaSessionCompat
import androidx.media.VolumeProviderCompat
import android.media.AudioManager
import android.support.v4.media.session.PlaybackStateCompat
import android.util.Log
import androidx.annotation.RequiresApi
@@ -94,6 +96,7 @@ class PlayerNotificationService : MediaBrowserServiceCompat() {
private lateinit var mediaSessionConnector: MediaSessionConnector
private lateinit var playerNotificationManager: PlayerNotificationManager
lateinit var mediaSession: MediaSessionCompat
private var remoteVolumeProvider: VolumeProviderCompat? = null
private lateinit var transportControls: MediaControllerCompat.TransportControls
lateinit var mediaManager: MediaManager
@@ -705,11 +708,13 @@ class PlayerNotificationService : MediaBrowserServiceCompat() {
Log.d(tag, "switchToPlayer: Using Cast Player " + castPlayer?.deviceInfo)
mediaSessionConnector.setPlayer(castPlayer)
playerNotificationManager.setPlayer(castPlayer)
setMediaSessionToCastVolume()
castPlayer as CastPlayer
} else {
Log.d(tag, "switchToPlayer: Using ExoPlayer")
mediaSessionConnector.setPlayer(mPlayer)
playerNotificationManager.setPlayer(mPlayer)
setMediaSessionToLocalVolume()
mPlayer
}
@@ -726,6 +731,44 @@ class PlayerNotificationService : MediaBrowserServiceCompat() {
}
}
private fun setMediaSessionToCastVolume() {
val currentVol = try { castPlayer?.getDeviceVolume() ?: 0 } catch (e: Exception) { 0 }
val provider = object : VolumeProviderCompat(VolumeProviderCompat.VOLUME_CONTROL_ABSOLUTE, 100, currentVol) {
override fun onSetVolumeTo(volume: Int) {
val clamped = volume.coerceIn(0, 100)
try {
castPlayer?.setDeviceVolume(clamped)
val actual = castPlayer?.getDeviceVolume() ?: clamped
setCurrentVolume(actual)
} catch (_: Exception) {}
}
override fun onAdjustVolume(direction: Int) {
val current = try { castPlayer?.getDeviceVolume() ?: currentVolume } catch (_: Exception) { currentVolume }
val step = if (direction > 0) 1 else if (direction < 0) -1 else 0
if (step == 0) return
var target = (current + step).coerceIn(0, 100)
try {
castPlayer?.setDeviceVolume(target)
var actual = castPlayer?.getDeviceVolume() ?: current
// If device ignored tiny step, try nudging up to 3 steps total.
if (actual == current) {
target = (current + step * 3).coerceIn(0, 100)
castPlayer?.setDeviceVolume(target)
actual = castPlayer?.getDeviceVolume() ?: current
}
setCurrentVolume(actual)
} catch (_: Exception) {}
}
}
remoteVolumeProvider = provider
mediaSession.setPlaybackToRemote(provider)
}
private fun setMediaSessionToLocalVolume() {
mediaSession.setPlaybackToLocal(AudioManager.STREAM_MUSIC)
remoteVolumeProvider = null
}
fun getCurrentTrackStartOffsetMs(): Long {
return if (currentPlayer.mediaItemCount > 1) {
val windowIndex = currentPlayer.currentMediaItemIndex