summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/bluetooth/qsdialog/AudioSharingInteractorTest.kt152
-rw-r--r--packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/AudioSharingInteractor.kt27
2 files changed, 61 insertions, 118 deletions
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/bluetooth/qsdialog/AudioSharingInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/bluetooth/qsdialog/AudioSharingInteractorTest.kt
index 4c329dcf2f2b..cebd05d92537 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/bluetooth/qsdialog/AudioSharingInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/bluetooth/qsdialog/AudioSharingInteractorTest.kt
@@ -18,20 +18,12 @@ package com.android.systemui.bluetooth.qsdialog
import android.bluetooth.BluetoothLeBroadcast
import android.bluetooth.BluetoothLeBroadcastMetadata
-import android.content.ContentResolver
-import android.content.applicationContext
import android.testing.TestableLooper
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
-import com.android.settingslib.bluetooth.BluetoothEventManager
import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcast
-import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcastAssistant
-import com.android.settingslib.bluetooth.LocalBluetoothProfileManager
-import com.android.settingslib.bluetooth.VolumeControlProfile
-import com.android.settingslib.volume.shared.AudioSharingLogger
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.kosmos.testDispatcher
import com.android.systemui.kosmos.testScope
import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
@@ -46,14 +38,10 @@ import org.junit.runner.RunWith
import org.mockito.ArgumentCaptor
import org.mockito.Captor
import org.mockito.Mock
-import org.mockito.Mockito.never
import org.mockito.Mockito.verify
import org.mockito.junit.MockitoJUnit
import org.mockito.junit.MockitoRule
import org.mockito.kotlin.any
-import org.mockito.kotlin.doReturn
-import org.mockito.kotlin.mock
-import org.mockito.kotlin.spy
import org.mockito.kotlin.times
import org.mockito.kotlin.whenever
@@ -78,7 +66,7 @@ class AudioSharingInteractorTest : SysuiTestCase() {
}
@Test
- fun testIsAudioSharingOn_flagOff_false() =
+ fun isAudioSharingOn_flagOff_false() =
with(kosmos) {
testScope.runTest {
bluetoothTileDialogAudioSharingRepository.setAudioSharingAvailable(false)
@@ -90,7 +78,7 @@ class AudioSharingInteractorTest : SysuiTestCase() {
}
@Test
- fun testIsAudioSharingOn_flagOn_notInAudioSharing_false() =
+ fun isAudioSharingOn_flagOn_notInAudioSharing_false() =
with(kosmos) {
testScope.runTest {
bluetoothTileDialogAudioSharingRepository.setAudioSharingAvailable(true)
@@ -103,7 +91,7 @@ class AudioSharingInteractorTest : SysuiTestCase() {
}
@Test
- fun testIsAudioSharingOn_flagOn_inAudioSharing_true() =
+ fun isAudioSharingOn_flagOn_inAudioSharing_true() =
with(kosmos) {
testScope.runTest {
bluetoothTileDialogAudioSharingRepository.setAudioSharingAvailable(true)
@@ -116,7 +104,7 @@ class AudioSharingInteractorTest : SysuiTestCase() {
}
@Test
- fun testAudioSourceStateUpdate_notInAudioSharing_returnEmpty() =
+ fun audioSourceStateUpdate_notInAudioSharing_returnEmpty() =
with(kosmos) {
testScope.runTest {
bluetoothTileDialogAudioSharingRepository.setAudioSharingAvailable(true)
@@ -129,7 +117,7 @@ class AudioSharingInteractorTest : SysuiTestCase() {
}
@Test
- fun testAudioSourceStateUpdate_inAudioSharing_returnUnit() =
+ fun audioSourceStateUpdate_inAudioSharing_returnUnit() =
with(kosmos) {
testScope.runTest {
bluetoothTileDialogAudioSharingRepository.setAudioSharingAvailable(true)
@@ -144,7 +132,7 @@ class AudioSharingInteractorTest : SysuiTestCase() {
}
@Test
- fun testHandleAudioSourceWhenReady_flagOff_sourceNotAdded() =
+ fun handleAudioSourceWhenReady_flagOff_sourceNotAdded() =
with(kosmos) {
testScope.runTest {
bluetoothTileDialogAudioSharingRepository.setAudioSharingAvailable(false)
@@ -157,7 +145,7 @@ class AudioSharingInteractorTest : SysuiTestCase() {
}
@Test
- fun testHandleAudioSourceWhenReady_noProfile_sourceNotAdded() =
+ fun handleAudioSourceWhenReady_noProfile_sourceNotAdded() =
with(kosmos) {
testScope.runTest {
bluetoothTileDialogAudioSharingRepository.setAudioSharingAvailable(true)
@@ -171,36 +159,41 @@ class AudioSharingInteractorTest : SysuiTestCase() {
}
@Test
- fun testHandleAudioSourceWhenReady_hasProfileButAudioSharingOff_sourceNotAdded() =
+ fun handleAudioSourceWhenReady_hasProfileButAudioSharingNeverTriggered_sourceNotAdded() =
with(kosmos) {
testScope.runTest {
- bluetoothTileDialogAudioSharingRepository.setInAudioSharing(true)
bluetoothTileDialogAudioSharingRepository.setAudioSharingAvailable(true)
bluetoothTileDialogAudioSharingRepository.setLeAudioBroadcastProfile(
localBluetoothLeBroadcast
)
val job = launch { underTest.handleAudioSourceWhenReady() }
runCurrent()
- bluetoothTileDialogAudioSharingRepository.setInAudioSharing(false)
- runCurrent()
+ // Verify callback registered for onBroadcastStartedOrStopped
+ verify(localBluetoothLeBroadcast).registerServiceCallBack(any(), any())
assertThat(bluetoothTileDialogAudioSharingRepository.sourceAdded).isFalse()
job.cancel()
}
}
@Test
- fun testHandleAudioSourceWhenReady_audioSharingOnButNoPlayback_sourceNotAdded() =
+ fun handleAudioSourceWhenReady_audioSharingTriggeredButFailed_sourceNotAdded() =
with(kosmos) {
testScope.runTest {
- bluetoothTileDialogAudioSharingRepository.setInAudioSharing(false)
bluetoothTileDialogAudioSharingRepository.setAudioSharingAvailable(true)
bluetoothTileDialogAudioSharingRepository.setLeAudioBroadcastProfile(
localBluetoothLeBroadcast
)
val job = launch { underTest.handleAudioSourceWhenReady() }
runCurrent()
- bluetoothTileDialogAudioSharingRepository.setInAudioSharing(true)
+ // Verify callback registered for onBroadcastStartedOrStopped
+ verify(localBluetoothLeBroadcast)
+ .registerServiceCallBack(any(), callbackCaptor.capture())
+ // Audio sharing started failed, trigger onBroadcastStartFailed
+ whenever(localBluetoothLeBroadcast.isEnabled(null)).thenReturn(false)
+ underTest.startAudioSharing()
+ runCurrent()
+ callbackCaptor.value.onBroadcastStartFailed(0)
runCurrent()
assertThat(bluetoothTileDialogAudioSharingRepository.sourceAdded).isFalse()
@@ -209,122 +202,59 @@ class AudioSharingInteractorTest : SysuiTestCase() {
}
@Test
- fun testHandleAudioSourceWhenReady_audioSharingOnAndPlaybackStarts_sourceAdded() =
+ fun handleAudioSourceWhenReady_audioSharingTriggeredButMetadataNotReady_sourceNotAdded() =
with(kosmos) {
testScope.runTest {
- bluetoothTileDialogAudioSharingRepository.setInAudioSharing(false)
bluetoothTileDialogAudioSharingRepository.setAudioSharingAvailable(true)
bluetoothTileDialogAudioSharingRepository.setLeAudioBroadcastProfile(
localBluetoothLeBroadcast
)
val job = launch { underTest.handleAudioSourceWhenReady() }
runCurrent()
- bluetoothTileDialogAudioSharingRepository.setInAudioSharing(true)
- runCurrent()
+ // Verify callback registered for onBroadcastStartedOrStopped
verify(localBluetoothLeBroadcast)
.registerServiceCallBack(any(), callbackCaptor.capture())
runCurrent()
- callbackCaptor.value.onBroadcastMetadataChanged(0, bluetoothLeBroadcastMetadata)
+ underTest.startAudioSharing()
runCurrent()
+ // Verify callback registered for onBroadcastMetadataChanged
+ verify(localBluetoothLeBroadcast, times(2))
+ .registerServiceCallBack(any(), callbackCaptor.capture())
- assertThat(bluetoothTileDialogAudioSharingRepository.sourceAdded).isTrue()
- job.cancel()
- }
- }
-
- @Test
- fun testHandleAudioSourceWhenReady_skipInitialValue_noAudioSharing_sourceNotAdded() =
- with(kosmos) {
- testScope.runTest {
- val (broadcast, repository) = setupRepositoryImpl()
- val interactor =
- object :
- AudioSharingInteractorImpl(
- applicationContext,
- localBluetoothManager,
- repository,
- testDispatcher,
- ) {
- override suspend fun audioSharingAvailable() = true
- }
- val job = launch { interactor.handleAudioSourceWhenReady() }
- runCurrent()
- // Verify callback registered for onBroadcastStartedOrStopped
- verify(broadcast).registerServiceCallBack(any(), callbackCaptor.capture())
- runCurrent()
- // Verify source is not added
- verify(repository, never()).addSource()
+ assertThat(bluetoothTileDialogAudioSharingRepository.sourceAdded).isFalse()
job.cancel()
}
}
@Test
- fun testHandleAudioSourceWhenReady_skipInitialValue_newAudioSharing_sourceAdded() =
+ fun handleAudioSourceWhenReady_audioSharingTriggeredAndMetadataReady_sourceAdded() =
with(kosmos) {
testScope.runTest {
- val (broadcast, repository) = setupRepositoryImpl()
- val interactor =
- object :
- AudioSharingInteractorImpl(
- applicationContext,
- localBluetoothManager,
- repository,
- testDispatcher,
- ) {
- override suspend fun audioSharingAvailable() = true
- }
- val job = launch { interactor.handleAudioSourceWhenReady() }
+ bluetoothTileDialogAudioSharingRepository.setAudioSharingAvailable(true)
+ bluetoothTileDialogAudioSharingRepository.setLeAudioBroadcastProfile(
+ localBluetoothLeBroadcast
+ )
+ val job = launch { underTest.handleAudioSourceWhenReady() }
runCurrent()
// Verify callback registered for onBroadcastStartedOrStopped
- verify(broadcast).registerServiceCallBack(any(), callbackCaptor.capture())
+ verify(localBluetoothLeBroadcast)
+ .registerServiceCallBack(any(), callbackCaptor.capture())
// Audio sharing started, trigger onBroadcastStarted
- whenever(broadcast.isEnabled(null)).thenReturn(true)
+ whenever(localBluetoothLeBroadcast.isEnabled(null)).thenReturn(true)
+ underTest.startAudioSharing()
+ runCurrent()
callbackCaptor.value.onBroadcastStarted(0, 0)
runCurrent()
// Verify callback registered for onBroadcastMetadataChanged
- verify(broadcast, times(2)).registerServiceCallBack(any(), callbackCaptor.capture())
+ verify(localBluetoothLeBroadcast, times(2))
+ .registerServiceCallBack(any(), callbackCaptor.capture())
runCurrent()
// Trigger onBroadcastMetadataChanged (ready to add source)
callbackCaptor.value.onBroadcastMetadataChanged(0, bluetoothLeBroadcastMetadata)
runCurrent()
- // Verify source added
- verify(repository).addSource()
+
+ assertThat(bluetoothTileDialogAudioSharingRepository.sourceAdded).isTrue()
job.cancel()
}
}
-
- private fun setupRepositoryImpl(): Pair<LocalBluetoothLeBroadcast, AudioSharingRepositoryImpl> {
- with(kosmos) {
- val broadcast =
- mock<LocalBluetoothLeBroadcast> {
- on { isProfileReady } doReturn true
- on { isEnabled(null) } doReturn false
- }
- val assistant =
- mock<LocalBluetoothLeBroadcastAssistant> { on { isProfileReady } doReturn true }
- val volumeControl = mock<VolumeControlProfile> { on { isProfileReady } doReturn true }
- val profileManager =
- mock<LocalBluetoothProfileManager> {
- on { leAudioBroadcastProfile } doReturn broadcast
- on { leAudioBroadcastAssistantProfile } doReturn assistant
- on { volumeControlProfile } doReturn volumeControl
- }
- whenever(localBluetoothManager.profileManager).thenReturn(profileManager)
- whenever(localBluetoothManager.eventManager).thenReturn(mock<BluetoothEventManager> {})
-
- val repository =
- AudioSharingRepositoryImpl(
- localBluetoothManager,
- com.android.settingslib.volume.data.repository.AudioSharingRepositoryImpl(
- mock<ContentResolver> {},
- localBluetoothManager,
- testScope.backgroundScope,
- testScope.testScheduler,
- mock<AudioSharingLogger> {},
- ),
- testDispatcher,
- )
- return Pair(broadcast, spy(repository))
- }
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/AudioSharingInteractor.kt b/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/AudioSharingInteractor.kt
index d82311f6ca7c..832afb1799b1 100644
--- a/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/AudioSharingInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/AudioSharingInteractor.kt
@@ -22,20 +22,25 @@ import com.android.settingslib.bluetooth.BluetoothUtils
import com.android.settingslib.bluetooth.CachedBluetoothDevice
import com.android.settingslib.bluetooth.LocalBluetoothManager
import com.android.settingslib.bluetooth.onBroadcastMetadataChanged
+import com.android.settingslib.bluetooth.onBroadcastStartedOrStopped
import com.android.settingslib.flags.Flags.audioSharingQsDialogImprovement
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Background
import javax.inject.Inject
import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.drop
import kotlinx.coroutines.flow.emptyFlow
+import kotlinx.coroutines.flow.filterNot
import kotlinx.coroutines.flow.firstOrNull
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.flowOn
+import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.mapNotNull
+import kotlinx.coroutines.flow.merge
+import kotlinx.coroutines.flow.receiveAsFlow
import kotlinx.coroutines.withContext
/** Holds business logic for the audio sharing state. */
@@ -71,6 +76,7 @@ constructor(
@Background private val backgroundDispatcher: CoroutineDispatcher,
) : AudioSharingInteractor {
+ private val audioSharingStartedEvents = Channel<Unit>(Channel.BUFFERED)
private var previewEnabled: Boolean? = null
override val isAudioSharingOn: Flow<Boolean> =
@@ -99,12 +105,18 @@ constructor(
withContext(backgroundDispatcher) {
if (audioSharingAvailable()) {
audioSharingRepository.leAudioBroadcastProfile?.let { profile ->
- isAudioSharingOn
- // Skip the default value, we only care about adding source for newly
- // started audio sharing session
- .drop(1)
- .mapNotNull { audioSharingOn ->
- if (audioSharingOn) {
+ merge(
+ // Register and start listen to onBroadcastMetadataChanged (means ready
+ // to add source)
+ audioSharingStartedEvents.receiveAsFlow().map { true },
+ // When session is off or failed to start, stop listening to
+ // onBroadcastMetadataChanged as we won't be adding source
+ profile.onBroadcastStartedOrStopped
+ .filterNot { profile.isEnabled(null) }
+ .map { false },
+ )
+ .mapNotNull { shouldListenToMetadata ->
+ if (shouldListenToMetadata) {
// onBroadcastMetadataChanged could emit multiple times during one
// audio sharing session, we only perform add source on the first
// time
@@ -146,6 +158,7 @@ constructor(
if (!audioSharingAvailable()) {
return
}
+ audioSharingStartedEvents.trySend(Unit)
audioSharingRepository.startAudioSharing()
}