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

Commit 6552622b authored by Anton Potapov's avatar Anton Potapov Committed by Android (Google) Code Review
Browse files

Merge "Add spatial audio button when builtin speaker supports it" into main

parents e9e4e080 c528a6be
Loading
Loading
Loading
Loading
+32 −23
Original line number Diff line number Diff line
@@ -16,6 +16,8 @@

package com.android.systemui.volume.panel.component.spatial.domain

import android.media.AudioDeviceAttributes
import android.media.AudioDeviceInfo
import android.media.session.MediaSession
import android.media.session.PlaybackState
import android.testing.TestableLooper.RunWithLooper
@@ -49,26 +51,27 @@ import org.junit.runner.RunWith
class SpatialAudioAvailabilityCriteriaTest : SysuiTestCase() {

    private val kosmos = testKosmos()
    private val cachedBluetoothDevice: CachedBluetoothDevice = mock {
        whenever(address).thenReturn("test_address")
    }
    private val bluetoothMediaDevice: BluetoothMediaDevice = mock {
        whenever(cachedDevice).thenReturn(cachedBluetoothDevice)
    }

    private lateinit var underTest: SpatialAudioAvailabilityCriteria

    @Before
    fun setup() {
        with(kosmos) {
            mediaControllerRepository.setActiveLocalMediaController(
                mediaController.apply {
                    whenever(packageName).thenReturn("test.pkg")
                    whenever(sessionToken).thenReturn(MediaSession.Token(0, mock {}))
                    whenever(playbackState).thenReturn(PlaybackState.Builder().build())
            val cachedBluetoothDevice: CachedBluetoothDevice = mock {
                whenever(address).thenReturn("test_address")
            }
            localMediaRepository.updateCurrentConnectedDevice(
                mock<BluetoothMediaDevice> {
                    whenever(name).thenReturn("test_device")
                    whenever(cachedDevice).thenReturn(cachedBluetoothDevice)
                }
            )

            whenever(mediaController.packageName).thenReturn("test.pkg")
            whenever(mediaController.sessionToken).thenReturn(MediaSession.Token(0, mock {}))
            whenever(mediaController.playbackState).thenReturn(PlaybackState.Builder().build())

            mediaControllerRepository.setActiveLocalMediaController(mediaController)

            underTest = SpatialAudioAvailabilityCriteria(spatialAudioComponentInteractor)
        }
    }
@@ -77,9 +80,8 @@ class SpatialAudioAvailabilityCriteriaTest : SysuiTestCase() {
    fun noSpatialAudio_noHeadTracking_unavailable() {
        with(kosmos) {
            testScope.runTest {
                localMediaRepository.updateCurrentConnectedDevice(bluetoothMediaDevice)
                spatializerRepository.defaultHeadTrackingAvailable = false
                spatializerRepository.defaultSpatialAudioAvailable = false
                spatializerRepository.setIsSpatialAudioAvailable(headset, false)
                spatializerRepository.setIsHeadTrackingAvailable(headset, false)

                val isAvailable by collectLastValue(underTest.isAvailable())
                runCurrent()
@@ -93,9 +95,8 @@ class SpatialAudioAvailabilityCriteriaTest : SysuiTestCase() {
    fun spatialAudio_noHeadTracking_available() {
        with(kosmos) {
            testScope.runTest {
                localMediaRepository.updateCurrentConnectedDevice(bluetoothMediaDevice)
                spatializerRepository.defaultHeadTrackingAvailable = false
                spatializerRepository.defaultSpatialAudioAvailable = true
                spatializerRepository.setIsSpatialAudioAvailable(headset, true)
                spatializerRepository.setIsHeadTrackingAvailable(headset, false)

                val isAvailable by collectLastValue(underTest.isAvailable())
                runCurrent()
@@ -109,9 +110,8 @@ class SpatialAudioAvailabilityCriteriaTest : SysuiTestCase() {
    fun spatialAudio_headTracking_available() {
        with(kosmos) {
            testScope.runTest {
                localMediaRepository.updateCurrentConnectedDevice(bluetoothMediaDevice)
                spatializerRepository.defaultHeadTrackingAvailable = true
                spatializerRepository.defaultSpatialAudioAvailable = true
                spatializerRepository.setIsSpatialAudioAvailable(headset, true)
                spatializerRepository.setIsHeadTrackingAvailable(headset, true)

                val isAvailable by collectLastValue(underTest.isAvailable())
                runCurrent()
@@ -125,8 +125,8 @@ class SpatialAudioAvailabilityCriteriaTest : SysuiTestCase() {
    fun spatialAudio_headTracking_noDevice_unavailable() {
        with(kosmos) {
            testScope.runTest {
                spatializerRepository.defaultHeadTrackingAvailable = true
                spatializerRepository.defaultSpatialAudioAvailable = true
                localMediaRepository.updateCurrentConnectedDevice(null)
                spatializerRepository.setIsSpatialAudioAvailable(headset, false)

                val isAvailable by collectLastValue(underTest.isAvailable())
                runCurrent()
@@ -135,4 +135,13 @@ class SpatialAudioAvailabilityCriteriaTest : SysuiTestCase() {
            }
        }
    }

    private companion object {
        val headset =
            AudioDeviceAttributes(
                AudioDeviceAttributes.ROLE_OUTPUT,
                AudioDeviceInfo.TYPE_BLE_HEADSET,
                "test_address"
            )
    }
}
+91 −10
Original line number Diff line number Diff line
@@ -26,6 +26,7 @@ import androidx.test.filters.SmallTest
import com.android.settingslib.bluetooth.CachedBluetoothDevice
import com.android.settingslib.media.BluetoothMediaDevice
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.coroutines.collectValues
import com.android.systemui.kosmos.testScope
import com.android.systemui.media.spatializerInteractor
@@ -37,6 +38,7 @@ import com.android.systemui.volume.localMediaRepository
import com.android.systemui.volume.mediaController
import com.android.systemui.volume.mediaControllerRepository
import com.android.systemui.volume.mediaOutputInteractor
import com.android.systemui.volume.panel.component.spatial.domain.model.SpatialAudioAvailabilityModel
import com.android.systemui.volume.panel.component.spatial.domain.model.SpatialAudioEnabledModel
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -74,16 +76,6 @@ class SpatialAudioComponentInteractorTest : SysuiTestCase() {

            mediaControllerRepository.setActiveLocalMediaController(mediaController)

            spatializerRepository.setIsSpatialAudioAvailable(
                AudioDeviceAttributes(
                    AudioDeviceAttributes.ROLE_OUTPUT,
                    AudioDeviceInfo.TYPE_BLE_HEADSET,
                    "test_address"
                ),
                true
            )
            spatializerRepository.defaultHeadTrackingAvailable = true

            underTest =
                SpatialAudioComponentInteractor(
                    mediaOutputInteractor,
@@ -97,6 +89,7 @@ class SpatialAudioComponentInteractorTest : SysuiTestCase() {
    fun setEnabled_changesIsEnabled() {
        with(kosmos) {
            testScope.runTest {
                spatializerRepository.setIsSpatialAudioAvailable(headset, true)
                val values by collectValues(underTest.isEnabled)

                underTest.setEnabled(SpatialAudioEnabledModel.Disabled)
@@ -117,4 +110,92 @@ class SpatialAudioComponentInteractorTest : SysuiTestCase() {
            }
        }
    }

    @Test
    fun connectedDeviceSupports_isAvailable_SpatialAudio() {
        with(kosmos) {
            testScope.runTest {
                spatializerRepository.setIsSpatialAudioAvailable(headset, true)

                val isAvailable by collectLastValue(underTest.isAvailable)

                assertThat(isAvailable)
                    .isInstanceOf(SpatialAudioAvailabilityModel.SpatialAudio::class.java)
            }
        }
    }

    @Test
    fun connectedDeviceSupportsHeadTracking_isAvailable_HeadTracking() {
        with(kosmos) {
            testScope.runTest {
                spatializerRepository.setIsSpatialAudioAvailable(headset, true)
                spatializerRepository.setIsHeadTrackingAvailable(headset, true)

                val isAvailable by collectLastValue(underTest.isAvailable)

                assertThat(isAvailable)
                    .isInstanceOf(SpatialAudioAvailabilityModel.HeadTracking::class.java)
            }
        }
    }

    @Test
    fun connectedDeviceDoesntSupport_isAvailable_Unavailable() {
        with(kosmos) {
            testScope.runTest {
                spatializerRepository.setIsSpatialAudioAvailable(headset, false)

                val isAvailable by collectLastValue(underTest.isAvailable)

                assertThat(isAvailable)
                    .isInstanceOf(SpatialAudioAvailabilityModel.Unavailable::class.java)
            }
        }
    }

    @Test
    fun noConnectedDeviceBuiltinSupports_isAvailable_SpatialAudio() {
        with(kosmos) {
            testScope.runTest {
                localMediaRepository.updateCurrentConnectedDevice(null)
                spatializerRepository.setIsSpatialAudioAvailable(builtinSpeaker, true)

                val isAvailable by collectLastValue(underTest.isAvailable)

                assertThat(isAvailable)
                    .isInstanceOf(SpatialAudioAvailabilityModel.SpatialAudio::class.java)
            }
        }
    }

    @Test
    fun noConnectedDeviceBuiltinDoesntSupport_isAvailable_Unavailable() {
        with(kosmos) {
            testScope.runTest {
                localMediaRepository.updateCurrentConnectedDevice(null)
                spatializerRepository.setIsSpatialAudioAvailable(builtinSpeaker, false)

                val isAvailable by collectLastValue(underTest.isAvailable)

                assertThat(isAvailable)
                    .isInstanceOf(SpatialAudioAvailabilityModel.Unavailable::class.java)
            }
        }
    }

    private companion object {
        val headset =
            AudioDeviceAttributes(
                AudioDeviceAttributes.ROLE_OUTPUT,
                AudioDeviceInfo.TYPE_BLE_HEADSET,
                "test_address"
            )
        val builtinSpeaker =
            AudioDeviceAttributes(
                AudioDeviceAttributes.ROLE_OUTPUT,
                AudioDeviceInfo.TYPE_BUILTIN_SPEAKER,
                ""
            )
    }
}
+48 −34
Original line number Diff line number Diff line
@@ -18,8 +18,9 @@ package com.android.systemui.volume.panel.component.spatial.domain.interactor

import android.media.AudioDeviceAttributes
import android.media.AudioDeviceInfo
import com.android.settingslib.bluetooth.CachedBluetoothDevice
import com.android.settingslib.media.BluetoothMediaDevice
import com.android.settingslib.media.MediaDevice
import com.android.settingslib.media.PhoneMediaDevice
import com.android.settingslib.media.domain.interactor.SpatializerInteractor
import com.android.systemui.volume.panel.component.mediaoutput.domain.interactor.MediaOutputInteractor
import com.android.systemui.volume.panel.component.spatial.domain.model.SpatialAudioAvailabilityModel
@@ -54,12 +55,9 @@ constructor(
    private val currentAudioDeviceAttributes: StateFlow<AudioDeviceAttributes?> =
        mediaOutputInteractor.currentConnectedDevice
            .map { mediaDevice ->
                mediaDevice ?: return@map null
                val btDevice: CachedBluetoothDevice =
                    (mediaDevice as? BluetoothMediaDevice)?.cachedDevice ?: return@map null
                btDevice.getAudioDeviceAttributes()
                if (mediaDevice == null) builtinSpeaker else mediaDevice.getAudioDeviceAttributes()
            }
            .stateIn(coroutineScope, SharingStarted.WhileSubscribed(), null)
            .stateIn(coroutineScope, SharingStarted.WhileSubscribed(), builtinSpeaker)

    /**
     * Returns spatial audio availability model. It can be:
@@ -137,34 +135,50 @@ constructor(
        changes.emit(Unit)
    }

    private suspend fun CachedBluetoothDevice.getAudioDeviceAttributes(): AudioDeviceAttributes? {
    private suspend fun MediaDevice.getAudioDeviceAttributes(): AudioDeviceAttributes? {
        when (this) {
            is PhoneMediaDevice -> return builtinSpeaker
            is BluetoothMediaDevice -> {
                val device = cachedDevice ?: return null
                return listOf(
                        AudioDeviceAttributes(
                            AudioDeviceAttributes.ROLE_OUTPUT,
                            AudioDeviceInfo.TYPE_BLE_HEADSET,
                    address
                            device.address,
                        ),
                        AudioDeviceAttributes(
                            AudioDeviceAttributes.ROLE_OUTPUT,
                            AudioDeviceInfo.TYPE_BLE_SPEAKER,
                    address
                            device.address,
                        ),
                        AudioDeviceAttributes(
                            AudioDeviceAttributes.ROLE_OUTPUT,
                            AudioDeviceInfo.TYPE_BLE_BROADCAST,
                    address
                            device.address,
                        ),
                        AudioDeviceAttributes(
                            AudioDeviceAttributes.ROLE_OUTPUT,
                            AudioDeviceInfo.TYPE_BLUETOOTH_A2DP,
                    address
                            device.address,
                        ),
                        AudioDeviceAttributes(
                            AudioDeviceAttributes.ROLE_OUTPUT,
                            AudioDeviceInfo.TYPE_HEARING_AID,
                    address
                            device.address,
                        )
                    )
                    .firstOrNull { spatializerInteractor.isSpatialAudioAvailable(it) }
            }
            else -> return null
        }
    }

    private companion object {
        val builtinSpeaker =
            AudioDeviceAttributes(
                AudioDeviceAttributes.ROLE_OUTPUT,
                AudioDeviceInfo.TYPE_BUILTIN_SPEAKER,
                ""
            )
    }
}