1 /*
2  * Copyright (C) 2024 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.settingslib.volume.data.repository
18 
19 import android.media.session.MediaController
20 import android.media.session.MediaSessionManager
21 import com.android.settingslib.bluetooth.LocalBluetoothManager
22 import com.android.settingslib.bluetooth.headsetAudioModeChanges
23 import com.android.settingslib.media.session.activeMediaChanges
24 import com.android.settingslib.media.session.defaultRemoteSessionChanged
25 import com.android.settingslib.volume.shared.AudioManagerEventsReceiver
26 import com.android.settingslib.volume.shared.model.AudioManagerEvent
27 import kotlin.coroutines.CoroutineContext
28 import kotlinx.coroutines.CoroutineScope
29 import kotlinx.coroutines.flow.SharingStarted
30 import kotlinx.coroutines.flow.StateFlow
31 import kotlinx.coroutines.flow.emptyFlow
32 import kotlinx.coroutines.flow.filterIsInstance
33 import kotlinx.coroutines.flow.filterNotNull
34 import kotlinx.coroutines.flow.flowOn
35 import kotlinx.coroutines.flow.map
36 import kotlinx.coroutines.flow.merge
37 import kotlinx.coroutines.flow.onStart
38 import kotlinx.coroutines.flow.stateIn
39 
40 /** Provides controllers for currently active device media sessions. */
41 interface MediaControllerRepository {
42 
43     /**
44      * Get a list of controllers for all ongoing sessions. The controllers will be provided in
45      * priority order with the most important controller at index 0.
46      *
47      * This requires the [android.Manifest.permission.MEDIA_CONTENT_CONTROL] permission be held by
48      * the calling app.
49      */
50     val activeSessions: StateFlow<List<MediaController>>
51 }
52 
53 class MediaControllerRepositoryImpl(
54     audioManagerEventsReceiver: AudioManagerEventsReceiver,
55     private val mediaSessionManager: MediaSessionManager,
56     localBluetoothManager: LocalBluetoothManager?,
57     coroutineScope: CoroutineScope,
58     backgroundContext: CoroutineContext,
59 ) : MediaControllerRepository {
60 
61     override val activeSessions: StateFlow<List<MediaController>> =
62         merge(
<lambda>null63                 mediaSessionManager.defaultRemoteSessionChanged.map {
64                     mediaSessionManager.getActiveSessions(null)
65                 },
66                 mediaSessionManager.activeMediaChanges.filterNotNull(),
<lambda>null67                 localBluetoothManager?.headsetAudioModeChanges?.map {
68                     mediaSessionManager.getActiveSessions(null)
69                 } ?: emptyFlow(),
70                 audioManagerEventsReceiver.events
71                     .filterIsInstance(AudioManagerEvent.StreamDevicesChanged::class)
<lambda>null72                     .map { mediaSessionManager.getActiveSessions(null) },
73             )
<lambda>null74             .onStart { emit(mediaSessionManager.getActiveSessions(null)) }
75             .flowOn(backgroundContext)
76             .stateIn(coroutineScope, SharingStarted.WhileSubscribed(), emptyList())
77 }
78