Skip to content

Commit 1f68eee

Browse files
authored
Improve media artwork display in MediaRouteControllerDialog (#66)
* Improve media artwork display in `MediaRouteControllerDialog` * Update screenshots * Update Detekt configuration to ignore tests for undocumented symbols * Remove unnecessary Kotlin BOM declaration * Switch to the platform APIs instead of the legacy ones * Fix display of the media artwork --------- Co-authored-by: MGaetan89 <1009664+MGaetan89@users.noreply.github.com>
1 parent 1413c31 commit 1f68eee

16 files changed

Lines changed: 296 additions & 101 deletions

File tree

config/detekt.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,16 +17,16 @@ comments:
1717

1818
UndocumentedPublicClass:
1919
active: true
20-
excludes: [ '**/demo/**' ]
20+
excludes: [ '**/demo/**', '**/*Test.kt' ]
2121
ignoreDefaultCompanionObject: true
2222

2323
UndocumentedPublicFunction:
2424
active: true
25-
excludes: [ '**/demo/**' ]
25+
excludes: [ '**/demo/**', '**/*Test.kt' ]
2626

2727
UndocumentedPublicProperty:
2828
active: true
29-
excludes: [ '**/demo/**' ]
29+
excludes: [ '**/demo/**', '**/*Test.kt' ]
3030

3131
complexity:
3232
LongParameterList:

demo/src/main/java/ch/srgssr/androidx/mediarouter/compose/demo/MainViewModel.kt

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@
66
package ch.srgssr.androidx.mediarouter.compose.demo
77

88
import android.app.Application
9+
import android.media.MediaMetadata.METADATA_KEY_ART_URI
10+
import android.media.MediaMetadata.METADATA_KEY_TITLE
11+
import android.media.session.MediaSession
12+
import android.media.session.PlaybackState
913
import androidx.annotation.OptIn
1014
import androidx.core.net.toUri
1115
import androidx.lifecycle.AndroidViewModel
@@ -15,18 +19,23 @@ import androidx.media3.cast.SessionAvailabilityListener
1519
import androidx.media3.common.MediaItem
1620
import androidx.media3.common.MediaMetadata
1721
import androidx.media3.common.MimeTypes
22+
import androidx.media3.common.Player
1823
import androidx.media3.common.util.UnstableApi
1924
import androidx.media3.exoplayer.ExoPlayer
25+
import androidx.mediarouter.media.MediaRouter
2026
import com.google.android.gms.cast.framework.CastContext
2127
import kotlinx.coroutines.flow.MutableStateFlow
2228
import kotlinx.coroutines.flow.SharingStarted.Companion.WhileSubscribed
2329
import kotlinx.coroutines.flow.onEach
2430
import kotlinx.coroutines.flow.stateIn
2531
import kotlinx.coroutines.flow.update
2632
import kotlin.time.Duration.Companion.seconds
33+
import android.media.MediaMetadata as PlatformMediaMetadata
2734

2835
@OptIn(UnstableApi::class)
2936
class MainViewModel(application: Application) : AndroidViewModel(application) {
37+
private val playerListener = PlayerListener()
38+
private val mediaSession = MediaSession(application, "androidx-mediarouter-compose-demo")
3039
private val localPlayer = ExoPlayer.Builder(application).build()
3140
private val castPlayer = CastPlayer(CastContext.getSharedInstance(application))
3241
private val currentPlayer =
@@ -35,13 +44,15 @@ class MainViewModel(application: Application) : AndroidViewModel(application) {
3544
val player = currentPlayer
3645
.onEach { player ->
3746
val oldPlayer = if (player == localPlayer) castPlayer else localPlayer
47+
player.addListener(playerListener)
3848
player.volume = oldPlayer.volume
3949
player.repeatMode = oldPlayer.repeatMode
4050
player.playWhenReady = oldPlayer.playWhenReady
4151

4252
oldPlayer.currentMediaItem?.let {
4353
player.setMediaItem(it, oldPlayer.currentPosition)
4454
}
55+
oldPlayer.removeListener(playerListener)
4556
oldPlayer.stop()
4657
}
4758
.stateIn(
@@ -51,6 +62,8 @@ class MainViewModel(application: Application) : AndroidViewModel(application) {
5162
)
5263

5364
init {
65+
MediaRouter.getInstance(application).setMediaSession(mediaSession)
66+
5467
localPlayer.setMediaItem(mediaItem)
5568
localPlayer.volume = 0f
5669
localPlayer.prepare()
@@ -68,11 +81,51 @@ class MainViewModel(application: Application) : AndroidViewModel(application) {
6881
}
6982

7083
override fun onCleared() {
84+
mediaSession.release()
7185
localPlayer.release()
7286
castPlayer.setSessionAvailabilityListener(null)
7387
castPlayer.release()
7488
}
7589

90+
private inner class PlayerListener : Player.Listener {
91+
override fun onMediaMetadataChanged(mediaMetadata: MediaMetadata) {
92+
mediaSession.setMetadata(
93+
PlatformMediaMetadata.Builder()
94+
.putText(METADATA_KEY_TITLE, mediaItem.mediaMetadata.title)
95+
.putText(METADATA_KEY_ART_URI, mediaItem.mediaMetadata.artworkUri.toString())
96+
.build()
97+
)
98+
}
99+
100+
override fun onPlaybackStateChanged(@Player.State playbackState: Int) {
101+
val state = when (playbackState) {
102+
Player.STATE_IDLE -> PlaybackState.STATE_CONNECTING
103+
Player.STATE_BUFFERING -> PlaybackState.STATE_BUFFERING
104+
Player.STATE_READY -> PlaybackState.STATE_PLAYING
105+
Player.STATE_ENDED -> PlaybackState.STATE_STOPPED
106+
else -> PlaybackState.STATE_NONE
107+
}
108+
val player = currentPlayer.value
109+
val position = player.currentPosition
110+
val playbackSpeed = player.playbackParameters.speed
111+
val actions = listOfNotNull(
112+
PlaybackState.ACTION_PAUSE.takeIf { player.isCommandAvailable(Player.COMMAND_PLAY_PAUSE) },
113+
PlaybackState.ACTION_PLAY.takeIf { player.isCommandAvailable(Player.COMMAND_PLAY_PAUSE) },
114+
PlaybackState.ACTION_PLAY_PAUSE.takeIf { player.isCommandAvailable(Player.COMMAND_PLAY_PAUSE) },
115+
PlaybackState.ACTION_STOP.takeIf { player.isCommandAvailable(Player.COMMAND_STOP) },
116+
).fold(0L) { actions, action ->
117+
actions or action
118+
}
119+
120+
mediaSession.setPlaybackState(
121+
PlaybackState.Builder()
122+
.setState(state, position, playbackSpeed)
123+
.setActions(actions)
124+
.build()
125+
)
126+
}
127+
}
128+
76129
private companion object {
77130
@Suppress("MaxLineLength")
78131
private val mediaItem = MediaItem.Builder()

gradle/libs.versions.toml

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,10 +64,11 @@ androidx-test-core = { group = "androidx.test", name = "core", version.ref = "an
6464
androidx-test-ext-junit = { group = "androidx.test.ext", name = "junit", version.ref = "androidx-test-ext-junit" }
6565
androidx-test-monitor = { group = "androidx.test", name = "monitor", version.ref = "androidx-test-monitor" }
6666
coil-compose = { group = "io.coil-kt.coil3", name = "coil-compose", version.ref = "coil" }
67+
coil-compose-core = { group = "io.coil-kt.coil3", name = "coil-compose-core", version.ref = "coil" }
68+
coil-core = { group = "io.coil-kt.coil3", name = "coil-core", version.ref = "coil" }
6769
coil-network-okhttp = { group = "io.coil-kt.coil3", name = "coil-network-okhttp", version.ref = "coil" }
6870
junit = { group = "junit", name = "junit", version.ref = "junit" }
69-
kotlin-bom = { group = "org.jetbrains.kotlin", name = "kotlin-bom", version.ref = "kotlin" }
70-
kotlin-test = { group = "org.jetbrains.kotlin", name = "kotlin-test" }
71+
kotlin-test = { group = "org.jetbrains.kotlin", name = "kotlin-test", version.ref = "kotlin" }
7172
kotlinx-coroutines-core = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-core", version.ref = "kotlinx-coroutines" }
7273
kotlinx-coroutines-test = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-test", version.ref = "kotlinx-coroutines" }
7374
play-services-cast-framework = { group = "com.google.android.gms", name = "play-services-cast-framework", version.ref = "play-services-cast-framework" }

mediarouter-compose/build.gradle.kts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -118,16 +118,17 @@ dependencies {
118118
implementation(libs.androidx.media)
119119
api(libs.androidx.mediarouter)
120120
implementation(libs.coil.compose)
121+
implementation(libs.coil.compose.core)
122+
implementation(libs.coil.core)
121123
implementation(libs.coil.network.okhttp)
122-
implementation(platform(libs.kotlin.bom))
123-
testImplementation(libs.kotlinx.coroutines.core)
124124

125125
testImplementation(libs.androidx.activity)
126126
testImplementation(libs.androidx.test.core)
127127
testImplementation(libs.androidx.test.ext.junit)
128128
testImplementation(libs.androidx.test.monitor)
129129
testImplementation(libs.junit)
130130
testImplementation(libs.kotlin.test)
131+
testImplementation(libs.kotlinx.coroutines.core)
131132
testImplementation(libs.kotlinx.coroutines.test)
132133
testImplementation(libs.robolectric)
133134
testImplementation(libs.robolectric.annotations)
Loading
Loading
Loading

0 commit comments

Comments
 (0)