summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Yiyi Shen <yiyishen@google.com> 2024-07-22 23:45:32 +0800
committer Yiyi Shen <yiyishen@google.com> 2024-07-24 10:57:08 +0800
commit4c66984562ba77455b2b57448ce18f316384e840 (patch)
tree9608c3ad822e3b3848b5c8a238dae3fe33c377f8
parent1b1fcd1708929e6cab0d6af611c3b0e60ab7670a (diff)
[Audiosharing] Refine audio sharing volume support
1. Extract flags to modules 2. Extract broadcast callback flow to LocalBluetoothLeBroadcastCallbackExt Test: atest Bug: 336716411 Flag: com.android.settingslib.flags.volume_dialog_audio_sharing_fix Change-Id: I9bd2c9b1060fa68fdd1522d5c185ad84a8c79ec2
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcastCallbackExt.kt70
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/volume/data/repository/AudioSharingRepository.kt155
-rw-r--r--packages/SettingsLib/tests/integ/src/com/android/settingslib/volume/data/repository/AudioSharingRepositoryEmptyImplTest.kt174
-rw-r--r--packages/SettingsLib/tests/integ/src/com/android/settingslib/volume/data/repository/AudioSharingRepositoryTest.kt73
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/dagger/AudioModule.kt19
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/dagger/AudioSharingModule.kt17
6 files changed, 319 insertions, 189 deletions
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcastCallbackExt.kt b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcastCallbackExt.kt
new file mode 100644
index 000000000000..0bcf7fed5c80
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcastCallbackExt.kt
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.bluetooth
+
+import android.bluetooth.BluetoothLeBroadcast
+import android.bluetooth.BluetoothLeBroadcastMetadata
+import com.android.internal.util.ConcurrentUtils
+import kotlinx.coroutines.channels.Channel
+import kotlinx.coroutines.channels.awaitClose
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.buffer
+import kotlinx.coroutines.flow.callbackFlow
+import kotlinx.coroutines.launch
+
+/** [Flow] for [BluetoothLeBroadcast.Callback] source start/stop events */
+val LocalBluetoothLeBroadcast.onBroadcastStartedOrStopped: Flow<Unit>
+ get() =
+ callbackFlow {
+ val listener =
+ object : BluetoothLeBroadcast.Callback {
+ override fun onBroadcastStarted(reason: Int, broadcastId: Int) {
+ launch { trySend(Unit) }
+ }
+
+ override fun onBroadcastStartFailed(reason: Int) {
+ launch { trySend(Unit) }
+ }
+
+ override fun onBroadcastStopped(reason: Int, broadcastId: Int) {
+ launch { trySend(Unit) }
+ }
+
+ override fun onBroadcastStopFailed(reason: Int) {
+ launch { trySend(Unit) }
+ }
+
+ override fun onPlaybackStarted(reason: Int, broadcastId: Int) {}
+
+ 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)
diff --git a/packages/SettingsLib/src/com/android/settingslib/volume/data/repository/AudioSharingRepository.kt b/packages/SettingsLib/src/com/android/settingslib/volume/data/repository/AudioSharingRepository.kt
index eb33a7a10524..e78b8a738b1c 100644
--- a/packages/SettingsLib/src/com/android/settingslib/volume/data/repository/AudioSharingRepository.kt
+++ b/packages/SettingsLib/src/com/android/settingslib/volume/data/repository/AudioSharingRepository.kt
@@ -19,21 +19,18 @@ package com.android.settingslib.volume.data.repository
import android.bluetooth.BluetoothAdapter
import android.bluetooth.BluetoothCsipSetCoordinator
import android.bluetooth.BluetoothDevice
-import android.bluetooth.BluetoothLeBroadcast
-import android.bluetooth.BluetoothLeBroadcastMetadata
import android.bluetooth.BluetoothProfile
import android.bluetooth.BluetoothVolumeControl
import android.content.ContentResolver
-import android.content.Context
import android.database.ContentObserver
import android.provider.Settings
import androidx.annotation.IntRange
import com.android.internal.util.ConcurrentUtils
import com.android.settingslib.bluetooth.BluetoothUtils
import com.android.settingslib.bluetooth.LocalBluetoothManager
+import com.android.settingslib.bluetooth.onBroadcastStartedOrStopped
import com.android.settingslib.bluetooth.onProfileConnectionStateChanged
import com.android.settingslib.bluetooth.onSourceConnectedOrRemoved
-import com.android.settingslib.flags.Flags
import com.android.settingslib.volume.data.repository.AudioSharingRepository.Companion.AUDIO_SHARING_VOLUME_MAX
import com.android.settingslib.volume.data.repository.AudioSharingRepository.Companion.AUDIO_SHARING_VOLUME_MIN
import kotlin.coroutines.CoroutineContext
@@ -41,10 +38,10 @@ import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.callbackFlow
-import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.emptyFlow
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.flatMapLatest
@@ -85,63 +82,18 @@ interface AudioSharingRepository {
@OptIn(ExperimentalCoroutinesApi::class)
class AudioSharingRepositoryImpl(
- private val context: Context,
private val contentResolver: ContentResolver,
- private val btManager: LocalBluetoothManager?,
+ private val btManager: LocalBluetoothManager,
private val coroutineScope: CoroutineScope,
private val backgroundCoroutineContext: CoroutineContext,
) : AudioSharingRepository {
override val inAudioSharing: Flow<Boolean> =
- if (Flags.enableLeAudioSharing()) {
- btManager?.profileManager?.leAudioBroadcastProfile?.let { leBroadcast ->
- callbackFlow {
- val listener =
- object : BluetoothLeBroadcast.Callback {
- override fun onBroadcastStarted(reason: Int, broadcastId: Int) {
- launch { send(isBroadcasting()) }
- }
-
- override fun onBroadcastStartFailed(reason: Int) {
- launch { send(isBroadcasting()) }
- }
-
- override fun onBroadcastStopped(reason: Int, broadcastId: Int) {
- launch { send(isBroadcasting()) }
- }
-
- override fun onBroadcastStopFailed(reason: Int) {
- launch { send(isBroadcasting()) }
- }
-
- override fun onPlaybackStarted(reason: Int, broadcastId: Int) {}
-
- 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
- ) {}
- }
-
- leBroadcast.registerServiceCallBack(
- ConcurrentUtils.DIRECT_EXECUTOR,
- listener,
- )
- awaitClose { leBroadcast.unregisterServiceCallBack(listener) }
- }
- .onStart { emit(isBroadcasting()) }
- .flowOn(backgroundCoroutineContext)
- } ?: flowOf(false)
- } else {
- flowOf(false)
- }
+ btManager.profileManager.leAudioBroadcastProfile?.let { broadcast ->
+ broadcast.onBroadcastStartedOrStopped
+ .map { isBroadcasting() }
+ .onStart { emit(isBroadcasting()) }
+ .flowOn(backgroundCoroutineContext)
+ } ?: flowOf(false)
private val primaryChange: Flow<Unit> = callbackFlow {
val callback =
@@ -158,34 +110,24 @@ class AudioSharingRepositoryImpl(
}
override val secondaryGroupId: StateFlow<Int> =
- if (Flags.volumeDialogAudioSharingFix()) {
- merge(
- btManager
- ?.profileManager
- ?.leAudioBroadcastAssistantProfile
- ?.onSourceConnectedOrRemoved
- ?.map { getSecondaryGroupId() } ?: emptyFlow(),
- btManager
- ?.eventManager
- ?.onProfileConnectionStateChanged
- ?.filter { profileConnection ->
- profileConnection.state == BluetoothAdapter.STATE_DISCONNECTED &&
- profileConnection.bluetoothProfile ==
- BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT
- }
- ?.map { getSecondaryGroupId() } ?: emptyFlow(),
- primaryChange.map { getSecondaryGroupId() })
- .onStart { emit(getSecondaryGroupId()) }
- .distinctUntilChanged()
- .flowOn(backgroundCoroutineContext)
- } else {
- emptyFlow()
- }
+ merge(
+ btManager.profileManager.leAudioBroadcastAssistantProfile
+ ?.onSourceConnectedOrRemoved
+ ?.map { getSecondaryGroupId() } ?: emptyFlow(),
+ btManager.eventManager.onProfileConnectionStateChanged
+ .filter { profileConnection ->
+ profileConnection.state == BluetoothAdapter.STATE_DISCONNECTED &&
+ profileConnection.bluetoothProfile ==
+ BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT
+ }
+ .map { getSecondaryGroupId() },
+ primaryChange.map { getSecondaryGroupId() })
+ .onStart { emit(getSecondaryGroupId()) }
+ .flowOn(backgroundCoroutineContext)
.stateIn(coroutineScope, SharingStarted.WhileSubscribed(), getSecondaryGroupId())
override val volumeMap: StateFlow<GroupIdToVolumes> =
- if (Flags.volumeDialogAudioSharingFix()) {
- btManager?.profileManager?.volumeControlProfile?.let { volumeControl ->
+ (btManager.profileManager.volumeControlProfile?.let { volumeControl ->
inAudioSharing.flatMapLatest { isSharing ->
if (isSharing) {
callbackFlow {
@@ -210,50 +152,53 @@ class AudioSharingRepositoryImpl(
.runningFold(emptyMap<Int, Int>()) { acc, value ->
val groupId =
BluetoothUtils.getGroupId(
- btManager.cachedDeviceManager?.findDevice(value.first))
+ btManager.cachedDeviceManager.findDevice(value.first))
if (groupId != BluetoothCsipSetCoordinator.GROUP_ID_INVALID) {
acc + Pair(groupId, value.second)
} else {
acc
}
}
- .distinctUntilChanged()
.flowOn(backgroundCoroutineContext)
} else {
emptyFlow()
}
}
- } ?: emptyFlow()
- } else {
- emptyFlow()
- }
- .stateIn(coroutineScope, SharingStarted.WhileSubscribed(), emptyMap())
+ } ?: emptyFlow())
+ .stateIn(coroutineScope, SharingStarted.WhileSubscribed(), emptyMap())
override suspend fun setSecondaryVolume(
@IntRange(from = AUDIO_SHARING_VOLUME_MIN.toLong(), to = AUDIO_SHARING_VOLUME_MAX.toLong())
volume: Int
) {
withContext(backgroundCoroutineContext) {
- if (Flags.volumeDialogAudioSharingFix()) {
- btManager?.profileManager?.volumeControlProfile?.let {
- // Find secondary headset and set volume.
- val cachedDevice =
- BluetoothUtils.getSecondaryDeviceForBroadcast(context, btManager)
- if (cachedDevice != null) {
- it.setDeviceVolume(cachedDevice.device, volume, /* isGroupOp= */ true)
- }
+ btManager.profileManager.volumeControlProfile?.let {
+ // Find secondary headset and set volume.
+ val cachedDevice =
+ BluetoothUtils.getSecondaryDeviceForBroadcast(contentResolver, btManager)
+ if (cachedDevice != null) {
+ it.setDeviceVolume(cachedDevice.device, volume, /* isGroupOp= */ true)
}
}
}
}
- private fun isBroadcasting(): Boolean {
- return Flags.enableLeAudioSharing() &&
- (btManager?.profileManager?.leAudioBroadcastProfile?.isEnabled(null) ?: false)
- }
+ private fun isBroadcasting(): Boolean =
+ btManager.profileManager.leAudioBroadcastProfile?.isEnabled(null) ?: false
- private fun getSecondaryGroupId(): Int {
- return BluetoothUtils.getGroupId(
- BluetoothUtils.getSecondaryDeviceForBroadcast(context, btManager))
- }
+ private fun getSecondaryGroupId(): Int =
+ BluetoothUtils.getGroupId(
+ BluetoothUtils.getSecondaryDeviceForBroadcast(contentResolver, btManager))
+}
+
+class AudioSharingRepositoryEmptyImpl : AudioSharingRepository {
+ override val inAudioSharing: Flow<Boolean> = flowOf(false)
+ override val secondaryGroupId: StateFlow<Int> =
+ MutableStateFlow(BluetoothCsipSetCoordinator.GROUP_ID_INVALID)
+ override val volumeMap: StateFlow<GroupIdToVolumes> = MutableStateFlow(emptyMap())
+
+ override suspend fun setSecondaryVolume(
+ @IntRange(from = AUDIO_SHARING_VOLUME_MIN.toLong(), to = AUDIO_SHARING_VOLUME_MAX.toLong())
+ volume: Int
+ ) {}
}
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/volume/data/repository/AudioSharingRepositoryEmptyImplTest.kt b/packages/SettingsLib/tests/integ/src/com/android/settingslib/volume/data/repository/AudioSharingRepositoryEmptyImplTest.kt
new file mode 100644
index 000000000000..2601592ce5ee
--- /dev/null
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/volume/data/repository/AudioSharingRepositoryEmptyImplTest.kt
@@ -0,0 +1,174 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.volume.data.repository
+
+import android.bluetooth.BluetoothDevice
+import android.bluetooth.BluetoothLeBroadcastReceiveState
+import android.content.Context
+import android.platform.test.flag.junit.SetFlagsRule
+import android.provider.Settings
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.settingslib.bluetooth.BluetoothEventManager
+import com.android.settingslib.bluetooth.BluetoothUtils
+import com.android.settingslib.bluetooth.CachedBluetoothDevice
+import com.android.settingslib.bluetooth.CachedBluetoothDeviceManager
+import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcast
+import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcastAssistant
+import com.android.settingslib.bluetooth.LocalBluetoothManager
+import com.android.settingslib.bluetooth.LocalBluetoothProfileManager
+import com.android.settingslib.bluetooth.VolumeControlProfile
+import com.google.common.truth.Truth
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.launchIn
+import kotlinx.coroutines.flow.onEach
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers.any
+import org.mockito.ArgumentMatchers.anyBoolean
+import org.mockito.ArgumentMatchers.anyInt
+import org.mockito.Mock
+import org.mockito.Mockito.never
+import org.mockito.Mockito.verify
+import org.mockito.Mockito.`when`
+import org.mockito.junit.MockitoJUnit
+import org.mockito.junit.MockitoRule
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class AudioSharingRepositoryEmptyImplTest {
+ @get:Rule val mockito: MockitoRule = MockitoJUnit.rule()
+
+ @get:Rule val setFlagsRule: SetFlagsRule = SetFlagsRule()
+
+ @Mock private lateinit var btManager: LocalBluetoothManager
+
+ @Mock private lateinit var profileManager: LocalBluetoothProfileManager
+
+ @Mock private lateinit var broadcast: LocalBluetoothLeBroadcast
+
+ @Mock private lateinit var assistant: LocalBluetoothLeBroadcastAssistant
+
+ @Mock private lateinit var volumeControl: VolumeControlProfile
+
+ @Mock private lateinit var eventManager: BluetoothEventManager
+
+ @Mock private lateinit var deviceManager: CachedBluetoothDeviceManager
+
+ @Mock private lateinit var device1: BluetoothDevice
+
+ @Mock private lateinit var device2: BluetoothDevice
+
+ @Mock private lateinit var cachedDevice1: CachedBluetoothDevice
+
+ @Mock private lateinit var cachedDevice2: CachedBluetoothDevice
+
+ @Mock private lateinit var receiveState: BluetoothLeBroadcastReceiveState
+
+ private val testScope = TestScope()
+ private val context: Context = ApplicationProvider.getApplicationContext()
+ private lateinit var underTest: AudioSharingRepository
+
+ @Before
+ fun setup() {
+ `when`(btManager.profileManager).thenReturn(profileManager)
+ `when`(profileManager.leAudioBroadcastProfile).thenReturn(broadcast)
+ `when`(profileManager.leAudioBroadcastAssistantProfile).thenReturn(assistant)
+ `when`(profileManager.volumeControlProfile).thenReturn(volumeControl)
+ `when`(btManager.eventManager).thenReturn(eventManager)
+ `when`(btManager.cachedDeviceManager).thenReturn(deviceManager)
+ `when`(broadcast.isEnabled(null)).thenReturn(true)
+ `when`(cachedDevice1.groupId).thenReturn(TEST_GROUP_ID1)
+ `when`(cachedDevice1.device).thenReturn(device1)
+ `when`(deviceManager.findDevice(device1)).thenReturn(cachedDevice1)
+ `when`(cachedDevice2.groupId).thenReturn(TEST_GROUP_ID2)
+ `when`(cachedDevice2.device).thenReturn(device2)
+ `when`(deviceManager.findDevice(device2)).thenReturn(cachedDevice2)
+ `when`(receiveState.bisSyncState).thenReturn(arrayListOf(TEST_RECEIVE_STATE_CONTENT))
+ `when`(assistant.getAllSources(any())).thenReturn(listOf(receiveState))
+ underTest = AudioSharingRepositoryEmptyImpl()
+ }
+
+ @Test
+ fun inAudioSharing_returnFalse() {
+ testScope.runTest {
+ val states = mutableListOf<Boolean?>()
+ underTest.inAudioSharing.onEach { states.add(it) }.launchIn(backgroundScope)
+ runCurrent()
+
+ Truth.assertThat(states).containsExactly(false)
+ verify(broadcast, never()).registerServiceCallBack(any(), any())
+ verify(broadcast, never()).isEnabled(any())
+ }
+ }
+
+ @Test
+ fun secondaryGroupIdChange_returnFalse() {
+ testScope.runTest {
+ val groupIds = mutableListOf<Int?>()
+ underTest.secondaryGroupId.onEach { groupIds.add(it) }.launchIn(backgroundScope)
+ runCurrent()
+
+ Truth.assertThat(groupIds).containsExactly(TEST_GROUP_ID_INVALID)
+ verify(assistant, never()).registerServiceCallBack(any(), any())
+ verify(eventManager, never()).registerCallback(any())
+ }
+ }
+
+ @Test
+ fun volumeMapChange_returnFalse() {
+ testScope.runTest {
+ val volumeMaps = mutableListOf<GroupIdToVolumes?>()
+ underTest.volumeMap.onEach { volumeMaps.add(it) }.launchIn(backgroundScope)
+ runCurrent()
+
+ Truth.assertThat(volumeMaps).containsExactly(emptyMap<Int, Int>())
+ verify(broadcast, never()).registerServiceCallBack(any(), any())
+ verify(volumeControl, never()).registerCallback(any(), any())
+ }
+ }
+
+ @Test
+ fun setSecondaryVolume_doNothing() {
+ testScope.runTest {
+ Settings.Secure.putInt(
+ context.contentResolver,
+ BluetoothUtils.getPrimaryGroupIdUriForBroadcast(),
+ TEST_GROUP_ID2)
+ `when`(assistant.allConnectedDevices).thenReturn(listOf(device1, device2))
+ underTest.setSecondaryVolume(TEST_VOLUME1)
+
+ runCurrent()
+ verify(volumeControl, never()).setDeviceVolume(any(), anyInt(), anyBoolean())
+ }
+ }
+
+ private companion object {
+ const val TEST_GROUP_ID_INVALID = -1
+ const val TEST_GROUP_ID1 = 1
+ const val TEST_GROUP_ID2 = 2
+ const val TEST_RECEIVE_STATE_CONTENT = 1L
+ const val TEST_VOLUME1 = 10
+ }
+}
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/volume/data/repository/AudioSharingRepositoryTest.kt b/packages/SettingsLib/tests/integ/src/com/android/settingslib/volume/data/repository/AudioSharingRepositoryTest.kt
index 000664dd1552..94595d3be8df 100644
--- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/volume/data/repository/AudioSharingRepositoryTest.kt
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/volume/data/repository/AudioSharingRepositoryTest.kt
@@ -26,8 +26,6 @@ import android.bluetooth.BluetoothVolumeControl
import android.content.ContentResolver
import android.content.Context
import android.database.ContentObserver
-import android.platform.test.annotations.DisableFlags
-import android.platform.test.annotations.EnableFlags
import android.platform.test.flag.junit.SetFlagsRule
import android.provider.Settings
import androidx.test.core.app.ApplicationProvider
@@ -43,7 +41,6 @@ import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcastAssistant
import com.android.settingslib.bluetooth.LocalBluetoothManager
import com.android.settingslib.bluetooth.LocalBluetoothProfileManager
import com.android.settingslib.bluetooth.VolumeControlProfile
-import com.android.settingslib.flags.Flags
import com.google.common.truth.Truth
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.launchIn
@@ -57,14 +54,12 @@ import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.ArgumentCaptor
import org.mockito.ArgumentMatchers.any
-import org.mockito.ArgumentMatchers.anyBoolean
-import org.mockito.ArgumentMatchers.anyInt
import org.mockito.ArgumentMatchers.eq
import org.mockito.Captor
import org.mockito.Mock
-import org.mockito.Mockito.never
import org.mockito.Mockito.verify
import org.mockito.Mockito.`when`
+import org.mockito.Spy
import org.mockito.junit.MockitoJUnit
import org.mockito.junit.MockitoRule
@@ -100,8 +95,6 @@ class AudioSharingRepositoryTest {
@Mock private lateinit var receiveState: BluetoothLeBroadcastReceiveState
- @Mock private lateinit var contentResolver: ContentResolver
-
@Captor
private lateinit var broadcastCallbackCaptor: ArgumentCaptor<BluetoothLeBroadcast.Callback>
@@ -118,6 +111,7 @@ class AudioSharingRepositoryTest {
private val testScope = TestScope()
private val context: Context = ApplicationProvider.getApplicationContext()
+ @Spy private val contentResolver: ContentResolver = context.contentResolver
private lateinit var underTest: AudioSharingRepository
@Before
@@ -139,7 +133,6 @@ class AudioSharingRepositoryTest {
`when`(assistant.getAllSources(any())).thenReturn(listOf(receiveState))
underTest =
AudioSharingRepositoryImpl(
- context,
contentResolver,
btManager,
testScope.backgroundScope,
@@ -148,7 +141,6 @@ class AudioSharingRepositoryTest {
}
@Test
- @EnableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING)
fun audioSharingStateChange_emitValues() {
testScope.runTest {
val states = mutableListOf<Boolean?>()
@@ -164,21 +156,6 @@ class AudioSharingRepositoryTest {
}
@Test
- @DisableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING)
- fun audioSharingFlagOff_returnFalse() {
- testScope.runTest {
- val states = mutableListOf<Boolean?>()
- underTest.inAudioSharing.onEach { states.add(it) }.launchIn(backgroundScope)
- runCurrent()
-
- Truth.assertThat(states).containsExactly(false)
- verify(broadcast, never()).registerServiceCallBack(any(), any())
- verify(broadcast, never()).isEnabled(any())
- }
- }
-
- @Test
- @EnableFlags(Flags.FLAG_VOLUME_DIALOG_AUDIO_SHARING_FIX)
fun secondaryGroupIdChange_emitValues() {
testScope.runTest {
val groupIds = mutableListOf<Int?>()
@@ -214,21 +191,6 @@ class AudioSharingRepositoryTest {
}
@Test
- @DisableFlags(Flags.FLAG_VOLUME_DIALOG_AUDIO_SHARING_FIX)
- fun secondaryGroupIdChange_audioSharingFlagOff_returnFalse() {
- testScope.runTest {
- val groupIds = mutableListOf<Int?>()
- underTest.secondaryGroupId.onEach { groupIds.add(it) }.launchIn(backgroundScope)
- runCurrent()
-
- Truth.assertThat(groupIds).containsExactly(TEST_GROUP_ID_INVALID)
- verify(assistant, never()).registerServiceCallBack(any(), any())
- verify(eventManager, never()).registerCallback(any())
- }
- }
-
- @Test
- @EnableFlags(Flags.FLAG_VOLUME_DIALOG_AUDIO_SHARING_FIX)
fun volumeMapChange_emitValues() {
testScope.runTest {
val volumeMaps = mutableListOf<GroupIdToVolumes?>()
@@ -252,21 +214,6 @@ class AudioSharingRepositoryTest {
}
@Test
- @DisableFlags(Flags.FLAG_VOLUME_DIALOG_AUDIO_SHARING_FIX)
- fun volumeMapChange_audioSharingFlagOff_returnFalse() {
- testScope.runTest {
- val volumeMaps = mutableListOf<GroupIdToVolumes?>()
- underTest.volumeMap.onEach { volumeMaps.add(it) }.launchIn(backgroundScope)
- runCurrent()
-
- Truth.assertThat(volumeMaps).isEmpty()
- verify(broadcast, never()).registerServiceCallBack(any(), any())
- verify(volumeControl, never()).registerCallback(any(), any())
- }
- }
-
- @Test
- @EnableFlags(Flags.FLAG_VOLUME_DIALOG_AUDIO_SHARING_FIX)
fun setSecondaryVolume_setValue() {
testScope.runTest {
Settings.Secure.putInt(
@@ -281,22 +228,6 @@ class AudioSharingRepositoryTest {
}
}
- @Test
- @DisableFlags(Flags.FLAG_VOLUME_DIALOG_AUDIO_SHARING_FIX)
- fun setSecondaryVolume_audioSharingFlagOff_doNothing() {
- testScope.runTest {
- Settings.Secure.putInt(
- context.contentResolver,
- BluetoothUtils.getPrimaryGroupIdUriForBroadcast(),
- TEST_GROUP_ID2)
- `when`(assistant.allConnectedDevices).thenReturn(listOf(device1, device2))
- underTest.setSecondaryVolume(TEST_VOLUME1)
-
- runCurrent()
- verify(volumeControl, never()).setDeviceVolume(any(), anyInt(), anyBoolean())
- }
- }
-
private fun triggerAudioSharingStateChange(
type: TriggerType,
broadcastAction: BluetoothLeBroadcast.Callback.() -> Unit
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dagger/AudioModule.kt b/packages/SystemUI/src/com/android/systemui/volume/dagger/AudioModule.kt
index eb2f71a1cd7d..0c1bc21decfe 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dagger/AudioModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dagger/AudioModule.kt
@@ -19,11 +19,13 @@ package com.android.systemui.volume.dagger
import android.content.ContentResolver
import android.content.Context
import android.media.AudioManager
+import com.android.settingslib.bluetooth.BluetoothUtils
import com.android.settingslib.bluetooth.LocalBluetoothManager
import com.android.settingslib.notification.domain.interactor.NotificationsSoundPolicyInteractor
import com.android.settingslib.volume.data.repository.AudioRepository
import com.android.settingslib.volume.data.repository.AudioRepositoryImpl
import com.android.settingslib.volume.data.repository.AudioSharingRepository
+import com.android.settingslib.volume.data.repository.AudioSharingRepositoryEmptyImpl
import com.android.settingslib.volume.data.repository.AudioSharingRepositoryImpl
import com.android.settingslib.volume.domain.interactor.AudioModeInteractor
import com.android.settingslib.volume.domain.interactor.AudioVolumeInteractor
@@ -79,13 +81,16 @@ interface AudioModule {
@Application coroutineScope: CoroutineScope,
@Background coroutineContext: CoroutineContext,
): AudioSharingRepository =
- AudioSharingRepositoryImpl(
- context,
- contentResolver,
- localBluetoothManager,
- coroutineScope,
- coroutineContext
- )
+ if (BluetoothUtils.isAudioSharingEnabled() && localBluetoothManager != null) {
+ AudioSharingRepositoryImpl(
+ contentResolver,
+ localBluetoothManager,
+ coroutineScope,
+ coroutineContext
+ )
+ } else {
+ AudioSharingRepositoryEmptyImpl()
+ }
@Provides
@SysUISingleton
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dagger/AudioSharingModule.kt b/packages/SystemUI/src/com/android/systemui/volume/dagger/AudioSharingModule.kt
index 9f1e60e855e2..1c80887dd3e8 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dagger/AudioSharingModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dagger/AudioSharingModule.kt
@@ -16,14 +16,14 @@
package com.android.systemui.volume.dagger
-import com.android.settingslib.volume.data.repository.AudioSharingRepository
+import com.android.settingslib.flags.Flags
import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.volume.domain.interactor.AudioSharingInteractor
+import com.android.systemui.volume.domain.interactor.AudioSharingInteractorEmptyImpl
import com.android.systemui.volume.domain.interactor.AudioSharingInteractorImpl
+import dagger.Lazy
import dagger.Module
import dagger.Provides
-import kotlinx.coroutines.CoroutineScope
/** Dagger module for audio sharing code in the volume package */
@Module
@@ -33,8 +33,13 @@ interface AudioSharingModule {
@Provides
@SysUISingleton
fun provideAudioSharingInteractor(
- @Application coroutineScope: CoroutineScope,
- repository: AudioSharingRepository
- ): AudioSharingInteractor = AudioSharingInteractorImpl(coroutineScope, repository)
+ impl: Lazy<AudioSharingInteractorImpl>,
+ emptyImpl: Lazy<AudioSharingInteractorEmptyImpl>,
+ ): AudioSharingInteractor =
+ if (Flags.volumeDialogAudioSharingFix()) {
+ impl.get()
+ } else {
+ emptyImpl.get()
+ }
}
}