1 /*
2  * Copyright (C) 2024 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package com.android.systemui.bluetooth.qsdialog
17 
18 import android.bluetooth.BluetoothDevice
19 import android.platform.test.annotations.DisableFlags
20 import android.platform.test.annotations.EnableFlags
21 import android.testing.AndroidTestingRunner
22 import android.testing.TestableLooper
23 import androidx.test.filters.SmallTest
24 import com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession
25 import com.android.dx.mockito.inline.extended.StaticMockitoSession
26 import com.android.settingslib.bluetooth.BluetoothUtils
27 import com.android.settingslib.bluetooth.LeAudioProfile
28 import com.android.settingslib.flags.Flags
29 import com.android.systemui.SysuiTestCase
30 import com.android.systemui.kosmos.testDispatcher
31 import com.android.systemui.kosmos.testScope
32 import com.android.systemui.plugins.activityStarter
33 import com.android.systemui.statusbar.phone.SystemUIDialog
34 import com.android.systemui.testKosmos
35 import kotlinx.coroutines.ExperimentalCoroutinesApi
36 import kotlinx.coroutines.test.UnconfinedTestDispatcher
37 import kotlinx.coroutines.test.runTest
38 import org.junit.After
39 import org.junit.Before
40 import org.junit.Rule
41 import org.junit.Test
42 import org.junit.runner.RunWith
43 import org.mockito.ArgumentMatchers
44 import org.mockito.ArgumentMatchers.anyBoolean
45 import org.mockito.Mock
46 import org.mockito.Mockito
47 import org.mockito.Mockito.verify
48 import org.mockito.junit.MockitoJUnit
49 import org.mockito.junit.MockitoRule
50 import org.mockito.kotlin.any
51 import org.mockito.kotlin.eq
52 import org.mockito.kotlin.never
53 import org.mockito.kotlin.whenever
54 
55 @SmallTest
56 @RunWith(AndroidTestingRunner::class)
57 @TestableLooper.RunWithLooper(setAsMainLooper = true)
58 @OptIn(ExperimentalCoroutinesApi::class)
59 class AudioSharingDeviceItemActionInteractorTest : SysuiTestCase() {
60     @get:Rule val mockitoRule: MockitoRule = MockitoJUnit.rule()
<lambda>null61     private val kosmos = testKosmos().apply { testDispatcher = UnconfinedTestDispatcher() }
62     private lateinit var actionInteractorImpl: DeviceItemActionInteractor
63     private lateinit var mockitoSession: StaticMockitoSession
64     private lateinit var connectedAudioSharingMediaDeviceItem: DeviceItem
65     private lateinit var connectedMediaDeviceItem: DeviceItem
66     private lateinit var inAudioSharingMediaDeviceItem: DeviceItem
67     @Mock private lateinit var dialog: SystemUIDialog
68     @Mock private lateinit var leAudioProfile: LeAudioProfile
69     @Mock private lateinit var bluetoothDevice: BluetoothDevice
70 
71     @Before
setUpnull72     fun setUp() {
73         mockitoSession =
74             mockitoSession().initMocks(this).mockStatic(BluetoothUtils::class.java).startMocking()
75         connectedMediaDeviceItem =
76             DeviceItem(
77                 type = DeviceItemType.AVAILABLE_MEDIA_BLUETOOTH_DEVICE,
78                 cachedBluetoothDevice = kosmos.cachedBluetoothDevice,
79                 deviceName = DEVICE_NAME,
80                 connectionSummary = DEVICE_CONNECTION_SUMMARY,
81                 iconWithDescription = null,
82                 background = null,
83             )
84         inAudioSharingMediaDeviceItem =
85             DeviceItem(
86                 type = DeviceItemType.AUDIO_SHARING_MEDIA_BLUETOOTH_DEVICE,
87                 cachedBluetoothDevice = kosmos.cachedBluetoothDevice,
88                 deviceName = DEVICE_NAME,
89                 connectionSummary = DEVICE_CONNECTION_SUMMARY,
90                 iconWithDescription = null,
91                 background = null,
92             )
93         connectedAudioSharingMediaDeviceItem =
94             DeviceItem(
95                 type = DeviceItemType.AVAILABLE_AUDIO_SHARING_MEDIA_BLUETOOTH_DEVICE,
96                 cachedBluetoothDevice = kosmos.cachedBluetoothDevice,
97                 deviceName = DEVICE_NAME,
98                 connectionSummary = DEVICE_CONNECTION_SUMMARY,
99                 iconWithDescription = null,
100                 background = null,
101             )
102         actionInteractorImpl = kosmos.audioSharingDeviceItemActionInteractorImpl
103     }
104 
105     @After
tearDownnull106     fun tearDown() {
107         mockitoSession.finishMocking()
108     }
109 
110     @Test
111     @EnableFlags(Flags.FLAG_AUDIO_SHARING_QS_DIALOG_IMPROVEMENT)
testOnClick_connectedAudioSharingMediaDevice_flagOn_createDialognull112     fun testOnClick_connectedAudioSharingMediaDevice_flagOn_createDialog() {
113         with(kosmos) {
114             testScope.runTest {
115                 bluetoothTileDialogAudioSharingRepository.setAudioSharingAvailable(true)
116                 actionInteractorImpl.onClick(connectedAudioSharingMediaDeviceItem, dialog)
117                 verify(dialogTransitionAnimator)
118                     .showFromDialog(any(), any(), eq(null), anyBoolean())
119             }
120         }
121     }
122 
123     @Test
124     @DisableFlags(Flags.FLAG_AUDIO_SHARING_QS_DIALOG_IMPROVEMENT)
testOnClick_connectedAudioSharingMediaDevice_flagOff_previewOn_createDialognull125     fun testOnClick_connectedAudioSharingMediaDevice_flagOff_previewOn_createDialog() {
126         with(kosmos) {
127             testScope.runTest {
128                 whenever(BluetoothUtils.isAudioSharingPreviewEnabled(any())).thenReturn(true)
129                 bluetoothTileDialogAudioSharingRepository.setAudioSharingAvailable(true)
130                 actionInteractorImpl.onClick(connectedAudioSharingMediaDeviceItem, dialog)
131                 verify(dialogTransitionAnimator)
132                     .showFromDialog(any(), any(), eq(null), anyBoolean())
133             }
134         }
135     }
136 
137     @Test
138     @DisableFlags(Flags.FLAG_AUDIO_SHARING_QS_DIALOG_IMPROVEMENT)
testOnClick_connectedAudioSharingMediaDevice_flagOff_shouldLaunchSettingsnull139     fun testOnClick_connectedAudioSharingMediaDevice_flagOff_shouldLaunchSettings() {
140         with(kosmos) {
141             testScope.runTest {
142                 whenever(BluetoothUtils.isAudioSharingPreviewEnabled(any())).thenReturn(false)
143                 bluetoothTileDialogAudioSharingRepository.setAudioSharingAvailable(true)
144                 whenever(cachedBluetoothDevice.device).thenReturn(bluetoothDevice)
145                 actionInteractorImpl.onClick(connectedAudioSharingMediaDeviceItem, dialog)
146                 verify(activityStarter)
147                     .postStartActivityDismissingKeyguard(
148                         ArgumentMatchers.any(),
149                         ArgumentMatchers.anyInt(),
150                         ArgumentMatchers.any(),
151                     )
152                 verify(dialogTransitionAnimator, never())
153                     .showFromDialog(any(), any(), eq(null), anyBoolean())
154             }
155         }
156     }
157 
158     @Test
testOnClick_inAudioSharingMediaDevice_doNothingnull159     fun testOnClick_inAudioSharingMediaDevice_doNothing() {
160         with(kosmos) {
161             testScope.runTest {
162                 bluetoothTileDialogAudioSharingRepository.setAudioSharingAvailable(true)
163                 actionInteractorImpl.onClick(inAudioSharingMediaDeviceItem, dialog)
164 
165                 verify(dialogTransitionAnimator, never())
166                     .showFromDialog(any(), any(), eq(null), anyBoolean())
167             }
168         }
169     }
170 
171     @Test
testOnClick_inAudioSharing_clickedDeviceHasSource_shouldNotLaunchSettingsnull172     fun testOnClick_inAudioSharing_clickedDeviceHasSource_shouldNotLaunchSettings() {
173         with(kosmos) {
174             testScope.runTest {
175                 bluetoothTileDialogAudioSharingRepository.setAudioSharingAvailable(true)
176                 whenever(cachedBluetoothDevice.uiAccessibleProfiles)
177                     .thenReturn(listOf(leAudioProfile))
178                 whenever(BluetoothUtils.isBroadcasting(ArgumentMatchers.any())).thenReturn(true)
179                 whenever(
180                         BluetoothUtils.hasConnectedBroadcastSource(
181                             ArgumentMatchers.any(),
182                             ArgumentMatchers.any(),
183                         )
184                     )
185                     .thenReturn(true)
186 
187                 actionInteractorImpl.onClick(connectedMediaDeviceItem, dialog)
188                 verify(activityStarter, Mockito.never())
189                     .postStartActivityDismissingKeyguard(
190                         ArgumentMatchers.any(),
191                         ArgumentMatchers.anyInt(),
192                         ArgumentMatchers.any(),
193                     )
194             }
195         }
196     }
197 
198     @Test
testOnClick_inAudioSharing_clickedDeviceNoSource_shouldLaunchSettingsnull199     fun testOnClick_inAudioSharing_clickedDeviceNoSource_shouldLaunchSettings() {
200         with(kosmos) {
201             testScope.runTest {
202                 bluetoothTileDialogAudioSharingRepository.setAudioSharingAvailable(true)
203                 whenever(cachedBluetoothDevice.device).thenReturn(bluetoothDevice)
204                 whenever(cachedBluetoothDevice.uiAccessibleProfiles)
205                     .thenReturn(listOf(leAudioProfile))
206 
207                 whenever(BluetoothUtils.isBroadcasting(ArgumentMatchers.any())).thenReturn(true)
208                 whenever(
209                         BluetoothUtils.hasConnectedBroadcastSource(
210                             ArgumentMatchers.any(),
211                             ArgumentMatchers.any(),
212                         )
213                     )
214                     .thenReturn(false)
215 
216                 actionInteractorImpl.onClick(connectedMediaDeviceItem, dialog)
217                 verify(activityStarter)
218                     .postStartActivityDismissingKeyguard(
219                         ArgumentMatchers.any(),
220                         ArgumentMatchers.anyInt(),
221                         ArgumentMatchers.any(),
222                     )
223             }
224         }
225     }
226 
227     private companion object {
228         const val DEVICE_NAME = "device"
229         const val DEVICE_CONNECTION_SUMMARY = "active"
230     }
231 }
232