Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 89eaf2bd authored by chelseahao's avatar chelseahao
Browse files

Listen to audio sharing state change and add the audio sharing source to device if ready.

Test: atest
Bug: 360759048
Flag: com.android.settingslib.flags.audio_sharing_qs_dialog_improvement
Change-Id: I4f8e19f56d699199aad24d6cd2c3c8573e158b15
parent b51480a5
Loading
Loading
Loading
Loading
+41 −0
Original line number Diff line number Diff line
@@ -68,3 +68,44 @@ val LocalBluetoothLeBroadcast.onBroadcastStartedOrStopped: Flow<Unit>
                awaitClose { unregisterServiceCallBack(listener) }
            }
            .buffer(capacity = Channel.CONFLATED)

/** [Flow] for [BluetoothLeBroadcast.Callback] onPlaybackStarted event */
val LocalBluetoothLeBroadcast.onPlaybackStarted: Flow<Unit>
    get() =
        callbackFlow {
            val listener =
                object : BluetoothLeBroadcast.Callback {
                    override fun onBroadcastStarted(reason: Int, broadcastId: Int) {}

                    override fun onBroadcastStartFailed(reason: Int) {
                    }

                    override fun onBroadcastStopped(reason: Int, broadcastId: Int) {
                    }

                    override fun onBroadcastStopFailed(reason: Int) {
                    }

                    override fun onPlaybackStarted(reason: Int, broadcastId: Int) {
                        launch { trySend(Unit) }
                    }

                    override fun onPlaybackStopped(reason: Int, broadcastId: Int) {
                    }

                    override fun onBroadcastUpdated(reason: Int, broadcastId: Int) {}

                    override fun onBroadcastUpdateFailed(reason: Int, broadcastId: Int) {}

                    override fun onBroadcastMetadataChanged(
                        broadcastId: Int,
                        metadata: BluetoothLeBroadcastMetadata
                    ) {}
                }
            registerServiceCallBack(
                ConcurrentUtils.DIRECT_EXECUTOR,
                listener,
            )
            awaitClose { unregisterServiceCallBack(listener) }
        }
            .buffer(capacity = Channel.CONFLATED)
+32 −0
Original line number Diff line number Diff line
@@ -19,19 +19,27 @@ package com.android.systemui.bluetooth.qsdialog
import com.android.settingslib.bluetooth.BluetoothUtils
import com.android.settingslib.bluetooth.CachedBluetoothDevice
import com.android.settingslib.bluetooth.LocalBluetoothManager
import com.android.settingslib.bluetooth.onPlaybackStarted
import com.android.settingslib.volume.data.repository.AudioSharingRepository as SettingsLibAudioSharingRepository
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Background
import javax.inject.Inject
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.emptyFlow
import kotlinx.coroutines.flow.firstOrNull
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.mapNotNull
import kotlinx.coroutines.withContext

/** Holds business logic for the audio sharing state. */
interface AudioSharingInteractor {
    val isAudioSharingOn: Flow<Boolean>

    val audioSourceStateUpdate: Flow<Unit>

    suspend fun handleAudioSourceWhenReady()

    suspend fun isAvailableAudioSharingMediaBluetoothDevice(
        cachedBluetoothDevice: CachedBluetoothDevice
    ): Boolean
@@ -53,6 +61,26 @@ constructor(

    override val isAudioSharingOn = settingsLibAudioSharingRepository.inAudioSharing

    override val audioSourceStateUpdate = audioSharingRepository.audioSourceStateUpdate

    override suspend fun handleAudioSourceWhenReady() {
        withContext(backgroundDispatcher) {
            audioSharingRepository.leAudioBroadcastProfile?.let { profile ->
                isAudioSharingOn
                    .mapNotNull { audioSharingOn ->
                        if (audioSharingOn) {
                            // onPlaybackStarted could emit multiple times during one audio sharing
                            // session, we only perform add source on the first time
                            profile.onPlaybackStarted.firstOrNull()
                        } else {
                            null
                        }
                    }
                    .collect { audioSharingRepository.addSource() }
            }
        }
    }

    override suspend fun isAvailableAudioSharingMediaBluetoothDevice(
        cachedBluetoothDevice: CachedBluetoothDevice
    ): Boolean {
@@ -77,6 +105,10 @@ constructor(
class AudioSharingInteractorEmptyImpl @Inject constructor() : AudioSharingInteractor {
    override val isAudioSharingOn: Flow<Boolean> = flowOf(false)

    override val audioSourceStateUpdate: Flow<Unit> = emptyFlow()

    override suspend fun handleAudioSourceWhenReady() {}

    override suspend fun isAvailableAudioSharingMediaBluetoothDevice(
        cachedBluetoothDevice: CachedBluetoothDevice
    ) = false
+31 −1
Original line number Diff line number Diff line
@@ -18,13 +18,22 @@ package com.android.systemui.bluetooth.qsdialog

import com.android.settingslib.bluetooth.CachedBluetoothDevice
import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcast
import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcastAssistant
import com.android.settingslib.bluetooth.LocalBluetoothManager
import com.android.settingslib.bluetooth.onSourceConnectedOrRemoved
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Background
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.emptyFlow
import kotlinx.coroutines.withContext

interface AudioSharingRepository {
    val leAudioBroadcastProfile: LocalBluetoothLeBroadcast?

    val audioSourceStateUpdate: Flow<Unit>

    suspend fun addSource()

    suspend fun setActive(cachedBluetoothDevice: CachedBluetoothDevice)

@@ -37,9 +46,25 @@ class AudioSharingRepositoryImpl(
    @Background private val backgroundDispatcher: CoroutineDispatcher,
) : AudioSharingRepository {

    private val leAudioBroadcastProfile: LocalBluetoothLeBroadcast?
    override val leAudioBroadcastProfile: LocalBluetoothLeBroadcast?
        get() = localBluetoothManager.profileManager?.leAudioBroadcastProfile

    private val leAudioBroadcastAssistantProfile: LocalBluetoothLeBroadcastAssistant?
        get() = localBluetoothManager.profileManager?.leAudioBroadcastAssistantProfile

    override val audioSourceStateUpdate: Flow<Unit> =
        leAudioBroadcastAssistantProfile?.onSourceConnectedOrRemoved ?: emptyFlow()

    override suspend fun addSource() {
        withContext(backgroundDispatcher) {
            leAudioBroadcastProfile?.latestBluetoothLeBroadcastMetadata?.let { metadata ->
                leAudioBroadcastAssistantProfile?.let {
                    it.allConnectedDevices.forEach { sink -> it.addSource(sink, metadata, false) }
                }
            }
        }
    }

    override suspend fun setActive(cachedBluetoothDevice: CachedBluetoothDevice) {
        withContext(backgroundDispatcher) { cachedBluetoothDevice.setActive() }
    }
@@ -51,6 +76,11 @@ class AudioSharingRepositoryImpl(

@SysUISingleton
class AudioSharingRepositoryEmptyImpl : AudioSharingRepository {
    override val leAudioBroadcastProfile: LocalBluetoothLeBroadcast? = null

    override val audioSourceStateUpdate: Flow<Unit> = emptyFlow()

    override suspend fun addSource() {}

    override suspend fun setActive(cachedBluetoothDevice: CachedBluetoothDevice) {}

+1 −1
Original line number Diff line number Diff line
@@ -300,7 +300,7 @@ internal constructor(
    }

    private fun getProgressBarBackground(dialog: SystemUIDialog): View {
        return dialog.requireViewById(R.id.bluetooth_tile_dialog_progress_animation)
        return dialog.requireViewById(R.id.bluetooth_tile_dialog_progress_background)
    }

    private fun getScrollViewContent(dialog: SystemUIDialog): View {
+16 −1
Original line number Diff line number Diff line
@@ -30,6 +30,7 @@ import com.android.internal.jank.InteractionJankMonitor
import com.android.internal.logging.UiEventLogger
import com.android.settingslib.bluetooth.BluetoothUtils
import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcast
import com.android.settingslib.flags.Flags.audioSharingQsDialogImprovement
import com.android.systemui.Prefs
import com.android.systemui.animation.DialogCuj
import com.android.systemui.animation.DialogTransitionAnimator
@@ -51,6 +52,7 @@ import kotlinx.coroutines.Job
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.channels.produce
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.emptyFlow
import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.merge
@@ -67,6 +69,7 @@ constructor(
    private val deviceItemActionInteractor: DeviceItemActionInteractor,
    private val bluetoothStateInteractor: BluetoothStateInteractor,
    private val bluetoothAutoOnInteractor: BluetoothAutoOnInteractor,
    private val audioSharingInteractor: AudioSharingInteractor,
    private val audioSharingButtonViewModelFactory: AudioSharingButtonViewModel.Factory,
    private val bluetoothDeviceMetadataInteractor: BluetoothDeviceMetadataInteractor,
    private val dialogTransitionAnimator: DialogTransitionAnimator,
@@ -140,7 +143,15 @@ constructor(
                // the device item list and animate the progress bar.
                merge(
                        deviceItemInteractor.deviceItemUpdateRequest,
                        bluetoothDeviceMetadataInteractor.metadataUpdate
                        bluetoothDeviceMetadataInteractor.metadataUpdate,
                        if (
                            BluetoothUtils.isAudioSharingEnabled() &&
                                audioSharingQsDialogImprovement()
                        ) {
                            audioSharingInteractor.audioSourceStateUpdate
                        } else {
                            emptyFlow()
                        }
                    )
                    .onEach {
                        dialogDelegate.animateProgressBar(dialog, true)
@@ -155,6 +166,10 @@ constructor(
                    .launchIn(this)

                if (BluetoothUtils.isAudioSharingEnabled()) {
                    if (audioSharingQsDialogImprovement()) {
                        launch { audioSharingInteractor.handleAudioSourceWhenReady() }
                    }

                    audioSharingButtonViewModelFactory.create().run {
                        audioSharingButtonStateUpdate
                            .onEach {
Loading