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 
17 package com.android.systemui.bluetooth.qsdialog
18 
19 import android.bluetooth.BluetoothDevice
20 import android.graphics.drawable.Drawable
21 import android.media.AudioManager
22 import android.platform.test.annotations.DisableFlags
23 import android.platform.test.annotations.EnableFlags
24 import android.testing.TestableLooper
25 import android.util.Pair
26 import androidx.test.ext.junit.runners.AndroidJUnit4
27 import androidx.test.filters.SmallTest
28 import com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession
29 import com.android.dx.mockito.inline.extended.StaticMockitoSession
30 import com.android.settingslib.bluetooth.BluetoothUtils
31 import com.android.settingslib.bluetooth.CachedBluetoothDevice
32 import com.android.settingslib.bluetooth.LocalBluetoothManager
33 import com.android.settingslib.flags.Flags
34 import com.android.systemui.SysuiTestCase
35 import com.android.systemui.res.R
36 import com.google.common.truth.Truth.assertThat
37 import org.junit.After
38 import org.junit.Before
39 import org.junit.Rule
40 import org.junit.Test
41 import org.junit.runner.RunWith
42 import org.mockito.Mock
43 import org.mockito.Mockito.`when`
44 import org.mockito.junit.MockitoJUnit
45 import org.mockito.junit.MockitoRule
46 import org.mockito.kotlin.any
47 
48 @SmallTest
49 @RunWith(AndroidJUnit4::class)
50 @TestableLooper.RunWithLooper(setAsMainLooper = true)
51 class DeviceItemFactoryTest : SysuiTestCase() {
52     @get:Rule val mockitoRule: MockitoRule = MockitoJUnit.rule()
53 
54     private lateinit var mockitoSession: StaticMockitoSession
55     @Mock private lateinit var cachedDevice: CachedBluetoothDevice
56     @Mock private lateinit var bluetoothDevice: BluetoothDevice
57     @Mock private lateinit var localBluetoothManager: LocalBluetoothManager
58     @Mock private lateinit var drawable: Drawable
59 
60     private val availableMediaDeviceItemFactory = AvailableMediaDeviceItemFactory()
61     private val connectedDeviceItemFactory = ConnectedDeviceItemFactory()
62     private val savedDeviceItemFactory = SavedDeviceItemFactory()
63 
64     private val audioManager = context.getSystemService(AudioManager::class.java)!!
65 
66     @Before
setupnull67     fun setup() {
68         mockitoSession =
69             mockitoSession().initMocks(this).mockStatic(BluetoothUtils::class.java).startMocking()
70     }
71 
72     @After
tearDownnull73     fun tearDown() {
74         mockitoSession.finishMocking()
75     }
76 
77     @Test
testAvailableMediaDeviceItemFactory_createFromCachedDevicenull78     fun testAvailableMediaDeviceItemFactory_createFromCachedDevice() {
79         `when`(cachedDevice.name).thenReturn(DEVICE_NAME)
80         `when`(cachedDevice.connectionSummary).thenReturn(CONNECTION_SUMMARY)
81         `when`(cachedDevice.drawableWithDescription).thenReturn(Pair.create(drawable, ""))
82         val deviceItem = availableMediaDeviceItemFactory.create(context, cachedDevice)
83 
84         assertDeviceItem(deviceItem, DeviceItemType.AVAILABLE_MEDIA_BLUETOOTH_DEVICE)
85     }
86 
87     @Test
testConnectedDeviceItemFactory_createFromCachedDevicenull88     fun testConnectedDeviceItemFactory_createFromCachedDevice() {
89         `when`(cachedDevice.name).thenReturn(DEVICE_NAME)
90         `when`(cachedDevice.connectionSummary).thenReturn(CONNECTION_SUMMARY)
91         `when`(cachedDevice.drawableWithDescription).thenReturn(Pair.create(drawable, ""))
92         val deviceItem = connectedDeviceItemFactory.create(context, cachedDevice)
93 
94         assertDeviceItem(deviceItem, DeviceItemType.CONNECTED_BLUETOOTH_DEVICE)
95     }
96 
97     @Test
testSavedDeviceItemFactory_createFromCachedDevicenull98     fun testSavedDeviceItemFactory_createFromCachedDevice() {
99         `when`(cachedDevice.name).thenReturn(DEVICE_NAME)
100         `when`(cachedDevice.connectionSummary).thenReturn(CONNECTION_SUMMARY)
101         `when`(cachedDevice.drawableWithDescription).thenReturn(Pair.create(drawable, ""))
102         val deviceItem = savedDeviceItemFactory.create(context, cachedDevice)
103 
104         assertDeviceItem(deviceItem, DeviceItemType.SAVED_BLUETOOTH_DEVICE)
105         assertThat(deviceItem.background).isNotNull()
106     }
107 
108     @Test
testAvailableAudioSharingMediaDeviceItemFactory_createFromCachedDevicenull109     fun testAvailableAudioSharingMediaDeviceItemFactory_createFromCachedDevice() {
110         `when`(cachedDevice.name).thenReturn(DEVICE_NAME)
111         `when`(cachedDevice.drawableWithDescription).thenReturn(Pair.create(drawable, ""))
112         val deviceItem =
113             AvailableAudioSharingMediaDeviceItemFactory(localBluetoothManager)
114                 .create(context, cachedDevice)
115 
116         assertThat(deviceItem).isNotNull()
117         assertThat(deviceItem.type)
118             .isEqualTo(DeviceItemType.AVAILABLE_AUDIO_SHARING_MEDIA_BLUETOOTH_DEVICE)
119         assertThat(deviceItem.cachedBluetoothDevice).isEqualTo(cachedDevice)
120         assertThat(deviceItem.deviceName).isEqualTo(DEVICE_NAME)
121         assertThat(deviceItem.isActive).isFalse()
122         assertThat(deviceItem.connectionSummary)
123             .isEqualTo(
124                 context.getString(
125                     R.string.quick_settings_bluetooth_device_audio_sharing_or_switch_active
126                 )
127             )
128     }
129 
130     @Test
testAvailableAudioSharingMediaDeviceItemFactory_isFilterMatched_flagOff_returnsFalsenull131     fun testAvailableAudioSharingMediaDeviceItemFactory_isFilterMatched_flagOff_returnsFalse() {
132         assertThat(
133                 AvailableAudioSharingMediaDeviceItemFactory(localBluetoothManager)
134                     .isFilterMatched(context, cachedDevice, audioManager, false)
135             )
136             .isFalse()
137     }
138 
139     @Test
testAvailableAudioSharingMediaDeviceItemFactory_isFilterMatched_isActiveDevice_falsenull140     fun testAvailableAudioSharingMediaDeviceItemFactory_isFilterMatched_isActiveDevice_false() {
141         `when`(BluetoothUtils.isActiveMediaDevice(any())).thenReturn(true)
142 
143         assertThat(
144                 AvailableAudioSharingMediaDeviceItemFactory(localBluetoothManager)
145                     .isFilterMatched(context, cachedDevice, audioManager, true)
146             )
147             .isFalse()
148     }
149 
150     @Test
testAvailableAudioSharingMediaDeviceItemFactory_isFilterMatched_isNotAvailable_falsenull151     fun testAvailableAudioSharingMediaDeviceItemFactory_isFilterMatched_isNotAvailable_false() {
152         `when`(BluetoothUtils.isActiveMediaDevice(any())).thenReturn(false)
153         `when`(BluetoothUtils.isAvailableMediaBluetoothDevice(any(), any())).thenReturn(true)
154         `when`(BluetoothUtils.isAvailableAudioSharingMediaBluetoothDevice(any(), any()))
155             .thenReturn(false)
156 
157         assertThat(
158                 AvailableAudioSharingMediaDeviceItemFactory(localBluetoothManager)
159                     .isFilterMatched(context, cachedDevice, audioManager, true)
160             )
161             .isFalse()
162     }
163 
164     @Test
testAvailableAudioSharingMediaDeviceItemFactory_isFilterMatched_returnsTruenull165     fun testAvailableAudioSharingMediaDeviceItemFactory_isFilterMatched_returnsTrue() {
166         `when`(BluetoothUtils.isActiveMediaDevice(any())).thenReturn(false)
167         `when`(BluetoothUtils.isAvailableMediaBluetoothDevice(any(), any())).thenReturn(true)
168         `when`(BluetoothUtils.isAvailableAudioSharingMediaBluetoothDevice(any(), any()))
169             .thenReturn(true)
170 
171         assertThat(
172                 AvailableAudioSharingMediaDeviceItemFactory(localBluetoothManager)
173                     .isFilterMatched(context, cachedDevice, audioManager, true)
174             )
175             .isTrue()
176     }
177 
178     @Test
179     @DisableFlags(Flags.FLAG_ENABLE_HIDE_EXCLUSIVELY_MANAGED_BLUETOOTH_DEVICE)
testSavedFactory_isFilterMatched_bondedAndNotConnected_returnsTruenull180     fun testSavedFactory_isFilterMatched_bondedAndNotConnected_returnsTrue() {
181         `when`(cachedDevice.bondState).thenReturn(BluetoothDevice.BOND_BONDED)
182         `when`(cachedDevice.isConnected).thenReturn(false)
183 
184         assertThat(savedDeviceItemFactory.isFilterMatched(context, cachedDevice, audioManager))
185             .isTrue()
186     }
187 
188     @Test
189     @DisableFlags(Flags.FLAG_ENABLE_HIDE_EXCLUSIVELY_MANAGED_BLUETOOTH_DEVICE)
testSavedFactory_isFilterMatched_connected_returnsFalsenull190     fun testSavedFactory_isFilterMatched_connected_returnsFalse() {
191         `when`(cachedDevice.bondState).thenReturn(BluetoothDevice.BOND_BONDED)
192         `when`(cachedDevice.isConnected).thenReturn(true)
193 
194         assertThat(savedDeviceItemFactory.isFilterMatched(context, cachedDevice, audioManager))
195             .isFalse()
196     }
197 
198     @Test
199     @DisableFlags(Flags.FLAG_ENABLE_HIDE_EXCLUSIVELY_MANAGED_BLUETOOTH_DEVICE)
testSavedFactory_isFilterMatched_notBonded_returnsFalsenull200     fun testSavedFactory_isFilterMatched_notBonded_returnsFalse() {
201         `when`(cachedDevice.bondState).thenReturn(BluetoothDevice.BOND_NONE)
202 
203         assertThat(savedDeviceItemFactory.isFilterMatched(context, cachedDevice, audioManager))
204             .isFalse()
205     }
206 
207     @Test
208     @EnableFlags(Flags.FLAG_ENABLE_HIDE_EXCLUSIVELY_MANAGED_BLUETOOTH_DEVICE)
testSavedFactory_isFilterMatched_exclusivelyManaged_returnsFalsenull209     fun testSavedFactory_isFilterMatched_exclusivelyManaged_returnsFalse() {
210         `when`(cachedDevice.device).thenReturn(bluetoothDevice)
211         `when`(BluetoothUtils.isExclusivelyManagedBluetoothDevice(any(), any())).thenReturn(true)
212 
213         assertThat(savedDeviceItemFactory.isFilterMatched(context, cachedDevice, audioManager))
214             .isFalse()
215     }
216 
217     @Test
218     @EnableFlags(Flags.FLAG_ENABLE_HIDE_EXCLUSIVELY_MANAGED_BLUETOOTH_DEVICE)
testSavedFactory_isFilterMatched_notExclusiveManaged_returnsTruenull219     fun testSavedFactory_isFilterMatched_notExclusiveManaged_returnsTrue() {
220         `when`(cachedDevice.device).thenReturn(bluetoothDevice)
221         `when`(BluetoothUtils.isExclusivelyManagedBluetoothDevice(any(), any())).thenReturn(false)
222         `when`(cachedDevice.bondState).thenReturn(BluetoothDevice.BOND_BONDED)
223         `when`(cachedDevice.isConnected).thenReturn(false)
224 
225         assertThat(savedDeviceItemFactory.isFilterMatched(context, cachedDevice, audioManager))
226             .isTrue()
227     }
228 
229     @Test
230     @EnableFlags(Flags.FLAG_ENABLE_HIDE_EXCLUSIVELY_MANAGED_BLUETOOTH_DEVICE)
testSavedFactory_isFilterMatched_notExclusivelyManaged_connected_returnsFalsenull231     fun testSavedFactory_isFilterMatched_notExclusivelyManaged_connected_returnsFalse() {
232         `when`(cachedDevice.device).thenReturn(bluetoothDevice)
233         `when`(BluetoothUtils.isExclusivelyManagedBluetoothDevice(any(), any())).thenReturn(false)
234         `when`(cachedDevice.bondState).thenReturn(BluetoothDevice.BOND_BONDED)
235         `when`(cachedDevice.isConnected).thenReturn(true)
236 
237         assertThat(savedDeviceItemFactory.isFilterMatched(context, cachedDevice, audioManager))
238             .isFalse()
239     }
240 
241     @Test
242     @DisableFlags(Flags.FLAG_ENABLE_HIDE_EXCLUSIVELY_MANAGED_BLUETOOTH_DEVICE)
testConnectedFactory_isFilterMatched_bondedAndConnected_returnsTruenull243     fun testConnectedFactory_isFilterMatched_bondedAndConnected_returnsTrue() {
244         `when`(BluetoothUtils.isConnectedBluetoothDevice(any(), any())).thenReturn(true)
245 
246         assertThat(connectedDeviceItemFactory.isFilterMatched(context, cachedDevice, audioManager))
247             .isTrue()
248     }
249 
250     @Test
251     @DisableFlags(Flags.FLAG_ENABLE_HIDE_EXCLUSIVELY_MANAGED_BLUETOOTH_DEVICE)
testConnectedFactory_isFilterMatched_notConnected_returnsFalsenull252     fun testConnectedFactory_isFilterMatched_notConnected_returnsFalse() {
253         assertThat(connectedDeviceItemFactory.isFilterMatched(context, cachedDevice, audioManager))
254             .isFalse()
255     }
256 
257     @Test
258     @EnableFlags(Flags.FLAG_ENABLE_HIDE_EXCLUSIVELY_MANAGED_BLUETOOTH_DEVICE)
testConnectedFactory_isFilterMatched_exclusivelyManaged_returnsFalsenull259     fun testConnectedFactory_isFilterMatched_exclusivelyManaged_returnsFalse() {
260         `when`(cachedDevice.device).thenReturn(bluetoothDevice)
261         `when`(BluetoothUtils.isExclusivelyManagedBluetoothDevice(any(), any())).thenReturn(true)
262 
263         assertThat(connectedDeviceItemFactory.isFilterMatched(context, cachedDevice, audioManager))
264             .isFalse()
265     }
266 
267     @Test
268     @EnableFlags(Flags.FLAG_ENABLE_HIDE_EXCLUSIVELY_MANAGED_BLUETOOTH_DEVICE)
testConnectedFactory_isFilterMatched_noExclusiveManager_returnsTruenull269     fun testConnectedFactory_isFilterMatched_noExclusiveManager_returnsTrue() {
270         `when`(cachedDevice.device).thenReturn(bluetoothDevice)
271         `when`(BluetoothUtils.isExclusivelyManagedBluetoothDevice(any(), any())).thenReturn(false)
272         `when`(BluetoothUtils.isConnectedBluetoothDevice(any(), any())).thenReturn(true)
273 
274         assertThat(connectedDeviceItemFactory.isFilterMatched(context, cachedDevice, audioManager))
275             .isTrue()
276     }
277 
278     @Test
279     @EnableFlags(Flags.FLAG_ENABLE_HIDE_EXCLUSIVELY_MANAGED_BLUETOOTH_DEVICE)
testConnectedFactory_isFilterMatched_notExclusivelyManaged_notConnected_returnsFalsenull280     fun testConnectedFactory_isFilterMatched_notExclusivelyManaged_notConnected_returnsFalse() {
281         `when`(cachedDevice.device).thenReturn(bluetoothDevice)
282         `when`(BluetoothUtils.isExclusivelyManagedBluetoothDevice(any(), any())).thenReturn(false)
283         `when`(BluetoothUtils.isConnectedBluetoothDevice(any(), any())).thenReturn(false)
284 
285         assertThat(connectedDeviceItemFactory.isFilterMatched(context, cachedDevice, audioManager))
286             .isFalse()
287     }
288 
assertDeviceItemnull289     private fun assertDeviceItem(deviceItem: DeviceItem?, deviceItemType: DeviceItemType) {
290         assertThat(deviceItem).isNotNull()
291         assertThat(deviceItem!!.type).isEqualTo(deviceItemType)
292         assertThat(deviceItem.cachedBluetoothDevice).isEqualTo(cachedDevice)
293         assertThat(deviceItem.deviceName).isEqualTo(DEVICE_NAME)
294         assertThat(deviceItem.connectionSummary).isEqualTo(CONNECTION_SUMMARY)
295     }
296 
297     companion object {
298         const val DEVICE_NAME = "DeviceName"
299         const val CONNECTION_SUMMARY = "ConnectionSummary"
300     }
301 }
302