Created
November 22, 2017 21:20
-
-
Save wislon/03d0c44ee4132875256aba132af360c8 to your computer and use it in GitHub Desktop.
Android Bluetooth Helper (power up/down adapter, build config based on SDK level etc.)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public static class BluetoothHelper | |
{ | |
private static readonly BluetoothManager _manager; | |
private static readonly BluetoothAdapter _adapter; | |
static BluetoothHelper() | |
{ | |
var context = Application.Context; | |
_manager = (BluetoothManager)context.GetSystemService("bluetooth"); | |
_adapter = _manager.Adapter; | |
} | |
/// <summary> | |
/// ONLY works for API Level >= 21. Less than 21 needs the old OnLeScan. | |
/// Builds scan settings based on the API level of the device based on | |
/// https://developer.android.com/reference/android/bluetooth/le/ScanSettings.html | |
/// (see/set the Api Level tag to see what's available). | |
/// </summary> | |
/// <returns></returns> | |
public static ScanSettings GetScanSettingsForApiSdkLevel() | |
{ | |
switch (Build.VERSION.SdkInt) | |
{ | |
// disable this until we actually have a marshmallow device to test it on | |
// until then, treat 'M' devices as 'L' devices. | |
//case BuildVersionCodes.M: // 23, marshmallow | |
// { | |
// const int MATCH_NUM_ONE_ADVERTISEMENT = 1; | |
// const int MATCH_NUM_FEW_ADVERTISEMENT = 2; | |
// const int MATCH_NUM_MAX_ADVERTISEMENT = 3; | |
// long reportDelayMs = 3000; | |
// return new ScanSettings.Builder().SetScanMode(Android.Bluetooth.LE.ScanMode.LowLatency) | |
// .SetCallbackType(ScanCallbackType.AllMatches) | |
// .SetMatchMode(BluetoothScanMatchMode.Aggressive) | |
// .SetNumOfMatches(MATCH_NUM_MAX_ADVERTISEMENT) | |
// .SetReportDelay(reportDelayMs) | |
// .Build(); | |
// } | |
case BuildVersionCodes.M: // 23, marshmallow | |
case BuildVersionCodes.Lollipop: // 21 | |
case BuildVersionCodes.LollipopMr1: // 22 | |
default: | |
{ | |
return new ScanSettings.Builder().SetScanMode(Android.Bluetooth.LE.ScanMode.LowLatency) | |
.SetCallbackType(ScanCallbackType.AllMatches) | |
.Build(); | |
} | |
} | |
} | |
/// <summary> | |
/// Some devices take a little while longer to spin up the bluetooth adapter (e.g. Moto X, see issue #23) | |
/// This will wait up to 2000ms for it to transition to <see cref="State.On"/>, tho it should come up a lot | |
/// more quickly than that. | |
/// </summary> | |
/// <returns></returns> | |
public static async Task WaitForBluetoothToPowerOn(BluetoothAdapter adapter = null) | |
{ | |
adapter = adapter ?? _adapter; | |
if (adapter.State == State.On) return; | |
if (adapter.State != State.On) | |
{ | |
adapter.Enable(); | |
} | |
int i = 1; | |
// check 5 times a second for it to power on, maybe a bit aggressive. | |
while (adapter.State != State.On && i < 10) | |
{ | |
await Task.Delay(200); | |
i++; | |
} | |
LogHost.Default.Debug($"Waited {i * 200}ms for Bluetooth/BleScanner to power on"); | |
} | |
/// <summary> | |
/// Waits up to 2,000ms for Bluetooth adapter's power state to transition to <see cref="State.Off"/>, tho it should | |
/// go away a lot more quickly than that. | |
/// </summary> | |
/// <returns></returns> | |
public static async Task WaitForBluetoothToPowerOff(BluetoothAdapter adapter = null) | |
{ | |
adapter = adapter ?? _adapter; | |
int i = 1; | |
if (adapter.State == State.Off) return; | |
if (adapter.State != State.Off) | |
{ | |
adapter.Disable(); | |
} | |
while (adapter.State != State.Off && i < 10) | |
{ | |
await Task.Delay(200); | |
i++; | |
} | |
LogHost.Default.Debug($"Waited {i * 200}ms for Bluetooth/BleScanner to turn off"); | |
} | |
/// <summary> | |
/// Works for devices like headsets, handsfree car kits, etc., but fails miserably | |
/// at detecting bluetooth-tethered internet connections. On the plus side, if the profile | |
/// type is not supported, it means the device won't be connected to any of those types | |
/// either. | |
/// </summary> | |
/// <param name="manager"></param> | |
/// <returns></returns> | |
public static List<BluetoothDevice> GetConnectedDevices(BluetoothManager manager = null) | |
{ | |
// BondedDevices just returns the list of devices the device has paired with, previously, | |
// but doesn't say whether any of them are currently connected. | |
// see http://developer.android.com/reference/android/bluetooth/BluetoothDevice.html#BOND_BONDED | |
//var bondedDevices = manager.Adapter.BondedDevices; | |
//if (bondedDevices.Any()) | |
//{ | |
// this.Log().Debug(string.Format("Paired with {0}", string.Join(", ", bondedDevices.Select(bd => bd.Name)))); | |
//} | |
manager = manager ?? _manager; | |
var devices = new List<BluetoothDevice>(); | |
devices.AddRange(GetConnectedDevicesOfType(manager, ProfileType.Headset)); | |
if (devices.Any()) return devices.ToList(); | |
devices.AddRange(GetConnectedDevicesOfType(manager, ProfileType.A2dp)); | |
if (devices.Any()) return devices.ToList(); | |
devices.AddRange(GetConnectedDevicesOfType(manager, ProfileType.Health)); | |
if (devices.Any()) return devices.ToList(); | |
devices.AddRange(GetConnectedDevicesOfType(manager, ProfileType.Gatt)); | |
if (devices.Any()) return devices.ToList(); | |
devices.AddRange(GetConnectedDevicesOfType(manager, ProfileType.GattServer)); | |
if (devices.Any()) return devices.ToList(); | |
devices.AddRange(GetConnectedDevicesOfType(manager, ProfileType.Sap)); | |
return devices; | |
} | |
/// <summary> | |
/// Not all Bluetooth profile types are supported on all devices for lookup, | |
/// so they throw an illegal argument exception instead of just returning | |
/// 'no devices', so that's what we do here. | |
/// </summary> | |
/// <param name="manager"></param> | |
/// <param name="profileType"></param> | |
/// <returns></returns> | |
private static IEnumerable<BluetoothDevice> GetConnectedDevicesOfType(BluetoothManager manager = null, ProfileType profileType = ProfileType.Headset) | |
{ | |
manager = manager ?? _manager; | |
var devices = new List<BluetoothDevice>(); | |
try | |
{ | |
devices.AddRange(manager.GetConnectedDevices(profileType)); | |
return devices; | |
} | |
catch (Java.Lang.IllegalArgumentException ex) | |
{ | |
LogHost.Default.Debug(ex.Message); | |
} | |
return devices; | |
} | |
public static bool IsCurrentlyConnected(BluetoothManager manager = null) | |
{ | |
manager = manager ?? _manager; | |
return GetConnectedDevices(manager).Any(); | |
} | |
public static List<BluetoothDevice> ListConnectedDevices(this BluetoothManager manager) | |
{ | |
manager = manager ?? _manager; | |
return GetConnectedDevices(manager); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment