Merge "[LE]Gray out the a2dp and hfp when LeAudio is enabled" am: cfb201c400
am: 704147041b
Original change: https://android-review.googlesource.com/c/platform/packages/apps/Settings/+/2028084 Change-Id: Ieefa077cbd80848d1d8149c71dc384775a807b0d
This commit is contained in:
@@ -16,10 +16,12 @@
|
|||||||
|
|
||||||
package com.android.settings.bluetooth;
|
package com.android.settings.bluetooth;
|
||||||
|
|
||||||
|
import android.bluetooth.BluetoothCsipSetCoordinator;
|
||||||
import android.bluetooth.BluetoothDevice;
|
import android.bluetooth.BluetoothDevice;
|
||||||
import android.bluetooth.BluetoothProfile;
|
import android.bluetooth.BluetoothProfile;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
import androidx.annotation.VisibleForTesting;
|
import androidx.annotation.VisibleForTesting;
|
||||||
import androidx.preference.Preference;
|
import androidx.preference.Preference;
|
||||||
@@ -31,6 +33,7 @@ import androidx.preference.SwitchPreference;
|
|||||||
import com.android.settings.R;
|
import com.android.settings.R;
|
||||||
import com.android.settingslib.bluetooth.A2dpProfile;
|
import com.android.settingslib.bluetooth.A2dpProfile;
|
||||||
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
|
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
|
||||||
|
import com.android.settingslib.bluetooth.LeAudioProfile;
|
||||||
import com.android.settingslib.bluetooth.LocalBluetoothManager;
|
import com.android.settingslib.bluetooth.LocalBluetoothManager;
|
||||||
import com.android.settingslib.bluetooth.LocalBluetoothProfile;
|
import com.android.settingslib.bluetooth.LocalBluetoothProfile;
|
||||||
import com.android.settingslib.bluetooth.LocalBluetoothProfileManager;
|
import com.android.settingslib.bluetooth.LocalBluetoothProfileManager;
|
||||||
@@ -39,7 +42,10 @@ import com.android.settingslib.bluetooth.PanProfile;
|
|||||||
import com.android.settingslib.bluetooth.PbapServerProfile;
|
import com.android.settingslib.bluetooth.PbapServerProfile;
|
||||||
import com.android.settingslib.core.lifecycle.Lifecycle;
|
import com.android.settingslib.core.lifecycle.Lifecycle;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This class adds switches for toggling the individual profiles that a Bluetooth device
|
* This class adds switches for toggling the individual profiles that a Bluetooth device
|
||||||
@@ -48,8 +54,11 @@ import java.util.List;
|
|||||||
public class BluetoothDetailsProfilesController extends BluetoothDetailsController
|
public class BluetoothDetailsProfilesController extends BluetoothDetailsController
|
||||||
implements Preference.OnPreferenceClickListener,
|
implements Preference.OnPreferenceClickListener,
|
||||||
LocalBluetoothProfileManager.ServiceListener {
|
LocalBluetoothProfileManager.ServiceListener {
|
||||||
|
private static final String TAG = "BtDetailsProfilesCtrl";
|
||||||
|
|
||||||
private static final String KEY_PROFILES_GROUP = "bluetooth_profiles";
|
private static final String KEY_PROFILES_GROUP = "bluetooth_profiles";
|
||||||
private static final String KEY_BOTTOM_PREFERENCE = "bottom_preference";
|
private static final String KEY_BOTTOM_PREFERENCE = "bottom_preference";
|
||||||
|
private static final String HEADSET_CLIENT = "HEADSET_CLIENT";
|
||||||
private static final int ORDINAL = 99;
|
private static final int ORDINAL = 99;
|
||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
@@ -58,6 +67,9 @@ public class BluetoothDetailsProfilesController extends BluetoothDetailsControll
|
|||||||
private LocalBluetoothManager mManager;
|
private LocalBluetoothManager mManager;
|
||||||
private LocalBluetoothProfileManager mProfileManager;
|
private LocalBluetoothProfileManager mProfileManager;
|
||||||
private CachedBluetoothDevice mCachedDevice;
|
private CachedBluetoothDevice mCachedDevice;
|
||||||
|
private List<CachedBluetoothDevice> mAllOfCachedDevices;
|
||||||
|
private Map<String, List<CachedBluetoothDevice>> mProfileDeviceMap =
|
||||||
|
new HashMap<String, List<CachedBluetoothDevice>>();
|
||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
PreferenceCategory mProfilesContainer;
|
PreferenceCategory mProfilesContainer;
|
||||||
@@ -68,6 +80,7 @@ public class BluetoothDetailsProfilesController extends BluetoothDetailsControll
|
|||||||
mManager = manager;
|
mManager = manager;
|
||||||
mProfileManager = mManager.getProfileManager();
|
mProfileManager = mManager.getProfileManager();
|
||||||
mCachedDevice = device;
|
mCachedDevice = device;
|
||||||
|
mAllOfCachedDevices = getAllOfCachedBluetoothDevices();
|
||||||
lifecycle.addObserver(this);
|
lifecycle.addObserver(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -100,11 +113,66 @@ public class BluetoothDetailsProfilesController extends BluetoothDetailsControll
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Refreshes the state for an existing SwitchPreference for a profile.
|
* Refreshes the state for an existing SwitchPreference for a profile.
|
||||||
|
* If the LeAudio profile is enabled on the LeAudio devices, then the SwitchPreferences of
|
||||||
|
* A2dp profile and Hfp profile are graied out.
|
||||||
*/
|
*/
|
||||||
private void refreshProfilePreference(SwitchPreference profilePref,
|
private void refreshProfilePreference(SwitchPreference profilePref,
|
||||||
LocalBluetoothProfile profile) {
|
LocalBluetoothProfile profile) {
|
||||||
BluetoothDevice device = mCachedDevice.getDevice();
|
BluetoothDevice device = mCachedDevice.getDevice();
|
||||||
profilePref.setEnabled(!mCachedDevice.isBusy());
|
boolean isLeAudioEnabled = false;
|
||||||
|
if (profile instanceof A2dpProfile || HEADSET_CLIENT.equals(profile.toString())) {
|
||||||
|
LocalBluetoothProfile leAudio = mProfileManager.getLeAudioProfile();
|
||||||
|
if (leAudio != null) {
|
||||||
|
List<CachedBluetoothDevice> leAudioDeviceList = mProfileDeviceMap.get(
|
||||||
|
leAudio.toString());
|
||||||
|
if (leAudioDeviceList != null
|
||||||
|
&& leAudioDeviceList.stream()
|
||||||
|
.anyMatch(item -> leAudio.isEnabled(item.getDevice()))) {
|
||||||
|
isLeAudioEnabled = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (isLeAudioEnabled) {
|
||||||
|
// If the LeAudio profile is enabled on the LeAudio devices, then the
|
||||||
|
// SwitchPreferences of A2dp profile and Hfp profile are graied out.
|
||||||
|
profilePref.setEnabled(false);
|
||||||
|
} else {
|
||||||
|
List<CachedBluetoothDevice> deviceList = mProfileDeviceMap.get(
|
||||||
|
profile.toString());
|
||||||
|
boolean isBusy = deviceList != null
|
||||||
|
&& deviceList.stream().anyMatch(item -> item.isBusy());
|
||||||
|
profilePref.setEnabled(!isBusy);
|
||||||
|
}
|
||||||
|
} else if (profile instanceof LeAudioProfile) {
|
||||||
|
List<CachedBluetoothDevice> leAudioDeviceList = mProfileDeviceMap.get(
|
||||||
|
profile.toString());
|
||||||
|
boolean isLeAudioProfileEnable =
|
||||||
|
leAudioDeviceList != null && leAudioDeviceList.stream().anyMatch(
|
||||||
|
item -> profile.isEnabled(item.getDevice()));
|
||||||
|
boolean isBusy = leAudioDeviceList != null
|
||||||
|
&& leAudioDeviceList.stream().anyMatch(item -> item.isBusy());
|
||||||
|
if (isLeAudioProfileEnable && !isBusy) {
|
||||||
|
LocalBluetoothProfile a2dp = mProfileManager.getA2dpProfile();
|
||||||
|
LocalBluetoothProfile hfp = mProfileManager.getHfpClientProfile();
|
||||||
|
// If the LeAudio profile is enabled on the LeAudio devices, then the
|
||||||
|
// SwitchPreferences of A2dp profile and Hfp profile are graied out.
|
||||||
|
if (a2dp != null) {
|
||||||
|
SwitchPreference pref = mProfilesContainer.findPreference(a2dp.toString());
|
||||||
|
if (pref != null) {
|
||||||
|
pref.setEnabled(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (hfp != null) {
|
||||||
|
SwitchPreference pref = mProfilesContainer.findPreference(hfp.toString());
|
||||||
|
if (pref != null) {
|
||||||
|
pref.setEnabled(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
profilePref.setEnabled(!isBusy);
|
||||||
|
} else {
|
||||||
|
profilePref.setEnabled(!mCachedDevice.isBusy());
|
||||||
|
}
|
||||||
|
|
||||||
if (profile instanceof MapProfile) {
|
if (profile instanceof MapProfile) {
|
||||||
profilePref.setChecked(device.getMessageAccessPermission()
|
profilePref.setChecked(device.getMessageAccessPermission()
|
||||||
== BluetoothDevice.ACCESS_ALLOWED);
|
== BluetoothDevice.ACCESS_ALLOWED);
|
||||||
@@ -127,7 +195,7 @@ public class BluetoothDetailsProfilesController extends BluetoothDetailsControll
|
|||||||
highQualityPref.setVisible(true);
|
highQualityPref.setVisible(true);
|
||||||
highQualityPref.setTitle(a2dp.getHighQualityAudioOptionLabel(device));
|
highQualityPref.setTitle(a2dp.getHighQualityAudioOptionLabel(device));
|
||||||
highQualityPref.setChecked(a2dp.isHighQualityAudioEnabled(device));
|
highQualityPref.setChecked(a2dp.isHighQualityAudioEnabled(device));
|
||||||
highQualityPref.setEnabled(!mCachedDevice.isBusy());
|
highQualityPref.setEnabled(!mCachedDevice.isBusy() && !isLeAudioEnabled);
|
||||||
} else {
|
} else {
|
||||||
highQualityPref.setVisible(false);
|
highQualityPref.setVisible(false);
|
||||||
}
|
}
|
||||||
@@ -148,6 +216,12 @@ public class BluetoothDetailsProfilesController extends BluetoothDetailsControll
|
|||||||
if (profile instanceof MapProfile) {
|
if (profile instanceof MapProfile) {
|
||||||
bluetoothDevice.setMessageAccessPermission(BluetoothDevice.ACCESS_ALLOWED);
|
bluetoothDevice.setMessageAccessPermission(BluetoothDevice.ACCESS_ALLOWED);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (profile instanceof LeAudioProfile) {
|
||||||
|
enableLeAudioProfile(profile);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
profile.setEnabled(bluetoothDevice, true);
|
profile.setEnabled(bluetoothDevice, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -155,8 +229,14 @@ public class BluetoothDetailsProfilesController extends BluetoothDetailsControll
|
|||||||
* Helper method to disable a profile for a device
|
* Helper method to disable a profile for a device
|
||||||
*/
|
*/
|
||||||
private void disableProfile(LocalBluetoothProfile profile) {
|
private void disableProfile(LocalBluetoothProfile profile) {
|
||||||
|
if (profile instanceof LeAudioProfile) {
|
||||||
|
disableLeAudioProfile(profile);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
final BluetoothDevice bluetoothDevice = mCachedDevice.getDevice();
|
final BluetoothDevice bluetoothDevice = mCachedDevice.getDevice();
|
||||||
profile.setEnabled(bluetoothDevice, false);
|
profile.setEnabled(bluetoothDevice, false);
|
||||||
|
|
||||||
if (profile instanceof MapProfile) {
|
if (profile instanceof MapProfile) {
|
||||||
bluetoothDevice.setMessageAccessPermission(BluetoothDevice.ACCESS_REJECTED);
|
bluetoothDevice.setMessageAccessPermission(BluetoothDevice.ACCESS_REJECTED);
|
||||||
} else if (profile instanceof PbapServerProfile) {
|
} else if (profile instanceof PbapServerProfile) {
|
||||||
@@ -190,14 +270,33 @@ public class BluetoothDetailsProfilesController extends BluetoothDetailsControll
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Helper to get the list of connectable and special profiles.
|
* Helper to get the list of connectable and special profiles.
|
||||||
*/
|
*/
|
||||||
private List<LocalBluetoothProfile> getProfiles() {
|
private List<LocalBluetoothProfile> getProfiles() {
|
||||||
List<LocalBluetoothProfile> result = mCachedDevice.getConnectableProfiles();
|
List<LocalBluetoothProfile> result = new ArrayList<LocalBluetoothProfile>();
|
||||||
final BluetoothDevice device = mCachedDevice.getDevice();
|
mProfileDeviceMap.clear();
|
||||||
|
if (mAllOfCachedDevices == null || mAllOfCachedDevices.isEmpty()) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
for (CachedBluetoothDevice cachedItem : mAllOfCachedDevices) {
|
||||||
|
List<LocalBluetoothProfile> tmpResult = cachedItem.getConnectableProfiles();
|
||||||
|
for (LocalBluetoothProfile profile : tmpResult) {
|
||||||
|
if (mProfileDeviceMap.containsKey(profile.toString())) {
|
||||||
|
mProfileDeviceMap.get(profile.toString()).add(cachedItem);
|
||||||
|
Log.d(TAG, "getProfiles: " + profile.toString() + " add device "
|
||||||
|
+ cachedItem.getDevice().getAnonymizedAddress());
|
||||||
|
} else {
|
||||||
|
List<CachedBluetoothDevice> tmpCachedDeviceList =
|
||||||
|
new ArrayList<CachedBluetoothDevice>();
|
||||||
|
tmpCachedDeviceList.add(cachedItem);
|
||||||
|
mProfileDeviceMap.put(profile.toString(), tmpCachedDeviceList);
|
||||||
|
result.add(profile);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final BluetoothDevice device = mCachedDevice.getDevice();
|
||||||
final int pbapPermission = device.getPhonebookAccessPermission();
|
final int pbapPermission = device.getPhonebookAccessPermission();
|
||||||
// Only provide PBAP cabability if the client device has requested PBAP.
|
// Only provide PBAP cabability if the client device has requested PBAP.
|
||||||
if (pbapPermission != BluetoothDevice.ACCESS_UNKNOWN) {
|
if (pbapPermission != BluetoothDevice.ACCESS_UNKNOWN) {
|
||||||
@@ -210,10 +309,93 @@ public class BluetoothDetailsProfilesController extends BluetoothDetailsControll
|
|||||||
if (mapPermission != BluetoothDevice.ACCESS_UNKNOWN) {
|
if (mapPermission != BluetoothDevice.ACCESS_UNKNOWN) {
|
||||||
result.add(mapProfile);
|
result.add(mapProfile);
|
||||||
}
|
}
|
||||||
|
Log.d(TAG, "getProfiles:result:" + result);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private List<CachedBluetoothDevice> getAllOfCachedBluetoothDevices() {
|
||||||
|
List<CachedBluetoothDevice> cachedBluetoothDevices = new ArrayList<>();
|
||||||
|
if (mCachedDevice == null) {
|
||||||
|
return cachedBluetoothDevices;
|
||||||
|
}
|
||||||
|
cachedBluetoothDevices.add(mCachedDevice);
|
||||||
|
if (mCachedDevice.getGroupId() != BluetoothCsipSetCoordinator.GROUP_ID_INVALID) {
|
||||||
|
for (CachedBluetoothDevice member : mCachedDevice.getMemberDevice()) {
|
||||||
|
cachedBluetoothDevices.add(member);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return cachedBluetoothDevices;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* When user disable the Le Audio profile, the system needs to do two things.
|
||||||
|
* 1) Disable the Le Audio profile for each of the Le Audio devices.
|
||||||
|
* 2) Enable the A2dp profile and Hfp profile for the associated device. The system can't
|
||||||
|
* enable the A2dp profile and Hfp profile if the Le Audio profile is enabled.
|
||||||
|
*
|
||||||
|
* @param profile the LeAudio profile
|
||||||
|
*/
|
||||||
|
private void disableLeAudioProfile(LocalBluetoothProfile profile) {
|
||||||
|
if (profile == null || mProfileDeviceMap.get(profile.toString()) == null) {
|
||||||
|
Log.e(TAG, "There is no the LE profile or no device in mProfileDeviceMap. Do nothing.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (CachedBluetoothDevice leAudioDevice : mProfileDeviceMap.get(profile.toString())) {
|
||||||
|
profile.setEnabled(leAudioDevice.getDevice(), false);
|
||||||
|
}
|
||||||
|
|
||||||
|
LocalBluetoothProfile a2dp = mProfileManager.getA2dpProfile();
|
||||||
|
LocalBluetoothProfile hfp = mProfileManager.getHfpClientProfile();
|
||||||
|
if (a2dp != null && mProfileDeviceMap.get(a2dp.toString()) != null) {
|
||||||
|
for (CachedBluetoothDevice a2dpDevice : mProfileDeviceMap.get(a2dp.toString())) {
|
||||||
|
if (!a2dp.isEnabled(a2dpDevice.getDevice())) {
|
||||||
|
a2dp.setEnabled(a2dpDevice.getDevice(), true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (hfp != null && mProfileDeviceMap.get(hfp.toString()) != null) {
|
||||||
|
for (CachedBluetoothDevice hfpDevice : mProfileDeviceMap.get(hfp.toString())) {
|
||||||
|
if (!hfp.isEnabled(hfpDevice.getDevice())) {
|
||||||
|
hfp.setEnabled(hfpDevice.getDevice(), true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* When user enable the Le Audio profile, the system needs to do two things.
|
||||||
|
* 1) Disable the A2dp profile and Hfp profile for the associated device. The system can't
|
||||||
|
* enable the Le Audio if the A2dp profile and Hfp profile are enabled.
|
||||||
|
* 2) Enable the Le Audio profile for each of the Le Audio devices.
|
||||||
|
*
|
||||||
|
* @param profile the LeAudio profile
|
||||||
|
*/
|
||||||
|
private void enableLeAudioProfile(LocalBluetoothProfile profile) {
|
||||||
|
if (profile == null || mProfileDeviceMap.get(profile.toString()) == null) {
|
||||||
|
Log.e(TAG, "There is no the LE profile or no device in mProfileDeviceMap. Do nothing.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
LocalBluetoothProfile a2dp = mProfileManager.getA2dpProfile();
|
||||||
|
LocalBluetoothProfile hfp = mProfileManager.getHfpClientProfile();
|
||||||
|
if (a2dp != null && mProfileDeviceMap.get(a2dp.toString()) != null) {
|
||||||
|
for (CachedBluetoothDevice a2dpDevice : mProfileDeviceMap.get(a2dp.toString())) {
|
||||||
|
if (a2dp.isEnabled(a2dpDevice.getDevice())) {
|
||||||
|
a2dp.setEnabled(a2dpDevice.getDevice(), false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (hfp != null && mProfileDeviceMap.get(hfp.toString()) != null) {
|
||||||
|
for (CachedBluetoothDevice hfpDevice : mProfileDeviceMap.get(hfp.toString())) {
|
||||||
|
if (hfp.isEnabled(hfpDevice.getDevice())) {
|
||||||
|
hfp.setEnabled(hfpDevice.getDevice(), false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (CachedBluetoothDevice leAudioDevice : mProfileDeviceMap.get(profile.toString())) {
|
||||||
|
profile.setEnabled(leAudioDevice.getDevice(), true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This is a helper method to be called after adding a Preference for a profile. If that
|
* This is a helper method to be called after adding a Preference for a profile. If that
|
||||||
* profile happened to be A2dp and the device supports high quality audio, it will add a
|
* profile happened to be A2dp and the device supports high quality audio, it will add a
|
||||||
@@ -243,16 +425,33 @@ public class BluetoothDetailsProfilesController extends BluetoothDetailsControll
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onPause() {
|
public void onPause() {
|
||||||
super.onPause();
|
for (CachedBluetoothDevice item : mAllOfCachedDevices) {
|
||||||
|
item.unregisterCallback(this);
|
||||||
|
}
|
||||||
mProfileManager.removeServiceListener(this);
|
mProfileManager.removeServiceListener(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onResume() {
|
public void onResume() {
|
||||||
super.onResume();
|
for (CachedBluetoothDevice item : mAllOfCachedDevices) {
|
||||||
|
item.registerCallback(this);
|
||||||
|
}
|
||||||
mProfileManager.addServiceListener(this);
|
mProfileManager.addServiceListener(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDeviceAttributesChanged() {
|
||||||
|
for (CachedBluetoothDevice item : mAllOfCachedDevices) {
|
||||||
|
item.unregisterCallback(this);
|
||||||
|
}
|
||||||
|
mAllOfCachedDevices = getAllOfCachedBluetoothDevices();
|
||||||
|
for (CachedBluetoothDevice item : mAllOfCachedDevices) {
|
||||||
|
item.registerCallback(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
super.onDeviceAttributesChanged();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onServiceConnected() {
|
public void onServiceConnected() {
|
||||||
refresh();
|
refresh();
|
||||||
|
Reference in New Issue
Block a user