diff --git a/src/NmeaParser/BluetoothDevice.Android.cs b/src/NmeaParser/BluetoothDevice.Android.cs index 34bb3f5..a0a51cf 100644 --- a/src/NmeaParser/BluetoothDevice.Android.cs +++ b/src/NmeaParser/BluetoothDevice.Android.cs @@ -42,13 +42,15 @@ namespace NmeaParser public class BluetoothDevice : NmeaDevice { private static Java.Util.UUID SERIAL_UUID = Java.Util.UUID.FromString("00001101-0000-1000-8000-00805F9B34FB")!; - private Android.Bluetooth.BluetoothDevice m_device; + private readonly Android.Bluetooth.BluetoothDevice m_device; private BluetoothSocket? m_socket; + private readonly Android.Content.Context? m_context; /// /// Gets a list of bluetooth devices that supports serial communication /// /// A set of bluetooth devices available that supports serial connections + [Obsolete("Use GetBluetoothSerialDevices(Context)")] public static IEnumerable GetBluetoothSerialDevices() { var adapter = Android.Bluetooth.BluetoothAdapter.DefaultAdapter; @@ -59,19 +61,58 @@ namespace NmeaParser } } + /// + /// Gets a list of bluetooth devices that supports serial communication + /// + /// The Android context + /// A set of bluetooth devices available that supports serial connections + public static IEnumerable GetBluetoothSerialDevices(Android.Content.Context context) + { + var bluetoothManager = context.GetSystemService(Android.Content.Context.BluetoothService) as BluetoothManager; + var adapter = bluetoothManager?.Adapter; + if (adapter != null && adapter.IsEnabled && adapter.BondedDevices is not null) + { + foreach (var b in adapter.BondedDevices.Where(d => d.GetUuids()?.Any(t => SERIAL_UUID.CompareTo(t.Uuid) == 0) == true)) + yield return b; + } + } + /// /// Initializes a new instance of the class. /// /// The Android Bluetooth Device. + [Obsolete("Use BluetoothDevice(BluetoothDevice, Context)")] public BluetoothDevice(Android.Bluetooth.BluetoothDevice device) { m_device = device ?? throw new ArgumentNullException(nameof(device)); } - - /// - protected override Task OpenStreamAsync() + + /// + /// Initializes a new instance of the class. + /// + /// The Android Bluetooth Device. + /// The Android context + public BluetoothDevice(Android.Bluetooth.BluetoothDevice device, Android.Content.Context context) { - var adapter = Android.Bluetooth.BluetoothAdapter.DefaultAdapter; + m_device = device ?? throw new ArgumentNullException(nameof(device)); + m_context = context ?? throw new ArgumentNullException(nameof(context)); + } + + /// + protected override async Task OpenStreamAsync() + { + BluetoothAdapter? adapter; + if (m_context is not null) + { + var bluetoothManager = m_context.GetSystemService(Android.Content.Context.BluetoothService) as BluetoothManager; + adapter = bluetoothManager?.Adapter; + } + else + { +#pragma warning disable CS0618 // Type or member is obsolete. Only used if deprecated constructor is used + adapter = Android.Bluetooth.BluetoothAdapter.DefaultAdapter; +#pragma warning restore CS0618 // Type or member is obsolete + } if (adapter?.IsEnabled != true) throw new InvalidOperationException("Bluetooth Adapter not enabled"); var d = adapter.GetRemoteDevice(m_device.Address); @@ -80,11 +121,25 @@ namespace NmeaParser var socket = d.CreateRfcommSocketToServiceRecord(SERIAL_UUID); if (socket == null) throw new InvalidOperationException($"Failed to create socket"); - socket.Connect(); + try + { + await socket.ConnectAsync(); + } + catch (Java.IO.IOException) + { + // This sometimes fails. Use fallback approach to open socket + // Based on https://stackoverflow.com/a/41627149 + socket?.Dispose(); + var m = d.Class?.GetMethod("createRfcommSocket", new Java.Lang.Class[] { Java.Lang.Integer.Type! })!; + if (m is null) + throw; + socket = (Android.Bluetooth.BluetoothSocket)m.Invoke(d, new Java.Lang.Object[] { 1 })!; + socket.Connect(); + } m_socket = socket; if (socket.InputStream == null) throw new InvalidOperationException($"Failed to create socket input stream"); - return Task.FromResult(socket.InputStream); + return socket.InputStream; } /// diff --git a/src/NmeaParser/NmeaParser.csproj b/src/NmeaParser/NmeaParser.csproj index 298481b..7485663 100644 --- a/src/NmeaParser/NmeaParser.csproj +++ b/src/NmeaParser/NmeaParser.csproj @@ -31,6 +31,7 @@ enable logo.png + false diff --git a/src/NmeaParser/SystemNmeaDevice.Android.cs b/src/NmeaParser/SystemNmeaDevice.Android.cs index cb22a49..ea119dc 100644 --- a/src/NmeaParser/SystemNmeaDevice.Android.cs +++ b/src/NmeaParser/SystemNmeaDevice.Android.cs @@ -68,7 +68,11 @@ namespace NmeaParser stream = new StringStream(); listener = new Listener(); listener.NmeaMessage += (s, e) => stream?.Append(e); +#if MONOANDROID50 bool success = manager.AddNmeaListener(listener); +#else + bool success = manager.AddNmeaListener(listener, null); +#endif manager.RequestLocationUpdates(LocationManager.GpsProvider, 0, 0f, listener ); return Task.FromResult(stream); } @@ -76,10 +80,13 @@ namespace NmeaParser /// protected override Task CloseStreamAsync(Stream stream) { - manager.RemoveUpdates(listener); - manager.RemoveNmeaListener(listener); - listener?.Dispose(); - listener = null; + if (listener is not null) + { + manager.RemoveUpdates(listener); + manager.RemoveNmeaListener(listener); + listener.Dispose(); + listener = null; + } stream.Dispose(); return Task.CompletedTask; } diff --git a/src/SampleApp.Droid/MainActivity.cs b/src/SampleApp.Droid/MainActivity.cs index 397c97d..dd28e8b 100644 --- a/src/SampleApp.Droid/MainActivity.cs +++ b/src/SampleApp.Droid/MainActivity.cs @@ -1,4 +1,5 @@ using Android; +using Android.Bluetooth; using Android.Content.PM; using AndroidX.Core.App; @@ -32,15 +33,15 @@ public class MainActivity : Activity return; } - foreach (var d in NmeaParser.BluetoothDevice.GetBluetoothSerialDevices()) + foreach (var d in NmeaParser.BluetoothDevice.GetBluetoothSerialDevices(ApplicationContext)) { - devices[d.Name + " " + d.Address] = d.Address; + devices[d.Name + " " + d.Address] = d; } devicePicker.Adapter = new ArrayAdapter(this, Android.Resource.Layout.SimpleSpinnerDropDownItem, devices.Keys.ToArray()); devicePicker.SetSelection(0); } - private Dictionary devices = new Dictionary(); + private Dictionary devices = new Dictionary(); private void StopButton_Click(object? sender, EventArgs e) { @@ -100,25 +101,27 @@ public class MainActivity : Activity { try { - status.Text = "Opening bluetooth..."; - var adapter = Android.Bluetooth.BluetoothAdapter.DefaultAdapter; - var bt = Android.Bluetooth.BluetoothAdapter.DefaultAdapter.GetRemoteDevice(btAddress); - Java.Util.UUID SERIAL_UUID = Java.Util.UUID.FromString("00001101-0000-1000-8000-00805F9B34FB"); //UUID for Serial Device Service - socket = bt.CreateRfcommSocketToServiceRecord(SERIAL_UUID); - try - { - await socket.ConnectAsync(); - } - catch (Java.IO.IOException) - { - // This sometimes fails. Use fallback approach to open socket - // Based on https://stackoverflow.com/a/41627149 - socket?.Dispose(); - var m = bt.Class.GetMethod("createRfcommSocket", new Java.Lang.Class[] { Java.Lang.Integer.Type }); - socket = (Android.Bluetooth.BluetoothSocket)m.Invoke(bt, new Java.Lang.Object[] { 1 })!; - socket.Connect(); - } - listener = new NmeaParser.StreamDevice(socket.InputStream); + listener = new NmeaParser.BluetoothDevice(btAddress, ApplicationContext!); + //status.Text = "Opening bluetooth..."; + //var bluetoothManager = ApplicationContext!.GetSystemService(Android.Content.Context.BluetoothService) as Android.Bluetooth.BluetoothManager; + //var adapter = bluetoothManager?.Adapter; + //var bt = adapter.GetRemoteDevice(btAddress); + //Java.Util.UUID SERIAL_UUID = Java.Util.UUID.FromString("00001101-0000-1000-8000-00805F9B34FB"); //UUID for Serial Device Service + //socket = bt.CreateRfcommSocketToServiceRecord(SERIAL_UUID); + //try + //{ + // await socket.ConnectAsync(); + //} + //catch (Java.IO.IOException) + //{ + // // This sometimes fails. Use fallback approach to open socket + // // Based on https://stackoverflow.com/a/41627149 + // socket?.Dispose(); + // var m = bt.Class.GetMethod("createRfcommSocket", new Java.Lang.Class[] { Java.Lang.Integer.Type }); + // socket = (Android.Bluetooth.BluetoothSocket)m.Invoke(bt, new Java.Lang.Object[] { 1 })!; + // socket.Connect(); + //} + //listener = new NmeaParser.StreamDevice(socket.InputStream); } catch (System.Exception ex) { @@ -132,11 +135,23 @@ public class MainActivity : Activity { listener.MessageReceived += Listener_MessageReceived; status!.Text += "\nOpening device..."; - await listener.OpenAsync(); - status.Text += "\nConnected!"; - startButton.Enabled = !(stopButton.Enabled = true); - monitor = new NmeaParser.Gnss.GnssMonitor(listener); - monitor.LocationChanged += Monitor_LocationChanged; + try + { + await listener.OpenAsync(); + status.Text += "\nConnected!"; + monitor = new NmeaParser.Gnss.GnssMonitor(listener); + monitor.LocationChanged += Monitor_LocationChanged; + startButton.Enabled = !(stopButton.Enabled = true); + } + catch (Exception ex) + { + status.Text += "\nError opening device:\n" + ex.Message; + startButton.Enabled = true; + stopButton.Enabled = false; + } + finally + { + } } else {