Skip to content

Instantly share code, notes, and snippets.

@wislon
Created November 22, 2017 21:20
Show Gist options
  • Save wislon/03d0c44ee4132875256aba132af360c8 to your computer and use it in GitHub Desktop.
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.)
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