New file |
| | |
| | | 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 |
| | | } |
| | | |