diff --git a/components/tpx-bluetooth/nativeBlueTooth.js b/components/tpx-bluetooth/nativeBlueTooth.js new file mode 100644 index 0000000..b15dd06 --- /dev/null +++ b/components/tpx-bluetooth/nativeBlueTooth.js @@ -0,0 +1,454 @@ +/** + * html 5+ 串口蓝牙操作 + * 2021.04.23 uni-app版本 + * @auth boolTrue + */ + +/** + * 初始化参数 + */ +//#ifdef APP-PLUS +let BluetoothAdapter = plus.android.importClass("android.bluetooth.BluetoothAdapter"); +let Intent = plus.android.importClass("android.content.Intent"); +let IntentFilter = plus.android.importClass("android.content.IntentFilter"); +let BluetoothDevice = plus.android.importClass("android.bluetooth.BluetoothDevice"); +let UUID = plus.android.importClass("java.util.UUID"); +let Toast = plus.android.importClass("android.widget.Toast"); +//连接串口设备的 UUID +let MY_UUID = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB"); + +let invoke = plus.android.invoke; +let btAdapter = BluetoothAdapter.getDefaultAdapter(); +let activity = plus.android.runtimeMainActivity(); + +let btSocket = null; +let btInStream = null; +let btOutStream = null; +let setIntervalId = 0; + +let btFindReceiver = null; //蓝牙搜索广播接收器 +let btStatusReceiver = null; //蓝牙状态监听广播 +//#endif +/** + * 构造对象 + */ +var blueToothTool = { + state : { + bluetoothEnable: false, //蓝牙是否开启 + bluetoothState: "", //当前蓝牙状态 + discoveryDeviceState: false, //是否正在搜索蓝牙设备 + readThreadState: false, //数据读取线程状态 + }, + options : { + /** + * 监听蓝牙状态回调 + * @param {String} state + */ + listenBTStatusCallback: function(state) {}, + /** + * 搜索到新的蓝牙设备回调 + * @param {Device} newDevice + */ + discoveryDeviceCallback: function(newDevice) {}, + /** + * 蓝牙搜索完成回调 + */ + discoveryFinishedCallback: function() {}, + /** + * 接收到数据回调 + * @param {Array} dataByteArr + */ + readDataCallback: function(dataByteArr) {}, + /** + * 蓝牙连接中断回调 + * @param {Exception} e + */ + connExceptionCallback: function(e) {} + }, + init(setOptions) { + Object.assign(this.options, setOptions); + this.state.bluetoothEnable = this.getBluetoothStatus(); + this.listenBluetoothStatus(); + }, + shortToast(msg) { + Toast.makeText(activity, msg, Toast.LENGTH_SHORT).show(); + }, + /** + * 是否支持蓝牙 + * @return {boolean} + */ + isSupportBluetooth() { + if(btAdapter != null) { + return true; + } + return false; + }, + /** + * 获取蓝牙的状态 + * @return {boolean} 是否已开启 + */ + getBluetoothStatus() { + if(btAdapter != null) { + return btAdapter.isEnabled(); + } + return false; + }, + /** + * 打开蓝牙 + * @param activity + * @param requestCode + */ + turnOnBluetooth() { + if(btAdapter == null) { + shortToast("没有蓝牙"); + return; + } + if(!btAdapter.isEnabled()) { + if(activity == null) { + shortToast("未获取到activity"); + return; + } else { + let intent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE); + let requestCode = 1; + activity.startActivityForResult(intent, requestCode); + return; + } + } else { + shortToast("蓝牙已经打开"); + } + }, + /** + * 关闭蓝牙 + */ + turnOffBluetooth() { + if(btAdapter != null && btAdapter.isEnabled()) { + btAdapter.disable(); + } + if(btFindReceiver != null) { + try { + activity.unregisterReceiver(btFindReceiver); + } catch(e) { + + } + btFindReceiver = null; + } + this.state.bluetoothEnable = false; + this.cancelDiscovery(); + closeBtSocket(); + + if(btAdapter != null && btAdapter.isEnabled()) { + btAdapter.disable(); + shortToast("蓝牙关闭成功"); + } else { + shortToast("蓝牙已经关闭"); + } + }, + /** + * 获取已经配对的设备 + * @return {Array} connetedDevices + */ + getPairedDevices() { + let pairedDevices = []; + + //蓝牙连接android原生对象,是一个set集合 + let pairedDevicesAndroid = null; + if(btAdapter != null && btAdapter.isEnabled()) { + pairedDevicesAndroid = btAdapter.getBondedDevices(); + } else { + shortToast("蓝牙未开启"); + } + + if(!pairedDevicesAndroid) { + return pairedDevices; + } + + //遍历连接设备的set集合,转换为js数组 + let it = invoke(pairedDevicesAndroid, "iterator"); + while(invoke(it, "hasNext")) { + let device = invoke(it, "next"); + pairedDevices.push({ + "name": invoke(device, "getName"), + "address": invoke(device, "getAddress") + }); + } + return pairedDevices; + }, + /** + * 发现设备 + */ + discoveryNewDevice() { + if(btFindReceiver != null) { + try { + activity.unregisterReceiver(btFindReceiver); + } catch(e) { + console.error(e); + } + btFindReceiver = null; + this.cancelDiscovery(); + } + let Build = plus.android.importClass("android.os.Build"); + + //6.0以后的如果需要利用本机查找周围的wifi和蓝牙设备, 申请权限 + if(Build.VERSION.SDK_INT >= 6.0){ + + } + let options = this.options + btFindReceiver = plus.android.implements("io.dcloud.android.content.BroadcastReceiver", { + "onReceive": function(context, intent) { + console.log("btFindReceiver onReceive") + plus.android.importClass(context); + plus.android.importClass(intent); + let action = intent.getAction(); + + if(BluetoothDevice.ACTION_FOUND == action) { // 找到设备 + let device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); + let newDevice = { + "name": plus.android.invoke(device, "getName"), + "address": plus.android.invoke(device, "getAddress") + } + options.discoveryDeviceCallback && options.discoveryDeviceCallback(newDevice); + } + if(BluetoothAdapter.ACTION_DISCOVERY_FINISHED == action) { // 搜索完成 + cancelDiscovery(); + options.discoveryFinishedCallback && options.discoveryFinishedCallback(); + } + } + }); + let filter = new IntentFilter(); + filter.addAction(BluetoothDevice.ACTION_FOUND); + filter.addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED); + activity.registerReceiver(btFindReceiver, filter); + btAdapter.startDiscovery(); //开启搜索 + console.log("btAdapter.startDiscovery") + this.state.discoveryDeviceState = true; + }, + /** + * 蓝牙状态监听 + * @param {Activity} activity + */ + listenBluetoothStatus() { + if(btStatusReceiver != null) { + try { + activity.unregisterReceiver(btStatusReceiver); + } catch(e) { + console.error(e); + } + btStatusReceiver = null; + } + btStatusReceiver = plus.android.implements("io.dcloud.android.content.BroadcastReceiver", { + "onReceive": (context, intent)=> { + plus.android.importClass(context); + plus.android.importClass(intent); + + let action = intent.getAction(); + switch(action) { + case BluetoothAdapter.ACTION_STATE_CHANGED: + let blueState = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, 0); + let stateStr = ""; + switch(blueState) { + case BluetoothAdapter.STATE_TURNING_ON: + stateStr = "STATE_TURNING_ON"; + break; + case BluetoothAdapter.STATE_ON: + this.state.bluetoothEnable = true; + stateStr = "STATE_ON"; + break; + case BluetoothAdapter.STATE_TURNING_OFF: + stateStr = "STATE_TURNING_OFF"; + break; + case BluetoothAdapter.STATE_OFF: + stateStr = "STATE_OFF"; + this.state.bluetoothEnable = false; + break; + } + this.state.bluetoothState = stateStr; + this.options.listenBTStatusCallback && this.options.listenBTStatusCallback(stateStr); + break; + } + } + }); + let filter = new IntentFilter(); + filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED); + activity.registerReceiver(btStatusReceiver, filter); + // 首次连接 状态回调 + if(this.state.bluetoothEnable) { + this.options.listenBTStatusCallback && this.options.listenBTStatusCallback('STATE_ON'); + } + }, + /** + * 根据蓝牙地址,连接设备 + * @param {Stirng} address + * @return {Boolean} + */ + connDevice(address, callback) { + let InputStream = plus.android.importClass("java.io.InputStream"); + let OutputStream = plus.android.importClass("java.io.OutputStream"); + let BluetoothSocket = plus.android.importClass("android.bluetooth.BluetoothSocket"); + + this.cancelDiscovery(); + if(btSocket != null) { + this.closeBtSocket(); + } + this.state.readThreadState = false; + + try { + let device = invoke(btAdapter, "getRemoteDevice", address); + btSocket = invoke(device, "createRfcommSocketToServiceRecord", MY_UUID); + } catch(e) { + console.error(e); + shortToast("连接失败,获取Socket失败!"); + callback(false) + return false; + } + try { + invoke(btSocket, "connect"); + this.readData(); //读数据 + this.shortToast("连接成功"); + callback(true) + } catch(e) { + console.error(e); + this.shortToast("连接失败"); + callback(false) + try { + btSocket.close(); + btSocket = null; + } catch(e1) { + console.error(e1); + } + return false; + } + return true; + }, + /** + * 断开连接设备 + * @param {Object} address + * @return {Boolean} + */ + disConnDevice() { + if(btSocket != null) { + this.closeBtSocket(); + } + this.state.readThreadState = false; + this.shortToast("断开连接成功"); + }, + /** + * 断开连接设备 + * @param {Object} address + * @return {Boolean} + */ + closeBtSocket() { + this.state.readThreadState = false; + if(!btSocket) { + return; + } + try { + btSocket.close(); + } catch(e) { + console.error(e); + btSocket = null; + } + }, + /** + * 取消发现 + */ + cancelDiscovery() { + if(btAdapter.isDiscovering()) { + btAdapter.cancelDiscovery(); + } + if(btFindReceiver != null) { + activity.unregisterReceiver(btFindReceiver); + btFindReceiver = null; + } + this.state.discoveryDeviceState = false; + }, + /** + * 读取数据 + * @param {Object} activity + * @param {Function} callback + * @return {Boolean} + */ + readData() { + if(!btSocket) { + this.shortToast("请先连接蓝牙设备!"); + return false; + } + try { + btInStream = invoke(btSocket, "getInputStream"); + btOutStream = invoke(btSocket, "getOutputStream"); + } catch(e) { + console.error(e); + this.shortToast("创建输入输出流失败!"); + this.closeBtSocket(); + return false; + } + this.read(); + this.state.readThreadState = true; + return true; + }, + /** + * 模拟java多线程读取数据 + */ + read() { + let setTimeCount = 0; + clearInterval(setIntervalId); + setIntervalId = setInterval(()=> { + setTimeCount++; + if(this.state.readThreadState) { + let t = new Date().getTime(); + //心跳检测 + if(setTimeCount % 20 == 0) { + try { + btOutStream.write([0b00]); + } catch(e) { + this.state.readThreadState = false; + this.options.connExceptionCallback && this.options.connExceptionCallback(e); + } + } + let dataArr = []; + while(invoke(btInStream, "available") !== 0) { + let data = invoke(btInStream, "read"); + dataArr.push(data); + let ct = new Date().getTime(); + if(ct - t > 20) { + break; + } + } + if(dataArr.length > 0) { + this.options.readDataCallback && this.options.readDataCallback(dataArr); + } + } + }, 40); + }, + /** + * 发送数据 + * @param {String} dataStr + * @return {Boolean} + */ + sendData(dataStr) { + if(!btOutStream) { + this.shortToast("创建输出流失败!"); + return; + } + let bytes = invoke(dataStr, 'getBytes', 'gbk'); + try { + btOutStream.write(bytes); + } catch(e) { + return false; + } + return true; + }, + sendByteData(byteData) { + if(!btOutStream) { + this.shortToast("创建输出流失败!"); + return; + } + try { + btOutStream.write(byteData); + } catch(e) { + return false; + } + return true; + } +} + +export default blueToothTool \ No newline at end of file diff --git a/components/tpx-bluetooth/tpx-bluetooth-native.vue b/components/tpx-bluetooth/tpx-bluetooth-native.vue new file mode 100644 index 0000000..eb252bd --- /dev/null +++ b/components/tpx-bluetooth/tpx-bluetooth-native.vue @@ -0,0 +1,359 @@ + + + + + \ No newline at end of file