[Audiosharing] Sort sharing candidates in Utils.
The active device is placed before the inactive one. The bonded device is placed before the unbonded one. The device with earlier bond timestamp is placed before the later one. Otherwise, the device is ordered by name. Flagged with enable_le_audio_sharing Bug: 305620450 Test: Manual Change-Id: Ie1eea7361fbed1635313b92790cec034cf669bbf
This commit is contained in:
@@ -49,6 +49,7 @@ import com.android.settingslib.bluetooth.LocalBluetoothManager;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.Executors;
|
||||
@@ -174,6 +175,13 @@ public class AudioSharingDevicePreferenceController extends BasePreferenceContro
|
||||
+ source
|
||||
+ ", reason = "
|
||||
+ reason);
|
||||
AudioSharingUtils.toastMessage(
|
||||
mContext,
|
||||
String.format(
|
||||
Locale.US,
|
||||
"Fail to add source to %s reason %d",
|
||||
sink.getAddress(),
|
||||
reason));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -209,6 +217,13 @@ public class AudioSharingDevicePreferenceController extends BasePreferenceContro
|
||||
+ sourceId
|
||||
+ ", reason = "
|
||||
+ reason);
|
||||
AudioSharingUtils.toastMessage(
|
||||
mContext,
|
||||
String.format(
|
||||
Locale.US,
|
||||
"Fail to remove source from %s reason %d",
|
||||
sink.getAddress(),
|
||||
reason));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -284,7 +299,7 @@ public class AudioSharingDevicePreferenceController extends BasePreferenceContro
|
||||
mPreferenceGroup.setVisible(false);
|
||||
mAudioSharingSettingsPreference.setVisible(false);
|
||||
|
||||
if (isAvailable()) {
|
||||
if (isAvailable() && mBluetoothDeviceUpdater != null) {
|
||||
mBluetoothDeviceUpdater.setPrefContext(screen.getContext());
|
||||
mBluetoothDeviceUpdater.forceUpdate();
|
||||
}
|
||||
@@ -379,8 +394,8 @@ public class AudioSharingDevicePreferenceController extends BasePreferenceContro
|
||||
// Show audio sharing switch or join dialog according to device count in the sharing
|
||||
// session.
|
||||
ArrayList<AudioSharingDeviceItem> deviceItemsInSharingSession =
|
||||
AudioSharingUtils.buildOrderedDeviceItemsInSharingSession(
|
||||
groupedDevices, mLocalBtManager);
|
||||
AudioSharingUtils.buildOrderedConnectedLeadAudioSharingDeviceItem(
|
||||
mLocalBtManager, groupedDevices, /* filterByInSharing= */ true);
|
||||
// Show audio sharing switch dialog when the third eligible (LE audio) remote device
|
||||
// connected during a sharing session.
|
||||
if (deviceItemsInSharingSession.size() >= 2) {
|
||||
|
@@ -19,18 +19,22 @@ package com.android.settings.connecteddevice.audiosharing;
|
||||
import android.bluetooth.BluetoothCsipSetCoordinator;
|
||||
import android.bluetooth.BluetoothDevice;
|
||||
import android.bluetooth.BluetoothLeBroadcastReceiveState;
|
||||
import android.content.Context;
|
||||
import android.util.Log;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.android.settingslib.bluetooth.BluetoothUtils;
|
||||
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
|
||||
import com.android.settingslib.bluetooth.CachedBluetoothDeviceManager;
|
||||
import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcastAssistant;
|
||||
import com.android.settingslib.bluetooth.LocalBluetoothManager;
|
||||
import com.android.settingslib.utils.ThreadUtils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class AudioSharingUtils {
|
||||
private static final String TAG = "AudioSharingUtils";
|
||||
@@ -73,48 +77,102 @@ public class AudioSharingUtils {
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch a list of {@link AudioSharingDeviceItem}s in the audio sharing session.
|
||||
* Fetch a list of ordered connected lead {@link CachedBluetoothDevice}s eligible for audio
|
||||
* sharing. The active device is placed in the first place if it exists. The devices can be
|
||||
* filtered by whether it is already in the audio sharing session.
|
||||
*
|
||||
* @param localBtManager The BT manager to provide BT functions. *
|
||||
* @param groupedConnectedDevices devices connected to broadcast assistant grouped by CSIP group
|
||||
* id.
|
||||
* @param localBtManager The BT manager to provide BT functions.
|
||||
* @return A list of connected devices in the audio sharing session.
|
||||
* @param filterByInSharing Whether to filter the device by if is already in the sharing
|
||||
* session.
|
||||
* @return A list of ordered connected devices eligible for the audio sharing. The active device
|
||||
* is placed in the first place if it exists.
|
||||
*/
|
||||
public static ArrayList<AudioSharingDeviceItem> buildOrderedDeviceItemsInSharingSession(
|
||||
public static ArrayList<CachedBluetoothDevice> buildOrderedConnectedLeadDevices(
|
||||
LocalBluetoothManager localBtManager,
|
||||
Map<Integer, List<CachedBluetoothDevice>> groupedConnectedDevices,
|
||||
LocalBluetoothManager localBtManager) {
|
||||
ArrayList<AudioSharingDeviceItem> deviceItems = new ArrayList<>();
|
||||
boolean filterByInSharing) {
|
||||
ArrayList<CachedBluetoothDevice> orderedDevices = new ArrayList<>();
|
||||
LocalBluetoothLeBroadcastAssistant assistant =
|
||||
localBtManager.getProfileManager().getLeAudioBroadcastAssistantProfile();
|
||||
if (assistant == null) return deviceItems;
|
||||
CachedBluetoothDevice activeDevice = null;
|
||||
List<CachedBluetoothDevice> inactiveDevices = new ArrayList<>();
|
||||
if (assistant == null) return orderedDevices;
|
||||
for (List<CachedBluetoothDevice> devices : groupedConnectedDevices.values()) {
|
||||
CachedBluetoothDevice leadDevice = null;
|
||||
for (CachedBluetoothDevice device : devices) {
|
||||
List<BluetoothLeBroadcastReceiveState> sourceList =
|
||||
assistant.getAllSources(device.getDevice());
|
||||
if (!sourceList.isEmpty()) {
|
||||
// Use random device in the group within the sharing session to
|
||||
// represent the group.
|
||||
if (BluetoothUtils.isActiveLeAudioDevice(device)) {
|
||||
activeDevice = device;
|
||||
} else {
|
||||
inactiveDevices.add(device);
|
||||
}
|
||||
if (!device.getMemberDevice().isEmpty()) {
|
||||
leadDevice = device;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (leadDevice == null && !devices.isEmpty()) {
|
||||
leadDevice = devices.get(0);
|
||||
Log.d(
|
||||
TAG,
|
||||
"Empty member device, pick arbitrary device as the lead: "
|
||||
+ leadDevice.getDevice().getAnonymizedAddress());
|
||||
}
|
||||
if (leadDevice == null) {
|
||||
Log.d(TAG, "Skip due to no lead device");
|
||||
continue;
|
||||
}
|
||||
if (filterByInSharing && !hasBroadcastSource(leadDevice, localBtManager)) {
|
||||
Log.d(
|
||||
TAG,
|
||||
"Filtered the device due to not in sharing session: "
|
||||
+ leadDevice.getDevice().getAnonymizedAddress());
|
||||
continue;
|
||||
}
|
||||
orderedDevices.add(leadDevice);
|
||||
}
|
||||
if (activeDevice != null) {
|
||||
deviceItems.add(buildAudioSharingDeviceItem(activeDevice));
|
||||
}
|
||||
inactiveDevices.stream()
|
||||
.sorted(CachedBluetoothDevice::compareTo)
|
||||
.forEach(
|
||||
device -> {
|
||||
deviceItems.add(buildAudioSharingDeviceItem(device));
|
||||
});
|
||||
return deviceItems;
|
||||
orderedDevices.sort(
|
||||
(CachedBluetoothDevice d1, CachedBluetoothDevice d2) -> {
|
||||
// Active above not inactive
|
||||
int comparison =
|
||||
(isActiveLeAudioDevice(d2) ? 1 : 0)
|
||||
- (isActiveLeAudioDevice(d1) ? 1 : 0);
|
||||
if (comparison != 0) return comparison;
|
||||
// Bonded above not bonded
|
||||
comparison =
|
||||
(d2.getBondState() == BluetoothDevice.BOND_BONDED ? 1 : 0)
|
||||
- (d1.getBondState() == BluetoothDevice.BOND_BONDED ? 1 : 0);
|
||||
if (comparison != 0) return comparison;
|
||||
// Bond timestamp available above unavailable
|
||||
comparison =
|
||||
(d2.getBondTimestamp() != null ? 1 : 0)
|
||||
- (d1.getBondTimestamp() != null ? 1 : 0);
|
||||
if (comparison != 0) return comparison;
|
||||
// Order by bond timestamp if it is available
|
||||
// Otherwise order by device name
|
||||
return d1.getBondTimestamp() != null
|
||||
? d1.getBondTimestamp().compareTo(d2.getBondTimestamp())
|
||||
: d1.getName().compareTo(d2.getName());
|
||||
});
|
||||
return orderedDevices;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch a list of ordered connected lead {@link AudioSharingDeviceItem}s eligible for audio
|
||||
* sharing. The active device is placed in the first place if it exists. The devices can be
|
||||
* filtered by whether it is already in the audio sharing session.
|
||||
*
|
||||
* @param localBtManager The BT manager to provide BT functions. *
|
||||
* @param groupedConnectedDevices devices connected to broadcast assistant grouped by CSIP group
|
||||
* id.
|
||||
* @param filterByInSharing Whether to filter the device by if is already in the sharing
|
||||
* session.
|
||||
* @return A list of ordered connected devices eligible for the audio sharing. The active device
|
||||
* is placed in the first place if it exists.
|
||||
*/
|
||||
public static ArrayList<AudioSharingDeviceItem> buildOrderedConnectedLeadAudioSharingDeviceItem(
|
||||
LocalBluetoothManager localBtManager,
|
||||
Map<Integer, List<CachedBluetoothDevice>> groupedConnectedDevices,
|
||||
boolean filterByInSharing) {
|
||||
return buildOrderedConnectedLeadDevices(
|
||||
localBtManager, groupedConnectedDevices, filterByInSharing)
|
||||
.stream()
|
||||
.map(device -> buildAudioSharingDeviceItem(device))
|
||||
.collect(Collectors.toCollection(ArrayList::new));
|
||||
}
|
||||
|
||||
/** Build {@link AudioSharingDeviceItem} from {@link CachedBluetoothDevice}. */
|
||||
@@ -123,6 +181,57 @@ public class AudioSharingUtils {
|
||||
return new AudioSharingDeviceItem(
|
||||
cachedDevice.getName(),
|
||||
cachedDevice.getGroupId(),
|
||||
BluetoothUtils.isActiveLeAudioDevice(cachedDevice));
|
||||
isActiveLeAudioDevice(cachedDevice));
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if {@link CachedBluetoothDevice} is in an audio sharing session.
|
||||
*
|
||||
* @param cachedDevice The cached bluetooth device to check.
|
||||
* @param localBtManager The BT manager to provide BT functions.
|
||||
* @return Whether the device is in an audio sharing session.
|
||||
*/
|
||||
public static boolean hasBroadcastSource(
|
||||
CachedBluetoothDevice cachedDevice, LocalBluetoothManager localBtManager) {
|
||||
LocalBluetoothLeBroadcastAssistant assistant =
|
||||
localBtManager.getProfileManager().getLeAudioBroadcastAssistantProfile();
|
||||
if (assistant == null) {
|
||||
return false;
|
||||
}
|
||||
List<BluetoothLeBroadcastReceiveState> sourceList =
|
||||
assistant.getAllSources(cachedDevice.getDevice());
|
||||
if (!sourceList.isEmpty()) return true;
|
||||
// Return true if member device is in broadcast.
|
||||
for (CachedBluetoothDevice device : cachedDevice.getMemberDevice()) {
|
||||
List<BluetoothLeBroadcastReceiveState> list =
|
||||
assistant.getAllSources(device.getDevice());
|
||||
if (!list.isEmpty()) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if {@link CachedBluetoothDevice} is an active le audio device.
|
||||
*
|
||||
* @param cachedDevice The cached bluetooth device to check.
|
||||
* @return Whether the device is an active le audio device.
|
||||
*/
|
||||
public static boolean isActiveLeAudioDevice(CachedBluetoothDevice cachedDevice) {
|
||||
if (BluetoothUtils.isActiveLeAudioDevice(cachedDevice)) {
|
||||
return true;
|
||||
}
|
||||
// Return true if member device is an active le audio device.
|
||||
for (CachedBluetoothDevice device : cachedDevice.getMemberDevice()) {
|
||||
if (BluetoothUtils.isActiveLeAudioDevice(device)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/** Toast message on main thread. */
|
||||
public static void toastMessage(Context context, String message) {
|
||||
ThreadUtils.postOnMainThread(
|
||||
() -> Toast.makeText(context, message, Toast.LENGTH_LONG).show());
|
||||
}
|
||||
}
|
||||
|
@@ -62,14 +62,6 @@ public class CallsAndAlarmsPreferenceController extends AudioSharingBasePreferen
|
||||
@Override
|
||||
public void displayPreference(PreferenceScreen screen) {
|
||||
super.displayPreference(screen);
|
||||
updateDeviceItemsInSharingSession();
|
||||
// mDeviceItemsInSharingSession is ordered. The active device is the first place if exits.
|
||||
if (!mDeviceItemsInSharingSession.isEmpty()
|
||||
&& mDeviceItemsInSharingSession.get(0).isActive()) {
|
||||
mPreference.setSummary(mDeviceItemsInSharingSession.get(0).getName());
|
||||
} else {
|
||||
mPreference.setSummary("");
|
||||
}
|
||||
mPreference.setOnPreferenceClickListener(
|
||||
preference -> {
|
||||
if (mFragment == null) {
|
||||
@@ -106,6 +98,22 @@ public class CallsAndAlarmsPreferenceController extends AudioSharingBasePreferen
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateVisibility(boolean isVisible) {
|
||||
super.updateVisibility(isVisible);
|
||||
if (isVisible && mPreference != null) {
|
||||
updateDeviceItemsInSharingSession();
|
||||
// mDeviceItemsInSharingSession is ordered. The active device is the first place if
|
||||
// exits.
|
||||
if (!mDeviceItemsInSharingSession.isEmpty()
|
||||
&& mDeviceItemsInSharingSession.get(0).isActive()) {
|
||||
mPreference.setSummary(mDeviceItemsInSharingSession.get(0).getName());
|
||||
} else {
|
||||
mPreference.setSummary("");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActiveDeviceChanged(
|
||||
@Nullable CachedBluetoothDevice activeDevice, int bluetoothProfile) {
|
||||
@@ -129,7 +137,7 @@ public class CallsAndAlarmsPreferenceController extends AudioSharingBasePreferen
|
||||
mGroupedConnectedDevices =
|
||||
AudioSharingUtils.fetchConnectedDevicesByGroupId(mLocalBtManager);
|
||||
mDeviceItemsInSharingSession =
|
||||
AudioSharingUtils.buildOrderedDeviceItemsInSharingSession(
|
||||
mGroupedConnectedDevices, mLocalBtManager);
|
||||
AudioSharingUtils.buildOrderedConnectedLeadAudioSharingDeviceItem(
|
||||
mLocalBtManager, mGroupedConnectedDevices, /* filterByInSharing= */ true);
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user