Selects presets in device details page (2/2)
Updates UI and sets preset to remote device when corresponding callback is called. Bug: 300015207 Test: atest BluetoothDetailsHearingAidsPresetsControllerTest Change-Id: Ic013b96acaa6161b861fbae32ddfd77387f9bc47
This commit is contained in:
@@ -156,6 +156,8 @@
|
||||
<string name="bluetooth_hearing_device_settings_summary">Shortcut, hearing aid compatibility</string>
|
||||
<!-- Connected devices settings. Title for hearing aids presets. A preset is a set of hearing aid settings. User can apply different settings in different environments (e.g. Outdoor, Restaurant, Home) [CHAR LIMIT=60] -->
|
||||
<string name="bluetooth_hearing_aids_presets">Presets</string>
|
||||
<!-- Message when selecting hearing aids presets failed. [CHAR LIMIT=NONE] -->
|
||||
<string name="bluetooth_hearing_aids_presets_error">Couldn\u2019t update preset</string>
|
||||
<!-- Connected devices settings. Title of the preference to show the entrance of the audio output page. It can change different types of audio are played on phone or other bluetooth devices. [CHAR LIMIT=35] -->
|
||||
<string name="bluetooth_audio_routing_title">Audio output</string>
|
||||
<!-- Title for bluetooth audio routing page footer. [CHAR LIMIT=30] -->
|
||||
|
@@ -19,15 +19,18 @@ package com.android.settings.bluetooth;
|
||||
import static com.android.settings.bluetooth.BluetoothDetailsHearingDeviceController.KEY_HEARING_DEVICE_GROUP;
|
||||
import static com.android.settings.bluetooth.BluetoothDetailsHearingDeviceController.ORDER_HEARING_AIDS_PRESETS;
|
||||
|
||||
import android.bluetooth.BluetoothCsipSetCoordinator;
|
||||
import android.bluetooth.BluetoothDevice;
|
||||
import android.bluetooth.BluetoothHapClient;
|
||||
import android.bluetooth.BluetoothHapPresetInfo;
|
||||
import android.content.Context;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.VisibleForTesting;
|
||||
import androidx.preference.ListPreference;
|
||||
import androidx.preference.Preference;
|
||||
import androidx.preference.PreferenceCategory;
|
||||
@@ -88,9 +91,54 @@ public class BluetoothDetailsHearingAidsPresetsController extends
|
||||
@Override
|
||||
public boolean onPreferenceChange(@NonNull Preference preference, @Nullable Object newValue) {
|
||||
if (TextUtils.equals(preference.getKey(), getPreferenceKey())) {
|
||||
// TODO(b/300015207): Update the settings to remote device
|
||||
if (newValue instanceof final String value
|
||||
&& preference instanceof final ListPreference listPreference) {
|
||||
final int index = listPreference.findIndexOfValue(value);
|
||||
final String presetName = listPreference.getEntries()[index].toString();
|
||||
final int presetIndex = Integer.parseInt(
|
||||
listPreference.getEntryValues()[index].toString());
|
||||
listPreference.setSummary(presetName);
|
||||
boolean supportSynchronizedPresets = mHapClientProfile.supportsSynchronizedPresets(
|
||||
mCachedDevice.getDevice());
|
||||
int hapGroupId = mHapClientProfile.getHapGroup(mCachedDevice.getDevice());
|
||||
if (supportSynchronizedPresets
|
||||
&& hapGroupId != BluetoothCsipSetCoordinator.GROUP_ID_INVALID) {
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "onPreferenceChange, selectPresetForGroup "
|
||||
+ ", presetName: " + presetName
|
||||
+ ", presetIndex: " + presetIndex
|
||||
+ ", hapGroupId: " + hapGroupId
|
||||
+ ", device: " + mCachedDevice.getAddress());
|
||||
}
|
||||
mHapClientProfile.selectPresetForGroup(hapGroupId, presetIndex);
|
||||
} else {
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "onPreferenceChange, selectPreset "
|
||||
+ ", presetName: " + presetName
|
||||
+ ", presetIndex: " + presetIndex
|
||||
+ ", device: " + mCachedDevice.getAddress());
|
||||
}
|
||||
mHapClientProfile.selectPreset(mCachedDevice.getDevice(), presetIndex);
|
||||
final CachedBluetoothDevice subDevice = mCachedDevice.getSubDevice();
|
||||
if (subDevice != null) {
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "onPreferenceChange, selectPreset for subDevice"
|
||||
+ ", device: " + subDevice.getAddress());
|
||||
}
|
||||
mHapClientProfile.selectPreset(subDevice.getDevice(), presetIndex);
|
||||
}
|
||||
for (final CachedBluetoothDevice memberDevice :
|
||||
mCachedDevice.getMemberDevice()) {
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "onPreferenceChange, selectPreset for memberDevice"
|
||||
+ ", device: " + memberDevice.getAddress());
|
||||
}
|
||||
mHapClientProfile.selectPreset(memberDevice.getDevice(), presetIndex);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -115,13 +163,30 @@ public class BluetoothDetailsHearingAidsPresetsController extends
|
||||
return;
|
||||
}
|
||||
mPreference.setEnabled(mCachedDevice.isConnectedHapClientDevice());
|
||||
// TODO(b/300015207): Load preset from remote and show in UI
|
||||
|
||||
loadAllPresetInfo();
|
||||
if (mPreference.getEntries().length == 0) {
|
||||
mPreference.setEnabled(false);
|
||||
} else {
|
||||
int activePresetIndex = mHapClientProfile.getActivePresetIndex(
|
||||
mCachedDevice.getDevice());
|
||||
if (activePresetIndex != BluetoothHapClient.PRESET_INDEX_UNAVAILABLE) {
|
||||
mPreference.setValue(Integer.toString(activePresetIndex));
|
||||
mPreference.setSummary(mPreference.getEntry());
|
||||
} else {
|
||||
mPreference.setSummary(null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAvailable() {
|
||||
if (mHapClientProfile == null) {
|
||||
return false;
|
||||
}
|
||||
return mCachedDevice.getProfiles().stream().anyMatch(
|
||||
profile -> profile instanceof HapClientProfile);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPresetSelected(@NonNull BluetoothDevice device, int presetIndex, int reason) {
|
||||
@@ -130,7 +195,7 @@ public class BluetoothDetailsHearingAidsPresetsController extends
|
||||
Log.d(TAG, "onPresetSelected, device: " + device.getAddress()
|
||||
+ ", presetIndex: " + presetIndex + ", reason: " + reason);
|
||||
}
|
||||
// TODO(b/300015207): Update the UI
|
||||
mContext.getMainExecutor().execute(this::refresh);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -142,7 +207,10 @@ public class BluetoothDetailsHearingAidsPresetsController extends
|
||||
"onPresetSelectionFailed, device: " + device.getAddress()
|
||||
+ ", reason: " + reason);
|
||||
}
|
||||
// TODO(b/300015207): Update the UI
|
||||
mContext.getMainExecutor().execute(() -> {
|
||||
refresh();
|
||||
showErrorToast();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -153,7 +221,10 @@ public class BluetoothDetailsHearingAidsPresetsController extends
|
||||
Log.d(TAG, "onPresetSelectionForGroupFailed, group: " + hapGroupId
|
||||
+ ", reason: " + reason);
|
||||
}
|
||||
// TODO(b/300015207): Update the UI
|
||||
mContext.getMainExecutor().execute(() -> {
|
||||
refresh();
|
||||
showErrorToast();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -166,7 +237,7 @@ public class BluetoothDetailsHearingAidsPresetsController extends
|
||||
+ ", reason: " + reason
|
||||
+ ", infoList: " + presetInfoList);
|
||||
}
|
||||
// TODO(b/300015207): Update the UI
|
||||
mContext.getMainExecutor().execute(this::refresh);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -178,7 +249,10 @@ public class BluetoothDetailsHearingAidsPresetsController extends
|
||||
"onSetPresetNameFailed, device: " + device.getAddress()
|
||||
+ ", reason: " + reason);
|
||||
}
|
||||
// TODO(b/300015207): Update the UI
|
||||
mContext.getMainExecutor().execute(() -> {
|
||||
refresh();
|
||||
showErrorToast();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -189,7 +263,10 @@ public class BluetoothDetailsHearingAidsPresetsController extends
|
||||
Log.d(TAG, "onSetPresetNameForGroupFailed, group: " + hapGroupId
|
||||
+ ", reason: " + reason);
|
||||
}
|
||||
// TODO(b/300015207): Update the UI
|
||||
mContext.getMainExecutor().execute(() -> {
|
||||
refresh();
|
||||
showErrorToast();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -201,4 +278,31 @@ public class BluetoothDetailsHearingAidsPresetsController extends
|
||||
preference.setOnPreferenceChangeListener(this);
|
||||
return preference;
|
||||
}
|
||||
|
||||
private void loadAllPresetInfo() {
|
||||
if (mPreference == null) {
|
||||
return;
|
||||
}
|
||||
List<BluetoothHapPresetInfo> infoList = mHapClientProfile.getAllPresetInfo(
|
||||
mCachedDevice.getDevice());
|
||||
CharSequence[] presetNames = new CharSequence[infoList.size()];
|
||||
CharSequence[] presetIndexes = new CharSequence[infoList.size()];
|
||||
for (int i = 0; i < infoList.size(); i++) {
|
||||
presetNames[i] = infoList.get(i).getName();
|
||||
presetIndexes[i] = Integer.toString(infoList.get(i).getIndex());
|
||||
}
|
||||
mPreference.setEntries(presetNames);
|
||||
mPreference.setEntryValues(presetIndexes);
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
@Nullable
|
||||
ListPreference getPreference() {
|
||||
return mPreference;
|
||||
}
|
||||
|
||||
void showErrorToast() {
|
||||
Toast.makeText(mContext, R.string.bluetooth_hearing_aids_presets_error,
|
||||
Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
}
|
||||
|
@@ -16,21 +16,29 @@
|
||||
|
||||
package com.android.settings.bluetooth;
|
||||
|
||||
import static android.bluetooth.BluetoothCsipSetCoordinator.GROUP_ID_INVALID;
|
||||
import static android.bluetooth.BluetoothHapClient.PRESET_INDEX_UNAVAILABLE;
|
||||
|
||||
import static com.android.settings.bluetooth.BluetoothDetailsHearingDeviceController.KEY_HEARING_DEVICE_GROUP;
|
||||
import static com.android.settings.bluetooth.BluetoothDetailsHearingAidsPresetsController.KEY_HEARING_AIDS_PRESETS;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.bluetooth.BluetoothDevice;
|
||||
import android.bluetooth.BluetoothHapClient;
|
||||
import android.bluetooth.BluetoothHapPresetInfo;
|
||||
|
||||
import androidx.preference.ListPreference;
|
||||
import androidx.preference.PreferenceCategory;
|
||||
|
||||
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
|
||||
import com.android.settingslib.bluetooth.HapClientProfile;
|
||||
import com.android.settingslib.bluetooth.LocalBluetoothManager;
|
||||
import com.android.settingslib.bluetooth.LocalBluetoothProfileManager;
|
||||
@@ -43,7 +51,9 @@ import org.mockito.junit.MockitoJUnit;
|
||||
import org.mockito.junit.MockitoRule;
|
||||
import org.robolectric.RobolectricTestRunner;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
/** Tests for {@link BluetoothDetailsHearingAidsPresetsController}. */
|
||||
@@ -53,6 +63,7 @@ public class BluetoothDetailsHearingAidsPresetsControllerTest extends
|
||||
|
||||
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;
|
||||
|
||||
@Rule
|
||||
public final MockitoRule mockito = MockitoJUnit.rule();
|
||||
@@ -63,6 +74,10 @@ public class BluetoothDetailsHearingAidsPresetsControllerTest extends
|
||||
private LocalBluetoothProfileManager mProfileManager;
|
||||
@Mock
|
||||
private HapClientProfile mHapClientProfile;
|
||||
@Mock
|
||||
private CachedBluetoothDevice mCachedChildDevice;
|
||||
@Mock
|
||||
private BluetoothDevice mChildDevice;
|
||||
|
||||
private BluetoothDetailsHearingAidsPresetsController mController;
|
||||
|
||||
@@ -73,6 +88,8 @@ public class BluetoothDetailsHearingAidsPresetsControllerTest extends
|
||||
when(mLocalManager.getProfileManager()).thenReturn(mProfileManager);
|
||||
when(mProfileManager.getHapClientProfile()).thenReturn(mHapClientProfile);
|
||||
when(mCachedDevice.getProfiles()).thenReturn(List.of(mHapClientProfile));
|
||||
when(mCachedDevice.isConnectedHapClientDevice()).thenReturn(true);
|
||||
when(mCachedChildDevice.getDevice()).thenReturn(mChildDevice);
|
||||
PreferenceCategory deviceControls = new PreferenceCategory(mContext);
|
||||
deviceControls.setKey(KEY_HEARING_DEVICE_GROUP);
|
||||
mScreen.addPreference(deviceControls);
|
||||
@@ -81,6 +98,20 @@ public class BluetoothDetailsHearingAidsPresetsControllerTest extends
|
||||
mController.init(mScreen);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isAvailable_supportHap_returnTrue() {
|
||||
when(mCachedDevice.getProfiles()).thenReturn(List.of(mHapClientProfile));
|
||||
|
||||
assertThat(mController.isAvailable()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isAvailable_notSupportHap_returnFalse() {
|
||||
when(mCachedDevice.getProfiles()).thenReturn(new ArrayList<>());
|
||||
|
||||
assertThat(mController.isAvailable()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onResume_registerCallback() {
|
||||
mController.onResume();
|
||||
@@ -96,7 +127,6 @@ public class BluetoothDetailsHearingAidsPresetsControllerTest extends
|
||||
verify(mHapClientProfile).unregisterCallback(any(BluetoothHapClient.Callback.class));
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void onPreferenceChange_keyMatched_verifyStatusUpdated() {
|
||||
final ListPreference presetPreference = getTestPresetPreference(KEY_HEARING_AIDS_PRESETS);
|
||||
@@ -105,6 +135,7 @@ public class BluetoothDetailsHearingAidsPresetsControllerTest extends
|
||||
String.valueOf(TEST_PRESET_INDEX));
|
||||
|
||||
assertThat(handled).isTrue();
|
||||
verify(presetPreference).setSummary(TEST_PRESET_NAME);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -115,6 +146,116 @@ public class BluetoothDetailsHearingAidsPresetsControllerTest extends
|
||||
presetPreference, String.valueOf(TEST_PRESET_INDEX));
|
||||
|
||||
assertThat(handled).isFalse();
|
||||
verify(presetPreference, never()).setSummary(any());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onPreferenceChange_supportGroupOperation_validGroupId_verifySelectPresetForGroup() {
|
||||
final ListPreference presetPreference = getTestPresetPreference(KEY_HEARING_AIDS_PRESETS);
|
||||
when(mHapClientProfile.supportsSynchronizedPresets(mDevice)).thenReturn(true);
|
||||
when(mHapClientProfile.getHapGroup(mDevice)).thenReturn(TEST_HAP_GROUP_ID);
|
||||
|
||||
mController.onPreferenceChange(presetPreference, String.valueOf(TEST_PRESET_INDEX));
|
||||
|
||||
verify(mHapClientProfile).selectPresetForGroup(TEST_HAP_GROUP_ID, TEST_PRESET_INDEX);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onPreferenceChange_notSupportGroupOperation_verifySelectPreset() {
|
||||
final ListPreference presetPreference = getTestPresetPreference(KEY_HEARING_AIDS_PRESETS);
|
||||
when(mHapClientProfile.supportsSynchronizedPresets(mDevice)).thenReturn(false);
|
||||
when(mHapClientProfile.getHapGroup(mDevice)).thenReturn(TEST_HAP_GROUP_ID);
|
||||
|
||||
mController.onPreferenceChange(presetPreference, String.valueOf(TEST_PRESET_INDEX));
|
||||
|
||||
verify(mHapClientProfile).selectPreset(mDevice, TEST_PRESET_INDEX);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onPreferenceChange_invalidGroupId_verifySelectPreset() {
|
||||
final ListPreference presetPreference = getTestPresetPreference(KEY_HEARING_AIDS_PRESETS);
|
||||
when(mHapClientProfile.supportsSynchronizedPresets(mDevice)).thenReturn(true);
|
||||
when(mHapClientProfile.getHapGroup(mDevice)).thenReturn(GROUP_ID_INVALID);
|
||||
|
||||
mController.onPreferenceChange(presetPreference, String.valueOf(TEST_PRESET_INDEX));
|
||||
|
||||
verify(mHapClientProfile).selectPreset(mDevice, TEST_PRESET_INDEX);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onPreferenceChange_notSupportGroupOperation_hasSubDevice_verifyStatusUpdated() {
|
||||
final ListPreference presetPreference = getTestPresetPreference(KEY_HEARING_AIDS_PRESETS);
|
||||
when(mHapClientProfile.supportsSynchronizedPresets(mDevice)).thenReturn(false);
|
||||
when(mCachedDevice.getSubDevice()).thenReturn(mCachedChildDevice);
|
||||
|
||||
mController.onPreferenceChange(presetPreference, String.valueOf(TEST_PRESET_INDEX));
|
||||
|
||||
verify(mHapClientProfile).selectPreset(mDevice, TEST_PRESET_INDEX);
|
||||
verify(mHapClientProfile).selectPreset(mChildDevice, TEST_PRESET_INDEX);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onPreferenceChange_notSupportGroupOperation_hasMemberDevice_verifyStatusUpdated() {
|
||||
final ListPreference presetPreference = getTestPresetPreference(KEY_HEARING_AIDS_PRESETS);
|
||||
when(mHapClientProfile.supportsSynchronizedPresets(mDevice)).thenReturn(false);
|
||||
when(mCachedDevice.getMemberDevice()).thenReturn(Set.of(mCachedChildDevice));
|
||||
|
||||
mController.onPreferenceChange(presetPreference, String.valueOf(TEST_PRESET_INDEX));
|
||||
|
||||
verify(mHapClientProfile).selectPreset(mDevice, TEST_PRESET_INDEX);
|
||||
verify(mHapClientProfile).selectPreset(mChildDevice, TEST_PRESET_INDEX);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void refresh_emptyPresetInfo_preferenceDisabled() {
|
||||
when(mHapClientProfile.getAllPresetInfo(mDevice)).thenReturn(new ArrayList<>());
|
||||
|
||||
mController.refresh();
|
||||
|
||||
assertThat(mController.getPreference()).isNotNull();
|
||||
assertThat(mController.getPreference().isEnabled()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void refresh_validPresetInfo_preferenceEnabled() {
|
||||
BluetoothHapPresetInfo info = getTestPresetInfo();
|
||||
when(mHapClientProfile.getAllPresetInfo(mDevice)).thenReturn(List.of(info));
|
||||
|
||||
mController.refresh();
|
||||
|
||||
assertThat(mController.getPreference()).isNotNull();
|
||||
assertThat(mController.getPreference().isEnabled()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void refresh_invalidActivePresetIndex_summaryIsNull() {
|
||||
BluetoothHapPresetInfo info = getTestPresetInfo();
|
||||
when(mHapClientProfile.getAllPresetInfo(mDevice)).thenReturn(List.of(info));
|
||||
when(mHapClientProfile.getActivePresetIndex(mDevice)).thenReturn(PRESET_INDEX_UNAVAILABLE);
|
||||
|
||||
mController.refresh();
|
||||
|
||||
assertThat(mController.getPreference()).isNotNull();
|
||||
assertThat(mController.getPreference().getSummary()).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void refresh_validActivePresetIndex_summaryIsNotNull() {
|
||||
BluetoothHapPresetInfo info = getTestPresetInfo();
|
||||
when(mHapClientProfile.getAllPresetInfo(mDevice)).thenReturn(List.of(info));
|
||||
when(mHapClientProfile.getActivePresetIndex(mDevice)).thenReturn(TEST_PRESET_INDEX);
|
||||
|
||||
mController.refresh();
|
||||
|
||||
assertThat(mController.getPreference()).isNotNull();
|
||||
assertThat(mController.getPreference().getSummary()).isNotNull();
|
||||
}
|
||||
|
||||
private BluetoothHapPresetInfo getTestPresetInfo() {
|
||||
BluetoothHapPresetInfo info = mock(BluetoothHapPresetInfo.class);
|
||||
when(info.getName()).thenReturn(TEST_PRESET_NAME);
|
||||
when(info.getIndex()).thenReturn(TEST_PRESET_INDEX);
|
||||
return info;
|
||||
}
|
||||
|
||||
private ListPreference getTestPresetPreference(String key) {
|
||||
|
Reference in New Issue
Block a user