1+ package com.example.wear.snippets.audio
2+
3+ import android.app.Application
4+ import android.media.session.MediaSession
5+ import android.media.session.MediaSessionManager
6+ import android.os.Build
7+ import androidx.annotation.RequiresApi
8+ import androidx.core.content.ContextCompat
9+ import androidx.lifecycle.AndroidViewModel
10+ import androidx.media3.common.Player
11+ import androidx.media3.session.MediaController
12+ import androidx.media3.session.SessionToken
13+ import kotlinx.coroutines.ExperimentalCoroutinesApi
14+ import kotlinx.coroutines.channels.awaitClose
15+ import kotlinx.coroutines.channels.trySendBlocking
16+ import kotlinx.coroutines.flow.Flow
17+ import kotlinx.coroutines.flow.callbackFlow
18+ import kotlinx.coroutines.flow.distinctUntilChanged
19+ import kotlinx.coroutines.flow.flatMapLatest
20+ import kotlinx.coroutines.flow.map
21+ import kotlinx.coroutines.flow.mapNotNull
22+ import kotlinx.coroutines.guava.await
23+ import java.util.concurrent.Executor
24+
25+ class RemoteMediaActivityViewModel (application : Application ) : AndroidViewModel(application) {
26+ private val mediaSessionManager = application.getSystemService(MediaSessionManager ::class .java)
27+ private val mainExecutor: Executor = ContextCompat .getMainExecutor(application)
28+
29+ @RequiresApi(Build .VERSION_CODES .CUR_DEVELOPMENT )
30+ private val sessionFlow: Flow <List <MediaSession .Token >> = callbackFlow {
31+ // [START android_wear_remote_media_session_listener]
32+ val callback =
33+ MediaSessionManager .OnActiveSessionsChangedListener { controllers ->
34+ val tokens = controllers?.map { it.sessionToken } ? : emptyList()
35+ trySendBlocking(tokens)
36+ }
37+ // [END android_wear_remote_media_session_listener]
38+ // [START android_wear_remote_media_session_register_listener]
39+ mediaSessionManager.addOnActiveSessionsForPackageChangedListener(
40+ application.packageName,
41+ mainExecutor,
42+ callback,
43+ )
44+ // [END android_wear_remote_media_session_register_listener]
45+ // [START android_wear_remote_media_session_get_sessions]
46+ trySendBlocking(mediaSessionManager.getActiveSessionsForPackage(application.packageName))
47+ // [END android_wear_remote_media_session_get_sessions]
48+ // [START android_wear_remote_media_session_remove_listener]
49+ awaitClose { mediaSessionManager.removeOnActiveSessionsForPackageChangedListener(callback) }
50+ // [END android_wear_remote_media_session_remove_listener]
51+ }
52+
53+ @OptIn(ExperimentalCoroutinesApi ::class )
54+ @RequiresApi(37 )
55+ // [START android_wear_remote_media_session_media_controller]
56+ private val controllerFlow: Flow <MediaController ?> =
57+ sessionFlow
58+ .distinctUntilChanged()
59+ .flatMapLatest { tokens ->
60+ val token = tokens.firstOrNull() ? : return @flatMapLatest kotlinx.coroutines.flow.flowOf(null )
61+ callbackFlow {
62+ val sessionToken = SessionToken .createSessionToken(application, token).await()
63+ val controller = MediaController .Builder (application, sessionToken).buildAsync().await()
64+ val listener = object : Player .Listener {
65+ override fun onEvents (player : Player , events : Player .Events ) {
66+ trySendBlocking(controller)
67+ }
68+ }
69+ controller.addListener(listener)
70+ trySendBlocking(controller)
71+ awaitClose {
72+ controller.removeListener(listener)
73+ controller.release()
74+ }
75+ }
76+ }
77+ // [END android_wear_remote_media_session_media_controller]
78+
79+ // [START android_wear_remote_media_session_listen_events]
80+ private val MediaController .controllerEventFlow: Flow <Unit >
81+ get() =
82+ callbackFlow<Unit > {
83+ val listener =
84+ object : Player .Listener {
85+ override fun onEvents (player : Player , events : Player .Events ) {
86+ trySendBlocking(Unit )
87+ }
88+ }
89+ this @controllerEventFlow.addListener(listener)
90+ awaitClose { this @controllerEventFlow.removeListener(listener) }
91+ }
92+
93+ // [END android_wear_remote_media_session_listen_events]
94+ }
0 commit comments