using Android.Bluetooth; using Android.Bluetooth.LE; using Android.Content; using InABox.Core; using Microsoft.Maui.ApplicationModel; namespace InABox.Avalonia.Platform.Android; public class Android_Bluetooth : IBluetooth { public Logger? Logger { get; set; } public CoreObservableCollection Devices { get; private set; } = new CoreObservableCollection(); private readonly BluetoothLeScanner? _scanner; public event EventHandler? Changed; public Android_Bluetooth() { var _manager = Application.Context.GetSystemService(Context.BluetoothService) as BluetoothManager; var _adapter = _manager?.Adapter; _scanner = _adapter?.BluetoothLeScanner; Task.Run(() => { while (true) { var stale = Devices.ToArray().Where(x => (x == null) || (x.LastSeen < DateTime.Now.Subtract(new TimeSpan(0, 0, 5)))) .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 _scanner != null; return false; } BluetoothScanManager? _callback; public async Task StartScanningAsync(Guid configServiceId) { if (await IsAvailable()) { _callback = new BluetoothScanManager((d) => DoDeviceFound(d, configServiceId), ScanStopped); _scanner!.StartScan(_callback); return true; } return false; } public async Task StopScanningAsync() { if (await IsAvailable()) { if (_callback != null) { _scanner!.StopScan(_callback); return true; } } return false; } private void DoDeviceFound(ScanResult device, Guid configServiceId) { var isTarget = device.ScanRecord?.ServiceUuids? .Any(x=>string.Equals(x.ToString(),configServiceId.ToString(), StringComparison.OrdinalIgnoreCase)) ?? false; if (isTarget) { var abd = Devices.FirstOrDefault(x => x.ID == device.Device?.Address); if (abd == null) { if (isTarget) { var services = device.ScanRecord?.ServiceUuids? .Select(x => Guid.Parse(x.ToString())) .Where(x => !x.ToString().ToUpper().EndsWith("-0000-1000-8000-00805F9B34FB") && configServiceId != x) .ToArray() ?? []; if (services.Any()) { abd = new Android_BluetoothDevice(device, services, DateTime.Now); Devices.Add(abd); Changed?.Invoke(this, EventArgs.Empty); } } } else abd.LastSeen = DateTime.Now; } } private void ScanStopped() { _callback = null; } public async Task Connect(IBluetoothDevice device) { if (device is Android_BluetoothDevice d && d.Scan.Device is BluetoothDevice bd) { var result = new Android_ConnectedBluetoothDevice(bd); if (await result.ConnectAsync()) { await result.DiscoverServicesAsync(); return result; } } return null; } public async Task Disconnect(IConnectedBluetoothDevice device) { if (device is Android_ConnectedBluetoothDevice d) d.Dispose(); return await Task.FromResult(true); } }