|
@@ -1,34 +1,19 @@
|
|
|
-using System.Collections.ObjectModel;
|
|
|
+using System.Text;
|
|
|
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";
|
|
|
-
|
|
|
+ public DateTime LastSeen { get; set; } = DateTime.Now;
|
|
|
+
|
|
|
+ private Guid[] _availableServices = [];
|
|
|
+ public Guid[] AvailableServices => _availableServices;
|
|
|
+
|
|
|
private BluetoothGatt? _bluetoothGatt;
|
|
|
|
|
|
private TaskCompletionSource<bool> _connectionTaskCompletionSource;
|
|
@@ -64,6 +49,7 @@ public class Android_ConnectedBluetoothDevice(BluetoothDevice device) : Bluetoot
|
|
|
{
|
|
|
_serviceDiscoveryTaskCompletionSource = new TaskCompletionSource<bool>();
|
|
|
_bluetoothGatt?.DiscoverServices();
|
|
|
+ _availableServices = _bluetoothGatt?.Services?.Select(x => Guid.Parse(x.Uuid?.ToString() ?? Guid.Empty.ToString())).ToArray() ?? [];
|
|
|
return await _serviceDiscoveryTaskCompletionSource.Task;
|
|
|
}
|
|
|
|
|
@@ -83,7 +69,7 @@ public class Android_ConnectedBluetoothDevice(BluetoothDevice device) : Bluetoot
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- public async Task<byte[]?> ReadAsync(Guid serviceid, Guid characteristicid)
|
|
|
+ public async Task<byte[]?> ReadBytesAsync(Guid serviceid, Guid characteristicid)
|
|
|
{
|
|
|
byte[]? result = null;
|
|
|
if (_bluetoothGatt != null)
|
|
@@ -99,6 +85,12 @@ public class Android_ConnectedBluetoothDevice(BluetoothDevice device) : Bluetoot
|
|
|
return result;
|
|
|
}
|
|
|
|
|
|
+ public async Task<String?> ReadStringAsync(Guid serviceid, Guid characteristicid)
|
|
|
+ {
|
|
|
+ var data = await ReadBytesAsync(serviceid,characteristicid);
|
|
|
+ return data != null ? System.Text.Encoding.UTF8.GetString(data) : null;
|
|
|
+ }
|
|
|
+
|
|
|
private async Task<byte[]?> ReadCharacteristicAsync(BluetoothGattCharacteristic characteristic)
|
|
|
{
|
|
|
if (_bluetoothGatt != null)
|
|
@@ -132,7 +124,7 @@ public class Android_ConnectedBluetoothDevice(BluetoothDevice device) : Bluetoot
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- public async Task<bool> WriteAsync(Guid serviceid, Guid characteristicid, byte[] data)
|
|
|
+ public async Task<bool> WriteBytesAsync(Guid serviceid, Guid characteristicid, byte[] data)
|
|
|
{
|
|
|
var result = false;
|
|
|
if (_bluetoothGatt != null)
|
|
@@ -149,13 +141,19 @@ public class Android_ConnectedBluetoothDevice(BluetoothDevice device) : Bluetoot
|
|
|
return result;
|
|
|
}
|
|
|
|
|
|
+ public async Task<bool> WriteStringAsync(Guid serviceid, Guid characteristicid, string data)
|
|
|
+ {
|
|
|
+ var encoded = Encoding.UTF8.GetBytes(data);
|
|
|
+ return await WriteBytesAsync(serviceid, characteristicid, encoded);
|
|
|
+ }
|
|
|
+
|
|
|
private async Task<bool> WriteCharacteristicAsync(BluetoothGattCharacteristic characteristic, byte[] data)
|
|
|
{
|
|
|
bool result = false;
|
|
|
if (_bluetoothGatt != null)
|
|
|
{
|
|
|
_writeTaskCompletionSource = new TaskCompletionSource<bool>();
|
|
|
- _bluetoothGatt.WriteCharacteristic(characteristic, data, 2);
|
|
|
+ _bluetoothGatt.WriteCharacteristic(characteristic, data, 2);
|
|
|
result = await _writeTaskCompletionSource.Task;
|
|
|
}
|
|
|
return result;
|
|
@@ -195,147 +193,4 @@ public class Android_ConnectedBluetoothDevice(BluetoothDevice device) : Bluetoot
|
|
|
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);
|
|
|
- }
|
|
|
}
|