/** * 经典蓝牙工具(仅 Android APP-PLUS) * 用于佳博 D320FAX 等一体机的 Virtual BT Printer 连接 * 支持 RFCOMM 回退连接(部分设备需要) */ // #ifdef APP-PLUS function getAdapter () { if (typeof plus === 'undefined' || !plus.android) return null const BluetoothAdapter = plus.android.importClass('android.bluetooth.BluetoothAdapter') return BluetoothAdapter.getDefaultAdapter() } function getActivity () { return typeof plus !== 'undefined' ? plus.android.runtimeMainActivity() : null } function showToast (msg) { try { const activity = getActivity() if (activity) { const Toast = plus.android.importClass('android.widget.Toast') Toast.makeText(activity, String(msg), Toast.LENGTH_SHORT).show() } } catch (_) {} } function getInvoke () { return typeof plus !== 'undefined' && plus.android ? plus.android.invoke : null } function getMyUuid () { if (typeof plus === 'undefined' || !plus.android) return null return plus.android.importClass('java.util.UUID').fromString('00001101-0000-1000-8000-00805F9B34FB') } let btSocket = null let btInStream = null let btOutStream = null let btFindReceiver = null // #endif var blueToothTool = { state: { bluetoothEnable: false, readThreadState: false }, shortToast (msg) { // #ifdef APP-PLUS showToast(msg) // #endif }, getBluetoothStatus () { // #ifdef APP-PLUS const btAdapter = getAdapter() return btAdapter != null && btAdapter.isEnabled() // #endif return false }, /** 获取已配对设备(含 Virtual BT Printer / D320FAX) */ getPairedDevices () { // #ifdef APP-PLUS const pairedDevices = [] try { const btAdapter = getAdapter() if (!btAdapter || !btAdapter.isEnabled()) { this.shortToast('Bluetooth is off') return pairedDevices } const bonded = btAdapter.getBondedDevices() if (!bonded) return pairedDevices const inv = getInvoke() if (!inv) return pairedDevices const it = inv(bonded, 'iterator') while (inv(it, 'hasNext')) { const device = inv(it, 'next') const deviceType = inv(device, 'getType') const deviceId = inv(device, 'getAddress') let deviceName = inv(device, 'getName') if (deviceName == null) deviceName = '' deviceName = String(deviceName).trim() || 'Unknown Device' let typeStr = 'unknown' if (deviceType === 1) typeStr = 'classic' else if (deviceType === 2) typeStr = 'ble' else if (deviceType === 3) typeStr = 'dual' pairedDevices.push({ name: deviceName, deviceId: String(deviceId), type: typeStr }) } } catch (e) { console.error('getPairedDevices error:', e) } return pairedDevices // #endif return [] }, /** 经典蓝牙扫描(发现未配对设备,如 d320fax_295c)— 不过滤任何设备 */ startClassicDiscovery (onDeviceFound, onDiscoveryFinished) { // #ifdef APP-PLUS const btAdapter = getAdapter() const activity = getActivity() if (!btAdapter || !btAdapter.isEnabled() || !activity) return if (btFindReceiver) { try { activity.unregisterReceiver(btFindReceiver) } catch (_) {} btFindReceiver = null } if (btAdapter.isDiscovering()) btAdapter.cancelDiscovery() const BluetoothAdapter = plus.android.importClass('android.bluetooth.BluetoothAdapter') const BluetoothDevice = plus.android.importClass('android.bluetooth.BluetoothDevice') const IntentFilter = plus.android.importClass('android.content.IntentFilter') const inv = getInvoke() btFindReceiver = plus.android.implements('io.dcloud.android.content.BroadcastReceiver', { onReceive (context, intent) { const action = intent.getAction() if (BluetoothDevice.ACTION_FOUND === action) { const device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE) if (!device) return const deviceType = inv(device, 'getType') const deviceId = String(inv(device, 'getAddress') || '') let deviceName = inv(device, 'getName') if (deviceName == null) deviceName = '' deviceName = String(deviceName).trim() || 'Unknown Device' let typeStr = 'unknown' if (deviceType === 1) typeStr = 'classic' else if (deviceType === 2) typeStr = 'ble' else if (deviceType === 3) typeStr = 'dual' if (onDeviceFound) onDeviceFound({ name: deviceName, deviceId, type: typeStr }) } if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED === action) { try { activity.unregisterReceiver(btFindReceiver) } catch (_) {} btFindReceiver = null if (onDiscoveryFinished) onDiscoveryFinished() } }, }) const filter = new IntentFilter() filter.addAction(BluetoothDevice.ACTION_FOUND) filter.addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED) activity.registerReceiver(btFindReceiver, filter) btAdapter.startDiscovery() // #endif }, /** 停止经典蓝牙扫描 */ cancelClassicDiscovery () { // #ifdef APP-PLUS const btAdapter = getAdapter() const activity = getActivity() if (btAdapter && btAdapter.isDiscovering()) btAdapter.cancelDiscovery() if (btFindReceiver && activity) { try { activity.unregisterReceiver(btFindReceiver) } catch (_) {} btFindReceiver = null } // #endif }, /** 连接经典蓝牙设备(含 D320FAX Virtual BT Printer,支持 RFCOMM 回退) */ connDevice (address, callback) { // #ifdef APP-PLUS if (btSocket != null) this.closeBtSocket() this.state.readThreadState = false const btAdapter = getAdapter() if (!btAdapter) { this.shortToast('Bluetooth not available') if (callback) callback(false) return false } const inv = getInvoke() const uuid = getMyUuid() if (!inv || !uuid) { this.shortToast('Bluetooth not ready') if (callback) callback(false) return false } try { const device = inv(btAdapter, 'getRemoteDevice', address) btSocket = inv(device, 'createRfcommSocketToServiceRecord', uuid) } catch (e) { console.warn('createRfcommSocketToServiceRecord failed, try fallback:', e) try { const device = inv(btAdapter, 'getRemoteDevice', address) const cls = inv(device, 'getClass') const intClass = plus.android.importClass('java.lang.Integer').TYPE const m = inv(cls, 'getMethod', 'createRfcommSocket', intClass) btSocket = inv(m, 'invoke', device, 1) } catch (e2) { console.error('RFCOMM fallback failed:', e2) this.shortToast('Connect failed') if (callback) callback(false) return false } } try { inv(btSocket, 'connect') btInStream = inv(btSocket, 'getInputStream') btOutStream = inv(btSocket, 'getOutputStream') this.state.readThreadState = true this.shortToast('Connected') if (callback) callback(true) } catch (e) { try { btSocket.close() } catch (_) {} btSocket = null this.shortToast('Connect failed') if (callback) callback(false) return false } return true // #endif if (callback) callback(false) return false }, disConnDevice () { // #ifdef APP-PLUS this.closeBtSocket() this.state.readThreadState = false this.shortToast('Disconnected') // #endif }, closeBtSocket () { // #ifdef APP-PLUS this.state.readThreadState = false if (btSocket) { try { btSocket.close() } catch (_) {} btSocket = null btInStream = null btOutStream = null } // #endif }, sendByteData (byteData) { // #ifdef APP-PLUS if (!btOutStream) { this.shortToast('Not connected') return false } try { const CHUNK_SIZE = 4096 for (let i = 0; i < byteData.length; i += CHUNK_SIZE) { const chunk = byteData.slice(i, i + CHUNK_SIZE) btOutStream.write(chunk) } return true } catch (e) { return false } // #endif return false } } export default blueToothTool