Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Android] fix device state, remove unnecessary Device instances #62

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ protected override void OnCreate (Bundle bundle)

Xamarin.Forms.Forms.Init (this, bundle);

var a = new Robotics.Mobile.Core.Bluetooth.LE.Adapter ();
var a = new Robotics.Mobile.Core.Bluetooth.LE.Adapter (this);
App.SetAdapter (a);

LoadApplication (new App ());
Expand Down
282 changes: 151 additions & 131 deletions Source/Platform Stacks/Robotics.Mobile.Core.Droid/Bluetooth/LE/Adapter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,139 +2,159 @@
using System.Collections.Generic;
using Android.Bluetooth;
using System.Threading.Tasks;
using System.Linq;
using Android.Content;
using Java.Interop;

namespace Robotics.Mobile.Core.Bluetooth.LE
{
/// <summary>
/// TODO: this really should be a singleton.
/// </summary>
public class Adapter : Java.Lang.Object, BluetoothAdapter.ILeScanCallback, IAdapter
{
// events
public event EventHandler<DeviceDiscoveredEventArgs> DeviceDiscovered = delegate {};
public event EventHandler<DeviceConnectionEventArgs> DeviceConnected = delegate {};
public event EventHandler<DeviceConnectionEventArgs> DeviceDisconnected = delegate {};
public event EventHandler ScanTimeoutElapsed = delegate {};

// class members
protected BluetoothManager _manager;
protected BluetoothAdapter _adapter;
protected GattCallback _gattCallback;

public bool IsScanning {
get { return this._isScanning; }
} protected bool _isScanning;

public IList<IDevice> DiscoveredDevices {
get {
return this._discoveredDevices;
}
} protected IList<IDevice> _discoveredDevices = new List<IDevice> ();

public IList<IDevice> ConnectedDevices {
get {
return this._connectedDevices;
}
} protected IList<IDevice> _connectedDevices = new List<IDevice>();


public Adapter ()
{
var appContext = Android.App.Application.Context;
// get a reference to the bluetooth system service
this._manager = (BluetoothManager) appContext.GetSystemService("bluetooth");
this._adapter = this._manager.Adapter;

this._gattCallback = new GattCallback (this);

this._gattCallback.DeviceConnected += (object sender, DeviceConnectionEventArgs e) => {
this._connectedDevices.Add ( e.Device);
this.DeviceConnected (this, e);
};

this._gattCallback.DeviceDisconnected += (object sender, DeviceConnectionEventArgs e) => {
// TODO: remove the disconnected device from the _connectedDevices list
// i don't think this will actually work, because i'm created a new underlying device here.
//if(this._connectedDevices.Contains(
this.DeviceDisconnected (this, e);
};
}

//TODO: scan for specific service type eg. HeartRateMonitor
public async void StartScanningForDevices (Guid serviceUuid)
{
StartScanningForDevices ();
// throw new NotImplementedException ("Not implemented on Android yet, look at _adapter.StartLeScan() overload");
}
public async void StartScanningForDevices ()
{
Console.WriteLine ("Adapter: Starting a scan for devices.");

// clear out the list
this._discoveredDevices = new List<IDevice> ();

// start scanning
this._isScanning = true;
this._adapter.StartLeScan (this);

// in 10 seconds, stop the scan
await Task.Delay (10000);

// if we're still scanning
if (this._isScanning) {
Console.WriteLine ("BluetoothLEManager: Scan timeout has elapsed.");
this._adapter.StopLeScan (this);
this.ScanTimeoutElapsed (this, new EventArgs ());
}
}

public void StopScanningForDevices ()
{
Console.WriteLine ("Adapter: Stopping the scan for devices.");
this._isScanning = false;
this._adapter.StopLeScan (this);
}

public void OnLeScan (BluetoothDevice bleDevice, int rssi, byte[] scanRecord)
{
Console.WriteLine ("Adapter.LeScanCallback: " + bleDevice.Name);
// TODO: for some reason, this doesn't work, even though they have the same pointer,
// it thinks that the item doesn't exist. so i had to write my own implementation
// if(!this._discoveredDevices.Contains(device) ) {
// this._discoveredDevices.Add (device );
// }
Device device = new Device (bleDevice, null, null, rssi);

if (!DeviceExistsInDiscoveredList (bleDevice))
this._discoveredDevices.Add (device);
// TODO: in the cross platform API, cache the RSSI
// TODO: shouldn't i only raise this if it's not already in the list?
this.DeviceDiscovered (this, new DeviceDiscoveredEventArgs { Device = device });
}

protected bool DeviceExistsInDiscoveredList(BluetoothDevice device)
{
foreach (var d in this._discoveredDevices) {
// TODO: verify that address is unique
if (device.Address == ((BluetoothDevice)d.NativeDevice).Address)
return true;
}
return false;
}


public void ConnectToDevice (IDevice device)
{
// returns the BluetoothGatt, which is the API for BLE stuff
// TERRIBLE API design on the part of google here.
((BluetoothDevice)device.NativeDevice).ConnectGatt (Android.App.Application.Context, true, this._gattCallback);
}

public void DisconnectDevice (IDevice device)
{
((Device) device).Disconnect();
}

}
/// <summary>
/// TODO: this really should be a singleton.
/// </summary>
public class Adapter : Java.Lang.Object, BluetoothAdapter.ILeScanCallback, IAdapter
{
// events
public event EventHandler<DeviceDiscoveredEventArgs> DeviceDiscovered = delegate { };
public event EventHandler<DeviceConnectionEventArgs> DeviceConnected = delegate { };
public event EventHandler<DeviceConnectionEventArgs> DeviceDisconnected = delegate { };
public event EventHandler ScanTimeoutElapsed = delegate { };

// class members
protected BluetoothManager _manager;
protected BluetoothAdapter _adapter;
protected GattCallback _gattCallback;

public TimeSpan ScanTimeout { get; set; }

public bool IsScanning
{
get { return this._isScanning; }
}
protected bool _isScanning;

public IList<IDevice> DiscoveredDevices
{
get
{
return this._discoveredDevices;
}
}
protected IList<IDevice> _discoveredDevices = new List<IDevice>();

public IList<IDevice> ConnectedDevices
{
get
{
return this._connectedDevices;
}
}
protected IList<IDevice> _connectedDevices = new List<IDevice>();

private Context _appContext;

public Adapter(Context appContext)
{
ScanTimeout = TimeSpan.FromSeconds(10); // default timeout is 10 seconds
_appContext = appContext;
// get a reference to the bluetooth system service
this._manager = appContext.GetSystemService("bluetooth").JavaCast<BluetoothManager>();
this._adapter = this._manager.Adapter;

this._gattCallback = new GattCallback(this);

this._gattCallback.DeviceConnected += (object sender, DeviceConnectionEventArgs e) =>
{
if (ConnectedDevices.Find((BluetoothDevice)e.Device.NativeDevice) == null)
{
_connectedDevices.Add(e.Device);
this.DeviceConnected(this, e);
}
};

this._gattCallback.DeviceDisconnected += (object sender, DeviceConnectionEventArgs e) =>
{
var device = ConnectedDevices.Find((BluetoothDevice)e.Device.NativeDevice);
if (device != null)
{
_connectedDevices.Remove(device);
this.DeviceDisconnected(this, e);
}
};
}

//TODO: scan for specific service type eg. HeartRateMonitor
public void StartScanningForDevices(Guid serviceUuid)
{
StartScanningForDevices();
// throw new NotImplementedException ("Not implemented on Android yet, look at _adapter.StartLeScan() overload");
}

public async void StartScanningForDevices()
{
Console.WriteLine("Adapter: Starting a scan for devices.");

// clear out the list
_discoveredDevices.Clear();

if (_adapter == null)
return;

// start scanning
_isScanning = true;
_adapter.StartLeScan(this);

// after the given timeout, stop the scan
await Task.Delay(ScanTimeout);

// if we're still scanning
if (_isScanning)
{
Console.WriteLine("BluetoothLEManager: Scan timeout has elapsed.");
_adapter.StopLeScan(this);
ScanTimeoutElapsed(this, new EventArgs());
}
}

public void StopScanningForDevices()
{
Console.WriteLine("Adapter: Stopping the scan for devices.");
if (_adapter == null)
return;

_isScanning = false;
_adapter.StopLeScan(this);
}

public void OnLeScan(BluetoothDevice bleDevice, int rssi, byte[] scanRecord)
{
Console.WriteLine("Adapter.LeScanCallback: " + bleDevice.Name);
// TODO: for some reason, this doesn't work, even though they have the same pointer,
// it thinks that the item doesn't exist. so i had to write my own implementation
// if(!this._discoveredDevices.Contains(device) ) {
// this._discoveredDevices.Add (device );
// }

if (DiscoveredDevices.Find(bleDevice) == null)
{
var device = new Device(bleDevice, null, null, rssi);
this._discoveredDevices.Add(device);
// TODO: in the cross platform API, cache the RSSI
this.DeviceDiscovered(this, new DeviceDiscoveredEventArgs { Device = device });
}
}

public void ConnectToDevice(IDevice device)
{
// returns the BluetoothGatt, which is the API for BLE stuff
// TERRIBLE API design on the part of google here.
var androidDevice = device as Device;
androidDevice.Connect(_appContext, _gattCallback);
}

public void DisconnectDevice(IDevice device)
{
((Device)device).Disconnect();
}
}
}