diff options
7 files changed, 179 insertions, 6 deletions
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java index de60fdc2b47e..b3ac54a9f7bc 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java @@ -21,6 +21,8 @@ import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; +import android.media.AudioDeviceAttributes; +import android.media.AudioDeviceInfo; import android.media.AudioManager; import android.net.Uri; import android.provider.DeviceConfig; @@ -41,9 +43,12 @@ import com.android.settingslib.flags.Flags; import com.android.settingslib.widget.AdaptiveIcon; import com.android.settingslib.widget.AdaptiveOutlineDrawable; +import com.google.common.collect.ImmutableSet; + import java.io.IOException; import java.util.List; import java.util.Locale; +import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -57,6 +62,9 @@ public class BluetoothUtils { public static final String BT_ADVANCED_HEADER_ENABLED = "bt_advanced_header_enabled"; private static final int METADATA_FAST_PAIR_CUSTOMIZED_FIELDS = 25; private static final String KEY_HEARABLE_CONTROL_SLICE = "HEARABLE_CONTROL_SLICE_WITH_WIDTH"; + private static final Set<Integer> SA_PROFILES = + ImmutableSet.of( + BluetoothProfile.A2DP, BluetoothProfile.LE_AUDIO, BluetoothProfile.HEARING_AID); private static ErrorListener sErrorListener; @@ -895,4 +903,62 @@ public class BluetoothUtils { } return null; } + + /** + * Gets {@link AudioDeviceAttributes} of bluetooth device for spatial audio. Returns null if + * it's not an audio device(no A2DP, LE Audio and Hearing Aid profile). + */ + @Nullable + public static AudioDeviceAttributes getAudioDeviceAttributesForSpatialAudio( + CachedBluetoothDevice cachedDevice, + @AudioManager.AudioDeviceCategory int audioDeviceCategory) { + AudioDeviceAttributes saDevice = null; + for (LocalBluetoothProfile profile : cachedDevice.getProfiles()) { + // pick first enabled profile that is compatible with spatial audio + if (SA_PROFILES.contains(profile.getProfileId()) + && profile.isEnabled(cachedDevice.getDevice())) { + switch (profile.getProfileId()) { + case BluetoothProfile.A2DP: + saDevice = + new AudioDeviceAttributes( + AudioDeviceAttributes.ROLE_OUTPUT, + AudioDeviceInfo.TYPE_BLUETOOTH_A2DP, + cachedDevice.getAddress()); + break; + case BluetoothProfile.LE_AUDIO: + if (audioDeviceCategory + == AudioManager.AUDIO_DEVICE_CATEGORY_SPEAKER) { + saDevice = + new AudioDeviceAttributes( + AudioDeviceAttributes.ROLE_OUTPUT, + AudioDeviceInfo.TYPE_BLE_SPEAKER, + cachedDevice.getAddress()); + } else { + saDevice = + new AudioDeviceAttributes( + AudioDeviceAttributes.ROLE_OUTPUT, + AudioDeviceInfo.TYPE_BLE_HEADSET, + cachedDevice.getAddress()); + } + + break; + case BluetoothProfile.HEARING_AID: + saDevice = + new AudioDeviceAttributes( + AudioDeviceAttributes.ROLE_OUTPUT, + AudioDeviceInfo.TYPE_HEARING_AID, + cachedDevice.getAddress()); + break; + default: + Log.i( + TAG, + "unrecognized profile for spatial audio: " + + profile.getProfileId()); + break; + } + break; + } + } + return saDevice; + } } diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/DeviceSettingId.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/DeviceSettingId.java index 20a0339b9182..58dc8c7aad6c 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/DeviceSettingId.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/DeviceSettingId.java @@ -108,6 +108,12 @@ public @interface DeviceSettingId { /** Device setting ID for device details footer. */ int DEVICE_SETTING_ID_DEVICE_DETAILS_FOOTER = 19; + /** Device setting ID for spatial audio group. */ + int DEVICE_SETTING_ID_SPATIAL_AUDIO_MULTI_TOGGLE = 20; + + /** Device setting ID for "More Settings" page. */ + int DEVICE_SETTING_ID_MORE_SETTINGS = 21; + /** Device setting ID for ANC. */ int DEVICE_SETTING_ID_ANC = 1001; } diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/data/repository/DeviceSettingRepository.kt b/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/data/repository/DeviceSettingRepository.kt index a599dd1cc5d8..ce7064c9d781 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/data/repository/DeviceSettingRepository.kt +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/data/repository/DeviceSettingRepository.kt @@ -29,6 +29,7 @@ import com.android.settingslib.bluetooth.devicesettings.MultiTogglePreference import com.android.settingslib.bluetooth.devicesettings.ToggleInfo import com.android.settingslib.bluetooth.devicesettings.shared.model.DeviceSettingConfigItemModel import com.android.settingslib.bluetooth.devicesettings.shared.model.DeviceSettingConfigModel +import com.android.settingslib.bluetooth.devicesettings.shared.model.DeviceSettingIcon import com.android.settingslib.bluetooth.devicesettings.shared.model.DeviceSettingModel import com.android.settingslib.bluetooth.devicesettings.shared.model.DeviceSettingStateModel import com.android.settingslib.bluetooth.devicesettings.shared.model.ToggleModel @@ -117,7 +118,7 @@ class DeviceSettingRepositoryImpl( id = settingId, title = pref.title, summary = pref.summary, - icon = pref.icon, + icon = pref.icon?.let { DeviceSettingIcon.BitmapIcon(it) }, isAllowedChangingState = pref.isAllowedChangingState, intent = pref.intent, switchState = @@ -153,5 +154,6 @@ class DeviceSettingRepositoryImpl( else -> DeviceSettingModel.Unknown(cachedDevice, settingId) } - private fun ToggleInfo.toModel(): ToggleModel = ToggleModel(label, icon) + private fun ToggleInfo.toModel(): ToggleModel = + ToggleModel(label, DeviceSettingIcon.BitmapIcon(icon)) } diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/shared/model/DeviceSettingConfigModel.kt b/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/shared/model/DeviceSettingConfigModel.kt index 136abadc25e8..e97f76cca3a9 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/shared/model/DeviceSettingConfigModel.kt +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/shared/model/DeviceSettingConfigModel.kt @@ -35,7 +35,7 @@ sealed interface DeviceSettingConfigItemModel { /** A built-in item in Settings. */ data class BuiltinItem( @DeviceSettingId override val settingId: Int, - val preferenceKey: String + val preferenceKey: String? ) : DeviceSettingConfigItemModel /** A remote item provided by other apps. */ diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/shared/model/DeviceSettingModel.kt b/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/shared/model/DeviceSettingModel.kt index db782803937c..2a6321704a1a 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/shared/model/DeviceSettingModel.kt +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/shared/model/DeviceSettingModel.kt @@ -18,6 +18,7 @@ package com.android.settingslib.bluetooth.devicesettings.shared.model import android.content.Intent import android.graphics.Bitmap +import androidx.annotation.DrawableRes import com.android.settingslib.bluetooth.CachedBluetoothDevice import com.android.settingslib.bluetooth.devicesettings.DeviceSettingId @@ -32,7 +33,7 @@ sealed interface DeviceSettingModel { @DeviceSettingId override val id: Int, val title: String, val summary: String? = null, - val icon: Bitmap? = null, + val icon: DeviceSettingIcon? = null, val intent: Intent? = null, val switchState: DeviceSettingStateModel.ActionSwitchPreferenceState? = null, val isAllowedChangingState: Boolean = true, @@ -59,4 +60,12 @@ sealed interface DeviceSettingModel { } /** Models a toggle in [DeviceSettingModel.MultiTogglePreference]. */ -data class ToggleModel(val label: String, val icon: Bitmap) +data class ToggleModel(val label: String, val icon: DeviceSettingIcon) + +/** Models an icon in device settings. */ +sealed interface DeviceSettingIcon { + + data class BitmapIcon(val bitmap: Bitmap) : DeviceSettingIcon + + data class ResourceIcon(@DrawableRes val resId: Int) : DeviceSettingIcon +} diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothUtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothUtilsTest.java index 4551f1eba84b..926d3cb448e8 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothUtilsTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothUtilsTest.java @@ -31,10 +31,13 @@ import android.bluetooth.BluetoothClass; import android.bluetooth.BluetoothCsipSetCoordinator; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothLeBroadcastReceiveState; +import android.bluetooth.BluetoothProfile; import android.content.Context; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.graphics.drawable.Drawable; +import android.media.AudioDeviceAttributes; +import android.media.AudioDeviceInfo; import android.media.AudioManager; import android.net.Uri; import android.platform.test.flag.junit.SetFlagsRule; @@ -70,6 +73,9 @@ public class BluetoothUtilsTest { @Mock private BluetoothDevice mBluetoothDevice; @Mock private AudioManager mAudioManager; @Mock private PackageManager mPackageManager; + @Mock private LeAudioProfile mA2dpProfile; + @Mock private LeAudioProfile mLeAudioProfile; + @Mock private LeAudioProfile mHearingAid; @Mock private LocalBluetoothLeBroadcast mBroadcast; @Mock private LocalBluetoothProfileManager mProfileManager; @Mock private LocalBluetoothManager mLocalBluetoothManager; @@ -100,6 +106,9 @@ public class BluetoothUtilsTest { when(mLocalBluetoothManager.getProfileManager()).thenReturn(mProfileManager); when(mProfileManager.getLeAudioBroadcastProfile()).thenReturn(mBroadcast); when(mProfileManager.getLeAudioBroadcastAssistantProfile()).thenReturn(mAssistant); + when(mA2dpProfile.getProfileId()).thenReturn(BluetoothProfile.A2DP); + when(mLeAudioProfile.getProfileId()).thenReturn(BluetoothProfile.LE_AUDIO); + when(mHearingAid.getProfileId()).thenReturn(BluetoothProfile.HEARING_AID); } @Test @@ -756,4 +765,84 @@ public class BluetoothUtilsTest { mContext.getContentResolver(), mLocalBluetoothManager)) .isEqualTo(mCachedBluetoothDevice); } + + @Test + public void getAudioDeviceAttributesForSpatialAudio_bleHeadset() { + String address = "11:22:33:44:55:66"; + when(mCachedBluetoothDevice.getDevice()).thenReturn(mBluetoothDevice); + when(mCachedBluetoothDevice.getAddress()).thenReturn(address); + when(mCachedBluetoothDevice.getProfiles()).thenReturn(List.of(mLeAudioProfile)); + when(mLeAudioProfile.isEnabled(mBluetoothDevice)).thenReturn(true); + + AudioDeviceAttributes attr = + BluetoothUtils.getAudioDeviceAttributesForSpatialAudio( + mCachedBluetoothDevice, AudioManager.AUDIO_DEVICE_CATEGORY_HEADPHONES); + + assertThat(attr) + .isEqualTo( + new AudioDeviceAttributes( + AudioDeviceAttributes.ROLE_OUTPUT, + AudioDeviceInfo.TYPE_BLE_HEADSET, + address)); + } + + @Test + public void getAudioDeviceAttributesForSpatialAudio_bleSpeaker() { + String address = "11:22:33:44:55:66"; + when(mCachedBluetoothDevice.getDevice()).thenReturn(mBluetoothDevice); + when(mCachedBluetoothDevice.getAddress()).thenReturn(address); + when(mCachedBluetoothDevice.getProfiles()).thenReturn(List.of(mLeAudioProfile)); + when(mLeAudioProfile.isEnabled(mBluetoothDevice)).thenReturn(true); + + AudioDeviceAttributes attr = + BluetoothUtils.getAudioDeviceAttributesForSpatialAudio( + mCachedBluetoothDevice, AudioManager.AUDIO_DEVICE_CATEGORY_SPEAKER); + + assertThat(attr) + .isEqualTo( + new AudioDeviceAttributes( + AudioDeviceAttributes.ROLE_OUTPUT, + AudioDeviceInfo.TYPE_BLE_SPEAKER, + address)); + } + + @Test + public void getAudioDeviceAttributesForSpatialAudio_a2dp() { + String address = "11:22:33:44:55:66"; + when(mCachedBluetoothDevice.getDevice()).thenReturn(mBluetoothDevice); + when(mCachedBluetoothDevice.getAddress()).thenReturn(address); + when(mCachedBluetoothDevice.getProfiles()).thenReturn(List.of(mA2dpProfile)); + when(mA2dpProfile.isEnabled(mBluetoothDevice)).thenReturn(true); + + AudioDeviceAttributes attr = + BluetoothUtils.getAudioDeviceAttributesForSpatialAudio( + mCachedBluetoothDevice, AudioManager.AUDIO_DEVICE_CATEGORY_HEADPHONES); + + assertThat(attr) + .isEqualTo( + new AudioDeviceAttributes( + AudioDeviceAttributes.ROLE_OUTPUT, + AudioDeviceInfo.TYPE_BLUETOOTH_A2DP, + address)); + } + + @Test + public void getAudioDeviceAttributesForSpatialAudio_hearingAid() { + String address = "11:22:33:44:55:66"; + when(mCachedBluetoothDevice.getDevice()).thenReturn(mBluetoothDevice); + when(mCachedBluetoothDevice.getAddress()).thenReturn(address); + when(mCachedBluetoothDevice.getProfiles()).thenReturn(List.of(mHearingAid)); + when(mHearingAid.isEnabled(mBluetoothDevice)).thenReturn(true); + + AudioDeviceAttributes attr = + BluetoothUtils.getAudioDeviceAttributesForSpatialAudio( + mCachedBluetoothDevice, AudioManager.AUDIO_DEVICE_CATEGORY_HEARING_AID); + + assertThat(attr) + .isEqualTo( + new AudioDeviceAttributes( + AudioDeviceAttributes.ROLE_OUTPUT, + AudioDeviceInfo.TYPE_HEARING_AID, + address)); + } } diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/devicesettings/data/repository/DeviceSettingRepositoryTest.kt b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/devicesettings/data/repository/DeviceSettingRepositoryTest.kt index fee23945f7b5..4c5ee9e4ee4c 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/devicesettings/data/repository/DeviceSettingRepositoryTest.kt +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/devicesettings/data/repository/DeviceSettingRepositoryTest.kt @@ -40,6 +40,7 @@ import com.android.settingslib.bluetooth.devicesettings.MultiTogglePreferenceSta import com.android.settingslib.bluetooth.devicesettings.ToggleInfo import com.android.settingslib.bluetooth.devicesettings.shared.model.DeviceSettingConfigItemModel import com.android.settingslib.bluetooth.devicesettings.shared.model.DeviceSettingConfigModel +import com.android.settingslib.bluetooth.devicesettings.shared.model.DeviceSettingIcon import com.android.settingslib.bluetooth.devicesettings.shared.model.DeviceSettingModel import com.android.settingslib.bluetooth.devicesettings.shared.model.DeviceSettingStateModel import com.android.settingslib.bluetooth.devicesettings.shared.model.ToggleModel @@ -352,7 +353,7 @@ class DeviceSettingRepositoryTest { val pref = serviceResponse.preference as ActionSwitchPreference assertThat(actual.title).isEqualTo(pref.title) assertThat(actual.summary).isEqualTo(pref.summary) - assertThat(actual.icon).isEqualTo(pref.icon) + assertThat(actual.icon).isEqualTo(DeviceSettingIcon.BitmapIcon(pref.icon!!)) assertThat(actual.isAllowedChangingState).isEqualTo(pref.isAllowedChangingState) if (pref.hasSwitch()) { assertThat(actual.switchState!!.checked).isEqualTo(pref.checked) |