diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index c9c593f0..8df61dd5 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -11,7 +11,7 @@ ktlint = "1.8.0" ktlintGradle = "14.2.0" detekt = "1.23.8" preferences = "1.2.1" -media3 = "1.10.1" +media3 = "1.11.1" materialDesign = "1.14.0" constraintLayout = "2.2.2" diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/service/MediaLibrarySessionCallback.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/service/MediaLibrarySessionCallback.kt index 63c7f5ad..b0c6ae42 100644 --- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/service/MediaLibrarySessionCallback.kt +++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/service/MediaLibrarySessionCallback.kt @@ -241,14 +241,19 @@ class MediaLibrarySessionCallback( ) } - override fun onConnect( + override fun onConnectAsync( session: MediaSession, controller: MediaSession.ControllerInfo - ): MediaSession.ConnectionResult { + ): ListenableFuture { Timber.i("onConnect") - val connectionResult = super.onConnect(session, controller) - val availableSessionCommands = connectionResult.availableSessionCommands.buildUpon() + // media3 1.11 changed the default MediaSession.Callback.onConnect() to return a sentinel + // result with EMPTY commands (it is only used to detect that the callback is not + // overridden). Calling super and forwarding its player commands would therefore grant + // connecting controllers no player commands at all, breaking playback and queue + // updates. Build the connection result explicitly instead. + val availableSessionCommands = + MediaSession.ConnectionResult.DEFAULT_SESSION_AND_LIBRARY_COMMANDS.buildUpon() for (commandButton in allCustomCommands) { // Add custom command to available session commands. @@ -266,9 +271,11 @@ class MediaLibrarySessionCallback( configureRepeatMode(session.player) - return MediaSession.ConnectionResult.accept( - availableSessionCommands.build(), - connectionResult.availablePlayerCommands + return Futures.immediateFuture( + MediaSession.ConnectionResult.accept( + availableSessionCommands.build(), + MediaSession.ConnectionResult.DEFAULT_PLAYER_COMMANDS + ) ) } diff --git a/ultrasonic/src/test/kotlin/org/moire/ultrasonic/adapters/TrackPlayingIndicatorTest.kt b/ultrasonic/src/test/kotlin/org/moire/ultrasonic/adapters/TrackPlayingIndicatorTest.kt index d26ab8f3..bc6cafaf 100644 --- a/ultrasonic/src/test/kotlin/org/moire/ultrasonic/adapters/TrackPlayingIndicatorTest.kt +++ b/ultrasonic/src/test/kotlin/org/moire/ultrasonic/adapters/TrackPlayingIndicatorTest.kt @@ -5,8 +5,13 @@ import androidx.media3.common.MediaItem import org.junit.Assert.assertFalse import org.junit.Assert.assertTrue import org.junit.Test +import org.junit.runner.RunWith import org.moire.ultrasonic.service.PlaybackSnapshot +import org.robolectric.RobolectricTestRunner +// Player.Commands.EMPTY (used by PlaybackSnapshot) triggers Android framework calls in its +// static initializer on media3 1.11+, so plain JVM android.jar stubs are not enough here. +@RunWith(RobolectricTestRunner::class) class TrackPlayingIndicatorTest { @Test fun `shuffled queue marks play-order row rather than canonical row`() { diff --git a/ultrasonic/src/test/kotlin/org/moire/ultrasonic/service/PlaybackQueueActionsTest.kt b/ultrasonic/src/test/kotlin/org/moire/ultrasonic/service/PlaybackQueueActionsTest.kt index 305c96f4..fe5a7668 100644 --- a/ultrasonic/src/test/kotlin/org/moire/ultrasonic/service/PlaybackQueueActionsTest.kt +++ b/ultrasonic/src/test/kotlin/org/moire/ultrasonic/service/PlaybackQueueActionsTest.kt @@ -5,13 +5,18 @@ import androidx.media3.common.MediaItem import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.test.runTest import org.junit.Test +import org.junit.runner.RunWith import org.mockito.kotlin.any import org.mockito.kotlin.inOrder import org.mockito.kotlin.mock import org.mockito.kotlin.never import org.mockito.kotlin.verify import org.mockito.kotlin.whenever +import org.robolectric.RobolectricTestRunner +// Player.Commands.EMPTY (used by PlaybackSnapshot) triggers Android framework calls in its +// static initializer on media3 1.11+, so plain JVM android.jar stubs are not enough here. +@RunWith(RobolectricTestRunner::class) class PlaybackQueueActionsTest { @Test fun `play shuffled replaces queue before starting a full shuffle`() = runTest {