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 package com.android.settingslib.volume.data.repository
17 
18 import com.android.settingslib.media.LocalMediaManager
19 import com.android.settingslib.media.MediaDevice
20 import com.android.settingslib.media.flags.Flags
21 import com.android.settingslib.volume.shared.AudioManagerEventsReceiver
22 import com.android.settingslib.volume.shared.model.AudioManagerEvent
23 import kotlinx.coroutines.CoroutineScope
24 import kotlinx.coroutines.channels.awaitClose
25 import kotlinx.coroutines.flow.Flow
26 import kotlinx.coroutines.flow.SharingStarted
27 import kotlinx.coroutines.flow.StateFlow
28 import kotlinx.coroutines.flow.callbackFlow
29 import kotlinx.coroutines.flow.filterIsInstance
30 import kotlinx.coroutines.flow.map
31 import kotlinx.coroutines.flow.merge
32 import kotlinx.coroutines.flow.onStart
33 import kotlinx.coroutines.flow.shareIn
34 import kotlinx.coroutines.flow.stateIn
35 
36 /** Repository providing data about connected media devices. */
37 interface LocalMediaRepository {
38 
39     /** Currently connected media device */
40     val currentConnectedDevice: StateFlow<MediaDevice?>
41 }
42 
43 class LocalMediaRepositoryImpl(
44     audioManagerEventsReceiver: AudioManagerEventsReceiver,
45     private val localMediaManager: LocalMediaManager,
46     coroutineScope: CoroutineScope,
47 ) : LocalMediaRepository {
48 
49     private val devicesChanges =
50         audioManagerEventsReceiver.events.filterIsInstance(
51             AudioManagerEvent.StreamDevicesChanged::class
52         )
53     private val mediaDevicesUpdates: Flow<DevicesUpdate> =
<lambda>null54         callbackFlow {
55                 val callback =
56                     object : LocalMediaManager.DeviceCallback {
57                         override fun onDeviceListUpdate(newDevices: List<MediaDevice>?) {
58                             trySend(DevicesUpdate.DeviceListUpdate(newDevices ?: emptyList()))
59                         }
60 
61                         override fun onSelectedDeviceStateChanged(
62                             device: MediaDevice?,
63                             state: Int,
64                         ) {
65                             trySend(DevicesUpdate.SelectedDeviceStateChanged)
66                         }
67 
68                         override fun onDeviceAttributesChanged() {
69                             trySend(DevicesUpdate.DeviceAttributesChanged)
70                         }
71                     }
72                 localMediaManager.registerCallback(callback)
73                 if (!Flags.removeUnnecessaryRouteScanning()) {
74                     localMediaManager.startScan()
75                 }
76 
77                 awaitClose {
78                     if (!Flags.removeUnnecessaryRouteScanning()) {
79                         localMediaManager.stopScan()
80                     }
81                     localMediaManager.unregisterCallback(callback)
82                 }
83             }
84             .shareIn(coroutineScope, SharingStarted.Eagerly, replay = 0)
85 
86     override val currentConnectedDevice: StateFlow<MediaDevice?> =
87         merge(devicesChanges, mediaDevicesUpdates)
<lambda>null88             .map { localMediaManager.currentConnectedDevice }
<lambda>null89             .onStart { emit(localMediaManager.currentConnectedDevice) }
90             .stateIn(
91                 coroutineScope,
92                 SharingStarted.Eagerly,
93                 localMediaManager.currentConnectedDevice,
94             )
95 
96     private sealed interface DevicesUpdate {
97 
98         data class DeviceListUpdate(val newDevices: List<MediaDevice>?) : DevicesUpdate
99 
100         data object SelectedDeviceStateChanged : DevicesUpdate
101 
102         data object DeviceAttributesChanged : DevicesUpdate
103     }
104 }
105