陈嘉乐
2021-03-30 003abaf86b766eb40ef62300ab0c707774c36ba9
HDL_ON/UI/UI0-Stan/Logic/HdlBluetoothAndroidLogic.cs
New file
@@ -0,0 +1,590 @@
using Shared;
using System;
using System.Collections.Generic;
using System.Text;
namespace HDL_ON.Stan
{
#if __Android__
    public class HdlBluetoothLogic
    {
        #region ■ 变量声明___________________________
        /// <summary>
        /// 安卓蓝牙的逻辑
        /// </summary>
        private static HdlBluetoothLogic m_Current = null;
        /// <summary>
        /// 安卓蓝牙的逻辑
        /// </summary>
        public static HdlBluetoothLogic Current
        {
            get
            {
                if (m_Current == null)
                {
                    m_Current = new HdlBluetoothLogic();
                }
                return m_Current;
            }
        }
        /// <summary>
        /// 当前蓝牙客户端
        /// </summary>
        private Blufi.Espressif.BlufiClient nowBlufiClient = null;
        /// <summary>
        /// 接收事件
        /// </summary>
        private Action<string> ReceiveEvent = null;
        /// <summary>
        /// 发送状态(0:发送失败 1:发送成功)
        /// </summary>
        private int sendStatuValue = -1;
        #endregion
        #region ■ 蓝牙所需功能检测___________________
        /// <summary>
        /// 检测是否能够搜索蓝牙(内部会弹出Msg框,因为内部需要检测系统权限,所以参数采用回调的方式)
        /// </summary>
        /// <param name="resultEvent">检测结果事件</param>
        public void CheckCanScanBluetooth(Action<bool> resultEvent)
        {
            var adapter = Android.Bluetooth.BluetoothAdapter.DefaultAdapter;
            var scanner = adapter.BluetoothLeScanner;
            if (adapter.IsEnabled == false || scanner == null)
            {
                HdlMessageLogic.Current.ShowMassage(ShowMsgType.Tip, Language.StringByID(StringId.PleaseTurnOnBluetooth));
                resultEvent?.Invoke(false);
                return;
            }
            //检测是否打开了系统功能
            if (Android.OS.Build.VERSION.SdkInt >= Android.OS.BuildVersionCodes.M)
            {
                var locationManager = (Android.Locations.LocationManager)Application.Activity.GetSystemService(Android.Content.Context.LocationService);
                if (locationManager == null)
                {
                    //位置信息(GBS)不可用
                    HdlMessageLogic.Current.ShowMassage(ShowMsgType.Tip, Language.StringByID(StringId.GbsIsNotAvailable));
                    resultEvent?.Invoke(false);
                    return;
                }
                if (locationManager.IsProviderEnabled("network") == false)
                {
                    HdlMessageLogic.Current.ShowMassage(ShowMsgType.Tip, Language.StringByID(StringId.NetworkIsNotAvailable));
                    resultEvent?.Invoke(false);
                    return;
                }
                if (locationManager.IsProviderEnabled("gps") == false)
                {
                    //位置信息(GBS)不可用
                    HdlMessageLogic.Current.ShowMassage(ShowMsgType.Tip, Language.StringByID(StringId.GbsIsNotAvailable));
                    resultEvent?.Invoke(false);
                    return;
                }
            }
            //检测蓝牙需要的权限
            ((BaseActivity)Application.Activity).SetPermission((result1) =>
            {
                if (result1 == false)
                {
                    resultEvent?.Invoke(false);
                    return;
                }
                ((BaseActivity)Application.Activity).SetPermission((result2) =>
                {
                    if (result2 == false)
                    {
                        resultEvent?.Invoke(false);
                        return;
                    }
                    //全部通过
                    resultEvent?.Invoke(true);
                }, "android.permission.ACCESS_FINE_LOCATION");
            }, "android.permission.ACCESS_COARSE_LOCATION");
        }
        #endregion
        #region ■ 蓝牙扫描___________________________
        /// <summary>
        /// 搜索蓝牙
        /// </summary>
        /// <param name="waitTime">搜索时间(秒)</param>
        /// <param name="FinishEvent">搜索结束的事件</param>
        public void ScanBluetooth(int waitTime, Action<List<BluetoothInfo>> FinishEvent)
        {
            HdlThreadLogic.Current.RunMain(() =>
            {
                //再次检测是否能够搜索蓝牙
                this.CheckCanScanBluetooth((result) =>
                {
                    if (result == true)
                    {
                        HdlThreadLogic.Current.RunThread(() =>
                        {
                            //开始搜索蓝牙
                            this.DoScanBluetooth(waitTime, FinishEvent);
                        });
                    }
                });
            });
        }
        /// <summary>
        /// 开始搜索蓝牙
        /// </summary>
        /// <param name="waitTime">搜索时间(秒)</param>
        /// <param name="FinishEvent">搜索结束的事件</param>
        private void DoScanBluetooth(int waitTime, Action<List<BluetoothInfo>> FinishEvent)
        {
            var listBluetoothInfo = new List<BluetoothInfo>();
            BluetoothScanCallback scanCallback = null;
            Android.Bluetooth.BluetoothAdapter adapter = null;
            Android.Bluetooth.LE.BluetoothLeScanner scanner = null;
            //以防万一,蓝牙都丢在主线程中运行
            HdlThreadLogic.Current.RunMain(() =>
            {
                adapter = Android.Bluetooth.BluetoothAdapter.DefaultAdapter;
                scanner = adapter.BluetoothLeScanner;
                scanCallback = new BluetoothScanCallback();
                scanner.StartScan(null, new Android.Bluetooth.LE.ScanSettings.Builder().SetScanMode(Android.Bluetooth.LE.ScanMode.LowLatency).Build(), scanCallback);
            }, ShowErrorMode.NO);
            //等待
            System.Threading.Thread.Sleep(waitTime * 1000);
            HdlThreadLogic.Current.RunMain(() =>
            {
                scanner.StopScan(scanCallback);
                adapter.Dispose();
                foreach (var data in scanCallback.listData)
                {
                    listBluetoothInfo.Add(data);
                }
                scanCallback.listData.Clear();
                FinishEvent?.Invoke(listBluetoothInfo);
            });
        }
        /// <summary>
        /// 蓝牙的回调对象
        /// </summary>
        private class BluetoothScanCallback : Android.Bluetooth.LE.ScanCallback
        {
            /// <summary>
            /// 蓝牙列表
            /// </summary>
            public List<BluetoothInfo> listData = new List<BluetoothInfo>();
            /// <summary>
            /// 重复检测
            /// </summary>
            private List<string> listCheck = new List<string>();
            /// <summary>
            /// 蓝牙结果接收
            /// </summary>
            /// <param name="listResult"></param>
            public override void OnBatchScanResults(IList<Android.Bluetooth.LE.ScanResult> listResult)
            {
                foreach (var result in listResult)
                {
                    this.AddBluetoothResult(result);
                }
            }
            /// <summary>
            /// 蓝牙结果接收
            /// </summary>
            /// <param name="callbackType"></param>
            /// <param name="result"></param>
            public override void OnScanResult(Android.Bluetooth.LE.ScanCallbackType callbackType, Android.Bluetooth.LE.ScanResult result)
            {
                this.AddBluetoothResult(result);
            }
            /// <summary>
            /// 添加蓝牙缓存
            /// </summary>
            /// <param name="result"></param>
            private void AddBluetoothResult(Android.Bluetooth.LE.ScanResult result)
            {
                var device = result.Device;
                if (device == null || listCheck.Contains(device.Address) == true)
                {
                    return;
                }
                listCheck.Add(device.Address);
                var data = new BluetoothInfo();
                data.Name = device.Name;
                if (data.Name == null) { data.Name = string.Empty; }
                data.Address = device.Address;
                data.Device = device;
                listData.Add(data);
            }
        }
        #endregion
        #region ■ 蓝牙链接___________________________
        /// <summary>
        /// 蓝牙链接(false:连接失败 true:连接成功)
        /// </summary>
        /// <param name="bluetooth">需要链接的蓝牙对象</param>
        /// <param name="connectEvent">因为需要对方反馈,所以使用回调(链接结果 false:连接失败 true:连接成功)</param>
        public void ContectBluetooth(BluetoothInfo bluetooth, Action<bool> connectEvent)
        {
            HdlThreadLogic.Current.RunMain(() =>
            {
                try
                {
                    this.nowBlufiClient = new Blufi.Espressif.BlufiClient(Application.Activity, bluetooth.Device);
                    //一个回调事件
                    var callback = new InnerGattCallback();
                    callback.ConnectionStateEvent += (div, newState) =>
                    {
                        if (div == 1)
                        {
                            if (newState == Android.Bluetooth.ProfileState.Connected)
                            {
                                //链接建立成功
                                connectEvent?.Invoke(true);
                                //只通知一次
                                connectEvent = null;
                            }
                            else if (newState == Android.Bluetooth.ProfileState.Disconnected)
                            {
                                //关闭链接
                                this.DisContectBluetooth();
                                connectEvent?.Invoke(false);
                                //只通知一次
                                connectEvent = null;
                            }
                        }
                        else if (div == -1)
                        {
                            //关闭链接
                            this.DisContectBluetooth();
                            connectEvent?.Invoke(false);
                            //只通知一次
                            connectEvent = null;
                        }
                    };
                    nowBlufiClient.SetGattCallback(callback);
                    //另外一个回调事件
                    var blufiCall = new BlufiCallbackMain();
                    blufiCall.StateEvent += (div, data) =>
                    {
                        //-1:异常 1:正常 2:发送数据成功 3:发送数据失败
                        if (div == StatuEnum.A异常)
                        {
                            //关闭链接
                            this.DisContectBluetooth();
                            connectEvent?.Invoke(false);
                            //只通知一次
                            connectEvent = null;
                        }
                        else if (div == StatuEnum.A发送成功 || div == StatuEnum.A发送失败)
                        {
                            sendStatuValue = div == StatuEnum.A发送成功 ? 1 : 0;
                        }
                        else if (div == StatuEnum.A蓝牙反馈)
                        {
                            //蓝牙返回的结果
                            this.ReceiveEvent?.Invoke(data);
                        }
                    };
                    nowBlufiClient.SetBlufiCallback(blufiCall);
                    //执行链接
                    nowBlufiClient.Connect();
                }
                catch
                {
                    connectEvent?.Invoke(false);
                    connectEvent = null;
                }
            });
        }
        /// <summary>
        /// 先这么定义一个空的继承
        /// </summary>
        private class InnerGattCallback : Android.Bluetooth.BluetoothGattCallback
        {
            /// <summary>
            /// 状态事件回调(-1:异常 1:OnConnectionStateChange)
            /// </summary>
            public Action<int,Android.Bluetooth.ProfileState> ConnectionStateEvent = null;
            /// <summary>
            /// 链接状态改变
            /// </summary>
            /// <param name="gatt"></param>
            /// <param name="status"></param>
            /// <param name="newState"></param>
            public override void OnConnectionStateChange(Android.Bluetooth.BluetoothGatt gatt, Android.Bluetooth.GattStatus status, Android.Bluetooth.ProfileState newState)
            {
                if (status == Android.Bluetooth.GattStatus.Success)
                {
                    //回调事件
                    this.ConnectionStateEvent?.Invoke(1, newState);
                }
                else
                {
                    //回调事件
                    this.ConnectionStateEvent?.Invoke(-1, 0);
                }
            }
            /// <summary>
            /// 成功发现设备的services时,调用此方法
            /// </summary>
            /// <param name="gatt"></param>
            /// <param name="status"></param>
            public override void OnServicesDiscovered(Android.Bluetooth.BluetoothGatt gatt, Android.Bluetooth.GattStatus status)
            {
                if (status != Android.Bluetooth.GattStatus.Success)
                {
                    //回调事件
                    this.ConnectionStateEvent?.Invoke(-1, 0);
                }
            }
            /// <summary>
            /// 应该是写入事件吧
            /// </summary>
            /// <param name="gatt"></param>
            /// <param name="characteristic"></param>
            /// <param name="status"></param>
            public override void OnCharacteristicWrite(Android.Bluetooth.BluetoothGatt gatt, Android.Bluetooth.BluetoothGattCharacteristic characteristic, Android.Bluetooth.GattStatus status)
            {
                if (status != Android.Bluetooth.GattStatus.Success)
                {
                    //回调事件
                    this.ConnectionStateEvent?.Invoke(-1, 0);
                }
            }
        }
        /// <summary>
        /// 抄SDK的,我也不知道这个是什么
        /// </summary>
        private class BlufiCallbackMain : Blufi.Espressif.BlufiCallback
        {
            /// <summary>
            /// 状态事件回调 当第一个参数为"A蓝牙反馈"时,第二个参数为蓝牙返回的信息
            /// </summary>
            public Action<StatuEnum, string> StateEvent = null;
            /// <summary>
            /// 抄SDK的,我也不知道这个是什么
            /// </summary>
            /// <param name="client"></param>
            /// <param name="gatt"></param>
            /// <param name="service"></param>
            /// <param name="writeChar"></param>
            /// <param name="notifyChar"></param>
            public override void OnGattPrepared(Blufi.Espressif.BlufiClient client, Android.Bluetooth.BluetoothGatt gatt, Android.Bluetooth.BluetoothGattService service,
                Android.Bluetooth.BluetoothGattCharacteristic writeChar, Android.Bluetooth.BluetoothGattCharacteristic notifyChar)
            {
                if (service == null || writeChar == null || notifyChar == null)
                {
                    StateEvent?.Invoke(StatuEnum.A异常, null);
                    return;
                }
                try
                {
                    int mtu = 128;
                    if ((int)Android.OS.Build.VERSION.SdkInt == 29
                      && Android.OS.Build.Manufacturer.ToLower().StartsWith("samsung") == true)
                    {
                        mtu = 23;
                    }
                    var requestMtu = gatt.RequestMtu(mtu);
                    if (!requestMtu)
                    {
                        //Request mtu failed
                        client.SetPostPackageLengthLimit(20);
                    }
                    StateEvent?.Invoke(StatuEnum.A正常, null); ;
                }
                catch
                {
                    StateEvent?.Invoke(StatuEnum.A异常, null);
                    return;
                }
            }
            /// <summary>
            /// 手机端发送数据到蓝牙的结果
            /// </summary>
            /// <param name="client"></param>
            /// <param name="status">0:成功 其他都是失败</param>
            /// <param name="data">手机端发送的数据</param>
            public override void OnPostCustomDataResult(Blufi.Espressif.BlufiClient client, int status, byte[] data)
            {
                StateEvent?.Invoke(status == 0 ? StatuEnum.A发送成功 : StatuEnum.A发送失败, null);
            }
            /// <summary>
            /// 蓝牙回复的结果
            /// </summary>
            /// <param name="client"></param>
            /// <param name="status">0:成功 其他都是失败</param>
            /// <param name="data">蓝牙回复的数据</param>
            public override void OnReceiveCustomData(Blufi.Espressif.BlufiClient client, int status, byte[] data)
            {
                if (status == 0)
                {
                    var receiveData = System.Text.Encoding.UTF8.GetString(data);
                    StateEvent?.Invoke(StatuEnum.A蓝牙反馈, receiveData);
                }
            }
        }
        #endregion
        #region ■ 蓝牙关闭___________________________
        /// <summary>
        ///  关闭蓝牙链接
        /// </summary>
        public void DisContectBluetooth()
        {
            HdlThreadLogic.Current.RunMain(() =>
            {
                this.nowBlufiClient?.RequestCloseConnection();
                this.nowBlufiClient = null;
                m_Current = null;
            }, ShowErrorMode.NO);
        }
        /// <summary>
        /// 摧毁
        /// </summary>
        public void Dispone()
        {
            //关闭蓝牙链接
            this.DisContectBluetooth();
            this.ReceiveEvent = null;
            m_Current = null;
        }
        #endregion
        #region ■ 发送数据___________________________
        /// <summary>
        /// 发送数据给蓝牙
        /// </summary>
        /// <param name="i_data">发送的数据</param>
        /// <param name="waiTime">等待时间(秒),如果设置为0,则只要发送不出现异常,直接判定为成功</param>
        public bool SendData(string i_data, int waiTime = 0)
        {
            if (this.nowBlufiClient == null)
            {
                return false;
            }
            try
            {
                this.sendStatuValue = -1;
                HdlThreadLogic.Current.RunMain(() =>
                {
                    //发送数据
                    var byteData = System.Text.Encoding.UTF8.GetBytes(i_data);
                    this.nowBlufiClient.PostCustomData(byteData);
                }, ShowErrorMode.NO);
                if (waiTime == 0) { return true; }
                waiTime *= 5;
                while (this.sendStatuValue == -1 && waiTime > 0)
                {
                    System.Threading.Thread.Sleep(200);
                    waiTime--;
                }
                return this.sendStatuValue == 1;
            }
            catch { return false; }
        }
        #endregion
        #region ■ 一般方法___________________________
        /// <summary>
        /// 添加蓝牙的接收事件
        /// </summary>
        /// <param name="i_ReceiveEvent">蓝牙接收事件</param>
        public void AddReceiveEvent(Action<string> i_ReceiveEvent)
        {
            this.ReceiveEvent = i_ReceiveEvent;
        }
        /// <summary>
        /// 移除蓝牙的接收事件
        /// </summary>
        public void RemoveReceiveEvent()
        {
            this.ReceiveEvent = null;
        }
        #endregion
        #region ■ 结构体_____________________________
        /// <summary>
        /// 蓝牙返回的信息
        /// </summary>
        public class BluetoothInfo
        {
            /// <summary>
            /// 名字(此名字不会null,如果它本身是null,只会是string.empty)
            /// </summary>
            public string Name = string.Empty;
            /// <summary>
            /// 地址
            /// </summary>
            public string Address = string.Empty;
            /// <summary>
            /// 蓝牙设备
            /// </summary>
            public Android.Bluetooth.BluetoothDevice Device = null;
        }
        /// <summary>
        /// 状态枚举
        /// </summary>
        private enum StatuEnum
        {
            A异常 = -1,
            A正常 = 1,
            A发送成功 = 2,
            A发送失败 = 3,
            A蓝牙反馈 = 4
        }
        #endregion
    }
#endif
}