BluetoothTool.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452
  1. /**
  2. * html 5+ 串口蓝牙操作
  3. * 2021.04.23 uni-app版本
  4. * @auth boolTrue
  5. */
  6. /**
  7. * 初始化参数
  8. */
  9. //#ifdef APP-PLUS
  10. let BluetoothAdapter = plus.android.importClass("android.bluetooth.BluetoothAdapter");
  11. let Intent = plus.android.importClass("android.content.Intent");
  12. let IntentFilter = plus.android.importClass("android.content.IntentFilter");
  13. let BluetoothDevice = plus.android.importClass("android.bluetooth.BluetoothDevice");
  14. let UUID = plus.android.importClass("java.util.UUID");
  15. let Toast = plus.android.importClass("android.widget.Toast");
  16. //连接串口设备的 UUID
  17. let MY_UUID = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB");
  18. let invoke = plus.android.invoke;
  19. let btAdapter = BluetoothAdapter.getDefaultAdapter();
  20. let activity = plus.android.runtimeMainActivity();
  21. let btSocket = null;
  22. let btInStream = null;
  23. let btOutStream = null;
  24. let setIntervalId = 0;
  25. let btFindReceiver = null; //蓝牙搜索广播接收器
  26. let btStatusReceiver = null; //蓝牙状态监听广播
  27. //#endif
  28. /**
  29. * 构造对象
  30. */
  31. var blueToothTool = {
  32. state : {
  33. bluetoothEnable: false, //蓝牙是否开启
  34. bluetoothState: "", //当前蓝牙状态
  35. discoveryDeviceState: false, //是否正在搜索蓝牙设备
  36. readThreadState: false, //数据读取线程状态
  37. },
  38. options : {
  39. /**
  40. * 监听蓝牙状态回调
  41. * @param {String} state
  42. */
  43. listenBTStatusCallback: function(state) {},
  44. /**
  45. * 搜索到新的蓝牙设备回调
  46. * @param {Device} newDevice
  47. */
  48. discoveryDeviceCallback: function(newDevice) {},
  49. /**
  50. * 蓝牙搜索完成回调
  51. */
  52. discoveryFinishedCallback: function() {},
  53. /**
  54. * 接收到数据回调
  55. * @param {Array} dataByteArr
  56. */
  57. readDataCallback: function(dataByteArr) {},
  58. /**
  59. * 蓝牙连接中断回调
  60. * @param {Exception} e
  61. */
  62. connExceptionCallback: function(e) {}
  63. },
  64. init(setOptions) {
  65. Object.assign(this.options, setOptions);
  66. this.state.bluetoothEnable = this.getBluetoothStatus();
  67. this.listenBluetoothStatus();
  68. },
  69. shortToast(msg) {
  70. Toast.makeText(activity, msg, Toast.LENGTH_SHORT).show();
  71. },
  72. /**
  73. * 是否支持蓝牙
  74. * @return {boolean}
  75. */
  76. isSupportBluetooth() {
  77. if(btAdapter != null) {
  78. return true;
  79. }
  80. return false;
  81. },
  82. /**
  83. * 获取蓝牙的状态
  84. * @return {boolean} 是否已开启
  85. */
  86. getBluetoothStatus() {
  87. if(btAdapter != null) {
  88. return btAdapter.isEnabled();
  89. }
  90. return false;
  91. },
  92. /**
  93. * 打开蓝牙
  94. * @param activity
  95. * @param requestCode
  96. */
  97. turnOnBluetooth() {
  98. if(btAdapter == null) {
  99. shortToast("没有蓝牙");
  100. return;
  101. }
  102. if(!btAdapter.isEnabled()) {
  103. if(activity == null) {
  104. shortToast("未获取到activity");
  105. return;
  106. } else {
  107. let intent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
  108. let requestCode = 1;
  109. activity.startActivityForResult(intent, requestCode);
  110. return;
  111. }
  112. } else {
  113. shortToast("蓝牙已经打开");
  114. }
  115. },
  116. /**
  117. * 关闭蓝牙
  118. */
  119. turnOffBluetooth() {
  120. if(btAdapter != null && btAdapter.isEnabled()) {
  121. btAdapter.disable();
  122. }
  123. if(btFindReceiver != null) {
  124. try {
  125. activity.unregisterReceiver(btFindReceiver);
  126. } catch(e) {
  127. }
  128. btFindReceiver = null;
  129. }
  130. this.state.bluetoothEnable = false;
  131. this.cancelDiscovery();
  132. closeBtSocket();
  133. if(btAdapter != null && btAdapter.isEnabled()) {
  134. btAdapter.disable();
  135. shortToast("蓝牙关闭成功");
  136. } else {
  137. shortToast("蓝牙已经关闭");
  138. }
  139. },
  140. /**
  141. * 获取已经配对的设备
  142. * @return {Array} connetedDevices
  143. */
  144. getPairedDevices() {
  145. let pairedDevices = [];
  146. //蓝牙连接android原生对象,是一个set集合
  147. let pairedDevicesAndroid = null;
  148. if(btAdapter != null && btAdapter.isEnabled()) {
  149. pairedDevicesAndroid = btAdapter.getBondedDevices();
  150. } else {
  151. shortToast("蓝牙未开启");
  152. }
  153. if(!pairedDevicesAndroid) {
  154. return pairedDevices;
  155. }
  156. //遍历连接设备的set集合,转换为js数组
  157. let it = invoke(pairedDevicesAndroid, "iterator");
  158. while(invoke(it, "hasNext")) {
  159. let device = invoke(it, "next");
  160. pairedDevices.push({
  161. "name": invoke(device, "getName"),
  162. "address": invoke(device, "getAddress")
  163. });
  164. }
  165. return pairedDevices;
  166. },
  167. /**
  168. * 发现设备
  169. */
  170. discoveryNewDevice() {
  171. if(btFindReceiver != null) {
  172. try {
  173. activity.unregisterReceiver(btFindReceiver);
  174. } catch(e) {
  175. console.error(e);
  176. }
  177. btFindReceiver = null;
  178. this.cancelDiscovery();
  179. }
  180. let Build = plus.android.importClass("android.os.Build");
  181. //6.0以后的如果需要利用本机查找周围的wifi和蓝牙设备, 申请权限
  182. if(Build.VERSION.SDK_INT >= 6.0){
  183. }
  184. let options = this.options
  185. btFindReceiver = plus.android.implements("io.dcloud.android.content.BroadcastReceiver", {
  186. "onReceive": function(context, intent) {
  187. plus.android.importClass(context);
  188. plus.android.importClass(intent);
  189. let action = intent.getAction();
  190. if(BluetoothDevice.ACTION_FOUND == action) { // 找到设备
  191. let device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
  192. let newDevice = {
  193. "name": plus.android.invoke(device, "getName"),
  194. "address": plus.android.invoke(device, "getAddress")
  195. }
  196. options.discoveryDeviceCallback && options.discoveryDeviceCallback(newDevice);
  197. }
  198. if(BluetoothAdapter.ACTION_DISCOVERY_FINISHED == action) { // 搜索完成
  199. cancelDiscovery();
  200. options.discoveryFinishedCallback && options.discoveryFinishedCallback();
  201. }
  202. }
  203. });
  204. let filter = new IntentFilter();
  205. filter.addAction(BluetoothDevice.ACTION_FOUND);
  206. filter.addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
  207. activity.registerReceiver(btFindReceiver, filter);
  208. btAdapter.startDiscovery(); //开启搜索
  209. this.state.discoveryDeviceState = true;
  210. },
  211. /**
  212. * 蓝牙状态监听
  213. * @param {Activity} activity
  214. */
  215. listenBluetoothStatus() {
  216. if(btStatusReceiver != null) {
  217. try {
  218. activity.unregisterReceiver(btStatusReceiver);
  219. } catch(e) {
  220. console.error(e);
  221. }
  222. btStatusReceiver = null;
  223. }
  224. btStatusReceiver = plus.android.implements("io.dcloud.android.content.BroadcastReceiver", {
  225. "onReceive": (context, intent)=> {
  226. plus.android.importClass(context);
  227. plus.android.importClass(intent);
  228. let action = intent.getAction();
  229. switch(action) {
  230. case BluetoothAdapter.ACTION_STATE_CHANGED:
  231. let blueState = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, 0);
  232. let stateStr = "";
  233. switch(blueState) {
  234. case BluetoothAdapter.STATE_TURNING_ON:
  235. stateStr = "STATE_TURNING_ON";
  236. break;
  237. case BluetoothAdapter.STATE_ON:
  238. this.state.bluetoothEnable = true;
  239. stateStr = "STATE_ON";
  240. break;
  241. case BluetoothAdapter.STATE_TURNING_OFF:
  242. stateStr = "STATE_TURNING_OFF";
  243. break;
  244. case BluetoothAdapter.STATE_OFF:
  245. stateStr = "STATE_OFF";
  246. this.state.bluetoothEnable = false;
  247. break;
  248. }
  249. this.state.bluetoothState = stateStr;
  250. this.options.listenBTStatusCallback && this.options.listenBTStatusCallback(stateStr);
  251. break;
  252. }
  253. }
  254. });
  255. let filter = new IntentFilter();
  256. filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);
  257. activity.registerReceiver(btStatusReceiver, filter);
  258. // 首次连接 状态回调
  259. if(this.state.bluetoothEnable) {
  260. this.options.listenBTStatusCallback && this.options.listenBTStatusCallback('STATE_ON');
  261. }
  262. },
  263. /**
  264. * 根据蓝牙地址,连接设备
  265. * @param {Stirng} address
  266. * @return {Boolean}
  267. */
  268. connDevice(address, callback) {
  269. let InputStream = plus.android.importClass("java.io.InputStream");
  270. let OutputStream = plus.android.importClass("java.io.OutputStream");
  271. let BluetoothSocket = plus.android.importClass("android.bluetooth.BluetoothSocket");
  272. this.cancelDiscovery();
  273. if(btSocket != null) {
  274. this.closeBtSocket();
  275. }
  276. this.state.readThreadState = false;
  277. try {
  278. let device = invoke(btAdapter, "getRemoteDevice", address);
  279. btSocket = invoke(device, "createRfcommSocketToServiceRecord", MY_UUID);
  280. } catch(e) {
  281. console.error(e);
  282. shortToast("连接失败,获取Socket失败!");
  283. callback(false)
  284. return false;
  285. }
  286. try {
  287. invoke(btSocket, "connect");
  288. this.readData(); //读数据
  289. this.shortToast("连接成功");
  290. callback(true)
  291. } catch(e) {
  292. console.error(e);
  293. this.shortToast("连接失败");
  294. callback(false)
  295. try {
  296. btSocket.close();
  297. btSocket = null;
  298. } catch(e1) {
  299. console.error(e1);
  300. }
  301. return false;
  302. }
  303. return true;
  304. },
  305. /**
  306. * 断开连接设备
  307. * @param {Object} address
  308. * @return {Boolean}
  309. */
  310. disConnDevice() {
  311. if(btSocket != null) {
  312. this.closeBtSocket();
  313. }
  314. this.state.readThreadState = false;
  315. this.shortToast("断开连接成功");
  316. },
  317. /**
  318. * 断开连接设备
  319. * @param {Object} address
  320. * @return {Boolean}
  321. */
  322. closeBtSocket() {
  323. this.state.readThreadState = false;
  324. if(!btSocket) {
  325. return;
  326. }
  327. try {
  328. btSocket.close();
  329. } catch(e) {
  330. console.error(e);
  331. btSocket = null;
  332. }
  333. },
  334. /**
  335. * 取消发现
  336. */
  337. cancelDiscovery() {
  338. if(btAdapter.isDiscovering()) {
  339. btAdapter.cancelDiscovery();
  340. }
  341. if(btFindReceiver != null) {
  342. activity.unregisterReceiver(btFindReceiver);
  343. btFindReceiver = null;
  344. }
  345. this.state.discoveryDeviceState = false;
  346. },
  347. /**
  348. * 读取数据
  349. * @param {Object} activity
  350. * @param {Function} callback
  351. * @return {Boolean}
  352. */
  353. readData() {
  354. if(!btSocket) {
  355. this.shortToast("请先连接蓝牙设备!");
  356. return false;
  357. }
  358. try {
  359. btInStream = invoke(btSocket, "getInputStream");
  360. btOutStream = invoke(btSocket, "getOutputStream");
  361. } catch(e) {
  362. console.error(e);
  363. this.shortToast("创建输入输出流失败!");
  364. this.closeBtSocket();
  365. return false;
  366. }
  367. this.read();
  368. this.state.readThreadState = true;
  369. return true;
  370. },
  371. /**
  372. * 模拟java多线程读取数据
  373. */
  374. read() {
  375. let setTimeCount = 0;
  376. clearInterval(setIntervalId);
  377. setIntervalId = setInterval(()=> {
  378. setTimeCount++;
  379. if(this.state.readThreadState) {
  380. let t = new Date().getTime();
  381. //心跳检测
  382. if(setTimeCount % 20 == 0) {
  383. try {
  384. btOutStream.write([0b00]);
  385. } catch(e) {
  386. this.state.readThreadState = false;
  387. this.options.connExceptionCallback && this.options.connExceptionCallback(e);
  388. }
  389. }
  390. let dataArr = [];
  391. while(invoke(btInStream, "available") !== 0) {
  392. let data = invoke(btInStream, "read");
  393. dataArr.push(data);
  394. let ct = new Date().getTime();
  395. if(ct - t > 20) {
  396. break;
  397. }
  398. }
  399. if(dataArr.length > 0) {
  400. this.options.readDataCallback && this.options.readDataCallback(dataArr);
  401. }
  402. }
  403. }, 40);
  404. },
  405. /**
  406. * 发送数据
  407. * @param {String} dataStr
  408. * @return {Boolean}
  409. */
  410. sendData(dataStr) {
  411. if(!btOutStream) {
  412. this.shortToast("创建输出流失败!");
  413. return;
  414. }
  415. let bytes = invoke(dataStr, 'getBytes', 'gbk');
  416. try {
  417. btOutStream.write(bytes);
  418. } catch(e) {
  419. return false;
  420. }
  421. return true;
  422. },
  423. sendByteData(byteData) {
  424. if(!btOutStream) {
  425. this.shortToast("创建输出流失败!");
  426. return;
  427. }
  428. try {
  429. btOutStream.write(byteData);
  430. } catch(e) {
  431. return false;
  432. }
  433. return true;
  434. }
  435. }
  436. module.exports = blueToothTool