diff options
3 files changed, 309 insertions, 13 deletions
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegate.java b/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegate.java index 083f1db07886..e604abdc55d8 100644 --- a/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegate.java +++ b/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegate.java @@ -228,7 +228,7 @@ public class HearingDevicesDialogDelegate implements SystemUIDialog.Delegate, mHearingDeviceItemList = getHearingDevicesList(); if (mPresetsController != null) { activeHearingDevice = getActiveHearingDevice(mHearingDeviceItemList); - mPresetsController.setActiveHearingDevice(activeHearingDevice); + mPresetsController.setHearingDeviceIfSupportHap(activeHearingDevice); } else { activeHearingDevice = null; } @@ -336,7 +336,7 @@ public class HearingDevicesDialogDelegate implements SystemUIDialog.Delegate, } final CachedBluetoothDevice activeHearingDevice = getActiveHearingDevice( mHearingDeviceItemList); - mPresetsController.setActiveHearingDevice(activeHearingDevice); + mPresetsController.setHearingDeviceIfSupportHap(activeHearingDevice); mPresetInfoAdapter = new ArrayAdapter<>(dialog.getContext(), R.layout.hearing_devices_preset_spinner_selected, diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesPresetsController.java b/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesPresetsController.java index f81124eeeb7f..aa95fd038260 100644 --- a/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesPresetsController.java +++ b/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesPresetsController.java @@ -113,7 +113,7 @@ public class HearingDevicesPresetsController implements @Override public void onPresetSelectionForGroupFailed(int hapGroupId, int reason) { - if (mActiveHearingDevice == null) { + if (mActiveHearingDevice == null || mHapClientProfile == null) { return; } if (hapGroupId == mHapClientProfile.getHapGroup(mActiveHearingDevice.getDevice())) { @@ -137,7 +137,7 @@ public class HearingDevicesPresetsController implements @Override public void onSetPresetNameForGroupFailed(int hapGroupId, int reason) { - if (mActiveHearingDevice == null) { + if (mActiveHearingDevice == null || mHapClientProfile == null) { return; } if (hapGroupId == mHapClientProfile.getHapGroup(mActiveHearingDevice.getDevice())) { @@ -177,22 +177,33 @@ public class HearingDevicesPresetsController implements } /** - * Sets the hearing device for this controller to control the preset. + * Sets the hearing device for this controller to control the preset if it supports + * {@link HapClientProfile}. * * @param activeHearingDevice the {@link CachedBluetoothDevice} need to be hearing aid device + * and support {@link HapClientProfile}. */ - public void setActiveHearingDevice(CachedBluetoothDevice activeHearingDevice) { - mActiveHearingDevice = activeHearingDevice; + public void setHearingDeviceIfSupportHap(CachedBluetoothDevice activeHearingDevice) { + if (mHapClientProfile == null || activeHearingDevice == null) { + mActiveHearingDevice = null; + return; + } + if (activeHearingDevice.getProfiles().stream().anyMatch( + profile -> profile instanceof HapClientProfile)) { + mActiveHearingDevice = activeHearingDevice; + } else { + mActiveHearingDevice = null; + } } /** * Selects the currently active preset for {@code mActiveHearingDevice} individual device or - * the device group accoridng to whether it supports synchronized presets or not. + * the device group according to whether it supports synchronized presets or not. * * @param presetIndex an index of one of the available presets */ public void selectPreset(int presetIndex) { - if (mActiveHearingDevice == null) { + if (mActiveHearingDevice == null || mHapClientProfile == null) { return; } mSelectedPresetIndex = presetIndex; @@ -217,7 +228,7 @@ public class HearingDevicesPresetsController implements * @return a list of all known preset info */ public List<BluetoothHapPresetInfo> getAllPresetInfo() { - if (mActiveHearingDevice == null) { + if (mActiveHearingDevice == null || mHapClientProfile == null) { return emptyList(); } return mHapClientProfile.getAllPresetInfo(mActiveHearingDevice.getDevice()).stream().filter( @@ -230,14 +241,14 @@ public class HearingDevicesPresetsController implements * @return active preset index */ public int getActivePresetIndex() { - if (mActiveHearingDevice == null) { + if (mActiveHearingDevice == null || mHapClientProfile == null) { return BluetoothHapClient.PRESET_INDEX_UNAVAILABLE; } return mHapClientProfile.getActivePresetIndex(mActiveHearingDevice.getDevice()); } private void selectPresetSynchronously(int groupId, int presetIndex) { - if (mActiveHearingDevice == null) { + if (mActiveHearingDevice == null || mHapClientProfile == null) { return; } if (DEBUG) { @@ -250,7 +261,7 @@ public class HearingDevicesPresetsController implements } private void selectPresetIndependently(int presetIndex) { - if (mActiveHearingDevice == null) { + if (mActiveHearingDevice == null || mHapClientProfile == null) { return; } if (DEBUG) { diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesPresetsControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesPresetsControllerTest.java new file mode 100644 index 000000000000..2ac5d105ba99 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesPresetsControllerTest.java @@ -0,0 +1,285 @@ +/* + * 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.accessibility.hearingaid; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyList; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import static java.util.Collections.emptyList; + +import android.bluetooth.BluetoothCsipSetCoordinator; +import android.bluetooth.BluetoothDevice; +import android.bluetooth.BluetoothHapClient; +import android.bluetooth.BluetoothHapPresetInfo; +import android.testing.TestableLooper; + +import androidx.test.ext.junit.runners.AndroidJUnit4; +import androidx.test.filters.SmallTest; + +import com.android.settingslib.bluetooth.CachedBluetoothDevice; +import com.android.settingslib.bluetooth.HapClientProfile; +import com.android.settingslib.bluetooth.LocalBluetoothProfile; +import com.android.settingslib.bluetooth.LocalBluetoothProfileManager; +import com.android.systemui.SysuiTestCase; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; + +import java.util.List; +import java.util.concurrent.Executor; + +/** Tests for {@link HearingDevicesPresetsController}. */ +@RunWith(AndroidJUnit4.class) +@TestableLooper.RunWithLooper(setAsMainLooper = true) +@SmallTest +public class HearingDevicesPresetsControllerTest extends SysuiTestCase { + + private static final int TEST_PRESET_INDEX = 1; + private static final String TEST_PRESET_NAME = "test_preset"; + private static final int TEST_HAP_GROUP_ID = 1; + private static final int TEST_REASON = 1024; + + @Rule + public MockitoRule mockito = MockitoJUnit.rule(); + + @Mock + private LocalBluetoothProfileManager mProfileManager; + @Mock + private HapClientProfile mHapClientProfile; + @Mock + private CachedBluetoothDevice mCachedBluetoothDevice; + @Mock + private CachedBluetoothDevice mSubCachedBluetoothDevice; + @Mock + private BluetoothDevice mBluetoothDevice; + @Mock + private BluetoothDevice mSubBluetoothDevice; + + @Mock + private HearingDevicesPresetsController.PresetCallback mCallback; + + private HearingDevicesPresetsController mController; + + @Before + public void setUp() { + when(mProfileManager.getHapClientProfile()).thenReturn(mHapClientProfile); + when(mHapClientProfile.isProfileReady()).thenReturn(true); + when(mCachedBluetoothDevice.getDevice()).thenReturn(mBluetoothDevice); + when(mCachedBluetoothDevice.getSubDevice()).thenReturn(mSubCachedBluetoothDevice); + when(mSubCachedBluetoothDevice.getDevice()).thenReturn(mSubBluetoothDevice); + + mController = new HearingDevicesPresetsController(mProfileManager, mCallback); + } + + @Test + public void onServiceConnected_callExpectedCallback() { + mController.onServiceConnected(); + + verify(mHapClientProfile).registerCallback(any(Executor.class), + any(BluetoothHapClient.Callback.class)); + verify(mCallback).onPresetInfoUpdated(anyList(), anyInt()); + } + + @Test + public void getAllPresetInfo_setInvalidHearingDevice_getEmpty() { + when(mCachedBluetoothDevice.getProfiles()).thenReturn(emptyList()); + mController.setHearingDeviceIfSupportHap(mCachedBluetoothDevice); + BluetoothHapPresetInfo hapPresetInfo = getHapPresetInfo(true); + when(mHapClientProfile.getAllPresetInfo(mBluetoothDevice)).thenReturn( + List.of(hapPresetInfo)); + + assertThat(mController.getAllPresetInfo()).isEmpty(); + } + + @Test + public void getAllPresetInfo_containsNotAvailablePresetInfo_getEmpty() { + setValidHearingDeviceSupportHap(); + BluetoothHapPresetInfo hapPresetInfo = getHapPresetInfo(false); + when(mHapClientProfile.getAllPresetInfo(mBluetoothDevice)).thenReturn( + List.of(hapPresetInfo)); + + assertThat(mController.getAllPresetInfo()).isEmpty(); + } + + @Test + public void getAllPresetInfo_containsOnePresetInfo_getOnePresetInfo() { + setValidHearingDeviceSupportHap(); + BluetoothHapPresetInfo hapPresetInfo = getHapPresetInfo(true); + when(mHapClientProfile.getAllPresetInfo(mBluetoothDevice)).thenReturn( + List.of(hapPresetInfo)); + + assertThat(mController.getAllPresetInfo()).contains(hapPresetInfo); + } + + @Test + public void getActivePresetIndex_getExpectedIndex() { + setValidHearingDeviceSupportHap(); + when(mHapClientProfile.getActivePresetIndex(mBluetoothDevice)).thenReturn( + TEST_PRESET_INDEX); + + assertThat(mController.getActivePresetIndex()).isEqualTo(TEST_PRESET_INDEX); + } + + @Test + public void onPresetSelected_presetIndex_callOnPresetInfoUpdatedWithExpectedPresetIndex() { + setValidHearingDeviceSupportHap(); + BluetoothHapPresetInfo hapPresetInfo = getHapPresetInfo(true); + when(mHapClientProfile.getAllPresetInfo(mBluetoothDevice)).thenReturn( + List.of(hapPresetInfo)); + when(mHapClientProfile.getActivePresetIndex(mBluetoothDevice)).thenReturn( + TEST_PRESET_INDEX); + + mController.onPresetSelected(mBluetoothDevice, TEST_PRESET_INDEX, TEST_REASON); + + verify(mCallback).onPresetInfoUpdated(eq(List.of(hapPresetInfo)), eq(TEST_PRESET_INDEX)); + } + + @Test + public void onPresetInfoChanged_presetIndex_callOnPresetInfoUpdatedWithExpectedPresetIndex() { + setValidHearingDeviceSupportHap(); + BluetoothHapPresetInfo hapPresetInfo = getHapPresetInfo(true); + when(mHapClientProfile.getAllPresetInfo(mBluetoothDevice)).thenReturn( + List.of(hapPresetInfo)); + when(mHapClientProfile.getActivePresetIndex(mBluetoothDevice)).thenReturn( + TEST_PRESET_INDEX); + + mController.onPresetInfoChanged(mBluetoothDevice, List.of(hapPresetInfo), TEST_REASON); + + verify(mCallback).onPresetInfoUpdated(List.of(hapPresetInfo), TEST_PRESET_INDEX); + } + + @Test + public void onPresetSelectionFailed_callOnPresetCommandFailed() { + setValidHearingDeviceSupportHap(); + + mController.onPresetSelectionFailed(mBluetoothDevice, TEST_REASON); + + verify(mCallback).onPresetCommandFailed(TEST_REASON); + } + + @Test + public void onSetPresetNameFailed_callOnPresetCommandFailed() { + setValidHearingDeviceSupportHap(); + + mController.onSetPresetNameFailed(mBluetoothDevice, TEST_REASON); + + verify(mCallback).onPresetCommandFailed(TEST_REASON); + } + + @Test + public void onPresetSelectionForGroupFailed_callSelectPresetIndividual() { + setValidHearingDeviceSupportHap(); + mController.selectPreset(TEST_PRESET_INDEX); + Mockito.reset(mHapClientProfile); + when(mHapClientProfile.getHapGroup(mBluetoothDevice)).thenReturn(TEST_HAP_GROUP_ID); + + mController.onPresetSelectionForGroupFailed(TEST_HAP_GROUP_ID, TEST_REASON); + + + verify(mHapClientProfile).selectPreset(mBluetoothDevice, TEST_PRESET_INDEX); + verify(mHapClientProfile).selectPreset(mSubBluetoothDevice, TEST_PRESET_INDEX); + } + + @Test + public void onSetPresetNameForGroupFailed_callOnPresetCommandFailed() { + setValidHearingDeviceSupportHap(); + + mController.onSetPresetNameForGroupFailed(TEST_HAP_GROUP_ID, TEST_REASON); + + verify(mCallback).onPresetCommandFailed(TEST_REASON); + } + + @Test + public void registerHapCallback_callHapRegisterCallback() { + mController.registerHapCallback(); + + verify(mHapClientProfile).registerCallback(any(Executor.class), + any(BluetoothHapClient.Callback.class)); + } + + @Test + public void unregisterHapCallback_callHapUnregisterCallback() { + mController.unregisterHapCallback(); + + verify(mHapClientProfile).unregisterCallback(any(BluetoothHapClient.Callback.class)); + } + + @Test + public void selectPreset_supportSynchronized_validGroupId_callSelectPresetForGroup() { + setValidHearingDeviceSupportHap(); + when(mHapClientProfile.supportsSynchronizedPresets(mBluetoothDevice)).thenReturn(true); + when(mHapClientProfile.getHapGroup(mBluetoothDevice)).thenReturn(TEST_HAP_GROUP_ID); + + mController.selectPreset(TEST_PRESET_INDEX); + + verify(mHapClientProfile).selectPresetForGroup(TEST_HAP_GROUP_ID, TEST_PRESET_INDEX); + } + + @Test + public void selectPreset_supportSynchronized_invalidGroupId_callSelectPresetIndividual() { + setValidHearingDeviceSupportHap(); + when(mHapClientProfile.supportsSynchronizedPresets(mBluetoothDevice)).thenReturn(true); + when(mHapClientProfile.getHapGroup(mBluetoothDevice)).thenReturn( + BluetoothCsipSetCoordinator.GROUP_ID_INVALID); + + mController.selectPreset(TEST_PRESET_INDEX); + + verify(mHapClientProfile).selectPreset(mBluetoothDevice, TEST_PRESET_INDEX); + verify(mHapClientProfile).selectPreset(mSubBluetoothDevice, TEST_PRESET_INDEX); + } + + @Test + public void selectPreset_notSupportSynchronized_validGroupId_callSelectPresetIndividual() { + setValidHearingDeviceSupportHap(); + when(mHapClientProfile.supportsSynchronizedPresets(mBluetoothDevice)).thenReturn(false); + when(mHapClientProfile.getHapGroup(mBluetoothDevice)).thenReturn(TEST_HAP_GROUP_ID); + + mController.selectPreset(TEST_PRESET_INDEX); + + verify(mHapClientProfile).selectPreset(mBluetoothDevice, TEST_PRESET_INDEX); + verify(mHapClientProfile).selectPreset(mSubBluetoothDevice, TEST_PRESET_INDEX); + } + + private BluetoothHapPresetInfo getHapPresetInfo(boolean available) { + BluetoothHapPresetInfo info = mock(BluetoothHapPresetInfo.class); + when(info.getName()).thenReturn(TEST_PRESET_NAME); + when(info.getIndex()).thenReturn(TEST_PRESET_INDEX); + when(info.isAvailable()).thenReturn(available); + return info; + } + + private void setValidHearingDeviceSupportHap() { + LocalBluetoothProfile hapClientProfile = mock(HapClientProfile.class); + List<LocalBluetoothProfile> profiles = List.of(hapClientProfile); + when(mCachedBluetoothDevice.getProfiles()).thenReturn(profiles); + + mController.setHearingDeviceIfSupportHap(mCachedBluetoothDevice); + } +} |