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

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

Merge "Add bluetooth address check to determine a valid bluetooth device" into main

parents 06537ee6 7f8c528c
Loading
Loading
Loading
Loading
+95 −0
Original line number Diff line number Diff line
/*
 * 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.systemui.volume.ui.navigation

import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.internal.logging.uiEventLoggerFake
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.plugins.ActivityStarter
import com.android.systemui.plugins.activityStarter
import com.android.systemui.testKosmos
import com.android.systemui.volume.domain.model.VolumePanelRoute
import com.android.systemui.volume.panel.domain.interactor.volumePanelGlobalStateInteractor
import com.android.systemui.volume.panel.ui.viewmodel.volumePanelViewModelFactory
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.ArgumentMatchers.anyBoolean
import org.mockito.ArgumentMatchers.anyInt
import org.mockito.kotlin.any
import org.mockito.kotlin.mock
import org.mockito.kotlin.whenever

@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
@RunWith(AndroidJUnit4::class)
class VolumeNavigatorTest : SysuiTestCase() {

    private val kosmos = testKosmos()

    private val underTest: VolumeNavigator =
        with(kosmos) {
            VolumeNavigator(
                testScope.backgroundScope,
                testDispatcher,
                mock {},
                activityStarter,
                volumePanelViewModelFactory,
                mock {
                    on { create(any(), anyInt(), anyBoolean(), any()) }.thenReturn(mock {})
                    on { applicationContext }.thenReturn(context)
                },
                uiEventLoggerFake,
                volumePanelGlobalStateInteractor,
            )
        }

    @Test
    fun showNewVolumePanel_keyguardLocked_notShown() =
        with(kosmos) {
            testScope.runTest {
                val panelState by collectLastValue(volumePanelGlobalStateInteractor.globalState)

                underTest.openVolumePanel(VolumePanelRoute.COMPOSE_VOLUME_PANEL)
                runCurrent()

                assertThat(panelState!!.isVisible).isFalse()
            }
        }

    @Test
    fun showNewVolumePanel_keyguardUnlocked_shown() =
        with(kosmos) {
            testScope.runTest {
                whenever(activityStarter.dismissKeyguardThenExecute(any(), any(), anyBoolean()))
                    .then { (it.arguments[0] as ActivityStarter.OnDismissAction).onDismiss() }
                val panelState by collectLastValue(volumePanelGlobalStateInteractor.globalState)

                underTest.openVolumePanel(VolumePanelRoute.COMPOSE_VOLUME_PANEL)
                runCurrent()

                assertThat(panelState!!.isVisible).isTrue()
            }
        }
}
+23 −22
Original line number Diff line number Diff line
@@ -18,8 +18,6 @@ package com.android.systemui.volume.domain.interactor

import android.bluetooth.BluetoothAdapter
import android.media.AudioDeviceInfo
import android.media.AudioDeviceInfo.TYPE_WIRED_HEADPHONES
import android.media.AudioDeviceInfo.TYPE_WIRED_HEADSET
import com.android.settingslib.bluetooth.CachedBluetoothDevice
import com.android.settingslib.bluetooth.LocalBluetoothManager
import com.android.settingslib.media.BluetoothMediaDevice
@@ -81,27 +79,29 @@ constructor(
    val isInAudioSharing: Flow<Boolean> = audioSharingRepository.inAudioSharing

    private fun AudioDeviceInfo.toAudioOutputDevice(): AudioOutputDevice {
        if (type == TYPE_WIRED_HEADPHONES || type == TYPE_WIRED_HEADSET) {
            return AudioOutputDevice.Wired(
                name = productName.toString(),
                icon = deviceIconInteractor.loadIcon(type),
        if (
            BluetoothAdapter.checkBluetoothAddress(address) &&
                localBluetoothManager != null &&
                bluetoothAdapter != null
        ) {
            val remoteDevice = bluetoothAdapter.getRemoteDevice(address)
            localBluetoothManager.cachedDeviceManager.findDevice(remoteDevice)?.let {
                device: CachedBluetoothDevice ->
                return AudioOutputDevice.Bluetooth(
                    name = device.name,
                    icon = deviceIconInteractor.loadIcon(device),
                    cachedBluetoothDevice = device,
                )
            }
        val cachedBluetoothDevice: CachedBluetoothDevice? =
            if (address.isEmpty() || localBluetoothManager == null || bluetoothAdapter == null) {
                null
            } else {
                val remoteDevice = bluetoothAdapter.getRemoteDevice(address)
                localBluetoothManager.cachedDeviceManager.findDevice(remoteDevice)
        }
        return cachedBluetoothDevice?.let {
            AudioOutputDevice.Bluetooth(
                name = it.name,
                icon = deviceIconInteractor.loadIcon(it),
                cachedBluetoothDevice = it,
        // Built-in device has an empty address
        if (address.isNotEmpty()) {
            return AudioOutputDevice.Wired(
                name = productName.toString(),
                icon = deviceIconInteractor.loadIcon(type),
            )
        }
            ?: AudioOutputDevice.BuiltIn(
        return AudioOutputDevice.BuiltIn(
            name = productName.toString(),
            icon = deviceIconInteractor.loadIcon(type),
        )
@@ -115,7 +115,8 @@ constructor(
                    icon = icon,
                    cachedBluetoothDevice = cachedDevice,
                )
            deviceType == MediaDeviceType.TYPE_3POINT5_MM_AUDIO_DEVICE ->
            deviceType == MediaDeviceType.TYPE_3POINT5_MM_AUDIO_DEVICE ||
                deviceType == MediaDeviceType.TYPE_USB_C_AUDIO_DEVICE ->
                AudioOutputDevice.Wired(
                    name = name,
                    icon = icon,
+3 −3
Original line number Diff line number Diff line
@@ -98,12 +98,12 @@ constructor(

    private fun showNewVolumePanel() {
        activityStarter.dismissKeyguardThenExecute(
            {
            /* action = */ {
                volumePanelGlobalStateInteractor.setVisible(true)
                false
            },
            {},
            true
            /* cancel = */ {},
            /* afterKeyguardGone = */ true,
        )
    }

+6 −3
Original line number Diff line number Diff line
@@ -33,19 +33,22 @@ object TestAudioDevicesFactory {
        )
    }

    fun wiredDevice(deviceName: String = "wired"): AudioDeviceInfo {
    fun wiredDevice(
        deviceName: String = "wired",
        deviceAddress: String = "card=1;device=0",
    ): AudioDeviceInfo {
        return AudioDeviceInfo(
            AudioDevicePort.createForTesting(
                AudioDeviceInfo.TYPE_WIRED_HEADPHONES,
                deviceName,
                "",
                deviceAddress,
            )
        )
    }

    fun bluetoothDevice(
        deviceName: String = "bt",
        deviceAddress: String = "test_address",
        deviceAddress: String = "00:43:A8:23:10:F0",
    ): AudioDeviceInfo {
        return AudioDeviceInfo(
            AudioDevicePort.createForTesting(