Merge "Add Settings support for Bluetooth Multi-A2DP and Multi-HFP"
This commit is contained in:
@@ -129,14 +129,18 @@ public class A2dpProfile implements LocalBluetoothProfile {
|
||||
|
||||
public boolean connect(BluetoothDevice device) {
|
||||
if (mService == null) return false;
|
||||
List<BluetoothDevice> sinks = getConnectedDevices();
|
||||
if (sinks != null) {
|
||||
for (BluetoothDevice sink : sinks) {
|
||||
if (sink.equals(device)) {
|
||||
Log.w(TAG, "Connecting to device " + device + " : disconnect skipped");
|
||||
continue;
|
||||
int max_connected_devices = mLocalAdapter.getMaxConnectedAudioDevices();
|
||||
if (max_connected_devices == 1) {
|
||||
// Original behavior: disconnect currently connected device
|
||||
List<BluetoothDevice> sinks = getConnectedDevices();
|
||||
if (sinks != null) {
|
||||
for (BluetoothDevice sink : sinks) {
|
||||
if (sink.equals(device)) {
|
||||
Log.w(TAG, "Connecting to device " + device + " : disconnect skipped");
|
||||
continue;
|
||||
}
|
||||
mService.disconnect(sink);
|
||||
}
|
||||
mService.disconnect(sink);
|
||||
}
|
||||
}
|
||||
return mService.connect(device);
|
||||
@@ -158,6 +162,16 @@ public class A2dpProfile implements LocalBluetoothProfile {
|
||||
return mService.getConnectionState(device);
|
||||
}
|
||||
|
||||
public boolean setActiveDevice(BluetoothDevice device) {
|
||||
if (mService == null) return false;
|
||||
return mService.setActiveDevice(device);
|
||||
}
|
||||
|
||||
public BluetoothDevice getActiveDevice() {
|
||||
if (mService == null) return null;
|
||||
return mService.getActiveDevice();
|
||||
}
|
||||
|
||||
public boolean isPreferred(BluetoothDevice device) {
|
||||
if (mService == null) return false;
|
||||
return mService.getPriority(device) > BluetoothProfile.PRIORITY_OFF;
|
||||
@@ -181,8 +195,8 @@ public class A2dpProfile implements LocalBluetoothProfile {
|
||||
boolean isA2dpPlaying() {
|
||||
if (mService == null) return false;
|
||||
List<BluetoothDevice> sinks = mService.getConnectedDevices();
|
||||
if (!sinks.isEmpty()) {
|
||||
if (mService.isA2dpPlaying(sinks.get(0))) {
|
||||
for (BluetoothDevice device : sinks) {
|
||||
if (mService.isA2dpPlaying(device)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,4 +28,5 @@ public interface BluetoothCallback {
|
||||
void onDeviceDeleted(CachedBluetoothDevice cachedDevice);
|
||||
void onDeviceBondStateChanged(CachedBluetoothDevice cachedDevice, int bondState);
|
||||
void onConnectionStateChanged(CachedBluetoothDevice cachedDevice, int state);
|
||||
void onActiveDeviceChanged(CachedBluetoothDevice activeDevice, int bluetoothProfile);
|
||||
}
|
||||
|
||||
@@ -16,9 +16,12 @@
|
||||
|
||||
package com.android.settingslib.bluetooth;
|
||||
|
||||
import android.bluetooth.BluetoothA2dp;
|
||||
import android.bluetooth.BluetoothAdapter;
|
||||
import android.bluetooth.BluetoothClass;
|
||||
import android.bluetooth.BluetoothDevice;
|
||||
import android.bluetooth.BluetoothHeadset;
|
||||
import android.bluetooth.BluetoothProfile;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
@@ -31,6 +34,7 @@ import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
@@ -106,6 +110,12 @@ public class BluetoothEventManager {
|
||||
// Dock event broadcasts
|
||||
addHandler(Intent.ACTION_DOCK_EVENT, new DockEventHandler());
|
||||
|
||||
// Active device broadcasts
|
||||
addHandler(BluetoothA2dp.ACTION_ACTIVE_DEVICE_CHANGED,
|
||||
new ActiveDeviceChangedHandler());
|
||||
addHandler(BluetoothHeadset.ACTION_ACTIVE_DEVICE_CHANGED,
|
||||
new ActiveDeviceChangedHandler());
|
||||
|
||||
mContext.registerReceiver(mBroadcastReceiver, mAdapterIntentFilter, null, mReceiverHandler);
|
||||
mContext.registerReceiver(mProfileBroadcastReceiver, mProfileIntentFilter, null, mReceiverHandler);
|
||||
}
|
||||
@@ -409,4 +419,35 @@ public class BluetoothEventManager {
|
||||
|
||||
return deviceAdded;
|
||||
}
|
||||
|
||||
private class ActiveDeviceChangedHandler implements Handler {
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent, BluetoothDevice device) {
|
||||
String action = intent.getAction();
|
||||
if (action == null) {
|
||||
Log.w(TAG, "ActiveDeviceChangedHandler: action is null");
|
||||
return;
|
||||
}
|
||||
CachedBluetoothDevice activeDevice = mDeviceManager.findDevice(device);
|
||||
int bluetoothProfile = 0;
|
||||
if (Objects.equals(action, BluetoothA2dp.ACTION_ACTIVE_DEVICE_CHANGED)) {
|
||||
bluetoothProfile = BluetoothProfile.A2DP;
|
||||
} else if (Objects.equals(action, BluetoothHeadset.ACTION_ACTIVE_DEVICE_CHANGED)) {
|
||||
bluetoothProfile = BluetoothProfile.HEADSET;
|
||||
} else {
|
||||
Log.w(TAG, "ActiveDeviceChangedHandler: unknown action " + action);
|
||||
return;
|
||||
}
|
||||
dispatchActiveDeviceChanged(activeDevice, bluetoothProfile);
|
||||
}
|
||||
}
|
||||
|
||||
private void dispatchActiveDeviceChanged(CachedBluetoothDevice activeDevice,
|
||||
int bluetoothProfile) {
|
||||
synchronized (mCallbacks) {
|
||||
for (BluetoothCallback callback : mCallbacks) {
|
||||
callback.onActiveDeviceChanged(activeDevice, bluetoothProfile);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -105,6 +105,10 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice>
|
||||
private static final long MAX_UUID_DELAY_FOR_AUTO_CONNECT = 5000;
|
||||
private static final long MAX_HOGP_DELAY_FOR_AUTO_CONNECT = 30000;
|
||||
|
||||
// Active device state
|
||||
private boolean mIsActiveDeviceA2dp = false;
|
||||
private boolean mIsActiveDeviceHeadset = false;
|
||||
|
||||
/**
|
||||
* Describes the current device and profile for logging.
|
||||
*
|
||||
@@ -156,6 +160,7 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice>
|
||||
mRemovedProfiles.add(profile);
|
||||
mLocalNapRoleConnected = false;
|
||||
}
|
||||
fetchActiveDevices();
|
||||
}
|
||||
|
||||
CachedBluetoothDevice(Context context,
|
||||
@@ -359,6 +364,7 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice>
|
||||
fetchName();
|
||||
fetchBtClass();
|
||||
updateProfiles();
|
||||
fetchActiveDevices();
|
||||
migratePhonebookPermissionChoice();
|
||||
migrateMessagePermissionChoice();
|
||||
fetchMessageRejectionCount();
|
||||
@@ -454,6 +460,33 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice>
|
||||
return mDevice.getBondState();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the device status as active or non-active per Bluetooth profile.
|
||||
*
|
||||
* @param isActive true if the device is active
|
||||
* @param bluetoothProfile the Bluetooth profile
|
||||
*/
|
||||
public void setActiveDevice(boolean isActive, int bluetoothProfile) {
|
||||
boolean changed = false;
|
||||
switch (bluetoothProfile) {
|
||||
case BluetoothProfile.A2DP:
|
||||
changed = (mIsActiveDeviceA2dp != isActive);
|
||||
mIsActiveDeviceA2dp = isActive;
|
||||
break;
|
||||
case BluetoothProfile.HEADSET:
|
||||
changed = (mIsActiveDeviceHeadset != isActive);
|
||||
mIsActiveDeviceHeadset = isActive;
|
||||
break;
|
||||
default:
|
||||
Log.w(TAG, "setActiveDevice: unknown profile " + bluetoothProfile +
|
||||
" isActive " + isActive);
|
||||
break;
|
||||
}
|
||||
if (changed) {
|
||||
dispatchAttributesChanged();
|
||||
}
|
||||
}
|
||||
|
||||
void setRssi(short rssi) {
|
||||
if (mRssi != rssi) {
|
||||
mRssi = rssi;
|
||||
@@ -529,6 +562,17 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice>
|
||||
return true;
|
||||
}
|
||||
|
||||
private void fetchActiveDevices() {
|
||||
A2dpProfile a2dpProfile = mProfileManager.getA2dpProfile();
|
||||
if (a2dpProfile != null) {
|
||||
mIsActiveDeviceA2dp = mDevice.equals(a2dpProfile.getActiveDevice());
|
||||
}
|
||||
HeadsetProfile headsetProfile = mProfileManager.getHeadsetProfile();
|
||||
if (headsetProfile != null) {
|
||||
mIsActiveDeviceHeadset = mDevice.equals(headsetProfile.getActiveDevice());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Refreshes the UI for the BT class, including fetching the latest value
|
||||
* for the class.
|
||||
@@ -896,37 +940,60 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice>
|
||||
com.android.settingslib.Utils.formatPercentage(batteryLevel);
|
||||
}
|
||||
|
||||
// TODO: A temporary workaround solution using string description the device is active.
|
||||
// Issue tracked by b/72317067 .
|
||||
// An alternative solution would be visual indication.
|
||||
// Intentionally not adding the strings to strings.xml for now:
|
||||
// 1) If this is just a short-term solution, no need to waste translation effort
|
||||
// 2) The number of strings with all possible combinations becomes enormously large.
|
||||
// If string description becomes part of the final solution, we MUST NOT
|
||||
// concatenate the strings here: this does not translate well.
|
||||
String activeString = null;
|
||||
if (mIsActiveDeviceA2dp && mIsActiveDeviceHeadset) {
|
||||
activeString = ", active";
|
||||
} else {
|
||||
if (mIsActiveDeviceA2dp) {
|
||||
activeString = ", active(media)";
|
||||
}
|
||||
if (mIsActiveDeviceHeadset) {
|
||||
activeString = ", active(phone)";
|
||||
}
|
||||
}
|
||||
if (activeString == null) activeString = "";
|
||||
|
||||
if (profileConnected) {
|
||||
if (a2dpNotConnected && hfpNotConnected) {
|
||||
if (batteryLevelPercentageString != null) {
|
||||
return mContext.getString(
|
||||
R.string.bluetooth_connected_no_headset_no_a2dp_battery_level,
|
||||
batteryLevelPercentageString);
|
||||
batteryLevelPercentageString) + activeString;
|
||||
} else {
|
||||
return mContext.getString(R.string.bluetooth_connected_no_headset_no_a2dp);
|
||||
return mContext.getString(R.string.bluetooth_connected_no_headset_no_a2dp) +
|
||||
activeString;
|
||||
}
|
||||
|
||||
} else if (a2dpNotConnected) {
|
||||
if (batteryLevelPercentageString != null) {
|
||||
return mContext.getString(R.string.bluetooth_connected_no_a2dp_battery_level,
|
||||
batteryLevelPercentageString);
|
||||
batteryLevelPercentageString) + activeString;
|
||||
} else {
|
||||
return mContext.getString(R.string.bluetooth_connected_no_a2dp);
|
||||
return mContext.getString(R.string.bluetooth_connected_no_a2dp) + activeString;
|
||||
}
|
||||
|
||||
} else if (hfpNotConnected) {
|
||||
if (batteryLevelPercentageString != null) {
|
||||
return mContext.getString(R.string.bluetooth_connected_no_headset_battery_level,
|
||||
batteryLevelPercentageString);
|
||||
batteryLevelPercentageString) + activeString;
|
||||
} else {
|
||||
return mContext.getString(R.string.bluetooth_connected_no_headset);
|
||||
return mContext.getString(R.string.bluetooth_connected_no_headset)
|
||||
+ activeString;
|
||||
}
|
||||
} else {
|
||||
if (batteryLevelPercentageString != null) {
|
||||
return mContext.getString(R.string.bluetooth_connected_battery_level,
|
||||
batteryLevelPercentageString);
|
||||
batteryLevelPercentageString) + activeString;
|
||||
} else {
|
||||
return mContext.getString(R.string.bluetooth_connected);
|
||||
return mContext.getString(R.string.bluetooth_connected) + activeString;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -153,6 +153,16 @@ public class HeadsetProfile implements LocalBluetoothProfile {
|
||||
return BluetoothProfile.STATE_DISCONNECTED;
|
||||
}
|
||||
|
||||
public boolean setActiveDevice(BluetoothDevice device) {
|
||||
if (mService == null) return false;
|
||||
return mService.setActiveDevice(device);
|
||||
}
|
||||
|
||||
public BluetoothDevice getActiveDevice() {
|
||||
if (mService == null) return null;
|
||||
return mService.getActiveDevice();
|
||||
}
|
||||
|
||||
public boolean isPreferred(BluetoothDevice device) {
|
||||
if (mService == null) return false;
|
||||
return mService.getPriority(device) > BluetoothProfile.PRIORITY_OFF;
|
||||
|
||||
@@ -239,4 +239,8 @@ public class LocalBluetoothAdapter {
|
||||
public BluetoothDevice getRemoteDevice(String address) {
|
||||
return mAdapter.getRemoteDevice(address);
|
||||
}
|
||||
|
||||
public int getMaxConnectedAudioDevices() {
|
||||
return mAdapter.getMaxConnectedAudioDevices();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -608,6 +608,9 @@ public class KeyboardUI extends SystemUI implements InputManager.OnTabletModeCha
|
||||
public void onScanningStateChanged(boolean started) { }
|
||||
@Override
|
||||
public void onConnectionStateChanged(CachedBluetoothDevice cachedDevice, int state) { }
|
||||
@Override
|
||||
public void onActiveDeviceChanged(CachedBluetoothDevice activeDevice,
|
||||
int bluetoothProfile) { }
|
||||
}
|
||||
|
||||
private final class BluetoothErrorListener implements Utils.ErrorListener {
|
||||
|
||||
@@ -276,6 +276,9 @@ public class BluetoothControllerImpl implements BluetoothController, BluetoothCa
|
||||
mHandler.sendEmptyMessage(H.MSG_STATE_CHANGED);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActiveDeviceChanged(CachedBluetoothDevice activeDevice, int bluetoothProfile) {}
|
||||
|
||||
private ActuallyCachedState getCachedState(CachedBluetoothDevice device) {
|
||||
ActuallyCachedState state = mCachedState.get(device);
|
||||
if (state == null) {
|
||||
|
||||
Reference in New Issue
Block a user