mirror of
https://github.com/advplyr/audiobookshelf-app.git
synced 2026-08-06 20:08:48 +02:00
Merge pull request #1738 from eagleeyetom/chromecast-volume-fix-updated
Android cast: stabilize MediaSession volume handling; simplify Google…
This commit is contained in:
@@ -3,6 +3,7 @@ package com.audiobookshelf.app.player
|
|||||||
import android.annotation.SuppressLint
|
import android.annotation.SuppressLint
|
||||||
import android.os.Looper
|
import android.os.Looper
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
|
import kotlin.math.roundToInt
|
||||||
import android.view.Surface
|
import android.view.Surface
|
||||||
import android.view.SurfaceHolder
|
import android.view.SurfaceHolder
|
||||||
import android.view.SurfaceView
|
import android.view.SurfaceView
|
||||||
@@ -53,6 +54,10 @@ class CastPlayer(var castContext: CastContext) : BasePlayer() {
|
|||||||
private var myCurrentTrackGroups: TrackGroupArray
|
private var myCurrentTrackGroups: TrackGroupArray
|
||||||
private var myCurrentTrackSelections: TrackSelectionArray
|
private var myCurrentTrackSelections: TrackSelectionArray
|
||||||
|
|
||||||
|
// Cache for pending volume to handle async setVolume
|
||||||
|
private var pendingVolume: Int? = null
|
||||||
|
private var pendingVolumeSetAt: Long = 0L
|
||||||
|
|
||||||
var currentMediaItems:List<MediaItem> = mutableListOf()
|
var currentMediaItems:List<MediaItem> = mutableListOf()
|
||||||
var remoteMediaClient:RemoteMediaClient? = null
|
var remoteMediaClient:RemoteMediaClient? = null
|
||||||
var sessionAvailabilityListener:SessionAvailabilityListener? = null
|
var sessionAvailabilityListener:SessionAvailabilityListener? = null
|
||||||
@@ -864,12 +869,24 @@ class CastPlayer(var castContext: CastContext) : BasePlayer() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun getDeviceVolume(): Int {
|
override fun getDeviceVolume(): Int {
|
||||||
|
// Short-lived cache without even-value quantization
|
||||||
|
val now = System.currentTimeMillis()
|
||||||
|
if (pendingVolume != null && (now - pendingVolumeSetAt) < 100) {
|
||||||
|
return pendingVolume!!
|
||||||
|
}
|
||||||
|
|
||||||
val session = castContext.sessionManager.currentCastSession
|
val session = castContext.sessionManager.currentCastSession
|
||||||
return if (session != null && !session.isMute) {
|
val vol = if (session != null && !session.isMute) {
|
||||||
(session.volume * 100).toInt()
|
(session.volume * 100).roundToInt().coerceIn(0, 100)
|
||||||
} else {
|
} else {
|
||||||
0
|
0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (pendingVolume != null && vol == pendingVolume) {
|
||||||
|
pendingVolume = null
|
||||||
|
}
|
||||||
|
|
||||||
|
return vol
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun isDeviceMuted(): Boolean {
|
override fun isDeviceMuted(): Boolean {
|
||||||
@@ -881,22 +898,28 @@ class CastPlayer(var castContext: CastContext) : BasePlayer() {
|
|||||||
val session = castContext.sessionManager.currentCastSession
|
val session = castContext.sessionManager.currentCastSession
|
||||||
try {
|
try {
|
||||||
val clamped = volume.coerceIn(0, 100)
|
val clamped = volume.coerceIn(0, 100)
|
||||||
session?.volume = clamped / 100.0
|
val target = (clamped / 100.0).let { kotlin.math.round(it * 100.0) / 100.0 }
|
||||||
|
|
||||||
|
// Cache for 100ms to return in getDeviceVolume during async period
|
||||||
|
pendingVolume = clamped
|
||||||
|
pendingVolumeSetAt = System.currentTimeMillis()
|
||||||
|
|
||||||
|
session?.setVolume(target)
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
Log.e(tag, "Failed to set cast device volume", e)
|
Log.e(tag, "Failed to set cast device volume", e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun increaseDeviceVolume() {
|
override fun increaseDeviceVolume() {
|
||||||
val currentVolume = getDeviceVolume()
|
val current = getDeviceVolume()
|
||||||
val newVolume = (currentVolume + 1).coerceAtMost(100)
|
val next = current + 1
|
||||||
setDeviceVolume(newVolume)
|
setDeviceVolume(next.coerceAtMost(100))
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun decreaseDeviceVolume() {
|
override fun decreaseDeviceVolume() {
|
||||||
val currentVolume = getDeviceVolume()
|
val current = getDeviceVolume()
|
||||||
val newVolume = (currentVolume - 1).coerceAtLeast(0)
|
val next = current - 1
|
||||||
setDeviceVolume(newVolume)
|
setDeviceVolume(next.coerceAtLeast(0))
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun setDeviceMuted(muted: Boolean) {
|
override fun setDeviceMuted(muted: Boolean) {
|
||||||
|
|||||||
+10
-21
@@ -736,32 +736,21 @@ class PlayerNotificationService : MediaBrowserServiceCompat() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun setMediaSessionToCastVolume() {
|
private fun setMediaSessionToCastVolume() {
|
||||||
val currentVol = try { castPlayer?.getDeviceVolume() ?: 0 } catch (e: Exception) { 0 }
|
val currentVol = try { castPlayer?.getDeviceVolume() ?: 0 } catch (_: Exception) { 0 }
|
||||||
val provider = object : VolumeProviderCompat(VolumeProviderCompat.VOLUME_CONTROL_ABSOLUTE, 100, currentVol) {
|
val provider = object : VolumeProviderCompat(VolumeProviderCompat.VOLUME_CONTROL_ABSOLUTE, 100, currentVol) {
|
||||||
override fun onSetVolumeTo(volume: Int) {
|
override fun onSetVolumeTo(volume: Int) {
|
||||||
|
// Clamp, update UI immediately, then send to device
|
||||||
val clamped = volume.coerceIn(0, 100)
|
val clamped = volume.coerceIn(0, 100)
|
||||||
try {
|
setCurrentVolume(clamped)
|
||||||
castPlayer?.setDeviceVolume(clamped)
|
try { castPlayer?.setDeviceVolume(clamped) } catch (_: Exception) {}
|
||||||
val actual = castPlayer?.getDeviceVolume() ?: clamped
|
|
||||||
setCurrentVolume(actual)
|
|
||||||
} catch (_: Exception) {}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onAdjustVolume(direction: Int) {
|
override fun onAdjustVolume(direction: Int) {
|
||||||
val current = try { castPlayer?.getDeviceVolume() ?: currentVolume } catch (_: Exception) { currentVolume }
|
// Use Android-provided step (−1, 0, +1). Clamp, update UI immediately, then send.
|
||||||
val step = if (direction > 0) 1 else if (direction < 0) -1 else 0
|
val current = currentVolume
|
||||||
if (step == 0) return
|
val target = (current + direction).coerceIn(0, 100)
|
||||||
var target = (current + step).coerceIn(0, 100)
|
setCurrentVolume(target)
|
||||||
try {
|
try { castPlayer?.setDeviceVolume(target) } catch (_: Exception) {}
|
||||||
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
|
remoteVolumeProvider = provider
|
||||||
|
|||||||
Reference in New Issue
Block a user