123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341 |
- 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;
- namespace InABox.Avalonia.Platform.Android;
- public class Android_BluetoothDevice(ScanResult scan) : IBluetoothDevice, IDisposable
- {
- 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";
-
- 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
- {
- _bluetoothGatt?.Disconnect();
- _bluetoothGatt?.Close();
- }
- catch (Exception ex)
- {
- Console.WriteLine($"Error during disposal: {ex.Message}");
- }
- finally
- {
- _bluetoothGatt = null;
- }
- 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 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()
- {
- try
- {
- PermissionStatus status = await Permissions.CheckStatusAsync<TPermission>();
- if (status == PermissionStatus.Granted)
- return true;
- var request = await Permissions.RequestAsync<TPermission>();
- return request == PermissionStatus.Granted;
- }
- catch (TaskCanceledException ex)
- {
- return false;
- }
- }
- 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())
- {
- _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)
- {
- _scanner!.StopScan(_callback);
- return true;
- }
- }
- return false;
- }
-
- private void DoDeviceFound(ScanResult device, Guid serviceid)
- {
- bool bMatch = true;
-
- bMatch = false;
- if (device.ScanRecord?.ServiceUuids?.Any() == true)
- {
- 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);
- }
- }
- 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);
- }
- }
|