teotestbluetooth / TestTeoBluetooth / TestTeoBluetooth.Android / Services / BluetoothService.cs @ 4ca686e8
Historique | Voir | Annoter | Télécharger (8,48 ko)
1 |
using Android.App; |
---|---|
2 |
using Android.Bluetooth; |
3 |
using Android.Content; |
4 |
using Android.OS; |
5 |
using Android.Runtime; |
6 |
using Android.Views; |
7 |
using Android.Widget; |
8 |
using Java.IO; |
9 |
using Java.Util; |
10 |
using System; |
11 |
using System.Collections.Generic; |
12 |
using System.Collections.ObjectModel; |
13 |
using System.Linq; |
14 |
using System.Text; |
15 |
using System.Threading; |
16 |
using System.Threading.Tasks; |
17 |
using TeoTestBluetooth.Droid.Receivers; |
18 |
using TeoTestBluetooth.Droid.Utils; |
19 |
using TeoTestBluetooth.Models; |
20 |
using TeoTestBluetooth.Services; |
21 |
|
22 |
namespace TeoTestBluetooth.Droid.Services |
23 |
{ |
24 |
public class BluetoothService : IBluetoothService |
25 |
{ |
26 |
|
27 |
private readonly List<BluetoothDevice> discoveredDevices = new List<BluetoothDevice>(); |
28 |
private readonly BluetoothAdapter bluetoothAdapter; |
29 |
private readonly BluetoothReceiver bluetoothReceiver; |
30 |
private TaskCompletionSource<bool> scanCompletionSource; |
31 |
|
32 |
private Thread listeningThread; |
33 |
private BluetoothSocket socket; |
34 |
private readonly string sppUUID = "00001101-0000-1000-8000-00805f9b34fb"; |
35 |
|
36 |
public ObservableCollection<BTDevice> FoundDevices = new ObservableCollection<BTDevice>(); |
37 |
public BluetoothService() |
38 |
{ |
39 |
FoundDevices = new ObservableCollection<BTDevice>(); |
40 |
bluetoothAdapter = BluetoothAdapter.DefaultAdapter; |
41 |
bluetoothReceiver = new BluetoothReceiver(); |
42 |
bluetoothReceiver.OnBluetoothDeviceDiscovered += DeviceDiscovered; |
43 |
bluetoothReceiver.OnScanFinished += ScanFinished; |
44 |
|
45 |
// abonnements du BluetoothReceiver au intent ActionFound Et ActionDiscoveryFinished |
46 |
IntentFilter intentFilter = new IntentFilter(); |
47 |
intentFilter.AddAction(BluetoothDevice.ActionFound); |
48 |
intentFilter.AddAction(BluetoothAdapter.ActionDiscoveryFinished); |
49 |
intentFilter.AddAction(BluetoothDevice.ActionBondStateChanged); |
50 |
Application.Context.RegisterReceiver(bluetoothReceiver, intentFilter); |
51 |
} |
52 |
|
53 |
public async Task<bool> Connect(BTDevice btDevice) |
54 |
{ |
55 |
try |
56 |
{ |
57 |
// Si jamais on est en train de scanner |
58 |
// On annule le scan |
59 |
if (bluetoothAdapter.IsDiscovering) |
60 |
{ |
61 |
bluetoothAdapter.CancelDiscovery(); |
62 |
} |
63 |
|
64 |
BluetoothDevice droidBtDevice = bluetoothAdapter.GetRemoteDevice(btDevice.MACAddress); |
65 |
//Android.Bluetooth.BluetoothDevice droidBtDevice = adapter.BondedDevices.FirstOrDefault(x => (x.Address == bluetoothDevice.Address)); |
66 |
if (droidBtDevice != null) |
67 |
{ |
68 |
// Si le socket est occupé pour une autre connexion |
69 |
// On ferme la connexion existante |
70 |
if (socket != null) |
71 |
{ |
72 |
await Disconnect(btDevice); |
73 |
} |
74 |
|
75 |
// Creation du canal RFCOMM (non sécurisé) |
76 |
socket = droidBtDevice.CreateInsecureRfcommSocketToServiceRecord(UUID.FromString(sppUUID)); |
77 |
|
78 |
CancellationTokenSource cts = new CancellationTokenSource(); |
79 |
|
80 |
cts.CancelAfter(50000); |
81 |
await socket.ConnectAsync().WithCancellation(cts.Token); |
82 |
|
83 |
if (listeningThread != null) listeningThread.Abort(); |
84 |
listeningThread = new Thread(async delagate => await ListeningAsync(btDevice)); |
85 |
listeningThread.Start(); |
86 |
|
87 |
return true; |
88 |
} |
89 |
} |
90 |
catch (Exception ex) |
91 |
{ |
92 |
System.Diagnostics.Debug.WriteLine(ex); |
93 |
} |
94 |
|
95 |
return false; |
96 |
} |
97 |
|
98 |
// Fonction d'écoute pour le Thread d'écoute |
99 |
private async Task ListeningAsync(BTDevice btDevice) |
100 |
{ |
101 |
try |
102 |
{ |
103 |
byte[] buffer = new byte[1024]; |
104 |
while (socket.IsConnected) |
105 |
{ |
106 |
int byteAvailable = await socket.InputStream.ReadAsync(buffer, 0, buffer.Length); |
107 |
// Resize the byte array |
108 |
byte[] filledBuffer = buffer.Take(byteAvailable).ToArray(); |
109 |
Application.SynchronizationContext.Post(_ => { OnDataReceived(filledBuffer); }, null); |
110 |
} |
111 |
} |
112 |
catch (IOException ex) |
113 |
{ |
114 |
System.Diagnostics.Debug.WriteLine(ex); |
115 |
} |
116 |
catch (ThreadAbortException taEx) |
117 |
{ |
118 |
System.Diagnostics.Debug.WriteLine(taEx); |
119 |
} |
120 |
catch (Exception ex) |
121 |
{ |
122 |
System.Diagnostics.Debug.WriteLine(ex); |
123 |
} |
124 |
} |
125 |
|
126 |
public EventHandler<string> DataReceivedEvent { get; set; } |
127 |
private string _bufferedData; |
128 |
|
129 |
/* |
130 |
* Fonction appelée à la reception de données |
131 |
* Trigger l'evenement DataReceivedEvent |
132 |
* |
133 |
* IMPORTANT: Les trames peuvent arriver découpées, |
134 |
* Donc les données sont gardées dans un buffer d'envoi |
135 |
* nommé _bufferedData |
136 |
*/ |
137 |
private void OnDataReceived(byte[] buffer) |
138 |
{ |
139 |
// Si le premier character est 2 (ASCII: STX) |
140 |
// Alors c'est le début de la trame. |
141 |
if (buffer[0] == 2) |
142 |
{ |
143 |
_bufferedData = ""; |
144 |
// Si character seul on quitte |
145 |
if (buffer.Length == 1) |
146 |
{ |
147 |
return; |
148 |
} |
149 |
// On enlève STX |
150 |
buffer = buffer.Skip(1).ToArray(); |
151 |
// On mets le début de la trame dans buffer d'envoi |
152 |
_bufferedData += Encoding.ASCII.GetString(buffer); |
153 |
} |
154 |
|
155 |
// Si le dernier character est 13 (ASCII: CR) |
156 |
// Alors la trame est terminée |
157 |
if (buffer[^1] == 13) |
158 |
{ |
159 |
// On enlève CR |
160 |
buffer = buffer.SkipLast(1).ToArray(); |
161 |
// Conversion en chaîne de caractères |
162 |
// Et on complète le buffer d'envoi |
163 |
_bufferedData += Encoding.ASCII.GetString(buffer); |
164 |
// Trigger evènement |
165 |
DataReceivedEvent.Invoke(this, _bufferedData); |
166 |
} |
167 |
} |
168 |
|
169 |
private void ScanFinished(object sender, string e) |
170 |
{ |
171 |
scanCompletionSource?.SetResult(true); |
172 |
} |
173 |
|
174 |
private void DeviceDiscovered(object sender, BluetoothDevice e) |
175 |
{ |
176 |
if (discoveredDevices.FirstOrDefault(x => x.Address == e.Address) is null) |
177 |
{ |
178 |
discoveredDevices.Add(e); |
179 |
FoundDevices.Add(new BTDevice() { Name = e.Name, MACAddress = e.Address }); |
180 |
} |
181 |
} |
182 |
|
183 |
public ObservableCollection<BTDevice> DeviceList { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } |
184 |
|
185 |
public Task StartScan() |
186 |
{ |
187 |
if (bluetoothAdapter.IsDiscovering) |
188 |
{ |
189 |
return scanCompletionSource.Task; |
190 |
} |
191 |
|
192 |
scanCompletionSource = new TaskCompletionSource<bool>(); |
193 |
discoveredDevices.Clear(); |
194 |
FoundDevices.Clear(); |
195 |
|
196 |
// Lancement du scan |
197 |
bluetoothAdapter.StartDiscovery(); |
198 |
|
199 |
return scanCompletionSource.Task; |
200 |
} |
201 |
|
202 |
public Task<bool> Pair(BTDevice bluetoothDevice) |
203 |
{ |
204 |
throw new NotImplementedException(); |
205 |
} |
206 |
|
207 |
public Task<bool> Disconnect(BTDevice bluetoothDevice) |
208 |
{ |
209 |
while (socket.IsConnected) socket.Close(); |
210 |
return Task.FromResult(true); |
211 |
} |
212 |
|
213 |
public Task<bool> SendHexValues(byte[] hexValues) |
214 |
{ |
215 |
if (socket.IsConnected == false) |
216 |
{ |
217 |
return Task.FromResult(false); |
218 |
} else |
219 |
{ |
220 |
// Envoi de la commande |
221 |
socket.OutputStream.Write(hexValues, 0, hexValues.Length); |
222 |
socket.OutputStream.Flush(); |
223 |
} |
224 |
|
225 |
return Task.FromResult(true); |
226 |
} |
227 |
|
228 |
public ICollection<BTDevice> GetBondedDevices() |
229 |
{ |
230 |
ICollection<BTDevice> ret = new ObservableCollection<BTDevice>(); |
231 |
foreach (BluetoothDevice bondedDevice in bluetoothAdapter.BondedDevices) |
232 |
{ |
233 |
BTDevice toAddItem = new BTDevice |
234 |
{ |
235 |
MACAddress = bondedDevice.Address, |
236 |
Name = bondedDevice.Name |
237 |
}; |
238 |
ret.Add(toAddItem); |
239 |
} |
240 |
return ret; |
241 |
} |
242 |
} |
243 |
} |