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