Просмотр исходного кода

Working on Bluetooth Platform Issues

frankvandenbos 5 месяцев назад
Родитель
Сommit
f33f4e57cb

+ 112 - 0
InABox.Avalonia.Platform.Android/BTConnector.cs

@@ -0,0 +1,112 @@
+using Android.Bluetooth;
+using Android.Content;
+using System;
+
+namespace MyLibrary.Bluetooth
+{
+    public class BLEConnector
+    {
+        private BluetoothGatt _bluetoothGatt;
+        private BluetoothGattCallback _gattCallback;
+
+        public BLEConnector()
+        {
+            _gattCallback = new CustomGattCallback();
+        }
+
+        /// <summary>
+        /// Connect to the specified BluetoothDevice.
+        /// </summary>
+        /// <param name="device">The BluetoothDevice to connect to.</param>
+        /// <param name="context">The Android context required for the connection.</param>
+        public void Connect(BluetoothDevice device, Context context)
+        {
+            if (device == null)
+            {
+                throw new ArgumentNullException(nameof(device), "Device cannot be null");
+            }
+
+            // Initiate the connection
+            _bluetoothGatt = device.ConnectGatt(context, false, _gattCallback);
+        }
+
+        /// <summary>
+        /// Disconnect the current connection.
+        /// </summary>
+        public void Disconnect()
+        {
+            _bluetoothGatt?.Disconnect();
+            _bluetoothGatt?.Close();
+            _bluetoothGatt = null;
+        }
+
+        /// <summary>
+        /// Custom GATT callback to handle Bluetooth events.
+        /// </summary>
+        private class CustomGattCallback : BluetoothGattCallback
+        {
+            public override void OnConnectionStateChange(BluetoothGatt gatt, GattStatus status, ProfileState newState)
+            {
+                base.OnConnectionStateChange(gatt, status, newState);
+
+                if (status == GattStatus.Success && newState == ProfileState.Connected)
+                {
+                    Console.WriteLine("Connected to device!");
+                    gatt.DiscoverServices();
+                }
+                else if (newState == ProfileState.Disconnected)
+                {
+                    Console.WriteLine("Disconnected from device.");
+                }
+                else
+                {
+                    Console.WriteLine($"Connection state changed: {newState}, Status: {status}");
+                }
+            }
+
+            public override void OnServicesDiscovered(BluetoothGatt gatt, GattStatus status)
+            {
+                base.OnServicesDiscovered(gatt, status);
+
+                if (status == GattStatus.Success)
+                {
+                    Console.WriteLine("Services discovered:");
+                    foreach (var service in gatt.Services)
+                    {
+                        Console.WriteLine($"Service: {service.Uuid}");
+                    }
+                }
+                else
+                {
+                    Console.WriteLine($"Failed to discover services. Status: {status}");
+                }
+            }
+
+            public override void OnCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, GattStatus status)
+            {
+                base.OnCharacteristicRead(gatt, characteristic, status);
+
+                if (status == GattStatus.Success)
+                {
+                    Console.WriteLine($"Characteristic Read: {characteristic.Uuid}, Value: {BitConverter.ToString(characteristic.GetValue())}");
+                }
+            }
+
+            public override void OnCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, GattStatus status)
+            {
+                base.OnCharacteristicWrite(gatt, characteristic, status);
+
+                if (status == GattStatus.Success)
+                {
+                    Console.WriteLine($"Characteristic Write Successful: {characteristic.Uuid}");
+                }
+            }
+
+            public override void OnCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic)
+            {
+                base.OnCharacteristicChanged(gatt, characteristic);
+                Console.WriteLine($"Characteristic Changed: {characteristic.Uuid}, Value: {BitConverter.ToString(characteristic.GetValue())}");
+            }
+        }
+    }
+}

+ 77 - 0
InABox.Avalonia.Platform.Android/BTManager.cs

@@ -0,0 +1,77 @@
+using Android.Bluetooth;
+using Android.Bluetooth.LE;
+using Android.Content;
+using Android.OS;
+using System;
+using System.Collections.Generic;
+
+namespace MyLibrary.Bluetooth
+{
+    public class BLEScanner
+    {
+        private BluetoothManager _bluetoothManager;
+        private BluetoothAdapter _bluetoothAdapter;
+        private BluetoothLeScanner _bluetoothLeScanner;
+        private ScanCallback _scanCallback;
+
+        public BLEScanner(Context context)
+        {
+            _bluetoothManager = (BluetoothManager)context.GetSystemService(Context.BluetoothService);
+            _bluetoothAdapter = _bluetoothManager.Adapter;
+            _bluetoothLeScanner = _bluetoothAdapter?.BluetoothLeScanner;
+
+            if (_bluetoothLeScanner == null)
+            {
+                throw new Exception("Bluetooth LE Scanner is not available.");
+            }
+        }
+        
+        /*
+         var bleScanner = new BLEScanner(Android.App.Application.Context);
+
+bleScanner.StartScan(
+    
+);
+
+// Stop the scan after 10 seconds
+Task.Delay(10000).ContinueWith(_ => bleScanner.StopScan());
+
+         */
+
+        public void StartScan(Action<ScanResult> onDeviceFound, Action onScanStopped = null)
+        {
+            _scanCallback = new CustomScanCallback(onDeviceFound, onScanStopped);
+            _bluetoothLeScanner.StartScan(_scanCallback);
+        }
+
+        public void StopScan()
+        {
+            _bluetoothLeScanner?.StopScan(_scanCallback);
+        }
+
+        private class CustomScanCallback : ScanCallback
+        {
+            private readonly Action<ScanResult> _onDeviceFound;
+            private readonly Action _onScanStopped;
+
+            public CustomScanCallback(Action<ScanResult> onDeviceFound, Action onScanStopped)
+            {
+                _onDeviceFound = onDeviceFound;
+                _onScanStopped = onScanStopped;
+            }
+
+            public override void OnScanResult(ScanCallbackType callbackType, ScanResult result)
+            {
+                base.OnScanResult(callbackType, result);
+                _onDeviceFound?.Invoke(result);
+            }
+
+            public override void OnScanFailed(ScanFailure errorCode)
+            {
+                base.OnScanFailed(errorCode);
+                _onScanStopped?.Invoke();
+                throw new Exception($"Scan failed with error code: {errorCode}");
+            }
+        }
+    }
+}

+ 307 - 52
InABox.Avalonia.Platform.Android/Bluetooth.Android.cs

@@ -1,86 +1,341 @@
-using InABox.Avalonia.Platform;
+using System.Collections.ObjectModel;
+using Android.Bluetooth;
+using Android.Bluetooth.LE;
+using Android.Content;
+using Android.OS;
+using Android.Text.Style;
+using FluentResults;
 using InABox.Core;
 using Microsoft.Maui.ApplicationModel;
-using Plugin.BLE;
-using Plugin.BLE.Abstractions;
-using Plugin.BLE.Abstractions.Contracts;
 
 namespace InABox.Avalonia.Platform.Android;
 
-public class Android_Bluetooth : IBluetooth
+public class Android_BluetoothDevice(ScanResult scan) : IBluetoothDevice, IDisposable
 {
-    public Logger? Logger { get; set; }
+    public ScanResult Scan { get; } = scan;
+    public string ID { get; } = scan.Device?.Address ?? string.Empty;
+    public string Name { get; } = scan.ScanRecord?.DeviceName ?? "Unknown Device";
+
+    public void Dispose()
+    {
+        Scan.Dispose();
+    }
+}
+
+public class Android_ConnectedBluetoothDevice(BluetoothDevice device) : BluetoothGattCallback,IConnectedBluetoothDevice
+{
+
+    private BluetoothDevice _device = device;
+    public string ID { get; } = device?.Address ?? string.Empty;
+    public string Name { get; } = device?.Name ?? "Unknown Device";
     
-    public async Task<bool> IsAvailable()
+    private BluetoothGatt? _bluetoothGatt;
+    
+    private TaskCompletionSource<bool> _connectionTaskCompletionSource;
+    private TaskCompletionSource<bool> _serviceDiscoveryTaskCompletionSource;
+    private TaskCompletionSource<byte[]> _readTaskCompletionSource;
+    private TaskCompletionSource<bool> _writeTaskCompletionSource;
+
+    public async Task<bool> ConnectAsync()
+    {
+        _connectionTaskCompletionSource = new TaskCompletionSource<bool>();
+        _bluetoothGatt = _device.ConnectGatt(Application.Context, false, this);
+        return await _connectionTaskCompletionSource.Task;
+    }
+
+    public override void OnConnectionStateChange(BluetoothGatt? gatt, GattStatus status, ProfileState newState)
+    {
+        base.OnConnectionStateChange(gatt, status, newState);
+
+        if (newState == ProfileState.Connected && status == GattStatus.Success)
+        {
+            Console.WriteLine("Connected to GATT server.");
+            _connectionTaskCompletionSource?.TrySetResult(true);
+        }
+        else
+        {
+            Console.WriteLine("Failed to connect or disconnected.");
+            _connectionTaskCompletionSource?.TrySetResult(false);
+            Dispose();
+        }
+    }
+
+    public async Task<bool> DiscoverServicesAsync()
+    {
+        _serviceDiscoveryTaskCompletionSource = new TaskCompletionSource<bool>();
+        _bluetoothGatt?.DiscoverServices();
+        return await _serviceDiscoveryTaskCompletionSource.Task;
+    }
+
+    public override void OnServicesDiscovered(BluetoothGatt? gatt, GattStatus status)
+    {
+        base.OnServicesDiscovered(gatt, status);
+
+        if (status == GattStatus.Success)
+        {
+            Console.WriteLine("Services discovered.");
+            _serviceDiscoveryTaskCompletionSource?.TrySetResult(true);
+        }
+        else
+        {
+            Console.WriteLine("Service discovery failed.");
+            _serviceDiscoveryTaskCompletionSource?.TrySetResult(false);
+        }
+    }
+    
+    public async Task<byte[]?> ReadAsync(Guid serviceid, Guid characteristicid)
+    {
+        byte[]? result = null;
+        if (_bluetoothGatt != null)
+        {
+            var service = _bluetoothGatt.GetService(Java.Util.UUID.FromString(serviceid.ToString()));
+            if (service != null)
+            {
+                var characteristic = service.GetCharacteristic(Java.Util.UUID.FromString(characteristicid.ToString()));
+                if (characteristic != null)
+                    result = await ReadCharacteristicAsync(characteristic);
+            }
+        }
+        return result;
+    }
+
+    private async Task<byte[]?> ReadCharacteristicAsync(BluetoothGattCharacteristic characteristic)
+    {
+        if (_bluetoothGatt != null)
+        {
+            _readTaskCompletionSource = new TaskCompletionSource<byte[]>();
+
+            if (!_bluetoothGatt.ReadCharacteristic(characteristic))
+            {
+                _readTaskCompletionSource.TrySetException(
+                    new InvalidOperationException("Failed to initiate characteristic read."));
+            }
+
+            return await _readTaskCompletionSource.Task;
+        }
+        return null;
+    }
+
+    public override void OnCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, byte[] data, GattStatus status)
+    {
+        base.OnCharacteristicRead(gatt, characteristic, data, status);
+
+        if (status == GattStatus.Success)
+        {
+            Console.WriteLine("Characteristic read successfully.");
+            _readTaskCompletionSource?.TrySetResult(data);
+        }
+        else
+        {
+            Console.WriteLine("Failed to read characteristic.");
+            _readTaskCompletionSource?.TrySetException(new InvalidOperationException("Characteristic read failed."));
+        }
+    }
+    
+    public async Task<bool> WriteAsync(Guid serviceid, Guid characteristicid, byte[] data)
+    {
+        var result = false;
+        if (_bluetoothGatt != null)
+        {
+            var service = _bluetoothGatt.GetService(Java.Util.UUID.FromString(serviceid.ToString()));
+            if (service != null)
+            {
+                var characteristic = service.GetCharacteristic(Java.Util.UUID.FromString(characteristicid.ToString()));
+                if (characteristic != null)
+                    result = await WriteCharacteristicAsync(characteristic, data);
+            }
+        }
+
+        return result;
+    }
+    
+    private async Task<bool> WriteCharacteristicAsync(BluetoothGattCharacteristic characteristic, byte[] data)
+    {
+        bool result = false;
+        if (_bluetoothGatt != null)
+        { 
+            _writeTaskCompletionSource = new TaskCompletionSource<bool>();
+             _bluetoothGatt.WriteCharacteristic(characteristic, data, 2); 
+            result = await _writeTaskCompletionSource.Task;
+        }
+        return result;
+    }
+
+    public override void OnCharacteristicWrite(BluetoothGatt? gatt, BluetoothGattCharacteristic? characteristic, GattStatus status)
+    {
+        base.OnCharacteristicWrite(gatt, characteristic, status);
+        if (status == GattStatus.Success)
+        {
+            Console.WriteLine("Characteristic written successfully.");
+            _writeTaskCompletionSource?.TrySetResult(true);
+        }
+        else
+        {
+            Console.WriteLine("Failed to write characteristic.");
+            _writeTaskCompletionSource?.TrySetException(new InvalidOperationException("Characteristic write failed."));
+        }
+    }
+    
+    public void Dispose()
     {
         try
         {
-            PermissionStatus status = await Permissions.CheckStatusAsync<Permissions.Bluetooth>();
-            if (status != PermissionStatus.Granted)
-                status = await Permissions.RequestAsync<Permissions.Bluetooth>();
-            return status == PermissionStatus.Granted;
+            _bluetoothGatt?.Disconnect();
+            _bluetoothGatt?.Close();
+        }
+        catch (Exception ex)
+        {
+            Console.WriteLine($"Error during disposal: {ex.Message}");
         }
-        catch (Exception e)
+        finally
         {
-            Console.WriteLine(e);
+            _bluetoothGatt = null;
         }
 
-        return false;
+        Console.WriteLine("Resources released.");
+    }
+    
+}
 
+public class BluetoothScanManager : ScanCallback
+{
+    private readonly Action<ScanResult> _onDeviceFound;
+    private readonly Action _onScanStopped;
 
+    public BluetoothScanManager(Action<ScanResult> onDeviceFound, Action onScanStopped)
+    {
+        _onDeviceFound = onDeviceFound;
+        _onScanStopped = onScanStopped;
     }
 
-    public async Task<bool> WriteAsync(string macaddress, Guid serviceid, Guid characteristicid, byte[] data)
+    public override void OnScanResult(ScanCallbackType callbackType, ScanResult result)
+    {
+        base.OnScanResult(callbackType, result);
+        _onDeviceFound?.Invoke(result);
+    }
+
+    public override void OnScanFailed(ScanFailure errorCode)
+    {
+        base.OnScanFailed(errorCode);
+        _onScanStopped?.Invoke();
+        throw new Exception($"Scan failed with error code: {errorCode}");
+    }
+}
+
+public class Android_Bluetooth : IBluetooth
+{
+    public Logger? Logger { get; set; }
+
+    public CoreObservableCollection<IBluetoothDevice> Devices { get; private set; } = new CoreObservableCollection<IBluetoothDevice>();
+    
+    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;
+    }
+    
+    public static async Task<bool> IsPermitted<TPermission>() where TPermission : Permissions.BasePermission, new()
     {
-        if (await IsAvailable() != true)
-            return false;
-        
-        IDevice? _device = null;
-        var adapter = CrossBluetoothLE.Current.Adapter;
-        adapter.DeviceDiscovered += (s, e) => _device = e.Device;
         try
         {
-            var options = new ScanFilterOptions();
-            options.DeviceAddresses = [macaddress];
-            options.ServiceUuids = [serviceid];
-            await adapter.StartScanningForDevicesAsync(options, x=> true, false);
+            PermissionStatus status = await Permissions.CheckStatusAsync<TPermission>();
+            if (status == PermissionStatus.Granted)
+                return true;
+            var request = await Permissions.RequestAsync<TPermission>();
+            return request == PermissionStatus.Granted;
+
         }
-        catch (Exception e)
+        catch (TaskCanceledException ex)
         {
-            Console.WriteLine(e);
-            Logger?.Error(e.Message);
+            return false;
         }
-        
-        if (_device != null)
+    }
+
+    public async Task<bool> IsAvailable()
+    {
+        if (await IsPermitted<Permissions.Bluetooth>()) 
+            return _scanner != null;
+        return false;
+    }
+    BluetoothScanManager? _callback;
+    
+    public async Task<bool> StartScanningAsync(Guid serviceid)
+    {
+        if (await IsAvailable())
         {
-            try
+            _callback = new BluetoothScanManager((d) => DoDeviceFound(d, serviceid), ScanStopped);
+            _scanner!.StartScan(_callback);
+            return true;
+        }
+        return false;
+    }
+
+    public async Task<bool> StopScanningAsync()
+    {
+        if (await IsAvailable())
+        {
+            if (_callback != null)
             {
-                //await adapter.StopScanningForDevicesAsync();
-                //ConnectParameters connectParameters = new ConnectParameters(true, true);
-                await adapter.ConnectToDeviceAsync(_device);
-                try
-                {
-                    var service = await _device.GetServiceAsync(serviceid);
-                    var characteristic = await service.GetCharacteristicAsync(characteristicid);
-                    var bytes = await characteristic.ReadAsync();
-                    await characteristic.WriteAsync(data);
-                }
-                catch (Exception e)
-                {
-                    Logger?.Error(e.Message);
-                    return false;
-                }
-                await adapter.DisconnectDeviceAsync(_device);
+                _scanner!.StopScan(_callback);
                 return true;
             }
-            catch (Exception e)
+        }
+        return false;
+    }
+    
+
+    private void DoDeviceFound(ScanResult device, Guid serviceid)
+    {
+        bool bMatch = true;
+        
+            bMatch = false;
+            if (device.ScanRecord?.ServiceUuids?.Any() == true)
             {
-                Logger?.Error(e.Message);
-                return false;
+                foreach (var uuid in device.ScanRecord.ServiceUuids)
+                {
+                    if (Guid.TryParse(uuid.ToString(), out Guid guid))
+                        bMatch = bMatch || Guid.Equals(serviceid,guid);
+                }
             }
-           
             
+        if (bMatch && !Devices.Any(x => x.ID == device.Device?.Address))
+        {
+            var abd = new Android_BluetoothDevice(device); 
+            Devices.Add(abd);
         }
-        return false;
+
+    }
+
+    private void ScanStopped()
+    {
+        _callback = null;
+    }
+
+    public async Task<IConnectedBluetoothDevice?> 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<bool> Disconnect(IConnectedBluetoothDevice device)
+    {
+        if (device is Android_ConnectedBluetoothDevice d)
+            d.Dispose();
+        
+        return await Task.FromResult(true);
     }
 }

+ 5 - 3
InABox.Avalonia.Platform.Android/InABox.Avalonia.Platform.Android.csproj

@@ -2,6 +2,8 @@
 
     <PropertyGroup>
         <TargetFramework>net8.0-android</TargetFramework>
+        <SupportedOSPlatformVersion>34</SupportedOSPlatformVersion>
+        <TargetPlatformSdkRootOverride>34</TargetPlatformSdkRootOverride>
         <ImplicitUsings>enable</ImplicitUsings>
         <Nullable>enable</Nullable>
         <LangVersion>default</LangVersion>
@@ -18,14 +20,14 @@
     <ItemGroup>
       <ProjectReference Include="..\..\..\..\..\development\inabox\inabox.logging.shared\InABox.Logging.Shared.csproj" />
       <ProjectReference Include="..\InABox.Avalonia.Platform\InABox.Avalonia.Platform.csproj" />
+      <ProjectReference Include="..\InABox.Avalonia\InABox.Avalonia.csproj" />
     </ItemGroup>
     
     <ItemGroup>
       <PackageReference Include="Avalonia" Version="11.2.3" />
-      <PackageReference Include="bblanchon.PDFium.Android" Version="134.0.6968" />
-      <PackageReference Include="Microsoft.Maui.Essentials" />
+      <PackageReference Include="bblanchon.PDFium.Android" Version="135.0.7009" />
+      <PackageReference Include="Microsoft.Maui.Essentials" Version="9.0.40" />
       <PackageReference Include="PDFtoImage" Version="5.0.0" />
-      <PackageReference Include="Plugin.BLE" Version="3.1.0" />
     </ItemGroup>
 
 </Project>

+ 0 - 52
InABox.Avalonia.Platform.Desktop/Bluetooth.Desktop.cs

@@ -1,52 +0,0 @@
-using System.Security.Principal;
-using InABox.Avalonia.Platform;
-using InABox.Core;
-using InTheHand.Bluetooth;
-
-namespace InABox.Avalonia.Platform.Desktop;
-
-public class Desktop_Bluetooth : IBluetooth
-
-{
-    public Logger? Logger { get; set; }
-
-
-    public async Task<bool> IsAvailable()
-    {
-        return await Task.Run(() => false);
-    }
-
-    public async Task<bool> WriteAsync(string macaddress, Guid serviceid, Guid characteristicid, byte[] data)
-    {
-        var available = await InTheHand.Bluetooth.Bluetooth.GetAvailabilityAsync();
-        if (available)
-        {
-            var filter = new BluetoothLEScanFilter();
-            filter.Services.Add(serviceid);
-            var options = new RequestDeviceOptions()
-            {
-                AcceptAllDevices = false
-            };
-            options.Filters.Add(filter);
-            var devices = await InTheHand.Bluetooth.Bluetooth.ScanForDevicesAsync(options);
-            if (devices.Any())
-            {
-                return true;    
-            }
-        }
-        return false;
-        // IDevice? _device = null;
-        // var adapter = CrossBluetoothLE.Current.Adapter;
-        // adapter.DeviceDiscovered += (s, e) => _device = e.Device;
-        // await adapter.StartScanningForDevicesAsync([serviceid], null);
-        // if (_device != null)
-        // {
-        //     await adapter.ConnectToDeviceAsync(_device);
-        //     var service = await _device.GetServiceAsync(serviceid);
-        //     var characteristic = await service.GetCharacteristicAsync(characteristicid);
-        //     characteristic.WriteAsync(data);
-        //     return true;
-        // }
-        // return false;
-    }
-}

+ 111 - 0
InABox.Avalonia.Platform.Desktop/Desktop.Bluetooth.cs

@@ -0,0 +1,111 @@
+using System.Collections.ObjectModel;
+using BluetoothLENet;
+using InABox.Core;
+
+
+namespace InABox.Avalonia.Platform.Desktop;
+
+public class Desktop_BluetoothDevice(BLEDevice device) : IBluetoothDevice
+{
+    public BLEDevice Device { get; } = device;
+    public string ID { get; } = device.MacAddress ?? string.Empty;
+    public string Name { get; } = device.Native?.Name ?? "Unknown Device";
+}
+
+public class Desktop_ConnectedBluetoothDevice(BLEDevice device)
+    : Desktop_BluetoothDevice(device), IConnectedBluetoothDevice
+{
+    public async Task<bool> WriteAsync(Guid serviceid, Guid characteristicid, byte[] data)
+    {
+        var service = Device.Services.FirstOrDefault(x=>x.Native.Uuid == serviceid);
+        if (service != null)
+        {
+            var characteristic = service.Characteristics.FirstOrDefault(x=>x.Native.Uuid == characteristicid);
+            if (characteristic != null)
+                return await characteristic.WriteAsync(data);
+        }
+        return false;
+    }
+
+    public async Task<byte[]?> ReadAsync(Guid serviceid, Guid characteristicid)
+    {
+        var service = Device.Services.FirstOrDefault(x=>x.Native.Uuid == serviceid);
+        if (service != null)
+        {
+            var characteristic = service.Characteristics.FirstOrDefault(x=>x.Native.Uuid == characteristicid);
+            if (characteristic != null)
+                return await characteristic.ReadAsync();
+        }
+        return [];
+    }
+}
+
+public class Desktop_Bluetooth : IBluetooth
+{
+    public Logger? Logger { get; set; }
+
+    private BLE _adapter;
+    
+    public Action<IBluetoothDevice>? DeviceFound { get; set; }
+    public CoreObservableCollection<IBluetoothDevice> Devices { get; } = new();
+
+    public event EventHandler? Changed;
+    
+    public Desktop_Bluetooth()
+    {
+        Devices.CollectionChanged += (_,_) => Changed?.Invoke(this, EventArgs.Empty);
+        
+        _adapter = new BLE();
+        _adapter.Changed += (_,_) =>
+        {
+            var devices = _adapter.Devices.ToArray().Select(x=>new Desktop_BluetoothDevice(x));
+            Devices.ReplaceRange(devices);
+        };
+        
+        
+    }
+
+    public async Task<bool> IsAvailable()
+    {
+        return await Task.FromResult(true);   
+    }
+    
+    public async Task<bool> StartScanningAsync(Guid serviceId)
+    {
+        if (await IsAvailable())
+            return await _adapter.StartScanningAsync([serviceId]);
+        return false;
+    }
+
+    public async Task<bool> StopScanningAsync()
+    {
+        if (await IsAvailable())
+            return await _adapter.StopScanningAsync();
+        return false;
+    }
+
+    public async Task<IConnectedBluetoothDevice?> Connect(IBluetoothDevice device)
+    {
+        if (await IsAvailable())
+        {
+            if (device is Desktop_BluetoothDevice d)
+            {
+                var result = await _adapter.Connect(d.Device);
+                if (result == ConnectDeviceResult.Ok)
+                    return new Desktop_ConnectedBluetoothDevice(d.Device);
+            }
+        }
+        return null;
+        
+    }
+
+    public async Task<bool> Disconnect(IConnectedBluetoothDevice device)
+    {
+        if (await IsAvailable())
+        {
+            if (device is Desktop_BluetoothDevice d)
+                _adapter.Disconnect(d.Device);
+        }
+        return true;
+    }
+}

+ 0 - 0
InABox.Avalonia.Platform.Desktop/PDFRenderer.Desktop.cs → InABox.Avalonia.Platform.Desktop/Desktop.PdfRenderer.cs


+ 9 - 4
InABox.Avalonia.Platform.Desktop/InABox.Avalonia.Platform.Desktop.csproj

@@ -1,20 +1,25 @@
 <Project Sdk="Microsoft.NET.Sdk">
 
     <PropertyGroup>
-        <TargetFramework>net8.0-windows</TargetFramework>
+        <OutputType>Library</OutputType>
+        <TargetFramework>net8.0-windows10.0.19041.0</TargetFramework>
         <ImplicitUsings>enable</ImplicitUsings>
         <Nullable>enable</Nullable>
+        <Platforms>x86;x64;arm64</Platforms>
+        <RuntimeIdentifiers>win-x86;win-x64;win-arm64</RuntimeIdentifiers>
+        <UseWinUI>true</UseWinUI>
+        <WindowsPackaging>None</WindowsPackaging>
+        <EnableMsixTooling>true</EnableMsixTooling>
     </PropertyGroup>
 
     <ItemGroup>
+      <ProjectReference Include="..\..\3rdpartylibs\BluetoothLENet-master\BluetoothLeNet\BluetoothLeNet.csproj" />
       <ProjectReference Include="..\InABox.Avalonia.Platform\InABox.Avalonia.Platform.csproj" />
     </ItemGroup>
 
     <ItemGroup>
-      <PackageReference Include="InTheHand.BluetoothLE" Version="4.0.37" />
+      <PackageReference Include="Microsoft.WindowsAppSDK" Version="1.6.250205002" />
       <PackageReference Include="PDFtoImage" Version="5.0.0" />
-      <PackageReference Include="System.ComponentModel.Composition" Version="9.0.1" />
-      <PackageReference Include="System.ComponentModel.Composition.Registration" Version="9.0.1" />
     </ItemGroup>
 
 </Project>

+ 2 - 2
InABox.Avalonia.Platform.iOS/InABox.Avalonia.Platform.iOS.csproj

@@ -22,8 +22,8 @@
 
     <ItemGroup>
       <PackageReference Include="Avalonia.iOS" Version="11.2.3" />
-      <PackageReference Include="bblanchon.PDFium.iOS" Version="134.0.6968" />
-      <PackageReference Include="Microsoft.Maui.Essentials" />
+      <PackageReference Include="bblanchon.PDFium.iOS" Version="135.0.7009" />
+      <PackageReference Include="Microsoft.Maui.Essentials" Version="9.0.40" />
       <PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
     </ItemGroup>
     

+ 24 - 4
InABox.Avalonia.Platform/Bluetooth/DefaultBluetooth.cs

@@ -1,4 +1,5 @@
-using InABox.Core;
+using System.Collections.ObjectModel;
+using InABox.Core;
 
 namespace InABox.Avalonia.Platform;
 
@@ -8,11 +9,30 @@ public class DefaultBluetooth : IBluetooth
 
     public async Task<bool> IsAvailable()
     {
-        return await Task.Run(() => false);
+        return await Task.FromResult(false);   
+    }
+    
+    public async Task<bool> StartScanningAsync(Guid serviceid)
+    {
+        return await Task.FromResult(false);
+    }
+    
+    public async Task<bool> StopScanningAsync()
+    {
+        return await Task.FromResult(false);
     }
 
-    public async Task<bool> WriteAsync(string macaddress, Guid serviceid, Guid characteristicid, byte[] data)
+    public async Task<IConnectedBluetoothDevice?> Connect(IBluetoothDevice device)
     {
-        return await Task.Run(() => false);
+        return await Task.FromResult<IConnectedBluetoothDevice?>(null);
     }
+    
+    public async Task<bool> Disconnect(IConnectedBluetoothDevice device)
+    {
+        return await Task.FromResult<bool>(true);
+    }
+
+    public event EventHandler? Changed;
+    
+    public CoreObservableCollection<IBluetoothDevice> Devices { get; } = new();
 }

+ 34 - 3
InABox.Avalonia.Platform/Bluetooth/IBluetooth.cs

@@ -1,12 +1,43 @@
-using InABox.Core;
+using System.Collections.ObjectModel;
+using System.Security.Cryptography.X509Certificates;
+using InABox.Core;
 
 namespace InABox.Avalonia.Platform;
 
+public interface IBluetoothDevice
+{
+     String ID { get; }
+     
+     String Name { get; }
+}
+
+public interface IConnectedBluetoothDevice : IBluetoothDevice
+{
+
+     Task<bool> WriteAsync(Guid serviceid, Guid characteristicid, byte[] data);
+     
+     Task<byte[]?> ReadAsync(Guid serviceid, Guid characteristicid);
+     
+}
+
 public interface IBluetooth : ILoggable
 {
      Logger? Logger { get; set; }
 
      Task<bool> IsAvailable();
+
+     //Task<IBluetoothDevice?> FindDevice(Guid serviceId, TimeSpan timeout);
+     
+     Task<bool> StartScanningAsync(Guid serviceId);
+     
+     Task<bool> StopScanningAsync();
+     
+     Task<IConnectedBluetoothDevice?> Connect(IBluetoothDevice deviceid);
+     
+     Task<bool> Disconnect(IConnectedBluetoothDevice device);
+
+     event EventHandler? Changed;
+     
+     CoreObservableCollection<IBluetoothDevice> Devices { get; }
      
-     Task<bool> WriteAsync(string macaddress, Guid serviceid, Guid characteristicid, byte[] data);
-}
+}

+ 2 - 1
InABox.Avalonia.Platform/InABox.Avalonia.Platform.csproj

@@ -10,10 +10,11 @@
 
     <ItemGroup>
       <PackageReference Include="Autofac" Version="8.2.0" />
-      <PackageReference Include="Microsoft.Maui.Essentials" />
+      <PackageReference Include="Microsoft.Maui.Essentials" Version="9.0.40" />
     </ItemGroup>
 
     <ItemGroup>
+      <ProjectReference Include="..\InABox.Core\InABox.Core.csproj" />
       <ProjectReference Include="..\inabox.logging.shared\InABox.Logging.Shared.csproj" />
     </ItemGroup>