commit 0875d625e1b1ccf478bf40d9a4fe0997df1bebd6
Author: Martin Toutant <martin.toutant@inrae.fr>
Date:   Mon Aug 8 14:55:44 2022 +0200

    branch for larger service scope

diff --git a/TestTeoBluetooth/TestTeoBluetooth.Android/Connection/BluetoothConnection.cs b/TestTeoBluetooth/TestTeoBluetooth.Android/Connection/BluetoothConnection.cs
index 19be4c4..e1c30cb 100644
--- a/TestTeoBluetooth/TestTeoBluetooth.Android/Connection/BluetoothConnection.cs
+++ b/TestTeoBluetooth/TestTeoBluetooth.Android/Connection/BluetoothConnection.cs
@@ -1,21 +1,175 @@
 ﻿using Android.App;
+using Android.Bluetooth;
 using Android.Content;
 using Android.OS;
 using Android.Runtime;
 using Android.Views;
 using Android.Widget;
+using Java.Util;
 using System;
 using System.Collections.Generic;
+using System.IO;
 using System.Linq;
 using System.Text;
+using System.Threading;
+using System.Threading.Tasks;
+using TeoTestBluetooth.Droid.Utils;
+using TeoTestBluetooth.Models;
+using TestTeoBluetooth.Connection;
 
 namespace TestBLE.Droid.Connection
 {
     /* Classe permettant d'établir une connection Bluetooth
      * Avec socket RFCOMM connecté au SPP
      */
-    public class BluetoothConnection
+    public class BluetoothConnection : IBluetoothConnectionService
     {
 
+        private readonly BluetoothAdapter bluetoothAdapter;
+        private Thread listeningThread;
+        private BluetoothSocket socket;
+        private readonly string sppUUID = "00001101-0000-1000-8000-00805f9b34fb";
+
+        public BluetoothConnection()
+        { 
+            bluetoothAdapter = BluetoothAdapter.DefaultAdapter;
+        }
+        public async Task<bool> Connect(Device btDevice)
+        {
+            try
+            {
+                // Si jamais on est en train de scanner
+                // On annule le scan
+                if (bluetoothAdapter.IsDiscovering)
+                {
+                    bluetoothAdapter.CancelDiscovery();
+                }
+
+                BluetoothDevice droidBtDevice = bluetoothAdapter.GetRemoteDevice(btDevice.MACAddress);
+                //Android.Bluetooth.BluetoothDevice droidBtDevice = adapter.BondedDevices.FirstOrDefault(x => (x.Address == bluetoothDevice.Address));
+                if (droidBtDevice != null)
+                {
+                    // Si le socket est occupé pour une autre connexion
+                    // On ferme la connexion existante
+                    if (socket != null)
+                    {
+                        await CloseConnection();
+                    }
+
+                    // Creation du canal RFCOMM (non sécurisé)
+                    socket = droidBtDevice.CreateInsecureRfcommSocketToServiceRecord(UUID.FromString(sppUUID));
+
+                    CancellationTokenSource cts = new CancellationTokenSource();
+
+                    cts.CancelAfter(50000);
+                    await socket.ConnectAsync().WithCancellation(cts.Token);
+
+                    if (listeningThread != null) listeningThread.Abort();
+                    listeningThread = new Thread(async delagate => await ListeningAsync());
+                    listeningThread.Start();
+
+                    return true;
+                }
+            }
+            catch (Exception ex)
+            {
+                System.Diagnostics.Debug.WriteLine(ex);
+            }
+
+            return false;
+        }
+
+        // Fonction d'écoute pour le Thread d'écoute
+        private async Task ListeningAsync()
+        {
+            try
+            {
+                byte[] buffer = new byte[1024];
+                while (socket.IsConnected)
+                {
+                    int byteAvailable = await socket.InputStream.ReadAsync(buffer, 0, buffer.Length);
+                    // Resize the byte array 
+                    byte[] filledBuffer = buffer.Take(byteAvailable).ToArray();
+                    Application.SynchronizationContext.Post(_ => { OnDataReceived(filledBuffer); }, null);
+                }
+            }
+            catch (IOException ex)
+            {
+                System.Diagnostics.Debug.WriteLine(ex);
+            }
+            catch (ThreadAbortException taEx)
+            {
+                System.Diagnostics.Debug.WriteLine(taEx);
+            }
+            catch (Exception ex)
+            {
+                System.Diagnostics.Debug.WriteLine(ex);
+            }
+        }
+
+        public EventHandler<string> DataReceivedEvent { get; set; }
+        private string _bufferedData;
+
+        /* 
+         * Fonction appelée à la reception de données
+         * Trigger l'evenement DataReceivedEvent
+         * 
+         * IMPORTANT: Les trames peuvent arriver découpées,
+         * Donc les données sont gardées dans un buffer d'envoi
+         * nommé _bufferedData
+         */
+        private void OnDataReceived(byte[] buffer)
+        {
+            // Si le premier character est 2 (ASCII: STX)
+            // Alors c'est le début de la trame.
+            if (buffer[0] == 2)
+            {
+                _bufferedData = "";
+                // Si character seul on quitte
+                if (buffer.Length == 1)
+                {
+                    return;
+                }
+                // On enlève STX
+                buffer = buffer.Skip(1).ToArray();
+                // On mets le début de la trame dans buffer d'envoi
+                _bufferedData += Encoding.ASCII.GetString(buffer);
+            }
+
+            // Si le dernier character est 13 (ASCII: CR)
+            // Alors la trame est terminée
+            if (buffer[^1] == 13)
+            {
+                // On enlève CR
+                buffer = buffer.SkipLast(1).ToArray();
+                // Conversion en chaîne de caractères
+                // Et on complète le buffer d'envoi
+                _bufferedData += Encoding.ASCII.GetString(buffer);
+                // Trigger evènement
+                DataReceivedEvent.Invoke(this, _bufferedData);
+            }
+        }
+
+        private Task<bool> CloseConnection()
+        {
+            while (socket.IsConnected) socket.Close();
+            return Task.FromResult(true);
+        }
+
+        public Task<bool> SendCommand(byte[] hexValues)
+        {
+            if (socket.IsConnected == false)
+            {
+                return Task.FromResult(false);
+            }
+            else
+            {
+                // Envoi de la commande
+                socket.OutputStream.Write(hexValues, 0, hexValues.Length);
+                socket.OutputStream.Flush();
+            }
+            return Task.FromResult(true);
+        }
+
     }
 }
\ No newline at end of file
diff --git a/TestTeoBluetooth/TestTeoBluetooth.Android/Connection/InternConnection.cs b/TestTeoBluetooth/TestTeoBluetooth.Android/Connection/InternConnection.cs
index e43c41e..3b55dd5 100644
--- a/TestTeoBluetooth/TestTeoBluetooth.Android/Connection/InternConnection.cs
+++ b/TestTeoBluetooth/TestTeoBluetooth.Android/Connection/InternConnection.cs
@@ -8,6 +8,9 @@ using System;
 using System.Collections.Generic;
 using System.Linq;
 using System.Text;
+using System.Threading.Tasks;
+using TeoTestBluetooth.Models;
+using TestTeoBluetooth.Connection;
 
 namespace TestBLE.Droid.Connection
 {
@@ -15,7 +18,18 @@ namespace TestBLE.Droid.Connection
      * Pré-installée sur l'appareil
      * Basée sur filtrage d'intent sur BroadcastReceiver
      */
-    public class InternConnection
+    public class InternConnection : IConnectionService
     {
+        public EventHandler<string> DataReceivedEvent { get => throw new NotImplementedException(); set => throw new NotImplementedException(); }
+
+        public Task<bool> Connect(Device device)
+        {
+            throw new NotImplementedException();
+        }
+
+        public Task<bool> SendCommand(byte[] hexValues)
+        {
+            throw new NotImplementedException();
+        }
     }
 }
\ No newline at end of file
diff --git a/TestTeoBluetooth/TestTeoBluetooth.Android/MainActivity.cs b/TestTeoBluetooth/TestTeoBluetooth.Android/MainActivity.cs
index 25e76c3..6d6a333 100644
--- a/TestTeoBluetooth/TestTeoBluetooth.Android/MainActivity.cs
+++ b/TestTeoBluetooth/TestTeoBluetooth.Android/MainActivity.cs
@@ -6,6 +6,8 @@ using Android.Runtime;
 using Android.OS;
 using Xamarin.Forms;
 using TeoTestBluetooth.Services;
+using TestBLE.Droid.Connection;
+using TestTeoBluetooth.Connection;
 
 namespace TeoTestBluetooth.Droid
 {
@@ -17,8 +19,9 @@ namespace TeoTestBluetooth.Droid
             base.OnCreate(savedInstanceState);
 
             Xamarin.Essentials.Platform.Init(this, savedInstanceState);
-            global::Xamarin.Forms.Forms.Init(this, savedInstanceState);
-            DependencyService.RegisterSingleton<IBluetoothService>(new Services.BluetoothService());
+            Forms.Init(this, savedInstanceState);
+            DependencyService.Register<IConnectionService, BluetoothConnection>();
+            DependencyService.Register<IConnectionService, InternConnection>();
             LoadApplication(new App());
         }
         public override void OnRequestPermissionsResult(int requestCode, string[] permissions, [GeneratedEnum] Android.Content.PM.Permission[] grantResults)
diff --git a/TestTeoBluetooth/TestTeoBluetooth.Android/Receivers/BluetoothReceiver.cs b/TestTeoBluetooth/TestTeoBluetooth.Android/Receivers/BluetoothReceiver.cs
index bbc6dca..48afeea 100644
--- a/TestTeoBluetooth/TestTeoBluetooth.Android/Receivers/BluetoothReceiver.cs
+++ b/TestTeoBluetooth/TestTeoBluetooth.Android/Receivers/BluetoothReceiver.cs
@@ -23,7 +23,7 @@ namespace TeoTestBluetooth.Droid.Receivers
                 BluetoothDevice foundDevice = intent.GetParcelableExtra(BluetoothDevice.ExtraDevice) as BluetoothDevice;
                 if (foundDevice != null)
                 {
-                    var btDevice = new Models.BTDevice
+                    var btDevice = new Models.Device
                     {
                         MACAddress = foundDevice.Address,
                         Name = foundDevice.Name
diff --git a/TestTeoBluetooth/TestTeoBluetooth.Android/Services/BluetoothService.cs b/TestTeoBluetooth/TestTeoBluetooth.Android/Services/BluetoothService.cs
index 6eacae9..90b85e0 100644
--- a/TestTeoBluetooth/TestTeoBluetooth.Android/Services/BluetoothService.cs
+++ b/TestTeoBluetooth/TestTeoBluetooth.Android/Services/BluetoothService.cs
@@ -33,10 +33,10 @@ namespace TeoTestBluetooth.Droid.Services
         private BluetoothSocket socket;
         private readonly string sppUUID = "00001101-0000-1000-8000-00805f9b34fb";
 
-        public ObservableCollection<BTDevice> FoundDevices = new ObservableCollection<BTDevice>();
+        public ObservableCollection<Device> FoundDevices = new ObservableCollection<Device>();
         public BluetoothService()
         {
-            FoundDevices = new ObservableCollection<BTDevice>();
+            FoundDevices = new ObservableCollection<Device>();
             bluetoothAdapter = BluetoothAdapter.DefaultAdapter;
             bluetoothReceiver = new BluetoothReceiver();
             bluetoothReceiver.OnBluetoothDeviceDiscovered += DeviceDiscovered;
@@ -50,7 +50,7 @@ namespace TeoTestBluetooth.Droid.Services
             Application.Context.RegisterReceiver(bluetoothReceiver, intentFilter);
         }
 
-        public async Task<bool> Connect(BTDevice btDevice)
+        public async Task<bool> Connect(Device btDevice)
         {
             try
             {
@@ -96,7 +96,7 @@ namespace TeoTestBluetooth.Droid.Services
         }
 
         // Fonction d'écoute pour le Thread d'écoute
-        private async Task ListeningAsync(BTDevice btDevice)
+        private async Task ListeningAsync(Device btDevice)
         {
             try
             {
@@ -176,11 +176,11 @@ namespace TeoTestBluetooth.Droid.Services
             if (discoveredDevices.FirstOrDefault(x => x.Address == e.Address) is null)
             {
                 discoveredDevices.Add(e);
-                FoundDevices.Add(new BTDevice() { Name = e.Name, MACAddress = e.Address });
+                FoundDevices.Add(new Device() { Name = e.Name, MACAddress = e.Address });
             }
         }
 
-        public ObservableCollection<BTDevice> DeviceList { get => throw new NotImplementedException(); set => throw new NotImplementedException(); }
+        public ObservableCollection<Device> DeviceList { get => throw new NotImplementedException(); set => throw new NotImplementedException(); }
 
         public Task StartScan()
         {
@@ -199,12 +199,12 @@ namespace TeoTestBluetooth.Droid.Services
             return scanCompletionSource.Task;
         }
 
-        public Task<bool> Pair(BTDevice bluetoothDevice)
+        public Task<bool> Pair(Device bluetoothDevice)
         {
             throw new NotImplementedException();
         }
 
-        public Task<bool> Disconnect(BTDevice bluetoothDevice)
+        public Task<bool> Disconnect(Device bluetoothDevice)
         {
             while (socket.IsConnected) socket.Close();
             return Task.FromResult(true);
@@ -225,12 +225,12 @@ namespace TeoTestBluetooth.Droid.Services
             return Task.FromResult(true);
         }
 
-        public ICollection<BTDevice> GetBondedDevices()
+        public ICollection<Device> GetBondedDevices()
         {
-            ICollection<BTDevice> ret = new ObservableCollection<BTDevice>();
+            ICollection<Device> ret = new ObservableCollection<Device>();
             foreach (BluetoothDevice bondedDevice in bluetoothAdapter.BondedDevices)
             {
-                BTDevice toAddItem = new BTDevice
+                Device toAddItem = new Device
                 {
                     MACAddress = bondedDevice.Address,
                     Name = bondedDevice.Name
diff --git a/TestTeoBluetooth/TestTeoBluetooth/Connection/IBluetoothConnectionService.cs b/TestTeoBluetooth/TestTeoBluetooth/Connection/IBluetoothConnectionService.cs
new file mode 100644
index 0000000..7795110
--- /dev/null
+++ b/TestTeoBluetooth/TestTeoBluetooth/Connection/IBluetoothConnectionService.cs
@@ -0,0 +1,20 @@
+﻿using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Threading.Tasks;
+using TeoTestBluetooth.Models;
+
+namespace TestTeoBluetooth.Connection
+{
+    public enum ConnectionType
+    {
+        Intern,
+        Bluetooth,
+        Wifi
+    }
+
+    public interface IBluetoothConnectionService : IConnectionService
+    {
+
+    }
+}
diff --git a/TestTeoBluetooth/TestTeoBluetooth/Connection/IConnectionService.cs b/TestTeoBluetooth/TestTeoBluetooth/Connection/IConnectionService.cs
new file mode 100644
index 0000000..684ad50
--- /dev/null
+++ b/TestTeoBluetooth/TestTeoBluetooth/Connection/IConnectionService.cs
@@ -0,0 +1,15 @@
+﻿using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Threading.Tasks;
+using TeoTestBluetooth.Models;
+
+namespace TestTeoBluetooth.Connection
+{
+    public interface IConnectionService
+    {
+        Task<bool> Connect(Device device);
+        Task<bool> SendCommand(byte[] hexValues);
+        EventHandler<string> DataReceivedEvent { get; set; }
+    }
+}
diff --git a/TestTeoBluetooth/TestTeoBluetooth/Models/BTDevice.cs b/TestTeoBluetooth/TestTeoBluetooth/Models/BTDevice.cs
deleted file mode 100644
index 2d62669..0000000
--- a/TestTeoBluetooth/TestTeoBluetooth/Models/BTDevice.cs
+++ /dev/null
@@ -1,17 +0,0 @@
-﻿using System;
-using System.Collections.Generic;
-using System.Text;
-
-namespace TeoTestBluetooth.Models
-{
-    public class BTDevice
-    {
-        public string Name { get; set; }
-        public string MACAddress { get; set; }
-
-        public override string ToString()
-        {
-            return Name + ":" + MACAddress;
-        }
-    }
-}
diff --git a/TestTeoBluetooth/TestTeoBluetooth/Models/Device.cs b/TestTeoBluetooth/TestTeoBluetooth/Models/Device.cs
new file mode 100644
index 0000000..57498c4
--- /dev/null
+++ b/TestTeoBluetooth/TestTeoBluetooth/Models/Device.cs
@@ -0,0 +1,17 @@
+﻿using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace TeoTestBluetooth.Models
+{
+    public class Device
+    {
+        public string Name { get; set; }
+        public string MACAddress { get; set; }
+
+        public override string ToString()
+        {
+            return Name + ":" + MACAddress;
+        }
+    }
+}
diff --git a/TestTeoBluetooth/TestTeoBluetooth/Services/IBluetoothService.cs b/TestTeoBluetooth/TestTeoBluetooth/Services/IBluetoothService.cs
index 9f7b002..1d8150a 100644
--- a/TestTeoBluetooth/TestTeoBluetooth/Services/IBluetoothService.cs
+++ b/TestTeoBluetooth/TestTeoBluetooth/Services/IBluetoothService.cs
@@ -10,12 +10,12 @@ namespace TeoTestBluetooth.Services
     public interface IBluetoothService
     {
         Task StartScan();
-        Task<bool> Pair(BTDevice bluetoothDevice);
-        Task<bool> Connect(BTDevice bluetoothDevice);
-        Task<bool> Disconnect(BTDevice bluetoothDevice);
+        Task<bool> Pair(Device bluetoothDevice);
+        Task<bool> Connect(Device bluetoothDevice);
+        Task<bool> Disconnect(Device bluetoothDevice);
         Task<bool> SendHexValues(byte[] hexValues);
-        ICollection<BTDevice> GetBondedDevices();
+        ICollection<Device> GetBondedDevices();
         EventHandler<string> DataReceivedEvent { get; set; }
-        ObservableCollection<BTDevice> DeviceList { get; set; }
+        ObservableCollection<Device> DeviceList { get; set; }
     }
 }
diff --git a/TestTeoBluetooth/TestTeoBluetooth/ViewModels/AccueilViewModel.cs b/TestTeoBluetooth/TestTeoBluetooth/ViewModels/AccueilViewModel.cs
index 2ea8cda..bf6b560 100644
--- a/TestTeoBluetooth/TestTeoBluetooth/ViewModels/AccueilViewModel.cs
+++ b/TestTeoBluetooth/TestTeoBluetooth/ViewModels/AccueilViewModel.cs
@@ -39,7 +39,7 @@ namespace TeoTestBluetooth.ViewModels
         {
             ShowBondedDevices = true;
             BondedDevicesList.Clear();
-            foreach (BTDevice bondedDevice in BluetoothService.GetBondedDevices())
+            foreach (Models.Device bondedDevice in BluetoothService.GetBondedDevices())
             {
                 DeviceItemViewModel toAddItem = new DeviceItemViewModel(bondedDevice);
                 BondedDevicesList.Add(toAddItem);
diff --git a/TestTeoBluetooth/TestTeoBluetooth/ViewModels/DeviceItemViewModel.cs b/TestTeoBluetooth/TestTeoBluetooth/ViewModels/DeviceItemViewModel.cs
index 49cb505..dda721d 100644
--- a/TestTeoBluetooth/TestTeoBluetooth/ViewModels/DeviceItemViewModel.cs
+++ b/TestTeoBluetooth/TestTeoBluetooth/ViewModels/DeviceItemViewModel.cs
@@ -13,8 +13,8 @@ namespace TeoTestBluetooth.ViewModels
 {
     public class DeviceItemViewModel : ViewModelBase
     {
-        private BTDevice device;
-        public BTDevice Device
+        private Models.Device device;
+        public Models.Device Device
         {
 
             get => device;
@@ -29,13 +29,13 @@ namespace TeoTestBluetooth.ViewModels
 
         public ICommand NavigateToDeviceViewCommand { private set; get; }
 
-        public DeviceItemViewModel(BTDevice device)
+        public DeviceItemViewModel(Models.Device device)
         {
             Device = device;
             NavigateToDeviceViewCommand = new Command(async () => await NavigateToDevice());
         }
 
-        public void Update(BTDevice newDevice = null)
+        public void Update(Models.Device newDevice = null)
         {
             if (newDevice != null)
             {
diff --git a/TestTeoBluetooth/TestTeoBluetooth/ViewModels/DeviceViewModel.cs b/TestTeoBluetooth/TestTeoBluetooth/ViewModels/DeviceViewModel.cs
index 0c8dedb..8105660 100644
--- a/TestTeoBluetooth/TestTeoBluetooth/ViewModels/DeviceViewModel.cs
+++ b/TestTeoBluetooth/TestTeoBluetooth/ViewModels/DeviceViewModel.cs
@@ -15,7 +15,7 @@ namespace TeoTestBluetooth.ViewModels
     {
         private bool isConnected;
         private string data;
-        private BTDevice device;
+        private Models.Device device;
 
         private bool isVisibleData;
 
@@ -31,7 +31,7 @@ namespace TeoTestBluetooth.ViewModels
             set { SetProperty(ref isConnected, value); }
         }
 
-        public BTDevice Device
+        public Models.Device Device
         {
             get { return device; }
             set { SetProperty(ref device, value); }
@@ -55,7 +55,7 @@ namespace TeoTestBluetooth.ViewModels
         }
         public ICommand ConnectToDeviceCommand { private set; get; }
 
-        public DeviceViewModel(BTDevice device)
+        public DeviceViewModel(Models.Device device)
         {
             Device = device;
             IsConnected = false;
diff --git a/TestTeoBluetooth/TestTeoBluetooth/Views/DeviceView.xaml.cs b/TestTeoBluetooth/TestTeoBluetooth/Views/DeviceView.xaml.cs
index 75a5921..26db383 100644
--- a/TestTeoBluetooth/TestTeoBluetooth/Views/DeviceView.xaml.cs
+++ b/TestTeoBluetooth/TestTeoBluetooth/Views/DeviceView.xaml.cs
@@ -13,7 +13,7 @@ namespace TeoTestBluetooth.Views
     [XamlCompilation(XamlCompilationOptions.Compile)]
     public partial class DeviceView : ContentPage
     {
-        public DeviceView(BTDevice device)
+        public DeviceView(Models.Device device)
         {
             InitializeComponent();
             BindingContext = new DeviceViewModel(device);
