using System.Text; using Android.Bluetooth; namespace InABox.Avalonia.Platform.Android; public class Android_ConnectedBluetoothDevice : BluetoothGattCallback,IConnectedBluetoothDevice { private BluetoothDevice? _device; public string ID { get; } public string Name { get; } public DateTime LastSeen { get; set; } = DateTime.Now; private Guid[] _availableServices = []; public Guid[] AvailableServices => _availableServices; private BluetoothGatt? _bluetoothGatt; private TaskCompletionSource? _connectionTaskCompletionSource; private TaskCompletionSource? _serviceDiscoveryTaskCompletionSource; private TaskCompletionSource? _readTaskCompletionSource; private TaskCompletionSource? _writeTaskCompletionSource; public Android_ConnectedBluetoothDevice(BluetoothDevice? device) { _device = device; ID = device?.Address ?? string.Empty; Name = device?.Name ?? "Unknown Device"; } public async Task ConnectAsync() { _connectionTaskCompletionSource = new TaskCompletionSource(); _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 DiscoverServicesAsync() { _serviceDiscoveryTaskCompletionSource = new TaskCompletionSource(); _bluetoothGatt?.DiscoverServices(); _availableServices = _bluetoothGatt?.Services?.Select(x => Guid.Parse(x.Uuid?.ToString() ?? Guid.Empty.ToString())).ToArray() ?? []; 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 ReadBytesAsync(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; } public async Task ReadStringAsync(Guid serviceid, Guid characteristicid) { var data = await ReadBytesAsync(serviceid,characteristicid); return data != null ? Encoding.UTF8.GetString(data) : null; } private async Task ReadCharacteristicAsync(BluetoothGattCharacteristic characteristic) { if (_bluetoothGatt != null) { _readTaskCompletionSource = new TaskCompletionSource(); 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 WriteBytesAsync(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; } public async Task WriteStringAsync(Guid serviceid, Guid characteristicid, string data) { var encoded = Encoding.UTF8.GetBytes(data); return await WriteBytesAsync(serviceid, characteristicid, encoded); } private async Task WriteCharacteristicAsync(BluetoothGattCharacteristic characteristic, byte[] data) { bool result = false; if (_bluetoothGatt != null) { _writeTaskCompletionSource = new TaskCompletionSource(); _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 new void Dispose() { base.Dispose(); try { _bluetoothGatt?.Disconnect(); _bluetoothGatt?.Close(); } catch (Exception ex) { Console.WriteLine($"Error during disposal: {ex.Message}"); } _bluetoothGatt?.Dispose(); _bluetoothGatt = null; _device?.Dispose(); _device = null; Console.WriteLine("Resources released."); } }