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