using InABox.Core; using Plugin.BLE; using Plugin.BLE.Abstractions; using Plugin.BLE.Abstractions.Contracts; using Plugin.BLE.Abstractions.EventArgs; using System; using System.Linq; using System.Threading.Tasks; namespace InABox.Avalonia.Platform.iOS; public class iOS_Bluetooth : IBluetooth { public Logger? Logger { get; set; } public event EventHandler? Changed; public CoreObservableCollection Devices { get; } = new (); private IBluetoothLE _ble; private IAdapter _adapter; private ScanFilterOptions _scanoptions = new ScanFilterOptions(); private StaleItemMonitor _staleItemMonitor; public iOS_Bluetooth() { _staleItemMonitor = new StaleItemMonitor( Devices, x=>x.LastSeen, x =>x.ID, TimeSpan.FromSeconds(10), TimeSpan.FromMilliseconds(500) ); _ble = CrossBluetoothLE.Current; _adapter = CrossBluetoothLE.Current.Adapter; _adapter.ScanTimeout = 5000; _adapter.ScanTimeoutElapsed += async (sender, args) => { await _adapter.StartScanningForDevicesAsync(_scanoptions); }; _adapter.DeviceDiscovered += AdapterOnDeviceDiscovered; //_adapter.ScanMatchMode = ScanMatchMode.AGRESSIVE; //_adapter.ScanMode = ScanMode.LowLatency; // Task.Run(() => // { // while (true) // { // var stale = Devices.ToArray().Where(x => (x == null) || (x.LastSeen < DateTime.Now.Subtract(new TimeSpan(0, 0, 10)))) // .ToArray(); // if (stale.Any()) // { // Devices.RemoveRange(stale); // Changed?.Invoke(this, EventArgs.Empty); // } // // Task.Delay(500); // } // }); } // public static async Task IsPermitted() where TPermission : Permissions.BasePermission, new() // { // try // { // PermissionStatus status = await Permissions.CheckStatusAsync(); // if (status == PermissionStatus.Granted) // return true; // var request = await Permissions.RequestAsync(); // return request == PermissionStatus.Granted; // // } // catch (TaskCanceledException ex) // { // return false; // } // } public async Task IsAvailable() { //if (await IsPermitted()) //{ //} return _adapter != null; } public async Task StartScanningAsync(Guid configServiceId) { _scanoptions.ServiceUuids = [configServiceId]; return await StartScanningAsync(); } private async Task StartScanningAsync() { System.Diagnostics.Debug.WriteLine($"{DateTime.Now:O} Starting Bluetooth Scanning..."); await _staleItemMonitor.StartAsync(); await _adapter.StartScanningForDevicesAsync(_scanoptions); System.Diagnostics.Debug.WriteLine($"{DateTime.Now:O} Bluetooth Scanning Started."); return true; } private void AdapterOnDeviceDiscovered(object? sender, DeviceEventArgs e) { System.Diagnostics.Debug.WriteLine($"{DateTime.Now:O} Found device {e.Device.Name}..."); var device = Devices.FirstOrDefault(x => string.Equals(x.ID, e.Device.Id.ToString())); if (device == null) { System.Diagnostics.Debug.WriteLine($"{DateTime.Now:O} Adding device {e.Device.Id}..."); device = new iOS_BluetoothDevice(e.Device); Devices.Add(device); System.Diagnostics.Debug.WriteLine($"{DateTime.Now:O} Added device {device.ID}."); } device.LastSeen = DateTime.Now; device.ManufacturerData = e.Device.AdvertisementRecords .FirstOrDefault(x => x.Type == AdvertisementRecordType.ManufacturerSpecificData)?.Data ?? []; System.Diagnostics.Debug.WriteLine($"{DateTime.Now:O} Notifying Changes..."); Changed?.Invoke(this, EventArgs.Empty); System.Diagnostics.Debug.WriteLine($"{DateTime.Now:O} Changes Notified."); } public async Task StopScanningAsync() { System.Diagnostics.Debug.WriteLine($"{DateTime.Now:O} Stopping Bluetooth Scanning..."); await _staleItemMonitor.StopAsync(); await _adapter.StopScanningForDevicesAsync(); System.Diagnostics.Debug.WriteLine($"{DateTime.Now:O} Bluetooth Scanning Stopped."); return true; } public async Task Connect(IBluetoothDevice device) { if (Guid.TryParse(device.ID, out var deviceid)) { System.Diagnostics.Debug.WriteLine($"{DateTime.Now:O} Connecting Bluetooth Device {device.ID}..."); await StopScanningAsync(); var connected = await _adapter.ConnectToKnownDeviceAsync(deviceid); if (connected != null) { var result = new iOS_ConnectedBluetoothDevice(device); System.Diagnostics.Debug.WriteLine($"{DateTime.Now:O} Bluetooth Device {device.ID} connected."); return result; } await StartScanningAsync(); System.Diagnostics.Debug.WriteLine($"{DateTime.Now:O} Connection to {device.ID} failed."); } System.Diagnostics.Debug.WriteLine($"{DateTime.Now:O} Device has invalid ID."); return null; } public async Task Disconnect(IConnectedBluetoothDevice device) { if (device is iOS_ConnectedBluetoothDevice connected) { System.Diagnostics.Debug.WriteLine($"{DateTime.Now:O} Disconnecting Bluetooth Device {device.ID}..."); await _adapter.DisconnectDeviceAsync(connected.Device.Native); await StartScanningAsync(); System.Diagnostics.Debug.WriteLine($"{DateTime.Now:O} Bluetooth Device {device.ID} disconnected."); } return true; } }