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.settings.bluetooth; 18 19 import static android.bluetooth.BluetoothCsipSetCoordinator.GROUP_ID_INVALID; 20 import static android.bluetooth.BluetoothHapClient.PRESET_INDEX_UNAVAILABLE; 21 22 import static com.android.settings.bluetooth.BluetoothDetailsHearingDeviceController.KEY_HEARING_DEVICE_GROUP; 23 import static com.android.settings.bluetooth.BluetoothDetailsHearingAidsPresetsController.KEY_HEARING_AIDS_PRESETS; 24 25 import static com.google.common.truth.Truth.assertThat; 26 27 import static org.mockito.ArgumentMatchers.any; 28 import static org.mockito.Mockito.mock; 29 import static org.mockito.Mockito.never; 30 import static org.mockito.Mockito.spy; 31 import static org.mockito.Mockito.verify; 32 import static org.mockito.Mockito.when; 33 34 import android.bluetooth.BluetoothDevice; 35 import android.bluetooth.BluetoothHapClient; 36 import android.bluetooth.BluetoothHapPresetInfo; 37 38 import androidx.preference.ListPreference; 39 import androidx.preference.PreferenceCategory; 40 41 import com.android.settings.R; 42 import com.android.settingslib.bluetooth.CachedBluetoothDevice; 43 import com.android.settingslib.bluetooth.HapClientProfile; 44 import com.android.settingslib.bluetooth.LocalBluetoothManager; 45 import com.android.settingslib.bluetooth.LocalBluetoothProfileManager; 46 47 import org.junit.Rule; 48 import org.junit.Test; 49 import org.junit.runner.RunWith; 50 import org.mockito.Mock; 51 import org.mockito.junit.MockitoJUnit; 52 import org.mockito.junit.MockitoRule; 53 import org.robolectric.RobolectricTestRunner; 54 55 import java.util.ArrayList; 56 import java.util.List; 57 import java.util.Set; 58 import java.util.concurrent.Executor; 59 60 /** Tests for {@link BluetoothDetailsHearingAidsPresetsController}. */ 61 @RunWith(RobolectricTestRunner.class) 62 public class BluetoothDetailsHearingAidsPresetsControllerTest extends 63 BluetoothDetailsControllerTestBase { 64 65 private static final int TEST_PRESET_INDEX = 1; 66 private static final String TEST_PRESET_NAME = "test_preset"; 67 private static final int TEST_HAP_GROUP_ID = 1; 68 69 @Rule 70 public final MockitoRule mockito = MockitoJUnit.rule(); 71 72 @Mock 73 private LocalBluetoothManager mLocalManager; 74 @Mock 75 private LocalBluetoothProfileManager mProfileManager; 76 @Mock 77 private HapClientProfile mHapClientProfile; 78 @Mock 79 private CachedBluetoothDevice mCachedChildDevice; 80 @Mock 81 private BluetoothDevice mChildDevice; 82 83 private BluetoothDetailsHearingAidsPresetsController mController; 84 85 @Override setUp()86 public void setUp() { 87 super.setUp(); 88 89 when(mLocalManager.getProfileManager()).thenReturn(mProfileManager); 90 when(mProfileManager.getHapClientProfile()).thenReturn(mHapClientProfile); 91 when(mCachedDevice.getDevice()).thenReturn(mDevice); 92 when(mCachedDevice.getProfiles()).thenReturn(List.of(mHapClientProfile)); 93 when(mCachedDevice.isConnectedHapClientDevice()).thenReturn(true); 94 when(mCachedChildDevice.getDevice()).thenReturn(mChildDevice); 95 PreferenceCategory deviceControls = new PreferenceCategory(mContext); 96 deviceControls.setKey(KEY_HEARING_DEVICE_GROUP); 97 mScreen.addPreference(deviceControls); 98 mController = new BluetoothDetailsHearingAidsPresetsController(mContext, mFragment, 99 mLocalManager, mCachedDevice, mLifecycle); 100 mController.init(mScreen); 101 } 102 103 @Test isAvailable_supportHap_returnTrue()104 public void isAvailable_supportHap_returnTrue() { 105 when(mCachedDevice.getProfiles()).thenReturn(List.of(mHapClientProfile)); 106 107 assertThat(mController.isAvailable()).isTrue(); 108 } 109 110 @Test isAvailable_notSupportHap_returnFalse()111 public void isAvailable_notSupportHap_returnFalse() { 112 when(mCachedDevice.getProfiles()).thenReturn(new ArrayList<>()); 113 114 assertThat(mController.isAvailable()).isFalse(); 115 } 116 117 @Test onResume_registerCallback()118 public void onResume_registerCallback() { 119 mController.onResume(); 120 121 verify(mHapClientProfile).registerCallback(any(Executor.class), 122 any(BluetoothHapClient.Callback.class)); 123 } 124 125 @Test onPause_unregisterCallback()126 public void onPause_unregisterCallback() { 127 mController.onPause(); 128 129 verify(mHapClientProfile).unregisterCallback(any(BluetoothHapClient.Callback.class)); 130 } 131 132 @Test onPreferenceChange_keyMatched_verifyStatusUpdated()133 public void onPreferenceChange_keyMatched_verifyStatusUpdated() { 134 final ListPreference presetPreference = getTestPresetPreference(KEY_HEARING_AIDS_PRESETS); 135 136 boolean handled = mController.onPreferenceChange(presetPreference, 137 String.valueOf(TEST_PRESET_INDEX)); 138 139 assertThat(handled).isTrue(); 140 verify(presetPreference).setSummary(TEST_PRESET_NAME); 141 } 142 143 @Test onPreferenceChange_keyNotMatched_doNothing()144 public void onPreferenceChange_keyNotMatched_doNothing() { 145 final ListPreference presetPreference = getTestPresetPreference("wrong_key"); 146 147 boolean handled = mController.onPreferenceChange( 148 presetPreference, String.valueOf(TEST_PRESET_INDEX)); 149 150 assertThat(handled).isFalse(); 151 verify(presetPreference, never()).setSummary(any()); 152 } 153 154 @Test onPreferenceChange_supportGroupOperation_validGroupId_verifySelectPresetForGroup()155 public void onPreferenceChange_supportGroupOperation_validGroupId_verifySelectPresetForGroup() { 156 final ListPreference presetPreference = getTestPresetPreference(KEY_HEARING_AIDS_PRESETS); 157 when(mHapClientProfile.supportsSynchronizedPresets(mDevice)).thenReturn(true); 158 when(mHapClientProfile.getHapGroup(mDevice)).thenReturn(TEST_HAP_GROUP_ID); 159 160 mController.onPreferenceChange(presetPreference, String.valueOf(TEST_PRESET_INDEX)); 161 162 verify(mHapClientProfile).selectPresetForGroup(TEST_HAP_GROUP_ID, TEST_PRESET_INDEX); 163 } 164 165 @Test onPreferenceChange_notSupportGroupOperation_verifySelectPreset()166 public void onPreferenceChange_notSupportGroupOperation_verifySelectPreset() { 167 final ListPreference presetPreference = getTestPresetPreference(KEY_HEARING_AIDS_PRESETS); 168 when(mHapClientProfile.supportsSynchronizedPresets(mDevice)).thenReturn(false); 169 when(mHapClientProfile.getHapGroup(mDevice)).thenReturn(TEST_HAP_GROUP_ID); 170 171 mController.onPreferenceChange(presetPreference, String.valueOf(TEST_PRESET_INDEX)); 172 173 verify(mHapClientProfile).selectPreset(mDevice, TEST_PRESET_INDEX); 174 } 175 176 @Test onPreferenceChange_invalidGroupId_verifySelectPreset()177 public void onPreferenceChange_invalidGroupId_verifySelectPreset() { 178 final ListPreference presetPreference = getTestPresetPreference(KEY_HEARING_AIDS_PRESETS); 179 when(mHapClientProfile.supportsSynchronizedPresets(mDevice)).thenReturn(true); 180 when(mHapClientProfile.getHapGroup(mDevice)).thenReturn(GROUP_ID_INVALID); 181 182 mController.onPreferenceChange(presetPreference, String.valueOf(TEST_PRESET_INDEX)); 183 184 verify(mHapClientProfile).selectPreset(mDevice, TEST_PRESET_INDEX); 185 } 186 187 @Test onPreferenceChange_notSupportGroupOperation_hasSubDevice_verifyStatusUpdated()188 public void onPreferenceChange_notSupportGroupOperation_hasSubDevice_verifyStatusUpdated() { 189 final ListPreference presetPreference = getTestPresetPreference(KEY_HEARING_AIDS_PRESETS); 190 when(mHapClientProfile.supportsSynchronizedPresets(mDevice)).thenReturn(false); 191 when(mCachedDevice.getSubDevice()).thenReturn(mCachedChildDevice); 192 193 mController.onPreferenceChange(presetPreference, String.valueOf(TEST_PRESET_INDEX)); 194 195 verify(mHapClientProfile).selectPreset(mDevice, TEST_PRESET_INDEX); 196 verify(mHapClientProfile).selectPreset(mChildDevice, TEST_PRESET_INDEX); 197 } 198 199 @Test onPreferenceChange_notSupportGroupOperation_hasMemberDevice_verifyStatusUpdated()200 public void onPreferenceChange_notSupportGroupOperation_hasMemberDevice_verifyStatusUpdated() { 201 final ListPreference presetPreference = getTestPresetPreference(KEY_HEARING_AIDS_PRESETS); 202 when(mHapClientProfile.supportsSynchronizedPresets(mDevice)).thenReturn(false); 203 when(mCachedDevice.getMemberDevice()).thenReturn(Set.of(mCachedChildDevice)); 204 205 mController.onPreferenceChange(presetPreference, String.valueOf(TEST_PRESET_INDEX)); 206 207 verify(mHapClientProfile).selectPreset(mDevice, TEST_PRESET_INDEX); 208 verify(mHapClientProfile).selectPreset(mChildDevice, TEST_PRESET_INDEX); 209 } 210 211 @Test refresh_emptyPresetInfo_preferenceDisabled()212 public void refresh_emptyPresetInfo_preferenceDisabled() { 213 when(mHapClientProfile.getAllPresetInfo(mDevice)).thenReturn(new ArrayList<>()); 214 215 mController.refresh(); 216 217 assertThat(mController.getPreference()).isNotNull(); 218 assertThat(mController.getPreference().isEnabled()).isFalse(); 219 assertThat(String.valueOf(mController.getPreference().getSummary())).isEqualTo( 220 mContext.getString(R.string.bluetooth_hearing_aids_presets_empty_list_message)); 221 } 222 223 @Test refresh_validPresetInfo_preferenceEnabled()224 public void refresh_validPresetInfo_preferenceEnabled() { 225 BluetoothHapPresetInfo info = getTestPresetInfo(true); 226 when(mHapClientProfile.getAllPresetInfo(mDevice)).thenReturn(List.of(info)); 227 228 mController.refresh(); 229 230 assertThat(mController.getPreference()).isNotNull(); 231 assertThat(mController.getPreference().isEnabled()).isTrue(); 232 } 233 234 @Test refresh_invalidActivePresetIndex_summaryIsNull()235 public void refresh_invalidActivePresetIndex_summaryIsNull() { 236 BluetoothHapPresetInfo info = getTestPresetInfo(true); 237 when(mHapClientProfile.getAllPresetInfo(mDevice)).thenReturn(List.of(info)); 238 when(mHapClientProfile.getActivePresetIndex(mDevice)).thenReturn(PRESET_INDEX_UNAVAILABLE); 239 240 mController.refresh(); 241 242 assertThat(mController.getPreference()).isNotNull(); 243 assertThat(mController.getPreference().getSummary()).isNull(); 244 } 245 246 @Test refresh_validActivePresetIndex_summaryIsNotNull()247 public void refresh_validActivePresetIndex_summaryIsNotNull() { 248 BluetoothHapPresetInfo info = getTestPresetInfo(true); 249 when(mHapClientProfile.getAllPresetInfo(mDevice)).thenReturn(List.of(info)); 250 when(mHapClientProfile.getActivePresetIndex(mDevice)).thenReturn(TEST_PRESET_INDEX); 251 252 mController.refresh(); 253 254 assertThat(mController.getPreference()).isNotNull(); 255 assertThat(mController.getPreference().getSummary()).isNotNull(); 256 } 257 258 @Test onPresetSelectionForGroupFailed_selectPresetIsCalled()259 public void onPresetSelectionForGroupFailed_selectPresetIsCalled() { 260 when(mHapClientProfile.getHapGroup(mDevice)).thenReturn(TEST_HAP_GROUP_ID); 261 mController.getPreference().setValue(String.valueOf(TEST_PRESET_INDEX)); 262 263 mController.onPresetSelectionForGroupFailed(TEST_HAP_GROUP_ID, TEST_PRESET_INDEX); 264 265 verify(mHapClientProfile).selectPreset(mDevice, TEST_PRESET_INDEX); 266 } 267 268 @Test loadAllPresetInfo_unavailablePreset_notAddedToEntries()269 public void loadAllPresetInfo_unavailablePreset_notAddedToEntries() { 270 BluetoothHapPresetInfo info = getTestPresetInfo(false); 271 when(mHapClientProfile.getAllPresetInfo(mDevice)).thenReturn(List.of(info)); 272 273 mController.refresh(); 274 275 assertThat(mController.getPreference().getEntries().length).isEqualTo(0); 276 } 277 278 @Test loadAllPresetInfo_availablePreset_addedToEntries()279 public void loadAllPresetInfo_availablePreset_addedToEntries() { 280 BluetoothHapPresetInfo info = getTestPresetInfo(true); 281 when(mHapClientProfile.getAllPresetInfo(mDevice)).thenReturn(List.of(info)); 282 283 mController.refresh(); 284 285 assertThat(mController.getPreference().getEntries().length).isEqualTo(1); 286 } getTestPresetInfo(boolean available)287 private BluetoothHapPresetInfo getTestPresetInfo(boolean available) { 288 BluetoothHapPresetInfo info = mock(BluetoothHapPresetInfo.class); 289 when(info.getName()).thenReturn(TEST_PRESET_NAME); 290 when(info.getIndex()).thenReturn(TEST_PRESET_INDEX); 291 when(info.isAvailable()).thenReturn(available); 292 return info; 293 } 294 getTestPresetPreference(String key)295 private ListPreference getTestPresetPreference(String key) { 296 final ListPreference presetPreference = spy(new ListPreference(mContext)); 297 when(presetPreference.findIndexOfValue(String.valueOf(TEST_PRESET_INDEX))).thenReturn(0); 298 when(presetPreference.getEntries()).thenReturn(new CharSequence[]{TEST_PRESET_NAME}); 299 when(presetPreference.getEntryValues()).thenReturn( 300 new CharSequence[]{String.valueOf(TEST_PRESET_INDEX)}); 301 presetPreference.setKey(key); 302 return presetPreference; 303 } 304 } 305