1 /* 2 * Copyright (C) 2022 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.settings.bluetooth; 18 19 import static com.google.common.truth.Truth.assertThat; 20 21 import static org.mockito.Mockito.spy; 22 import static org.mockito.Mockito.verify; 23 import static org.mockito.Mockito.when; 24 25 import android.bluetooth.BluetoothDevice; 26 import android.media.AudioDeviceAttributes; 27 import android.media.AudioDeviceInfo; 28 import android.media.AudioManager; 29 import android.media.Spatializer; 30 31 import androidx.preference.PreferenceCategory; 32 import androidx.preference.SwitchPreference; 33 34 import com.android.settingslib.core.lifecycle.Lifecycle; 35 36 import org.junit.Before; 37 import org.junit.Test; 38 import org.junit.runner.RunWith; 39 import org.mockito.Mock; 40 import org.mockito.MockitoAnnotations; 41 import org.robolectric.RobolectricTestRunner; 42 import org.robolectric.RuntimeEnvironment; 43 44 import java.util.ArrayList; 45 import java.util.List; 46 47 @RunWith(RobolectricTestRunner.class) 48 public class BluetoothDetailsSpatialAudioControllerTest extends BluetoothDetailsControllerTestBase { 49 50 private static final String MAC_ADDRESS = "04:52:C7:0B:D8:3C"; 51 private static final String KEY_SPATIAL_AUDIO = "spatial_audio"; 52 private static final String KEY_HEAD_TRACKING = "head_tracking"; 53 54 @Mock 55 private AudioManager mAudioManager; 56 @Mock 57 private Spatializer mSpatializer; 58 @Mock 59 private Lifecycle mSpatialAudioLifecycle; 60 @Mock 61 private PreferenceCategory mProfilesContainer; 62 @Mock 63 private BluetoothDevice mBluetoothDevice; 64 65 private BluetoothDetailsSpatialAudioController mController; 66 private SwitchPreference mSpatialAudioPref; 67 private SwitchPreference mHeadTrackingPref; 68 69 @Before setUp()70 public void setUp() { 71 MockitoAnnotations.initMocks(this); 72 73 mContext = spy(RuntimeEnvironment.application); 74 when(mContext.getSystemService(AudioManager.class)).thenReturn(mAudioManager); 75 when(mAudioManager.getSpatializer()).thenReturn(mSpatializer); 76 when(mCachedDevice.getAddress()).thenReturn(MAC_ADDRESS); 77 when(mCachedDevice.getDevice()).thenReturn(mBluetoothDevice); 78 when(mBluetoothDevice.getAnonymizedAddress()).thenReturn(MAC_ADDRESS); 79 80 mController = new BluetoothDetailsSpatialAudioController(mContext, mFragment, 81 mCachedDevice, mSpatialAudioLifecycle); 82 mController.mProfilesContainer = mProfilesContainer; 83 84 mSpatialAudioPref = mController.createSpatialAudioPreference(mContext); 85 mHeadTrackingPref = mController.createHeadTrackingPreference(mContext); 86 87 when(mProfilesContainer.findPreference(KEY_SPATIAL_AUDIO)).thenReturn(mSpatialAudioPref); 88 when(mProfilesContainer.findPreference(KEY_HEAD_TRACKING)).thenReturn(mHeadTrackingPref); 89 } 90 91 @Test isAvailable_spatialAudioSupportA2dpDevice_returnsTrue()92 public void isAvailable_spatialAudioSupportA2dpDevice_returnsTrue() { 93 AudioDeviceAttributes a2dpDevice = new AudioDeviceAttributes( 94 AudioDeviceAttributes.ROLE_OUTPUT, 95 AudioDeviceInfo.TYPE_BLUETOOTH_A2DP, 96 MAC_ADDRESS); 97 when(mSpatializer.isAvailableForDevice(a2dpDevice)).thenReturn(true); 98 99 mController.setAvailableDevice(a2dpDevice); 100 101 assertThat(mController.isAvailable()).isTrue(); 102 assertThat(mController.mAudioDevice.getType()) 103 .isEqualTo(AudioDeviceInfo.TYPE_BLUETOOTH_A2DP); 104 } 105 106 @Test isAvailable_spatialAudioSupportBleHeadsetDevice_returnsTrue()107 public void isAvailable_spatialAudioSupportBleHeadsetDevice_returnsTrue() { 108 AudioDeviceAttributes bleHeadsetDevice = new AudioDeviceAttributes( 109 AudioDeviceAttributes.ROLE_OUTPUT, 110 AudioDeviceInfo.TYPE_BLE_HEADSET, 111 MAC_ADDRESS); 112 when(mSpatializer.isAvailableForDevice(bleHeadsetDevice)).thenReturn(true); 113 114 mController.setAvailableDevice(bleHeadsetDevice); 115 116 assertThat(mController.isAvailable()).isTrue(); 117 assertThat(mController.mAudioDevice.getType()) 118 .isEqualTo(AudioDeviceInfo.TYPE_BLE_HEADSET); 119 } 120 121 @Test isAvailable_spatialAudioSupportBleSpeakerDevice_returnsTrue()122 public void isAvailable_spatialAudioSupportBleSpeakerDevice_returnsTrue() { 123 AudioDeviceAttributes bleSpeakerDevice = new AudioDeviceAttributes( 124 AudioDeviceAttributes.ROLE_OUTPUT, 125 AudioDeviceInfo.TYPE_BLE_SPEAKER, 126 MAC_ADDRESS); 127 when(mSpatializer.isAvailableForDevice(bleSpeakerDevice)).thenReturn(true); 128 129 mController.setAvailableDevice(bleSpeakerDevice); 130 131 assertThat(mController.isAvailable()).isTrue(); 132 assertThat(mController.mAudioDevice.getType()) 133 .isEqualTo(AudioDeviceInfo.TYPE_BLE_SPEAKER); 134 } 135 136 @Test isAvailable_spatialAudioSupportBleBroadcastDevice_returnsTrue()137 public void isAvailable_spatialAudioSupportBleBroadcastDevice_returnsTrue() { 138 AudioDeviceAttributes bleBroadcastDevice = new AudioDeviceAttributes( 139 AudioDeviceAttributes.ROLE_OUTPUT, 140 AudioDeviceInfo.TYPE_BLE_BROADCAST, 141 MAC_ADDRESS); 142 when(mSpatializer.isAvailableForDevice(bleBroadcastDevice)).thenReturn(true); 143 144 mController.setAvailableDevice(bleBroadcastDevice); 145 146 assertThat(mController.isAvailable()).isTrue(); 147 assertThat(mController.mAudioDevice.getType()) 148 .isEqualTo(AudioDeviceInfo.TYPE_BLE_BROADCAST); 149 } 150 151 @Test isAvailable_spatialAudioSupportHearingAidDevice_returnsTrue()152 public void isAvailable_spatialAudioSupportHearingAidDevice_returnsTrue() { 153 AudioDeviceAttributes hearingAidDevice = new AudioDeviceAttributes( 154 AudioDeviceAttributes.ROLE_OUTPUT, 155 AudioDeviceInfo.TYPE_HEARING_AID, 156 MAC_ADDRESS); 157 when(mSpatializer.isAvailableForDevice(hearingAidDevice)).thenReturn(true); 158 159 mController.setAvailableDevice(hearingAidDevice); 160 161 assertThat(mController.isAvailable()).isTrue(); 162 assertThat(mController.mAudioDevice.getType()) 163 .isEqualTo(AudioDeviceInfo.TYPE_HEARING_AID); 164 } 165 166 @Test isAvailable_spatialAudioNotSupported_returnsFalse()167 public void isAvailable_spatialAudioNotSupported_returnsFalse() { 168 assertThat(mController.isAvailable()).isFalse(); 169 assertThat(mController.mAudioDevice.getType()) 170 .isEqualTo(AudioDeviceInfo.TYPE_HEARING_AID); 171 } 172 173 @Test refresh_spatialAudioIsTurnedOn_checksSpatialAudioPreference()174 public void refresh_spatialAudioIsTurnedOn_checksSpatialAudioPreference() { 175 List<AudioDeviceAttributes> compatibleAudioDevices = new ArrayList<>(); 176 compatibleAudioDevices.add(mController.mAudioDevice); 177 when(mSpatializer.getCompatibleAudioDevices()).thenReturn(compatibleAudioDevices); 178 179 mController.refresh(); 180 181 assertThat(mSpatialAudioPref.isChecked()).isTrue(); 182 } 183 184 @Test refresh_spatialAudioIsTurnedOff_unchecksSpatialAudioPreference()185 public void refresh_spatialAudioIsTurnedOff_unchecksSpatialAudioPreference() { 186 List<AudioDeviceAttributes> compatibleAudioDevices = new ArrayList<>(); 187 when(mSpatializer.getCompatibleAudioDevices()).thenReturn(compatibleAudioDevices); 188 189 mController.refresh(); 190 191 assertThat(mSpatialAudioPref.isChecked()).isFalse(); 192 } 193 194 @Test refresh_spatialAudioOnAndHeadTrackingIsAvailable_showsHeadTrackingPreference()195 public void refresh_spatialAudioOnAndHeadTrackingIsAvailable_showsHeadTrackingPreference() { 196 List<AudioDeviceAttributes> compatibleAudioDevices = new ArrayList<>(); 197 compatibleAudioDevices.add(mController.mAudioDevice); 198 when(mSpatializer.getCompatibleAudioDevices()).thenReturn(compatibleAudioDevices); 199 when(mSpatializer.hasHeadTracker(mController.mAudioDevice)).thenReturn(true); 200 201 mController.refresh(); 202 203 assertThat(mHeadTrackingPref.isVisible()).isTrue(); 204 } 205 206 @Test 207 public void refresh_spatialAudioOnAndHeadTrackingIsNotAvailable_hidesHeadTrackingPreference()208 refresh_spatialAudioOnAndHeadTrackingIsNotAvailable_hidesHeadTrackingPreference() { 209 List<AudioDeviceAttributes> compatibleAudioDevices = new ArrayList<>(); 210 compatibleAudioDevices.add(mController.mAudioDevice); 211 when(mSpatializer.getCompatibleAudioDevices()).thenReturn(compatibleAudioDevices); 212 when(mSpatializer.hasHeadTracker(mController.mAudioDevice)).thenReturn(false); 213 214 mController.refresh(); 215 216 assertThat(mHeadTrackingPref.isVisible()).isFalse(); 217 } 218 219 @Test refresh_spatialAudioOff_hidesHeadTrackingPreference()220 public void refresh_spatialAudioOff_hidesHeadTrackingPreference() { 221 List<AudioDeviceAttributes> compatibleAudioDevices = new ArrayList<>(); 222 when(mSpatializer.getCompatibleAudioDevices()).thenReturn(compatibleAudioDevices); 223 224 mController.refresh(); 225 226 assertThat(mHeadTrackingPref.isVisible()).isFalse(); 227 } 228 229 @Test refresh_headTrackingIsTurnedOn_checksHeadTrackingPreference()230 public void refresh_headTrackingIsTurnedOn_checksHeadTrackingPreference() { 231 List<AudioDeviceAttributes> compatibleAudioDevices = new ArrayList<>(); 232 compatibleAudioDevices.add(mController.mAudioDevice); 233 when(mSpatializer.getCompatibleAudioDevices()).thenReturn(compatibleAudioDevices); 234 when(mSpatializer.hasHeadTracker(mController.mAudioDevice)).thenReturn(true); 235 when(mSpatializer.isHeadTrackerEnabled(mController.mAudioDevice)).thenReturn(true); 236 237 mController.refresh(); 238 239 assertThat(mHeadTrackingPref.isChecked()).isTrue(); 240 } 241 242 @Test refresh_headTrackingIsTurnedOff_unchecksHeadTrackingPreference()243 public void refresh_headTrackingIsTurnedOff_unchecksHeadTrackingPreference() { 244 List<AudioDeviceAttributes> compatibleAudioDevices = new ArrayList<>(); 245 compatibleAudioDevices.add(mController.mAudioDevice); 246 when(mSpatializer.getCompatibleAudioDevices()).thenReturn(compatibleAudioDevices); 247 when(mSpatializer.hasHeadTracker(mController.mAudioDevice)).thenReturn(true); 248 when(mSpatializer.isHeadTrackerEnabled(mController.mAudioDevice)).thenReturn(false); 249 250 mController.refresh(); 251 252 assertThat(mHeadTrackingPref.isChecked()).isFalse(); 253 } 254 255 @Test turnedOnSpatialAudio_invokesAddCompatibleAudioDevice()256 public void turnedOnSpatialAudio_invokesAddCompatibleAudioDevice() { 257 mSpatialAudioPref.setChecked(true); 258 mController.onPreferenceClick(mSpatialAudioPref); 259 verify(mSpatializer).addCompatibleAudioDevice(mController.mAudioDevice); 260 } 261 262 @Test turnedOffSpatialAudio_invokesRemoveCompatibleAudioDevice()263 public void turnedOffSpatialAudio_invokesRemoveCompatibleAudioDevice() { 264 mSpatialAudioPref.setChecked(false); 265 mController.onPreferenceClick(mSpatialAudioPref); 266 verify(mSpatializer).removeCompatibleAudioDevice(mController.mAudioDevice); 267 } 268 269 @Test turnedOnHeadTracking_invokesSetHeadTrackerEnabled_setsTrue()270 public void turnedOnHeadTracking_invokesSetHeadTrackerEnabled_setsTrue() { 271 mHeadTrackingPref.setChecked(true); 272 mController.onPreferenceClick(mHeadTrackingPref); 273 verify(mSpatializer).setHeadTrackerEnabled(true, mController.mAudioDevice); 274 } 275 276 @Test turnedOffHeadTracking_invokesSetHeadTrackerEnabled_setsFalse()277 public void turnedOffHeadTracking_invokesSetHeadTrackerEnabled_setsFalse() { 278 mHeadTrackingPref.setChecked(false); 279 mController.onPreferenceClick(mHeadTrackingPref); 280 verify(mSpatializer).setHeadTrackerEnabled(false, mController.mAudioDevice); 281 } 282 } 283