diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 5dd8db9..ff9af45 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -68,8 +68,6 @@ - - diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 5dd8db9..ff9af45 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -68,8 +68,6 @@ - - diff --git a/app/src/main/java/com/casic/birmm/inspect/bean/BlueToothBean.java b/app/src/main/java/com/casic/birmm/inspect/bean/BlueToothBean.java index 14424ec..c842129 100644 --- a/app/src/main/java/com/casic/birmm/inspect/bean/BlueToothBean.java +++ b/app/src/main/java/com/casic/birmm/inspect/bean/BlueToothBean.java @@ -1,27 +1,29 @@ package com.casic.birmm.inspect.bean; +import android.bluetooth.BluetoothDevice; + public class BlueToothBean { - private String blueToothName; - private String blueToothAddress; + private BluetoothDevice bluetoothDevice; //蓝牙设备 + private int rssi; //蓝牙信号 - public BlueToothBean(String blueToothName, String blueToothAddress) { - this.blueToothName = blueToothName; - this.blueToothAddress = blueToothAddress; + public BlueToothBean(BluetoothDevice bluetoothDevice, int rssi) { + this.bluetoothDevice = bluetoothDevice; + this.rssi = rssi; } - public String getBlueToothName() { - return blueToothName; + public BluetoothDevice getBluetoothDevice() { + return bluetoothDevice; } - public void setBlueToothName(String blueToothName) { - this.blueToothName = blueToothName; + public void setBluetoothDevice(BluetoothDevice bluetoothDevice) { + this.bluetoothDevice = bluetoothDevice; } - public String getBlueToothAddress() { - return blueToothAddress; + public int getRssi() { + return rssi; } - public void setBlueToothAddress(String blueToothAddress) { - this.blueToothAddress = blueToothAddress; + public void setRssi(int rssi) { + this.rssi = rssi; } } diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 5dd8db9..ff9af45 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -68,8 +68,6 @@ - - diff --git a/app/src/main/java/com/casic/birmm/inspect/bean/BlueToothBean.java b/app/src/main/java/com/casic/birmm/inspect/bean/BlueToothBean.java index 14424ec..c842129 100644 --- a/app/src/main/java/com/casic/birmm/inspect/bean/BlueToothBean.java +++ b/app/src/main/java/com/casic/birmm/inspect/bean/BlueToothBean.java @@ -1,27 +1,29 @@ package com.casic.birmm.inspect.bean; +import android.bluetooth.BluetoothDevice; + public class BlueToothBean { - private String blueToothName; - private String blueToothAddress; + private BluetoothDevice bluetoothDevice; //蓝牙设备 + private int rssi; //蓝牙信号 - public BlueToothBean(String blueToothName, String blueToothAddress) { - this.blueToothName = blueToothName; - this.blueToothAddress = blueToothAddress; + public BlueToothBean(BluetoothDevice bluetoothDevice, int rssi) { + this.bluetoothDevice = bluetoothDevice; + this.rssi = rssi; } - public String getBlueToothName() { - return blueToothName; + public BluetoothDevice getBluetoothDevice() { + return bluetoothDevice; } - public void setBlueToothName(String blueToothName) { - this.blueToothName = blueToothName; + public void setBluetoothDevice(BluetoothDevice bluetoothDevice) { + this.bluetoothDevice = bluetoothDevice; } - public String getBlueToothAddress() { - return blueToothAddress; + public int getRssi() { + return rssi; } - public void setBlueToothAddress(String blueToothAddress) { - this.blueToothAddress = blueToothAddress; + public void setRssi(int rssi) { + this.rssi = rssi; } } diff --git a/app/src/main/java/com/casic/birmm/inspect/extensions/ByteArray.kt b/app/src/main/java/com/casic/birmm/inspect/extensions/ByteArray.kt new file mode 100644 index 0000000..8b2ea6e --- /dev/null +++ b/app/src/main/java/com/casic/birmm/inspect/extensions/ByteArray.kt @@ -0,0 +1,59 @@ +package com.casic.birmm.inspect.extensions + +import java.util.* +import kotlin.collections.ArrayList +import kotlin.math.abs + +/** + * ByteArray扩展方法 + */ + +//ByteArray转16进制 +fun ByteArray.toHexString(hasSpace: Boolean = true) = this.joinToString("") { + (it.toInt() and 0xFF).toString(16).padStart( + 2, + '0' + ).toUpperCase(Locale.ROOT) + if (hasSpace) " " else "" +} + +//ByteArray转ascii码字符串 +fun ByteArray.toAsciiString(): String { + val builder = StringBuilder() + for (index in 1..12) { + builder.append(this[index].toInt().toChar()) + } + return builder.toString() +} + +//ByteArray转十进制字符串集合 +fun ByteArray.toDecStringList(): ArrayList { + val data: ArrayList = ArrayList() + /** + * 170,0,0,0,100,1,0,100,0,0,0,100,13,10 + * + * 170是数据标头AA + * 13,10是数据结束位 + * */ + //AA 00 00 00 64 01 00 64 00 00 00 64 0D 0A + //浓度:4字节 0-99999 + var potencyValue = 0 + for (index in 1..4) { + potencyValue += abs(this[index].toString(10).toInt()) + } + data.add(potencyValue.toString()) + //报警标志:1字节 0:未报警 1:报警 + data.add(this[5].toString(10)) + //报警值:2字节 0-65535 + var alarmValue = 0 + for (index in 6..7) { + alarmValue += abs(this[index].toString(10).toInt()) + } + data.add(alarmValue.toString()) + //5s内最大值: 4字节 0-99999 + var maxPotencyValue = 0 + for (index in 8..11) { + maxPotencyValue += abs(this[index].toString(10).toInt()) + } + data.add(maxPotencyValue.toString()) + return data +} \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 5dd8db9..ff9af45 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -68,8 +68,6 @@ - - diff --git a/app/src/main/java/com/casic/birmm/inspect/bean/BlueToothBean.java b/app/src/main/java/com/casic/birmm/inspect/bean/BlueToothBean.java index 14424ec..c842129 100644 --- a/app/src/main/java/com/casic/birmm/inspect/bean/BlueToothBean.java +++ b/app/src/main/java/com/casic/birmm/inspect/bean/BlueToothBean.java @@ -1,27 +1,29 @@ package com.casic.birmm.inspect.bean; +import android.bluetooth.BluetoothDevice; + public class BlueToothBean { - private String blueToothName; - private String blueToothAddress; + private BluetoothDevice bluetoothDevice; //蓝牙设备 + private int rssi; //蓝牙信号 - public BlueToothBean(String blueToothName, String blueToothAddress) { - this.blueToothName = blueToothName; - this.blueToothAddress = blueToothAddress; + public BlueToothBean(BluetoothDevice bluetoothDevice, int rssi) { + this.bluetoothDevice = bluetoothDevice; + this.rssi = rssi; } - public String getBlueToothName() { - return blueToothName; + public BluetoothDevice getBluetoothDevice() { + return bluetoothDevice; } - public void setBlueToothName(String blueToothName) { - this.blueToothName = blueToothName; + public void setBluetoothDevice(BluetoothDevice bluetoothDevice) { + this.bluetoothDevice = bluetoothDevice; } - public String getBlueToothAddress() { - return blueToothAddress; + public int getRssi() { + return rssi; } - public void setBlueToothAddress(String blueToothAddress) { - this.blueToothAddress = blueToothAddress; + public void setRssi(int rssi) { + this.rssi = rssi; } } diff --git a/app/src/main/java/com/casic/birmm/inspect/extensions/ByteArray.kt b/app/src/main/java/com/casic/birmm/inspect/extensions/ByteArray.kt new file mode 100644 index 0000000..8b2ea6e --- /dev/null +++ b/app/src/main/java/com/casic/birmm/inspect/extensions/ByteArray.kt @@ -0,0 +1,59 @@ +package com.casic.birmm.inspect.extensions + +import java.util.* +import kotlin.collections.ArrayList +import kotlin.math.abs + +/** + * ByteArray扩展方法 + */ + +//ByteArray转16进制 +fun ByteArray.toHexString(hasSpace: Boolean = true) = this.joinToString("") { + (it.toInt() and 0xFF).toString(16).padStart( + 2, + '0' + ).toUpperCase(Locale.ROOT) + if (hasSpace) " " else "" +} + +//ByteArray转ascii码字符串 +fun ByteArray.toAsciiString(): String { + val builder = StringBuilder() + for (index in 1..12) { + builder.append(this[index].toInt().toChar()) + } + return builder.toString() +} + +//ByteArray转十进制字符串集合 +fun ByteArray.toDecStringList(): ArrayList { + val data: ArrayList = ArrayList() + /** + * 170,0,0,0,100,1,0,100,0,0,0,100,13,10 + * + * 170是数据标头AA + * 13,10是数据结束位 + * */ + //AA 00 00 00 64 01 00 64 00 00 00 64 0D 0A + //浓度:4字节 0-99999 + var potencyValue = 0 + for (index in 1..4) { + potencyValue += abs(this[index].toString(10).toInt()) + } + data.add(potencyValue.toString()) + //报警标志:1字节 0:未报警 1:报警 + data.add(this[5].toString(10)) + //报警值:2字节 0-65535 + var alarmValue = 0 + for (index in 6..7) { + alarmValue += abs(this[index].toString(10).toInt()) + } + data.add(alarmValue.toString()) + //5s内最大值: 4字节 0-99999 + var maxPotencyValue = 0 + for (index in 8..11) { + maxPotencyValue += abs(this[index].toString(10).toInt()) + } + data.add(maxPotencyValue.toString()) + return data +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/birmm/inspect/utils/BLEManager.kt b/app/src/main/java/com/casic/birmm/inspect/utils/BLEManager.kt new file mode 100644 index 0000000..a1fcddc --- /dev/null +++ b/app/src/main/java/com/casic/birmm/inspect/utils/BLEManager.kt @@ -0,0 +1,484 @@ +package com.casic.birmm.inspect.utils + +import android.bluetooth.* +import android.content.Context +import android.content.Intent +import android.os.Build +import android.os.Handler +import android.util.Log +import com.casic.birmm.inspect.bean.BlueToothBean +import com.casic.birmm.inspect.utils.callback.OnBleConnectListener +import com.casic.birmm.inspect.utils.callback.OnDeviceSearchListener +import java.util.* + + +/** + * 1、扫描设备 + * 2、配对设备 + * 3、解除设备配对 + * 4、连接设备 + * 6、发现服务 + * 7、打开读写功能 + * 8、数据通讯(发送数据、接收数据) + * 9、断开连接 + */ +object BLEManager { + private const val Tag = "BLEManager" + private const val MAX_CONNECT_TIME = 10000L//连接超时时间10s + private var context: Context? = null + private var bluetoothManager: BluetoothManager? = null + private var bluetoothAdapter: BluetoothAdapter? = null + private val handler = Handler() + private var onDeviceSearchListener: OnDeviceSearchListener? = null + private var isConnecting = false + private lateinit var curConnectDevice: BluetoothDevice + private lateinit var serviceUUID: UUID + private lateinit var readUUID: UUID + private lateinit var writeUUID: UUID + private var onBleConnectListener: OnBleConnectListener? = null + private var bluetoothGatt: BluetoothGatt? = null + private var bluetoothGattService: BluetoothGattService? = null + private var readCharacteristic: BluetoothGattCharacteristic? = null + private var writeCharacteristic: BluetoothGattCharacteristic? = null + + fun initBle(context: Context): Boolean { + this.context = context + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) { + bluetoothManager = + context.getSystemService(Context.BLUETOOTH_SERVICE) as BluetoothManager + if (bluetoothManager == null) { + return false + } + bluetoothAdapter = bluetoothManager!!.adapter + return bluetoothAdapter != null + } else { + return false + } + } + + fun isEnable(): Boolean { + if (bluetoothAdapter == null) { + return false + } + return bluetoothAdapter!!.isEnabled + } + + /** + * 打开蓝牙 + * @param isFast true 直接打开蓝牙 false 提示用户打开 + */ + fun openBluetooth(context: Context, isFast: Boolean) { + if (!isEnable()) { + if (isFast) { + Log.d(Tag, "直接打开手机蓝牙") + bluetoothAdapter!!.enable() + } else { + context.startActivity(Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE)) + } + } else { + Log.d(Tag, "手机蓝牙状态已开") + } + } + + /** + * 本地蓝牙是否处于正在扫描状态 + * @return true false + */ + fun isDiscovery(): Boolean { + if (bluetoothAdapter == null) { + return false + } + return bluetoothAdapter!!.isDiscovering + } + + fun stopDiscoveryDevice() { + handler.removeCallbacks(stopScanRunnable) + if (bluetoothAdapter == null) { + return + } + bluetoothAdapter!!.stopLeScan(scanCallback) + } + + private val stopScanRunnable = Runnable { + if (onDeviceSearchListener != null) { + onDeviceSearchListener!!.onDiscoveryOutTime() //扫描超时回调 + } + //scanTime之后还没有扫描到设备,就停止扫描。 + stopDiscoveryDevice() + } + + //扫描设备回调 + private val scanCallback = object : BluetoothAdapter.LeScanCallback { + override fun onLeScan(device: BluetoothDevice?, rssi: Int, scanRecord: ByteArray?) { + if (device == null) return + if (device.name != null) { + Log.d(Tag, device.name + "-->" + device.address) + if (onDeviceSearchListener != null) { + onDeviceSearchListener!!.onDeviceFound(BlueToothBean(device, rssi)) //扫描到设备回调 + } + } + } + } + + fun startDiscoveryDevice(onDeviceSearchListener: OnDeviceSearchListener, scanTime: Long) { + if (bluetoothAdapter == null) { + return + } + this.onDeviceSearchListener = onDeviceSearchListener + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) { + bluetoothAdapter!!.startLeScan(scanCallback) + } else { + return + } + //设定最长扫描时间 + handler.postDelayed(stopScanRunnable, scanTime) + } + + fun connectBleDevice( + context: Context, bluetoothDevice: BluetoothDevice, + outTime: Long, serviceUUID: String, + readUUID: String, writeUUID: String, + onBleConnectListener: OnBleConnectListener + ): BluetoothGatt? { + if (isConnecting) { + Log.d(Tag, "connectBleDevice()-->isConnecting = true") + return null + } + this.curConnectDevice = bluetoothDevice + this.serviceUUID = UUID.fromString(serviceUUID) + this.readUUID = UUID.fromString(readUUID) + this.writeUUID = UUID.fromString(writeUUID) + this.onBleConnectListener = onBleConnectListener + Log.d(Tag, "开始准备连接:" + bluetoothDevice.name + "-->" + bluetoothDevice.address) + try { + bluetoothGatt = bluetoothDevice.connectGatt(context, false, bluetoothGattCallback) + bluetoothGatt!!.connect() + isConnecting = true + } catch (e: Exception) { + e.printStackTrace() + } + //设置连接超时时间10s + handler.postDelayed(connectOutTimeRunnable, outTime) + return bluetoothGatt + } + + private val bluetoothGattCallback = object : BluetoothGattCallback() { + override fun onConnectionStateChange(gatt: BluetoothGatt?, status: Int, newState: Int) { + super.onConnectionStateChange(gatt, status, newState) + val bluetoothDevice = gatt!!.device + Log.d(Tag, "连接的设备:" + bluetoothDevice.name + " " + bluetoothDevice.address) + isConnecting = true + //移除连接超时 + handler.removeCallbacks(connectOutTimeRunnable) + when (newState) { + BluetoothGatt.STATE_CONNECTING -> { + Log.d(Tag, "正在连接...") + if (onBleConnectListener != null) { + onBleConnectListener!!.onConnecting(gatt, bluetoothDevice) //正在连接回调 + } + } + BluetoothGatt.STATE_CONNECTED -> { + Log.d(Tag, "连接成功") + //连接成功去发现服务 + gatt.discoverServices() + //设置发现服务超时时间 + handler.postDelayed(serviceDiscoverOutTimeRunnable, MAX_CONNECT_TIME) + if (onBleConnectListener != null) { + onBleConnectListener!!.onConnectSuccess( + gatt, + bluetoothDevice, + status + ) //连接成功回调 + } + } + BluetoothGatt.STATE_DISCONNECTING -> { + Log.d(Tag, "正在断开...") + if (onBleConnectListener != null) { + onBleConnectListener!!.onDisConnecting(gatt, bluetoothDevice) //正在断开回调 + } + } + BluetoothGatt.STATE_DISCONNECTED -> { + Log.d(Tag, "断开连接status: $status") + gatt.close() + when (status) { + 133 -> { + //无法连接 + if (onBleConnectListener != null) { + gatt.close() + onBleConnectListener!!.onConnectFailure( + gatt, + bluetoothDevice, + "连接异常!", + status + ) //133连接异常 异常断开 + Log.d(Tag, "连接失败status:" + status + " " + bluetoothDevice.address) + } + } + 62 -> { + if (onBleConnectListener != null) { + gatt.close() + onBleConnectListener!!.onConnectFailure( + gatt, + bluetoothDevice, + "连接成功服务未发现断开!", + status + )//62没有发现服务 异常断开 + Log.d(Tag, "连接成功服务未发现断开status:$status") + } + } + 0 -> { + if (onBleConnectListener != null) { + onBleConnectListener!!.onDisConnectSuccess( + gatt, + bluetoothDevice, + status + ) //0正常断开 回调 + } + } + 8 -> {//因为距离远或者电池无法供电断开连接 + // 已经成功发现服务 + if (onBleConnectListener != null) { + onBleConnectListener!!.onDisConnectSuccess( + gatt, + bluetoothDevice, + status + ) //8断电断开 回调 + } + } + 34 -> { + if (onBleConnectListener != null) { + onBleConnectListener!!.onDisConnectSuccess( + gatt, + bluetoothDevice, + status + ) //34断开 + } + } + else -> {//其它断开连接 + if (onBleConnectListener != null) { + onBleConnectListener!!.onDisConnectSuccess( + gatt, + bluetoothDevice, + status + ) //其它断开 + } + } + } + } + } + } + + override fun onServicesDiscovered(gatt: BluetoothGatt?, status: Int) { + super.onServicesDiscovered(gatt, status) + //移除发现服务超时 + handler.removeCallbacks(serviceDiscoverOutTimeRunnable) + //配置服务信息 + if (setupService(gatt!!, serviceUUID, readUUID, writeUUID)) { + if (onBleConnectListener != null) { + onBleConnectListener!!.onServiceDiscoverySucceed( + gatt, + gatt.device, + status + ) //成功发现服务回调 + } + } else { + if (onBleConnectListener != null) { + onBleConnectListener!!.onServiceDiscoveryFailed( + gatt, + gatt.device, + "获取服务特征异常" + ) //发现服务失败回调 + } + } + } + + //读取蓝牙设备发出来的数据回调 + override fun onCharacteristicRead( + gatt: BluetoothGatt?, characteristic: BluetoothGattCharacteristic?, status: Int + ) { + super.onCharacteristicRead(gatt, characteristic, status) + Log.d(Tag, "读status: $status") + } + + //向蓝牙设备写入数据结果回调 + override fun onCharacteristicWrite( + gatt: BluetoothGatt?, + characteristic: BluetoothGattCharacteristic?, + status: Int + ) { + super.onCharacteristicWrite(gatt, characteristic, status) + if (characteristic!!.value == null) { + Log.e(Tag, "characteristic.getValue() == null"); + return + } + //将收到的字节数组转换成十六进制字符串 + when (status) { + BluetoothGatt.GATT_SUCCESS -> { + //写入成功 + if (onBleConnectListener != null) { + onBleConnectListener!!.onWriteSuccess( + gatt, + gatt!!.device, + characteristic.value + ) //写入成功回调 + } + } + BluetoothGatt.GATT_FAILURE -> { + //写入失败 + if (onBleConnectListener != null) { + onBleConnectListener!!.onWriteFailure( + gatt, + gatt!!.device, + characteristic.value, + "写入失败" + ) //写入失败回调 + } + } + BluetoothGatt.GATT_WRITE_NOT_PERMITTED -> { + Log.d(Tag, "没有权限") + } + } + } + + override fun onCharacteristicChanged( + gatt: BluetoothGatt?, characteristic: BluetoothGattCharacteristic? + ) { + super.onCharacteristicChanged(gatt, characteristic) + //接收数据 + Log.d(Tag, "收到数据:" + characteristic!!.value) + if (onBleConnectListener != null) { + onBleConnectListener!!.onReceiveMessage(gatt, characteristic) //接收数据回调 + } else { + Log.d(Tag, "onCharacteristicChanged-->onBleConnectListener == null: ") + } + } + + override fun onDescriptorRead( + gatt: BluetoothGatt?, descriptor: BluetoothGattDescriptor?, status: Int + ) { + super.onDescriptorRead(gatt, descriptor, status) + //开启监听成功,可以从设备读数据了 + Log.d(Tag, "onDescriptorRead开启监听成功") + } + + override fun onDescriptorWrite( + gatt: BluetoothGatt?, descriptor: BluetoothGattDescriptor?, status: Int + ) { + super.onDescriptorWrite(gatt, descriptor, status) + //开启监听成功,可以向设备写入命令了 + Log.d(Tag, "onDescriptorWrite开启监听成功") + } + + //蓝牙信号强度 + override fun onReadRemoteRssi(gatt: BluetoothGatt?, rssi: Int, status: Int) { + super.onReadRemoteRssi(gatt, rssi, status) + when (status) { + BluetoothGatt.GATT_SUCCESS -> { + Log.w(Tag, "读取RSSI值成功,RSSI值: $rssi ,status: $status") + if (onBleConnectListener != null) { + onBleConnectListener!!.onReadRssi(gatt, rssi, status) //成功读取连接的信号强度回调 + } + } + BluetoothGatt.GATT_FAILURE -> Log.w(Tag, "读取RSSI值失败,status: $status") + } + } + } + + private val connectOutTimeRunnable = Runnable { + if (bluetoothGatt == null) { + Log.d(Tag, "connectOutTimeRunnable-->bluetoothGatt == null") + return@Runnable + } + isConnecting = false + bluetoothGatt!!.disconnect() + //连接超时当作连接失败回调 + if (onBleConnectListener != null) { + onBleConnectListener!!.onConnectFailure( + bluetoothGatt, + curConnectDevice, + "连接超时", + -1 + ) //连接失败回调 + } + } + + private val serviceDiscoverOutTimeRunnable = Runnable { + if (bluetoothGatt == null) { + Log.d(Tag, "serviceDiscoverOutTimeRunnable-->bluetoothGatt == null"); + return@Runnable + } + isConnecting = false + bluetoothGatt!!.disconnect() + //发现服务超时当作连接失败回调 + if (onBleConnectListener != null) { + onBleConnectListener!!.onConnectFailure( + bluetoothGatt, + curConnectDevice, + "发现服务超时!", + -1 + ); //连接失败回调 + } + } + + private fun setupService( + bluetoothGatt: BluetoothGatt, serviceUUID: UUID, readUUID: UUID, writeUUID: UUID + ): Boolean { + var notifyCharacteristic: BluetoothGattCharacteristic? = null + bluetoothGatt.services.forEach { service -> + if (service.uuid == serviceUUID) { + bluetoothGattService = service + bluetoothGattService!!.characteristics.forEach { characteristic -> + val charaProp = characteristic.properties + if (characteristic.uuid == readUUID) { //读特征 + readCharacteristic = characteristic + } + if (characteristic.uuid == writeUUID) { //写特征 + writeCharacteristic = characteristic + } + if (charaProp and BluetoothGattCharacteristic.PROPERTY_NOTIFY > 0) { + val notifyServiceUUID = bluetoothGattService!!.uuid + val notifyCharacteristicUUID = characteristic.uuid + Log.d( + Tag, + "notifyCharacteristicUUID=$notifyCharacteristicUUID, notifyServiceUUID=$notifyServiceUUID" + ) + notifyCharacteristic = bluetoothGatt.getService(notifyServiceUUID) + .getCharacteristic(notifyCharacteristicUUID) + } + } + } + } + //打开读通知,打开的是notifyCharacteristic!!!,不然死活不走onCharacteristicChanged回调 + bluetoothGatt.setCharacteristicNotification(notifyCharacteristic, true) + //一定要重新设置 + for (descriptor in notifyCharacteristic!!.descriptors) { + descriptor.value = BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE + bluetoothGatt.writeDescriptor(descriptor) + } + //延迟2s,保证所有通知都能及时打开 + handler.postDelayed({ }, 2000) + return true + } + + fun sendCommand(cmd: ByteArray): Boolean { + if (writeCharacteristic == null) { + Log.d(Tag, "sendCommand(ByteArray)-->writeGattCharacteristic == null") + return false + } + if (bluetoothGatt == null) { + Log.d(Tag, "sendCommand(ByteArray)-->bluetoothGatt == null") + return false + } + val value = writeCharacteristic!!.setValue(cmd) + Log.d(Tag, "写特征设置值结果:$value") + return bluetoothGatt!!.writeCharacteristic(writeCharacteristic) + } + + fun disConnectDevice() { + if (bluetoothGatt == null) { + Log.d(Tag, "disConnectDevice(ByteArray)-->bluetoothGatt == null"); + return + } + bluetoothGatt!!.disconnect() + } +} \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 5dd8db9..ff9af45 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -68,8 +68,6 @@ - - diff --git a/app/src/main/java/com/casic/birmm/inspect/bean/BlueToothBean.java b/app/src/main/java/com/casic/birmm/inspect/bean/BlueToothBean.java index 14424ec..c842129 100644 --- a/app/src/main/java/com/casic/birmm/inspect/bean/BlueToothBean.java +++ b/app/src/main/java/com/casic/birmm/inspect/bean/BlueToothBean.java @@ -1,27 +1,29 @@ package com.casic.birmm.inspect.bean; +import android.bluetooth.BluetoothDevice; + public class BlueToothBean { - private String blueToothName; - private String blueToothAddress; + private BluetoothDevice bluetoothDevice; //蓝牙设备 + private int rssi; //蓝牙信号 - public BlueToothBean(String blueToothName, String blueToothAddress) { - this.blueToothName = blueToothName; - this.blueToothAddress = blueToothAddress; + public BlueToothBean(BluetoothDevice bluetoothDevice, int rssi) { + this.bluetoothDevice = bluetoothDevice; + this.rssi = rssi; } - public String getBlueToothName() { - return blueToothName; + public BluetoothDevice getBluetoothDevice() { + return bluetoothDevice; } - public void setBlueToothName(String blueToothName) { - this.blueToothName = blueToothName; + public void setBluetoothDevice(BluetoothDevice bluetoothDevice) { + this.bluetoothDevice = bluetoothDevice; } - public String getBlueToothAddress() { - return blueToothAddress; + public int getRssi() { + return rssi; } - public void setBlueToothAddress(String blueToothAddress) { - this.blueToothAddress = blueToothAddress; + public void setRssi(int rssi) { + this.rssi = rssi; } } diff --git a/app/src/main/java/com/casic/birmm/inspect/extensions/ByteArray.kt b/app/src/main/java/com/casic/birmm/inspect/extensions/ByteArray.kt new file mode 100644 index 0000000..8b2ea6e --- /dev/null +++ b/app/src/main/java/com/casic/birmm/inspect/extensions/ByteArray.kt @@ -0,0 +1,59 @@ +package com.casic.birmm.inspect.extensions + +import java.util.* +import kotlin.collections.ArrayList +import kotlin.math.abs + +/** + * ByteArray扩展方法 + */ + +//ByteArray转16进制 +fun ByteArray.toHexString(hasSpace: Boolean = true) = this.joinToString("") { + (it.toInt() and 0xFF).toString(16).padStart( + 2, + '0' + ).toUpperCase(Locale.ROOT) + if (hasSpace) " " else "" +} + +//ByteArray转ascii码字符串 +fun ByteArray.toAsciiString(): String { + val builder = StringBuilder() + for (index in 1..12) { + builder.append(this[index].toInt().toChar()) + } + return builder.toString() +} + +//ByteArray转十进制字符串集合 +fun ByteArray.toDecStringList(): ArrayList { + val data: ArrayList = ArrayList() + /** + * 170,0,0,0,100,1,0,100,0,0,0,100,13,10 + * + * 170是数据标头AA + * 13,10是数据结束位 + * */ + //AA 00 00 00 64 01 00 64 00 00 00 64 0D 0A + //浓度:4字节 0-99999 + var potencyValue = 0 + for (index in 1..4) { + potencyValue += abs(this[index].toString(10).toInt()) + } + data.add(potencyValue.toString()) + //报警标志:1字节 0:未报警 1:报警 + data.add(this[5].toString(10)) + //报警值:2字节 0-65535 + var alarmValue = 0 + for (index in 6..7) { + alarmValue += abs(this[index].toString(10).toInt()) + } + data.add(alarmValue.toString()) + //5s内最大值: 4字节 0-99999 + var maxPotencyValue = 0 + for (index in 8..11) { + maxPotencyValue += abs(this[index].toString(10).toInt()) + } + data.add(maxPotencyValue.toString()) + return data +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/birmm/inspect/utils/BLEManager.kt b/app/src/main/java/com/casic/birmm/inspect/utils/BLEManager.kt new file mode 100644 index 0000000..a1fcddc --- /dev/null +++ b/app/src/main/java/com/casic/birmm/inspect/utils/BLEManager.kt @@ -0,0 +1,484 @@ +package com.casic.birmm.inspect.utils + +import android.bluetooth.* +import android.content.Context +import android.content.Intent +import android.os.Build +import android.os.Handler +import android.util.Log +import com.casic.birmm.inspect.bean.BlueToothBean +import com.casic.birmm.inspect.utils.callback.OnBleConnectListener +import com.casic.birmm.inspect.utils.callback.OnDeviceSearchListener +import java.util.* + + +/** + * 1、扫描设备 + * 2、配对设备 + * 3、解除设备配对 + * 4、连接设备 + * 6、发现服务 + * 7、打开读写功能 + * 8、数据通讯(发送数据、接收数据) + * 9、断开连接 + */ +object BLEManager { + private const val Tag = "BLEManager" + private const val MAX_CONNECT_TIME = 10000L//连接超时时间10s + private var context: Context? = null + private var bluetoothManager: BluetoothManager? = null + private var bluetoothAdapter: BluetoothAdapter? = null + private val handler = Handler() + private var onDeviceSearchListener: OnDeviceSearchListener? = null + private var isConnecting = false + private lateinit var curConnectDevice: BluetoothDevice + private lateinit var serviceUUID: UUID + private lateinit var readUUID: UUID + private lateinit var writeUUID: UUID + private var onBleConnectListener: OnBleConnectListener? = null + private var bluetoothGatt: BluetoothGatt? = null + private var bluetoothGattService: BluetoothGattService? = null + private var readCharacteristic: BluetoothGattCharacteristic? = null + private var writeCharacteristic: BluetoothGattCharacteristic? = null + + fun initBle(context: Context): Boolean { + this.context = context + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) { + bluetoothManager = + context.getSystemService(Context.BLUETOOTH_SERVICE) as BluetoothManager + if (bluetoothManager == null) { + return false + } + bluetoothAdapter = bluetoothManager!!.adapter + return bluetoothAdapter != null + } else { + return false + } + } + + fun isEnable(): Boolean { + if (bluetoothAdapter == null) { + return false + } + return bluetoothAdapter!!.isEnabled + } + + /** + * 打开蓝牙 + * @param isFast true 直接打开蓝牙 false 提示用户打开 + */ + fun openBluetooth(context: Context, isFast: Boolean) { + if (!isEnable()) { + if (isFast) { + Log.d(Tag, "直接打开手机蓝牙") + bluetoothAdapter!!.enable() + } else { + context.startActivity(Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE)) + } + } else { + Log.d(Tag, "手机蓝牙状态已开") + } + } + + /** + * 本地蓝牙是否处于正在扫描状态 + * @return true false + */ + fun isDiscovery(): Boolean { + if (bluetoothAdapter == null) { + return false + } + return bluetoothAdapter!!.isDiscovering + } + + fun stopDiscoveryDevice() { + handler.removeCallbacks(stopScanRunnable) + if (bluetoothAdapter == null) { + return + } + bluetoothAdapter!!.stopLeScan(scanCallback) + } + + private val stopScanRunnable = Runnable { + if (onDeviceSearchListener != null) { + onDeviceSearchListener!!.onDiscoveryOutTime() //扫描超时回调 + } + //scanTime之后还没有扫描到设备,就停止扫描。 + stopDiscoveryDevice() + } + + //扫描设备回调 + private val scanCallback = object : BluetoothAdapter.LeScanCallback { + override fun onLeScan(device: BluetoothDevice?, rssi: Int, scanRecord: ByteArray?) { + if (device == null) return + if (device.name != null) { + Log.d(Tag, device.name + "-->" + device.address) + if (onDeviceSearchListener != null) { + onDeviceSearchListener!!.onDeviceFound(BlueToothBean(device, rssi)) //扫描到设备回调 + } + } + } + } + + fun startDiscoveryDevice(onDeviceSearchListener: OnDeviceSearchListener, scanTime: Long) { + if (bluetoothAdapter == null) { + return + } + this.onDeviceSearchListener = onDeviceSearchListener + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) { + bluetoothAdapter!!.startLeScan(scanCallback) + } else { + return + } + //设定最长扫描时间 + handler.postDelayed(stopScanRunnable, scanTime) + } + + fun connectBleDevice( + context: Context, bluetoothDevice: BluetoothDevice, + outTime: Long, serviceUUID: String, + readUUID: String, writeUUID: String, + onBleConnectListener: OnBleConnectListener + ): BluetoothGatt? { + if (isConnecting) { + Log.d(Tag, "connectBleDevice()-->isConnecting = true") + return null + } + this.curConnectDevice = bluetoothDevice + this.serviceUUID = UUID.fromString(serviceUUID) + this.readUUID = UUID.fromString(readUUID) + this.writeUUID = UUID.fromString(writeUUID) + this.onBleConnectListener = onBleConnectListener + Log.d(Tag, "开始准备连接:" + bluetoothDevice.name + "-->" + bluetoothDevice.address) + try { + bluetoothGatt = bluetoothDevice.connectGatt(context, false, bluetoothGattCallback) + bluetoothGatt!!.connect() + isConnecting = true + } catch (e: Exception) { + e.printStackTrace() + } + //设置连接超时时间10s + handler.postDelayed(connectOutTimeRunnable, outTime) + return bluetoothGatt + } + + private val bluetoothGattCallback = object : BluetoothGattCallback() { + override fun onConnectionStateChange(gatt: BluetoothGatt?, status: Int, newState: Int) { + super.onConnectionStateChange(gatt, status, newState) + val bluetoothDevice = gatt!!.device + Log.d(Tag, "连接的设备:" + bluetoothDevice.name + " " + bluetoothDevice.address) + isConnecting = true + //移除连接超时 + handler.removeCallbacks(connectOutTimeRunnable) + when (newState) { + BluetoothGatt.STATE_CONNECTING -> { + Log.d(Tag, "正在连接...") + if (onBleConnectListener != null) { + onBleConnectListener!!.onConnecting(gatt, bluetoothDevice) //正在连接回调 + } + } + BluetoothGatt.STATE_CONNECTED -> { + Log.d(Tag, "连接成功") + //连接成功去发现服务 + gatt.discoverServices() + //设置发现服务超时时间 + handler.postDelayed(serviceDiscoverOutTimeRunnable, MAX_CONNECT_TIME) + if (onBleConnectListener != null) { + onBleConnectListener!!.onConnectSuccess( + gatt, + bluetoothDevice, + status + ) //连接成功回调 + } + } + BluetoothGatt.STATE_DISCONNECTING -> { + Log.d(Tag, "正在断开...") + if (onBleConnectListener != null) { + onBleConnectListener!!.onDisConnecting(gatt, bluetoothDevice) //正在断开回调 + } + } + BluetoothGatt.STATE_DISCONNECTED -> { + Log.d(Tag, "断开连接status: $status") + gatt.close() + when (status) { + 133 -> { + //无法连接 + if (onBleConnectListener != null) { + gatt.close() + onBleConnectListener!!.onConnectFailure( + gatt, + bluetoothDevice, + "连接异常!", + status + ) //133连接异常 异常断开 + Log.d(Tag, "连接失败status:" + status + " " + bluetoothDevice.address) + } + } + 62 -> { + if (onBleConnectListener != null) { + gatt.close() + onBleConnectListener!!.onConnectFailure( + gatt, + bluetoothDevice, + "连接成功服务未发现断开!", + status + )//62没有发现服务 异常断开 + Log.d(Tag, "连接成功服务未发现断开status:$status") + } + } + 0 -> { + if (onBleConnectListener != null) { + onBleConnectListener!!.onDisConnectSuccess( + gatt, + bluetoothDevice, + status + ) //0正常断开 回调 + } + } + 8 -> {//因为距离远或者电池无法供电断开连接 + // 已经成功发现服务 + if (onBleConnectListener != null) { + onBleConnectListener!!.onDisConnectSuccess( + gatt, + bluetoothDevice, + status + ) //8断电断开 回调 + } + } + 34 -> { + if (onBleConnectListener != null) { + onBleConnectListener!!.onDisConnectSuccess( + gatt, + bluetoothDevice, + status + ) //34断开 + } + } + else -> {//其它断开连接 + if (onBleConnectListener != null) { + onBleConnectListener!!.onDisConnectSuccess( + gatt, + bluetoothDevice, + status + ) //其它断开 + } + } + } + } + } + } + + override fun onServicesDiscovered(gatt: BluetoothGatt?, status: Int) { + super.onServicesDiscovered(gatt, status) + //移除发现服务超时 + handler.removeCallbacks(serviceDiscoverOutTimeRunnable) + //配置服务信息 + if (setupService(gatt!!, serviceUUID, readUUID, writeUUID)) { + if (onBleConnectListener != null) { + onBleConnectListener!!.onServiceDiscoverySucceed( + gatt, + gatt.device, + status + ) //成功发现服务回调 + } + } else { + if (onBleConnectListener != null) { + onBleConnectListener!!.onServiceDiscoveryFailed( + gatt, + gatt.device, + "获取服务特征异常" + ) //发现服务失败回调 + } + } + } + + //读取蓝牙设备发出来的数据回调 + override fun onCharacteristicRead( + gatt: BluetoothGatt?, characteristic: BluetoothGattCharacteristic?, status: Int + ) { + super.onCharacteristicRead(gatt, characteristic, status) + Log.d(Tag, "读status: $status") + } + + //向蓝牙设备写入数据结果回调 + override fun onCharacteristicWrite( + gatt: BluetoothGatt?, + characteristic: BluetoothGattCharacteristic?, + status: Int + ) { + super.onCharacteristicWrite(gatt, characteristic, status) + if (characteristic!!.value == null) { + Log.e(Tag, "characteristic.getValue() == null"); + return + } + //将收到的字节数组转换成十六进制字符串 + when (status) { + BluetoothGatt.GATT_SUCCESS -> { + //写入成功 + if (onBleConnectListener != null) { + onBleConnectListener!!.onWriteSuccess( + gatt, + gatt!!.device, + characteristic.value + ) //写入成功回调 + } + } + BluetoothGatt.GATT_FAILURE -> { + //写入失败 + if (onBleConnectListener != null) { + onBleConnectListener!!.onWriteFailure( + gatt, + gatt!!.device, + characteristic.value, + "写入失败" + ) //写入失败回调 + } + } + BluetoothGatt.GATT_WRITE_NOT_PERMITTED -> { + Log.d(Tag, "没有权限") + } + } + } + + override fun onCharacteristicChanged( + gatt: BluetoothGatt?, characteristic: BluetoothGattCharacteristic? + ) { + super.onCharacteristicChanged(gatt, characteristic) + //接收数据 + Log.d(Tag, "收到数据:" + characteristic!!.value) + if (onBleConnectListener != null) { + onBleConnectListener!!.onReceiveMessage(gatt, characteristic) //接收数据回调 + } else { + Log.d(Tag, "onCharacteristicChanged-->onBleConnectListener == null: ") + } + } + + override fun onDescriptorRead( + gatt: BluetoothGatt?, descriptor: BluetoothGattDescriptor?, status: Int + ) { + super.onDescriptorRead(gatt, descriptor, status) + //开启监听成功,可以从设备读数据了 + Log.d(Tag, "onDescriptorRead开启监听成功") + } + + override fun onDescriptorWrite( + gatt: BluetoothGatt?, descriptor: BluetoothGattDescriptor?, status: Int + ) { + super.onDescriptorWrite(gatt, descriptor, status) + //开启监听成功,可以向设备写入命令了 + Log.d(Tag, "onDescriptorWrite开启监听成功") + } + + //蓝牙信号强度 + override fun onReadRemoteRssi(gatt: BluetoothGatt?, rssi: Int, status: Int) { + super.onReadRemoteRssi(gatt, rssi, status) + when (status) { + BluetoothGatt.GATT_SUCCESS -> { + Log.w(Tag, "读取RSSI值成功,RSSI值: $rssi ,status: $status") + if (onBleConnectListener != null) { + onBleConnectListener!!.onReadRssi(gatt, rssi, status) //成功读取连接的信号强度回调 + } + } + BluetoothGatt.GATT_FAILURE -> Log.w(Tag, "读取RSSI值失败,status: $status") + } + } + } + + private val connectOutTimeRunnable = Runnable { + if (bluetoothGatt == null) { + Log.d(Tag, "connectOutTimeRunnable-->bluetoothGatt == null") + return@Runnable + } + isConnecting = false + bluetoothGatt!!.disconnect() + //连接超时当作连接失败回调 + if (onBleConnectListener != null) { + onBleConnectListener!!.onConnectFailure( + bluetoothGatt, + curConnectDevice, + "连接超时", + -1 + ) //连接失败回调 + } + } + + private val serviceDiscoverOutTimeRunnable = Runnable { + if (bluetoothGatt == null) { + Log.d(Tag, "serviceDiscoverOutTimeRunnable-->bluetoothGatt == null"); + return@Runnable + } + isConnecting = false + bluetoothGatt!!.disconnect() + //发现服务超时当作连接失败回调 + if (onBleConnectListener != null) { + onBleConnectListener!!.onConnectFailure( + bluetoothGatt, + curConnectDevice, + "发现服务超时!", + -1 + ); //连接失败回调 + } + } + + private fun setupService( + bluetoothGatt: BluetoothGatt, serviceUUID: UUID, readUUID: UUID, writeUUID: UUID + ): Boolean { + var notifyCharacteristic: BluetoothGattCharacteristic? = null + bluetoothGatt.services.forEach { service -> + if (service.uuid == serviceUUID) { + bluetoothGattService = service + bluetoothGattService!!.characteristics.forEach { characteristic -> + val charaProp = characteristic.properties + if (characteristic.uuid == readUUID) { //读特征 + readCharacteristic = characteristic + } + if (characteristic.uuid == writeUUID) { //写特征 + writeCharacteristic = characteristic + } + if (charaProp and BluetoothGattCharacteristic.PROPERTY_NOTIFY > 0) { + val notifyServiceUUID = bluetoothGattService!!.uuid + val notifyCharacteristicUUID = characteristic.uuid + Log.d( + Tag, + "notifyCharacteristicUUID=$notifyCharacteristicUUID, notifyServiceUUID=$notifyServiceUUID" + ) + notifyCharacteristic = bluetoothGatt.getService(notifyServiceUUID) + .getCharacteristic(notifyCharacteristicUUID) + } + } + } + } + //打开读通知,打开的是notifyCharacteristic!!!,不然死活不走onCharacteristicChanged回调 + bluetoothGatt.setCharacteristicNotification(notifyCharacteristic, true) + //一定要重新设置 + for (descriptor in notifyCharacteristic!!.descriptors) { + descriptor.value = BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE + bluetoothGatt.writeDescriptor(descriptor) + } + //延迟2s,保证所有通知都能及时打开 + handler.postDelayed({ }, 2000) + return true + } + + fun sendCommand(cmd: ByteArray): Boolean { + if (writeCharacteristic == null) { + Log.d(Tag, "sendCommand(ByteArray)-->writeGattCharacteristic == null") + return false + } + if (bluetoothGatt == null) { + Log.d(Tag, "sendCommand(ByteArray)-->bluetoothGatt == null") + return false + } + val value = writeCharacteristic!!.setValue(cmd) + Log.d(Tag, "写特征设置值结果:$value") + return bluetoothGatt!!.writeCharacteristic(writeCharacteristic) + } + + fun disConnectDevice() { + if (bluetoothGatt == null) { + Log.d(Tag, "disConnectDevice(ByteArray)-->bluetoothGatt == null"); + return + } + bluetoothGatt!!.disconnect() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/birmm/inspect/utils/BluetoothStateBroadcastReceiver.kt b/app/src/main/java/com/casic/birmm/inspect/utils/BluetoothStateBroadcastReceiver.kt index b6b5281..9b87bcb 100644 --- a/app/src/main/java/com/casic/birmm/inspect/utils/BluetoothStateBroadcastReceiver.kt +++ b/app/src/main/java/com/casic/birmm/inspect/utils/BluetoothStateBroadcastReceiver.kt @@ -20,12 +20,6 @@ } } } - BluetoothDevice.ACTION_ACL_CONNECTED -> { - MapActivity.sendEmptyMessage(Constant.DEVICE_CONNECTED) - } - BluetoothDevice.ACTION_ACL_DISCONNECTED -> { - MapActivity.sendEmptyMessage(Constant.DEVICE_DISCONNECTED) - } } } } \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 5dd8db9..ff9af45 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -68,8 +68,6 @@ - - diff --git a/app/src/main/java/com/casic/birmm/inspect/bean/BlueToothBean.java b/app/src/main/java/com/casic/birmm/inspect/bean/BlueToothBean.java index 14424ec..c842129 100644 --- a/app/src/main/java/com/casic/birmm/inspect/bean/BlueToothBean.java +++ b/app/src/main/java/com/casic/birmm/inspect/bean/BlueToothBean.java @@ -1,27 +1,29 @@ package com.casic.birmm.inspect.bean; +import android.bluetooth.BluetoothDevice; + public class BlueToothBean { - private String blueToothName; - private String blueToothAddress; + private BluetoothDevice bluetoothDevice; //蓝牙设备 + private int rssi; //蓝牙信号 - public BlueToothBean(String blueToothName, String blueToothAddress) { - this.blueToothName = blueToothName; - this.blueToothAddress = blueToothAddress; + public BlueToothBean(BluetoothDevice bluetoothDevice, int rssi) { + this.bluetoothDevice = bluetoothDevice; + this.rssi = rssi; } - public String getBlueToothName() { - return blueToothName; + public BluetoothDevice getBluetoothDevice() { + return bluetoothDevice; } - public void setBlueToothName(String blueToothName) { - this.blueToothName = blueToothName; + public void setBluetoothDevice(BluetoothDevice bluetoothDevice) { + this.bluetoothDevice = bluetoothDevice; } - public String getBlueToothAddress() { - return blueToothAddress; + public int getRssi() { + return rssi; } - public void setBlueToothAddress(String blueToothAddress) { - this.blueToothAddress = blueToothAddress; + public void setRssi(int rssi) { + this.rssi = rssi; } } diff --git a/app/src/main/java/com/casic/birmm/inspect/extensions/ByteArray.kt b/app/src/main/java/com/casic/birmm/inspect/extensions/ByteArray.kt new file mode 100644 index 0000000..8b2ea6e --- /dev/null +++ b/app/src/main/java/com/casic/birmm/inspect/extensions/ByteArray.kt @@ -0,0 +1,59 @@ +package com.casic.birmm.inspect.extensions + +import java.util.* +import kotlin.collections.ArrayList +import kotlin.math.abs + +/** + * ByteArray扩展方法 + */ + +//ByteArray转16进制 +fun ByteArray.toHexString(hasSpace: Boolean = true) = this.joinToString("") { + (it.toInt() and 0xFF).toString(16).padStart( + 2, + '0' + ).toUpperCase(Locale.ROOT) + if (hasSpace) " " else "" +} + +//ByteArray转ascii码字符串 +fun ByteArray.toAsciiString(): String { + val builder = StringBuilder() + for (index in 1..12) { + builder.append(this[index].toInt().toChar()) + } + return builder.toString() +} + +//ByteArray转十进制字符串集合 +fun ByteArray.toDecStringList(): ArrayList { + val data: ArrayList = ArrayList() + /** + * 170,0,0,0,100,1,0,100,0,0,0,100,13,10 + * + * 170是数据标头AA + * 13,10是数据结束位 + * */ + //AA 00 00 00 64 01 00 64 00 00 00 64 0D 0A + //浓度:4字节 0-99999 + var potencyValue = 0 + for (index in 1..4) { + potencyValue += abs(this[index].toString(10).toInt()) + } + data.add(potencyValue.toString()) + //报警标志:1字节 0:未报警 1:报警 + data.add(this[5].toString(10)) + //报警值:2字节 0-65535 + var alarmValue = 0 + for (index in 6..7) { + alarmValue += abs(this[index].toString(10).toInt()) + } + data.add(alarmValue.toString()) + //5s内最大值: 4字节 0-99999 + var maxPotencyValue = 0 + for (index in 8..11) { + maxPotencyValue += abs(this[index].toString(10).toInt()) + } + data.add(maxPotencyValue.toString()) + return data +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/birmm/inspect/utils/BLEManager.kt b/app/src/main/java/com/casic/birmm/inspect/utils/BLEManager.kt new file mode 100644 index 0000000..a1fcddc --- /dev/null +++ b/app/src/main/java/com/casic/birmm/inspect/utils/BLEManager.kt @@ -0,0 +1,484 @@ +package com.casic.birmm.inspect.utils + +import android.bluetooth.* +import android.content.Context +import android.content.Intent +import android.os.Build +import android.os.Handler +import android.util.Log +import com.casic.birmm.inspect.bean.BlueToothBean +import com.casic.birmm.inspect.utils.callback.OnBleConnectListener +import com.casic.birmm.inspect.utils.callback.OnDeviceSearchListener +import java.util.* + + +/** + * 1、扫描设备 + * 2、配对设备 + * 3、解除设备配对 + * 4、连接设备 + * 6、发现服务 + * 7、打开读写功能 + * 8、数据通讯(发送数据、接收数据) + * 9、断开连接 + */ +object BLEManager { + private const val Tag = "BLEManager" + private const val MAX_CONNECT_TIME = 10000L//连接超时时间10s + private var context: Context? = null + private var bluetoothManager: BluetoothManager? = null + private var bluetoothAdapter: BluetoothAdapter? = null + private val handler = Handler() + private var onDeviceSearchListener: OnDeviceSearchListener? = null + private var isConnecting = false + private lateinit var curConnectDevice: BluetoothDevice + private lateinit var serviceUUID: UUID + private lateinit var readUUID: UUID + private lateinit var writeUUID: UUID + private var onBleConnectListener: OnBleConnectListener? = null + private var bluetoothGatt: BluetoothGatt? = null + private var bluetoothGattService: BluetoothGattService? = null + private var readCharacteristic: BluetoothGattCharacteristic? = null + private var writeCharacteristic: BluetoothGattCharacteristic? = null + + fun initBle(context: Context): Boolean { + this.context = context + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) { + bluetoothManager = + context.getSystemService(Context.BLUETOOTH_SERVICE) as BluetoothManager + if (bluetoothManager == null) { + return false + } + bluetoothAdapter = bluetoothManager!!.adapter + return bluetoothAdapter != null + } else { + return false + } + } + + fun isEnable(): Boolean { + if (bluetoothAdapter == null) { + return false + } + return bluetoothAdapter!!.isEnabled + } + + /** + * 打开蓝牙 + * @param isFast true 直接打开蓝牙 false 提示用户打开 + */ + fun openBluetooth(context: Context, isFast: Boolean) { + if (!isEnable()) { + if (isFast) { + Log.d(Tag, "直接打开手机蓝牙") + bluetoothAdapter!!.enable() + } else { + context.startActivity(Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE)) + } + } else { + Log.d(Tag, "手机蓝牙状态已开") + } + } + + /** + * 本地蓝牙是否处于正在扫描状态 + * @return true false + */ + fun isDiscovery(): Boolean { + if (bluetoothAdapter == null) { + return false + } + return bluetoothAdapter!!.isDiscovering + } + + fun stopDiscoveryDevice() { + handler.removeCallbacks(stopScanRunnable) + if (bluetoothAdapter == null) { + return + } + bluetoothAdapter!!.stopLeScan(scanCallback) + } + + private val stopScanRunnable = Runnable { + if (onDeviceSearchListener != null) { + onDeviceSearchListener!!.onDiscoveryOutTime() //扫描超时回调 + } + //scanTime之后还没有扫描到设备,就停止扫描。 + stopDiscoveryDevice() + } + + //扫描设备回调 + private val scanCallback = object : BluetoothAdapter.LeScanCallback { + override fun onLeScan(device: BluetoothDevice?, rssi: Int, scanRecord: ByteArray?) { + if (device == null) return + if (device.name != null) { + Log.d(Tag, device.name + "-->" + device.address) + if (onDeviceSearchListener != null) { + onDeviceSearchListener!!.onDeviceFound(BlueToothBean(device, rssi)) //扫描到设备回调 + } + } + } + } + + fun startDiscoveryDevice(onDeviceSearchListener: OnDeviceSearchListener, scanTime: Long) { + if (bluetoothAdapter == null) { + return + } + this.onDeviceSearchListener = onDeviceSearchListener + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) { + bluetoothAdapter!!.startLeScan(scanCallback) + } else { + return + } + //设定最长扫描时间 + handler.postDelayed(stopScanRunnable, scanTime) + } + + fun connectBleDevice( + context: Context, bluetoothDevice: BluetoothDevice, + outTime: Long, serviceUUID: String, + readUUID: String, writeUUID: String, + onBleConnectListener: OnBleConnectListener + ): BluetoothGatt? { + if (isConnecting) { + Log.d(Tag, "connectBleDevice()-->isConnecting = true") + return null + } + this.curConnectDevice = bluetoothDevice + this.serviceUUID = UUID.fromString(serviceUUID) + this.readUUID = UUID.fromString(readUUID) + this.writeUUID = UUID.fromString(writeUUID) + this.onBleConnectListener = onBleConnectListener + Log.d(Tag, "开始准备连接:" + bluetoothDevice.name + "-->" + bluetoothDevice.address) + try { + bluetoothGatt = bluetoothDevice.connectGatt(context, false, bluetoothGattCallback) + bluetoothGatt!!.connect() + isConnecting = true + } catch (e: Exception) { + e.printStackTrace() + } + //设置连接超时时间10s + handler.postDelayed(connectOutTimeRunnable, outTime) + return bluetoothGatt + } + + private val bluetoothGattCallback = object : BluetoothGattCallback() { + override fun onConnectionStateChange(gatt: BluetoothGatt?, status: Int, newState: Int) { + super.onConnectionStateChange(gatt, status, newState) + val bluetoothDevice = gatt!!.device + Log.d(Tag, "连接的设备:" + bluetoothDevice.name + " " + bluetoothDevice.address) + isConnecting = true + //移除连接超时 + handler.removeCallbacks(connectOutTimeRunnable) + when (newState) { + BluetoothGatt.STATE_CONNECTING -> { + Log.d(Tag, "正在连接...") + if (onBleConnectListener != null) { + onBleConnectListener!!.onConnecting(gatt, bluetoothDevice) //正在连接回调 + } + } + BluetoothGatt.STATE_CONNECTED -> { + Log.d(Tag, "连接成功") + //连接成功去发现服务 + gatt.discoverServices() + //设置发现服务超时时间 + handler.postDelayed(serviceDiscoverOutTimeRunnable, MAX_CONNECT_TIME) + if (onBleConnectListener != null) { + onBleConnectListener!!.onConnectSuccess( + gatt, + bluetoothDevice, + status + ) //连接成功回调 + } + } + BluetoothGatt.STATE_DISCONNECTING -> { + Log.d(Tag, "正在断开...") + if (onBleConnectListener != null) { + onBleConnectListener!!.onDisConnecting(gatt, bluetoothDevice) //正在断开回调 + } + } + BluetoothGatt.STATE_DISCONNECTED -> { + Log.d(Tag, "断开连接status: $status") + gatt.close() + when (status) { + 133 -> { + //无法连接 + if (onBleConnectListener != null) { + gatt.close() + onBleConnectListener!!.onConnectFailure( + gatt, + bluetoothDevice, + "连接异常!", + status + ) //133连接异常 异常断开 + Log.d(Tag, "连接失败status:" + status + " " + bluetoothDevice.address) + } + } + 62 -> { + if (onBleConnectListener != null) { + gatt.close() + onBleConnectListener!!.onConnectFailure( + gatt, + bluetoothDevice, + "连接成功服务未发现断开!", + status + )//62没有发现服务 异常断开 + Log.d(Tag, "连接成功服务未发现断开status:$status") + } + } + 0 -> { + if (onBleConnectListener != null) { + onBleConnectListener!!.onDisConnectSuccess( + gatt, + bluetoothDevice, + status + ) //0正常断开 回调 + } + } + 8 -> {//因为距离远或者电池无法供电断开连接 + // 已经成功发现服务 + if (onBleConnectListener != null) { + onBleConnectListener!!.onDisConnectSuccess( + gatt, + bluetoothDevice, + status + ) //8断电断开 回调 + } + } + 34 -> { + if (onBleConnectListener != null) { + onBleConnectListener!!.onDisConnectSuccess( + gatt, + bluetoothDevice, + status + ) //34断开 + } + } + else -> {//其它断开连接 + if (onBleConnectListener != null) { + onBleConnectListener!!.onDisConnectSuccess( + gatt, + bluetoothDevice, + status + ) //其它断开 + } + } + } + } + } + } + + override fun onServicesDiscovered(gatt: BluetoothGatt?, status: Int) { + super.onServicesDiscovered(gatt, status) + //移除发现服务超时 + handler.removeCallbacks(serviceDiscoverOutTimeRunnable) + //配置服务信息 + if (setupService(gatt!!, serviceUUID, readUUID, writeUUID)) { + if (onBleConnectListener != null) { + onBleConnectListener!!.onServiceDiscoverySucceed( + gatt, + gatt.device, + status + ) //成功发现服务回调 + } + } else { + if (onBleConnectListener != null) { + onBleConnectListener!!.onServiceDiscoveryFailed( + gatt, + gatt.device, + "获取服务特征异常" + ) //发现服务失败回调 + } + } + } + + //读取蓝牙设备发出来的数据回调 + override fun onCharacteristicRead( + gatt: BluetoothGatt?, characteristic: BluetoothGattCharacteristic?, status: Int + ) { + super.onCharacteristicRead(gatt, characteristic, status) + Log.d(Tag, "读status: $status") + } + + //向蓝牙设备写入数据结果回调 + override fun onCharacteristicWrite( + gatt: BluetoothGatt?, + characteristic: BluetoothGattCharacteristic?, + status: Int + ) { + super.onCharacteristicWrite(gatt, characteristic, status) + if (characteristic!!.value == null) { + Log.e(Tag, "characteristic.getValue() == null"); + return + } + //将收到的字节数组转换成十六进制字符串 + when (status) { + BluetoothGatt.GATT_SUCCESS -> { + //写入成功 + if (onBleConnectListener != null) { + onBleConnectListener!!.onWriteSuccess( + gatt, + gatt!!.device, + characteristic.value + ) //写入成功回调 + } + } + BluetoothGatt.GATT_FAILURE -> { + //写入失败 + if (onBleConnectListener != null) { + onBleConnectListener!!.onWriteFailure( + gatt, + gatt!!.device, + characteristic.value, + "写入失败" + ) //写入失败回调 + } + } + BluetoothGatt.GATT_WRITE_NOT_PERMITTED -> { + Log.d(Tag, "没有权限") + } + } + } + + override fun onCharacteristicChanged( + gatt: BluetoothGatt?, characteristic: BluetoothGattCharacteristic? + ) { + super.onCharacteristicChanged(gatt, characteristic) + //接收数据 + Log.d(Tag, "收到数据:" + characteristic!!.value) + if (onBleConnectListener != null) { + onBleConnectListener!!.onReceiveMessage(gatt, characteristic) //接收数据回调 + } else { + Log.d(Tag, "onCharacteristicChanged-->onBleConnectListener == null: ") + } + } + + override fun onDescriptorRead( + gatt: BluetoothGatt?, descriptor: BluetoothGattDescriptor?, status: Int + ) { + super.onDescriptorRead(gatt, descriptor, status) + //开启监听成功,可以从设备读数据了 + Log.d(Tag, "onDescriptorRead开启监听成功") + } + + override fun onDescriptorWrite( + gatt: BluetoothGatt?, descriptor: BluetoothGattDescriptor?, status: Int + ) { + super.onDescriptorWrite(gatt, descriptor, status) + //开启监听成功,可以向设备写入命令了 + Log.d(Tag, "onDescriptorWrite开启监听成功") + } + + //蓝牙信号强度 + override fun onReadRemoteRssi(gatt: BluetoothGatt?, rssi: Int, status: Int) { + super.onReadRemoteRssi(gatt, rssi, status) + when (status) { + BluetoothGatt.GATT_SUCCESS -> { + Log.w(Tag, "读取RSSI值成功,RSSI值: $rssi ,status: $status") + if (onBleConnectListener != null) { + onBleConnectListener!!.onReadRssi(gatt, rssi, status) //成功读取连接的信号强度回调 + } + } + BluetoothGatt.GATT_FAILURE -> Log.w(Tag, "读取RSSI值失败,status: $status") + } + } + } + + private val connectOutTimeRunnable = Runnable { + if (bluetoothGatt == null) { + Log.d(Tag, "connectOutTimeRunnable-->bluetoothGatt == null") + return@Runnable + } + isConnecting = false + bluetoothGatt!!.disconnect() + //连接超时当作连接失败回调 + if (onBleConnectListener != null) { + onBleConnectListener!!.onConnectFailure( + bluetoothGatt, + curConnectDevice, + "连接超时", + -1 + ) //连接失败回调 + } + } + + private val serviceDiscoverOutTimeRunnable = Runnable { + if (bluetoothGatt == null) { + Log.d(Tag, "serviceDiscoverOutTimeRunnable-->bluetoothGatt == null"); + return@Runnable + } + isConnecting = false + bluetoothGatt!!.disconnect() + //发现服务超时当作连接失败回调 + if (onBleConnectListener != null) { + onBleConnectListener!!.onConnectFailure( + bluetoothGatt, + curConnectDevice, + "发现服务超时!", + -1 + ); //连接失败回调 + } + } + + private fun setupService( + bluetoothGatt: BluetoothGatt, serviceUUID: UUID, readUUID: UUID, writeUUID: UUID + ): Boolean { + var notifyCharacteristic: BluetoothGattCharacteristic? = null + bluetoothGatt.services.forEach { service -> + if (service.uuid == serviceUUID) { + bluetoothGattService = service + bluetoothGattService!!.characteristics.forEach { characteristic -> + val charaProp = characteristic.properties + if (characteristic.uuid == readUUID) { //读特征 + readCharacteristic = characteristic + } + if (characteristic.uuid == writeUUID) { //写特征 + writeCharacteristic = characteristic + } + if (charaProp and BluetoothGattCharacteristic.PROPERTY_NOTIFY > 0) { + val notifyServiceUUID = bluetoothGattService!!.uuid + val notifyCharacteristicUUID = characteristic.uuid + Log.d( + Tag, + "notifyCharacteristicUUID=$notifyCharacteristicUUID, notifyServiceUUID=$notifyServiceUUID" + ) + notifyCharacteristic = bluetoothGatt.getService(notifyServiceUUID) + .getCharacteristic(notifyCharacteristicUUID) + } + } + } + } + //打开读通知,打开的是notifyCharacteristic!!!,不然死活不走onCharacteristicChanged回调 + bluetoothGatt.setCharacteristicNotification(notifyCharacteristic, true) + //一定要重新设置 + for (descriptor in notifyCharacteristic!!.descriptors) { + descriptor.value = BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE + bluetoothGatt.writeDescriptor(descriptor) + } + //延迟2s,保证所有通知都能及时打开 + handler.postDelayed({ }, 2000) + return true + } + + fun sendCommand(cmd: ByteArray): Boolean { + if (writeCharacteristic == null) { + Log.d(Tag, "sendCommand(ByteArray)-->writeGattCharacteristic == null") + return false + } + if (bluetoothGatt == null) { + Log.d(Tag, "sendCommand(ByteArray)-->bluetoothGatt == null") + return false + } + val value = writeCharacteristic!!.setValue(cmd) + Log.d(Tag, "写特征设置值结果:$value") + return bluetoothGatt!!.writeCharacteristic(writeCharacteristic) + } + + fun disConnectDevice() { + if (bluetoothGatt == null) { + Log.d(Tag, "disConnectDevice(ByteArray)-->bluetoothGatt == null"); + return + } + bluetoothGatt!!.disconnect() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/birmm/inspect/utils/BluetoothStateBroadcastReceiver.kt b/app/src/main/java/com/casic/birmm/inspect/utils/BluetoothStateBroadcastReceiver.kt index b6b5281..9b87bcb 100644 --- a/app/src/main/java/com/casic/birmm/inspect/utils/BluetoothStateBroadcastReceiver.kt +++ b/app/src/main/java/com/casic/birmm/inspect/utils/BluetoothStateBroadcastReceiver.kt @@ -20,12 +20,6 @@ } } } - BluetoothDevice.ACTION_ACL_CONNECTED -> { - MapActivity.sendEmptyMessage(Constant.DEVICE_CONNECTED) - } - BluetoothDevice.ACTION_ACL_DISCONNECTED -> { - MapActivity.sendEmptyMessage(Constant.DEVICE_DISCONNECTED) - } } } } \ No newline at end of file diff --git a/app/src/main/java/com/casic/birmm/inspect/utils/Constant.kt b/app/src/main/java/com/casic/birmm/inspect/utils/Constant.kt index 507655b..55549ec 100644 --- a/app/src/main/java/com/casic/birmm/inspect/utils/Constant.kt +++ b/app/src/main/java/com/casic/birmm/inspect/utils/Constant.kt @@ -15,13 +15,15 @@ const val PAGE_LIMIT = 15 const val BLUETOOTH_ON = 20 const val BLUETOOTH_OFF = 21 - const val DEVICE_CONNECTED = 22 - const val DEVICE_CONNECT_FAIL = 23 - const val DEVICE_DISCONNECTED = 24 + const val CONNECT_SUCCESS = 22 + const val CONNECT_FAILURE = 23 + const val DISCONNECT_SUCCESS = 24 const val SEND_SUCCESS = 25 const val SEND_FAILURE = 26 const val RECEIVE_SUCCESS = 27 const val RECEIVE_FAILURE = 28 + const val DISCOVERY_DEVICE = 29 + const val DISCOVERY_OUT_TIME = 30 const val FIVE_YEARS = 5L * 365 * 60 * 60 * 24 * 1000L const val IMAGE_MINUS_SIZE = 100 * 1024 diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 5dd8db9..ff9af45 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -68,8 +68,6 @@ - - diff --git a/app/src/main/java/com/casic/birmm/inspect/bean/BlueToothBean.java b/app/src/main/java/com/casic/birmm/inspect/bean/BlueToothBean.java index 14424ec..c842129 100644 --- a/app/src/main/java/com/casic/birmm/inspect/bean/BlueToothBean.java +++ b/app/src/main/java/com/casic/birmm/inspect/bean/BlueToothBean.java @@ -1,27 +1,29 @@ package com.casic.birmm.inspect.bean; +import android.bluetooth.BluetoothDevice; + public class BlueToothBean { - private String blueToothName; - private String blueToothAddress; + private BluetoothDevice bluetoothDevice; //蓝牙设备 + private int rssi; //蓝牙信号 - public BlueToothBean(String blueToothName, String blueToothAddress) { - this.blueToothName = blueToothName; - this.blueToothAddress = blueToothAddress; + public BlueToothBean(BluetoothDevice bluetoothDevice, int rssi) { + this.bluetoothDevice = bluetoothDevice; + this.rssi = rssi; } - public String getBlueToothName() { - return blueToothName; + public BluetoothDevice getBluetoothDevice() { + return bluetoothDevice; } - public void setBlueToothName(String blueToothName) { - this.blueToothName = blueToothName; + public void setBluetoothDevice(BluetoothDevice bluetoothDevice) { + this.bluetoothDevice = bluetoothDevice; } - public String getBlueToothAddress() { - return blueToothAddress; + public int getRssi() { + return rssi; } - public void setBlueToothAddress(String blueToothAddress) { - this.blueToothAddress = blueToothAddress; + public void setRssi(int rssi) { + this.rssi = rssi; } } diff --git a/app/src/main/java/com/casic/birmm/inspect/extensions/ByteArray.kt b/app/src/main/java/com/casic/birmm/inspect/extensions/ByteArray.kt new file mode 100644 index 0000000..8b2ea6e --- /dev/null +++ b/app/src/main/java/com/casic/birmm/inspect/extensions/ByteArray.kt @@ -0,0 +1,59 @@ +package com.casic.birmm.inspect.extensions + +import java.util.* +import kotlin.collections.ArrayList +import kotlin.math.abs + +/** + * ByteArray扩展方法 + */ + +//ByteArray转16进制 +fun ByteArray.toHexString(hasSpace: Boolean = true) = this.joinToString("") { + (it.toInt() and 0xFF).toString(16).padStart( + 2, + '0' + ).toUpperCase(Locale.ROOT) + if (hasSpace) " " else "" +} + +//ByteArray转ascii码字符串 +fun ByteArray.toAsciiString(): String { + val builder = StringBuilder() + for (index in 1..12) { + builder.append(this[index].toInt().toChar()) + } + return builder.toString() +} + +//ByteArray转十进制字符串集合 +fun ByteArray.toDecStringList(): ArrayList { + val data: ArrayList = ArrayList() + /** + * 170,0,0,0,100,1,0,100,0,0,0,100,13,10 + * + * 170是数据标头AA + * 13,10是数据结束位 + * */ + //AA 00 00 00 64 01 00 64 00 00 00 64 0D 0A + //浓度:4字节 0-99999 + var potencyValue = 0 + for (index in 1..4) { + potencyValue += abs(this[index].toString(10).toInt()) + } + data.add(potencyValue.toString()) + //报警标志:1字节 0:未报警 1:报警 + data.add(this[5].toString(10)) + //报警值:2字节 0-65535 + var alarmValue = 0 + for (index in 6..7) { + alarmValue += abs(this[index].toString(10).toInt()) + } + data.add(alarmValue.toString()) + //5s内最大值: 4字节 0-99999 + var maxPotencyValue = 0 + for (index in 8..11) { + maxPotencyValue += abs(this[index].toString(10).toInt()) + } + data.add(maxPotencyValue.toString()) + return data +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/birmm/inspect/utils/BLEManager.kt b/app/src/main/java/com/casic/birmm/inspect/utils/BLEManager.kt new file mode 100644 index 0000000..a1fcddc --- /dev/null +++ b/app/src/main/java/com/casic/birmm/inspect/utils/BLEManager.kt @@ -0,0 +1,484 @@ +package com.casic.birmm.inspect.utils + +import android.bluetooth.* +import android.content.Context +import android.content.Intent +import android.os.Build +import android.os.Handler +import android.util.Log +import com.casic.birmm.inspect.bean.BlueToothBean +import com.casic.birmm.inspect.utils.callback.OnBleConnectListener +import com.casic.birmm.inspect.utils.callback.OnDeviceSearchListener +import java.util.* + + +/** + * 1、扫描设备 + * 2、配对设备 + * 3、解除设备配对 + * 4、连接设备 + * 6、发现服务 + * 7、打开读写功能 + * 8、数据通讯(发送数据、接收数据) + * 9、断开连接 + */ +object BLEManager { + private const val Tag = "BLEManager" + private const val MAX_CONNECT_TIME = 10000L//连接超时时间10s + private var context: Context? = null + private var bluetoothManager: BluetoothManager? = null + private var bluetoothAdapter: BluetoothAdapter? = null + private val handler = Handler() + private var onDeviceSearchListener: OnDeviceSearchListener? = null + private var isConnecting = false + private lateinit var curConnectDevice: BluetoothDevice + private lateinit var serviceUUID: UUID + private lateinit var readUUID: UUID + private lateinit var writeUUID: UUID + private var onBleConnectListener: OnBleConnectListener? = null + private var bluetoothGatt: BluetoothGatt? = null + private var bluetoothGattService: BluetoothGattService? = null + private var readCharacteristic: BluetoothGattCharacteristic? = null + private var writeCharacteristic: BluetoothGattCharacteristic? = null + + fun initBle(context: Context): Boolean { + this.context = context + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) { + bluetoothManager = + context.getSystemService(Context.BLUETOOTH_SERVICE) as BluetoothManager + if (bluetoothManager == null) { + return false + } + bluetoothAdapter = bluetoothManager!!.adapter + return bluetoothAdapter != null + } else { + return false + } + } + + fun isEnable(): Boolean { + if (bluetoothAdapter == null) { + return false + } + return bluetoothAdapter!!.isEnabled + } + + /** + * 打开蓝牙 + * @param isFast true 直接打开蓝牙 false 提示用户打开 + */ + fun openBluetooth(context: Context, isFast: Boolean) { + if (!isEnable()) { + if (isFast) { + Log.d(Tag, "直接打开手机蓝牙") + bluetoothAdapter!!.enable() + } else { + context.startActivity(Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE)) + } + } else { + Log.d(Tag, "手机蓝牙状态已开") + } + } + + /** + * 本地蓝牙是否处于正在扫描状态 + * @return true false + */ + fun isDiscovery(): Boolean { + if (bluetoothAdapter == null) { + return false + } + return bluetoothAdapter!!.isDiscovering + } + + fun stopDiscoveryDevice() { + handler.removeCallbacks(stopScanRunnable) + if (bluetoothAdapter == null) { + return + } + bluetoothAdapter!!.stopLeScan(scanCallback) + } + + private val stopScanRunnable = Runnable { + if (onDeviceSearchListener != null) { + onDeviceSearchListener!!.onDiscoveryOutTime() //扫描超时回调 + } + //scanTime之后还没有扫描到设备,就停止扫描。 + stopDiscoveryDevice() + } + + //扫描设备回调 + private val scanCallback = object : BluetoothAdapter.LeScanCallback { + override fun onLeScan(device: BluetoothDevice?, rssi: Int, scanRecord: ByteArray?) { + if (device == null) return + if (device.name != null) { + Log.d(Tag, device.name + "-->" + device.address) + if (onDeviceSearchListener != null) { + onDeviceSearchListener!!.onDeviceFound(BlueToothBean(device, rssi)) //扫描到设备回调 + } + } + } + } + + fun startDiscoveryDevice(onDeviceSearchListener: OnDeviceSearchListener, scanTime: Long) { + if (bluetoothAdapter == null) { + return + } + this.onDeviceSearchListener = onDeviceSearchListener + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) { + bluetoothAdapter!!.startLeScan(scanCallback) + } else { + return + } + //设定最长扫描时间 + handler.postDelayed(stopScanRunnable, scanTime) + } + + fun connectBleDevice( + context: Context, bluetoothDevice: BluetoothDevice, + outTime: Long, serviceUUID: String, + readUUID: String, writeUUID: String, + onBleConnectListener: OnBleConnectListener + ): BluetoothGatt? { + if (isConnecting) { + Log.d(Tag, "connectBleDevice()-->isConnecting = true") + return null + } + this.curConnectDevice = bluetoothDevice + this.serviceUUID = UUID.fromString(serviceUUID) + this.readUUID = UUID.fromString(readUUID) + this.writeUUID = UUID.fromString(writeUUID) + this.onBleConnectListener = onBleConnectListener + Log.d(Tag, "开始准备连接:" + bluetoothDevice.name + "-->" + bluetoothDevice.address) + try { + bluetoothGatt = bluetoothDevice.connectGatt(context, false, bluetoothGattCallback) + bluetoothGatt!!.connect() + isConnecting = true + } catch (e: Exception) { + e.printStackTrace() + } + //设置连接超时时间10s + handler.postDelayed(connectOutTimeRunnable, outTime) + return bluetoothGatt + } + + private val bluetoothGattCallback = object : BluetoothGattCallback() { + override fun onConnectionStateChange(gatt: BluetoothGatt?, status: Int, newState: Int) { + super.onConnectionStateChange(gatt, status, newState) + val bluetoothDevice = gatt!!.device + Log.d(Tag, "连接的设备:" + bluetoothDevice.name + " " + bluetoothDevice.address) + isConnecting = true + //移除连接超时 + handler.removeCallbacks(connectOutTimeRunnable) + when (newState) { + BluetoothGatt.STATE_CONNECTING -> { + Log.d(Tag, "正在连接...") + if (onBleConnectListener != null) { + onBleConnectListener!!.onConnecting(gatt, bluetoothDevice) //正在连接回调 + } + } + BluetoothGatt.STATE_CONNECTED -> { + Log.d(Tag, "连接成功") + //连接成功去发现服务 + gatt.discoverServices() + //设置发现服务超时时间 + handler.postDelayed(serviceDiscoverOutTimeRunnable, MAX_CONNECT_TIME) + if (onBleConnectListener != null) { + onBleConnectListener!!.onConnectSuccess( + gatt, + bluetoothDevice, + status + ) //连接成功回调 + } + } + BluetoothGatt.STATE_DISCONNECTING -> { + Log.d(Tag, "正在断开...") + if (onBleConnectListener != null) { + onBleConnectListener!!.onDisConnecting(gatt, bluetoothDevice) //正在断开回调 + } + } + BluetoothGatt.STATE_DISCONNECTED -> { + Log.d(Tag, "断开连接status: $status") + gatt.close() + when (status) { + 133 -> { + //无法连接 + if (onBleConnectListener != null) { + gatt.close() + onBleConnectListener!!.onConnectFailure( + gatt, + bluetoothDevice, + "连接异常!", + status + ) //133连接异常 异常断开 + Log.d(Tag, "连接失败status:" + status + " " + bluetoothDevice.address) + } + } + 62 -> { + if (onBleConnectListener != null) { + gatt.close() + onBleConnectListener!!.onConnectFailure( + gatt, + bluetoothDevice, + "连接成功服务未发现断开!", + status + )//62没有发现服务 异常断开 + Log.d(Tag, "连接成功服务未发现断开status:$status") + } + } + 0 -> { + if (onBleConnectListener != null) { + onBleConnectListener!!.onDisConnectSuccess( + gatt, + bluetoothDevice, + status + ) //0正常断开 回调 + } + } + 8 -> {//因为距离远或者电池无法供电断开连接 + // 已经成功发现服务 + if (onBleConnectListener != null) { + onBleConnectListener!!.onDisConnectSuccess( + gatt, + bluetoothDevice, + status + ) //8断电断开 回调 + } + } + 34 -> { + if (onBleConnectListener != null) { + onBleConnectListener!!.onDisConnectSuccess( + gatt, + bluetoothDevice, + status + ) //34断开 + } + } + else -> {//其它断开连接 + if (onBleConnectListener != null) { + onBleConnectListener!!.onDisConnectSuccess( + gatt, + bluetoothDevice, + status + ) //其它断开 + } + } + } + } + } + } + + override fun onServicesDiscovered(gatt: BluetoothGatt?, status: Int) { + super.onServicesDiscovered(gatt, status) + //移除发现服务超时 + handler.removeCallbacks(serviceDiscoverOutTimeRunnable) + //配置服务信息 + if (setupService(gatt!!, serviceUUID, readUUID, writeUUID)) { + if (onBleConnectListener != null) { + onBleConnectListener!!.onServiceDiscoverySucceed( + gatt, + gatt.device, + status + ) //成功发现服务回调 + } + } else { + if (onBleConnectListener != null) { + onBleConnectListener!!.onServiceDiscoveryFailed( + gatt, + gatt.device, + "获取服务特征异常" + ) //发现服务失败回调 + } + } + } + + //读取蓝牙设备发出来的数据回调 + override fun onCharacteristicRead( + gatt: BluetoothGatt?, characteristic: BluetoothGattCharacteristic?, status: Int + ) { + super.onCharacteristicRead(gatt, characteristic, status) + Log.d(Tag, "读status: $status") + } + + //向蓝牙设备写入数据结果回调 + override fun onCharacteristicWrite( + gatt: BluetoothGatt?, + characteristic: BluetoothGattCharacteristic?, + status: Int + ) { + super.onCharacteristicWrite(gatt, characteristic, status) + if (characteristic!!.value == null) { + Log.e(Tag, "characteristic.getValue() == null"); + return + } + //将收到的字节数组转换成十六进制字符串 + when (status) { + BluetoothGatt.GATT_SUCCESS -> { + //写入成功 + if (onBleConnectListener != null) { + onBleConnectListener!!.onWriteSuccess( + gatt, + gatt!!.device, + characteristic.value + ) //写入成功回调 + } + } + BluetoothGatt.GATT_FAILURE -> { + //写入失败 + if (onBleConnectListener != null) { + onBleConnectListener!!.onWriteFailure( + gatt, + gatt!!.device, + characteristic.value, + "写入失败" + ) //写入失败回调 + } + } + BluetoothGatt.GATT_WRITE_NOT_PERMITTED -> { + Log.d(Tag, "没有权限") + } + } + } + + override fun onCharacteristicChanged( + gatt: BluetoothGatt?, characteristic: BluetoothGattCharacteristic? + ) { + super.onCharacteristicChanged(gatt, characteristic) + //接收数据 + Log.d(Tag, "收到数据:" + characteristic!!.value) + if (onBleConnectListener != null) { + onBleConnectListener!!.onReceiveMessage(gatt, characteristic) //接收数据回调 + } else { + Log.d(Tag, "onCharacteristicChanged-->onBleConnectListener == null: ") + } + } + + override fun onDescriptorRead( + gatt: BluetoothGatt?, descriptor: BluetoothGattDescriptor?, status: Int + ) { + super.onDescriptorRead(gatt, descriptor, status) + //开启监听成功,可以从设备读数据了 + Log.d(Tag, "onDescriptorRead开启监听成功") + } + + override fun onDescriptorWrite( + gatt: BluetoothGatt?, descriptor: BluetoothGattDescriptor?, status: Int + ) { + super.onDescriptorWrite(gatt, descriptor, status) + //开启监听成功,可以向设备写入命令了 + Log.d(Tag, "onDescriptorWrite开启监听成功") + } + + //蓝牙信号强度 + override fun onReadRemoteRssi(gatt: BluetoothGatt?, rssi: Int, status: Int) { + super.onReadRemoteRssi(gatt, rssi, status) + when (status) { + BluetoothGatt.GATT_SUCCESS -> { + Log.w(Tag, "读取RSSI值成功,RSSI值: $rssi ,status: $status") + if (onBleConnectListener != null) { + onBleConnectListener!!.onReadRssi(gatt, rssi, status) //成功读取连接的信号强度回调 + } + } + BluetoothGatt.GATT_FAILURE -> Log.w(Tag, "读取RSSI值失败,status: $status") + } + } + } + + private val connectOutTimeRunnable = Runnable { + if (bluetoothGatt == null) { + Log.d(Tag, "connectOutTimeRunnable-->bluetoothGatt == null") + return@Runnable + } + isConnecting = false + bluetoothGatt!!.disconnect() + //连接超时当作连接失败回调 + if (onBleConnectListener != null) { + onBleConnectListener!!.onConnectFailure( + bluetoothGatt, + curConnectDevice, + "连接超时", + -1 + ) //连接失败回调 + } + } + + private val serviceDiscoverOutTimeRunnable = Runnable { + if (bluetoothGatt == null) { + Log.d(Tag, "serviceDiscoverOutTimeRunnable-->bluetoothGatt == null"); + return@Runnable + } + isConnecting = false + bluetoothGatt!!.disconnect() + //发现服务超时当作连接失败回调 + if (onBleConnectListener != null) { + onBleConnectListener!!.onConnectFailure( + bluetoothGatt, + curConnectDevice, + "发现服务超时!", + -1 + ); //连接失败回调 + } + } + + private fun setupService( + bluetoothGatt: BluetoothGatt, serviceUUID: UUID, readUUID: UUID, writeUUID: UUID + ): Boolean { + var notifyCharacteristic: BluetoothGattCharacteristic? = null + bluetoothGatt.services.forEach { service -> + if (service.uuid == serviceUUID) { + bluetoothGattService = service + bluetoothGattService!!.characteristics.forEach { characteristic -> + val charaProp = characteristic.properties + if (characteristic.uuid == readUUID) { //读特征 + readCharacteristic = characteristic + } + if (characteristic.uuid == writeUUID) { //写特征 + writeCharacteristic = characteristic + } + if (charaProp and BluetoothGattCharacteristic.PROPERTY_NOTIFY > 0) { + val notifyServiceUUID = bluetoothGattService!!.uuid + val notifyCharacteristicUUID = characteristic.uuid + Log.d( + Tag, + "notifyCharacteristicUUID=$notifyCharacteristicUUID, notifyServiceUUID=$notifyServiceUUID" + ) + notifyCharacteristic = bluetoothGatt.getService(notifyServiceUUID) + .getCharacteristic(notifyCharacteristicUUID) + } + } + } + } + //打开读通知,打开的是notifyCharacteristic!!!,不然死活不走onCharacteristicChanged回调 + bluetoothGatt.setCharacteristicNotification(notifyCharacteristic, true) + //一定要重新设置 + for (descriptor in notifyCharacteristic!!.descriptors) { + descriptor.value = BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE + bluetoothGatt.writeDescriptor(descriptor) + } + //延迟2s,保证所有通知都能及时打开 + handler.postDelayed({ }, 2000) + return true + } + + fun sendCommand(cmd: ByteArray): Boolean { + if (writeCharacteristic == null) { + Log.d(Tag, "sendCommand(ByteArray)-->writeGattCharacteristic == null") + return false + } + if (bluetoothGatt == null) { + Log.d(Tag, "sendCommand(ByteArray)-->bluetoothGatt == null") + return false + } + val value = writeCharacteristic!!.setValue(cmd) + Log.d(Tag, "写特征设置值结果:$value") + return bluetoothGatt!!.writeCharacteristic(writeCharacteristic) + } + + fun disConnectDevice() { + if (bluetoothGatt == null) { + Log.d(Tag, "disConnectDevice(ByteArray)-->bluetoothGatt == null"); + return + } + bluetoothGatt!!.disconnect() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/birmm/inspect/utils/BluetoothStateBroadcastReceiver.kt b/app/src/main/java/com/casic/birmm/inspect/utils/BluetoothStateBroadcastReceiver.kt index b6b5281..9b87bcb 100644 --- a/app/src/main/java/com/casic/birmm/inspect/utils/BluetoothStateBroadcastReceiver.kt +++ b/app/src/main/java/com/casic/birmm/inspect/utils/BluetoothStateBroadcastReceiver.kt @@ -20,12 +20,6 @@ } } } - BluetoothDevice.ACTION_ACL_CONNECTED -> { - MapActivity.sendEmptyMessage(Constant.DEVICE_CONNECTED) - } - BluetoothDevice.ACTION_ACL_DISCONNECTED -> { - MapActivity.sendEmptyMessage(Constant.DEVICE_DISCONNECTED) - } } } } \ No newline at end of file diff --git a/app/src/main/java/com/casic/birmm/inspect/utils/Constant.kt b/app/src/main/java/com/casic/birmm/inspect/utils/Constant.kt index 507655b..55549ec 100644 --- a/app/src/main/java/com/casic/birmm/inspect/utils/Constant.kt +++ b/app/src/main/java/com/casic/birmm/inspect/utils/Constant.kt @@ -15,13 +15,15 @@ const val PAGE_LIMIT = 15 const val BLUETOOTH_ON = 20 const val BLUETOOTH_OFF = 21 - const val DEVICE_CONNECTED = 22 - const val DEVICE_CONNECT_FAIL = 23 - const val DEVICE_DISCONNECTED = 24 + const val CONNECT_SUCCESS = 22 + const val CONNECT_FAILURE = 23 + const val DISCONNECT_SUCCESS = 24 const val SEND_SUCCESS = 25 const val SEND_FAILURE = 26 const val RECEIVE_SUCCESS = 27 const val RECEIVE_FAILURE = 28 + const val DISCOVERY_DEVICE = 29 + const val DISCOVERY_OUT_TIME = 30 const val FIVE_YEARS = 5L * 365 * 60 * 60 * 24 * 1000L const val IMAGE_MINUS_SIZE = 100 * 1024 diff --git a/app/src/main/java/com/casic/birmm/inspect/utils/callback/OnBleConnectListener.kt b/app/src/main/java/com/casic/birmm/inspect/utils/callback/OnBleConnectListener.kt new file mode 100644 index 0000000..b0b05a0 --- /dev/null +++ b/app/src/main/java/com/casic/birmm/inspect/utils/callback/OnBleConnectListener.kt @@ -0,0 +1,52 @@ +package com.casic.birmm.inspect.utils.callback + +import android.bluetooth.BluetoothDevice +import android.bluetooth.BluetoothGatt +import android.bluetooth.BluetoothGattCharacteristic + + +interface OnBleConnectListener { + fun onConnecting(bluetoothGatt: BluetoothGatt?, bluetoothDevice: BluetoothDevice?) //正在连接 + + fun onConnectSuccess( + bluetoothGatt: BluetoothGatt?, bluetoothDevice: BluetoothDevice?, + status: Int + ) //连接成功 + + fun onConnectFailure( + bluetoothGatt: BluetoothGatt?, bluetoothDevice: BluetoothDevice?, + exception: String?, status: Int + ) //连接失败 + + fun onDisConnecting(bluetoothGatt: BluetoothGatt?, bluetoothDevice: BluetoothDevice?) //正在断开 + + fun onDisConnectSuccess( + bluetoothGatt: BluetoothGatt?, bluetoothDevice: BluetoothDevice?, status: Int + ) // 断开连接 + + + fun onServiceDiscoverySucceed( + bluetoothGatt: BluetoothGatt?, bluetoothDevice: BluetoothDevice?, status: Int + ) //发现服务成功 + + fun onServiceDiscoveryFailed( + bluetoothGatt: BluetoothGatt?, bluetoothDevice: BluetoothDevice?, msg: String? + ) //发现服务失败 + + fun onReceiveMessage( + bluetoothGatt: BluetoothGatt?, characteristic: BluetoothGattCharacteristic? + ) //收到消息 + + fun onReceiveError(errorMsg: String?) //接收数据出错 + + fun onWriteSuccess( + bluetoothGatt: BluetoothGatt?, bluetoothDevice: BluetoothDevice?, msg: ByteArray? + ) //写入成功 + + fun onWriteFailure( + bluetoothGatt: BluetoothGatt?, bluetoothDevice: BluetoothDevice?, + msg: ByteArray?, errorMsg: String? + ) //写入失败 + + fun onReadRssi(bluetoothGatt: BluetoothGatt?, rssi: Int, status: Int) //成功读取到连接信号强度 +} \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 5dd8db9..ff9af45 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -68,8 +68,6 @@ - - diff --git a/app/src/main/java/com/casic/birmm/inspect/bean/BlueToothBean.java b/app/src/main/java/com/casic/birmm/inspect/bean/BlueToothBean.java index 14424ec..c842129 100644 --- a/app/src/main/java/com/casic/birmm/inspect/bean/BlueToothBean.java +++ b/app/src/main/java/com/casic/birmm/inspect/bean/BlueToothBean.java @@ -1,27 +1,29 @@ package com.casic.birmm.inspect.bean; +import android.bluetooth.BluetoothDevice; + public class BlueToothBean { - private String blueToothName; - private String blueToothAddress; + private BluetoothDevice bluetoothDevice; //蓝牙设备 + private int rssi; //蓝牙信号 - public BlueToothBean(String blueToothName, String blueToothAddress) { - this.blueToothName = blueToothName; - this.blueToothAddress = blueToothAddress; + public BlueToothBean(BluetoothDevice bluetoothDevice, int rssi) { + this.bluetoothDevice = bluetoothDevice; + this.rssi = rssi; } - public String getBlueToothName() { - return blueToothName; + public BluetoothDevice getBluetoothDevice() { + return bluetoothDevice; } - public void setBlueToothName(String blueToothName) { - this.blueToothName = blueToothName; + public void setBluetoothDevice(BluetoothDevice bluetoothDevice) { + this.bluetoothDevice = bluetoothDevice; } - public String getBlueToothAddress() { - return blueToothAddress; + public int getRssi() { + return rssi; } - public void setBlueToothAddress(String blueToothAddress) { - this.blueToothAddress = blueToothAddress; + public void setRssi(int rssi) { + this.rssi = rssi; } } diff --git a/app/src/main/java/com/casic/birmm/inspect/extensions/ByteArray.kt b/app/src/main/java/com/casic/birmm/inspect/extensions/ByteArray.kt new file mode 100644 index 0000000..8b2ea6e --- /dev/null +++ b/app/src/main/java/com/casic/birmm/inspect/extensions/ByteArray.kt @@ -0,0 +1,59 @@ +package com.casic.birmm.inspect.extensions + +import java.util.* +import kotlin.collections.ArrayList +import kotlin.math.abs + +/** + * ByteArray扩展方法 + */ + +//ByteArray转16进制 +fun ByteArray.toHexString(hasSpace: Boolean = true) = this.joinToString("") { + (it.toInt() and 0xFF).toString(16).padStart( + 2, + '0' + ).toUpperCase(Locale.ROOT) + if (hasSpace) " " else "" +} + +//ByteArray转ascii码字符串 +fun ByteArray.toAsciiString(): String { + val builder = StringBuilder() + for (index in 1..12) { + builder.append(this[index].toInt().toChar()) + } + return builder.toString() +} + +//ByteArray转十进制字符串集合 +fun ByteArray.toDecStringList(): ArrayList { + val data: ArrayList = ArrayList() + /** + * 170,0,0,0,100,1,0,100,0,0,0,100,13,10 + * + * 170是数据标头AA + * 13,10是数据结束位 + * */ + //AA 00 00 00 64 01 00 64 00 00 00 64 0D 0A + //浓度:4字节 0-99999 + var potencyValue = 0 + for (index in 1..4) { + potencyValue += abs(this[index].toString(10).toInt()) + } + data.add(potencyValue.toString()) + //报警标志:1字节 0:未报警 1:报警 + data.add(this[5].toString(10)) + //报警值:2字节 0-65535 + var alarmValue = 0 + for (index in 6..7) { + alarmValue += abs(this[index].toString(10).toInt()) + } + data.add(alarmValue.toString()) + //5s内最大值: 4字节 0-99999 + var maxPotencyValue = 0 + for (index in 8..11) { + maxPotencyValue += abs(this[index].toString(10).toInt()) + } + data.add(maxPotencyValue.toString()) + return data +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/birmm/inspect/utils/BLEManager.kt b/app/src/main/java/com/casic/birmm/inspect/utils/BLEManager.kt new file mode 100644 index 0000000..a1fcddc --- /dev/null +++ b/app/src/main/java/com/casic/birmm/inspect/utils/BLEManager.kt @@ -0,0 +1,484 @@ +package com.casic.birmm.inspect.utils + +import android.bluetooth.* +import android.content.Context +import android.content.Intent +import android.os.Build +import android.os.Handler +import android.util.Log +import com.casic.birmm.inspect.bean.BlueToothBean +import com.casic.birmm.inspect.utils.callback.OnBleConnectListener +import com.casic.birmm.inspect.utils.callback.OnDeviceSearchListener +import java.util.* + + +/** + * 1、扫描设备 + * 2、配对设备 + * 3、解除设备配对 + * 4、连接设备 + * 6、发现服务 + * 7、打开读写功能 + * 8、数据通讯(发送数据、接收数据) + * 9、断开连接 + */ +object BLEManager { + private const val Tag = "BLEManager" + private const val MAX_CONNECT_TIME = 10000L//连接超时时间10s + private var context: Context? = null + private var bluetoothManager: BluetoothManager? = null + private var bluetoothAdapter: BluetoothAdapter? = null + private val handler = Handler() + private var onDeviceSearchListener: OnDeviceSearchListener? = null + private var isConnecting = false + private lateinit var curConnectDevice: BluetoothDevice + private lateinit var serviceUUID: UUID + private lateinit var readUUID: UUID + private lateinit var writeUUID: UUID + private var onBleConnectListener: OnBleConnectListener? = null + private var bluetoothGatt: BluetoothGatt? = null + private var bluetoothGattService: BluetoothGattService? = null + private var readCharacteristic: BluetoothGattCharacteristic? = null + private var writeCharacteristic: BluetoothGattCharacteristic? = null + + fun initBle(context: Context): Boolean { + this.context = context + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) { + bluetoothManager = + context.getSystemService(Context.BLUETOOTH_SERVICE) as BluetoothManager + if (bluetoothManager == null) { + return false + } + bluetoothAdapter = bluetoothManager!!.adapter + return bluetoothAdapter != null + } else { + return false + } + } + + fun isEnable(): Boolean { + if (bluetoothAdapter == null) { + return false + } + return bluetoothAdapter!!.isEnabled + } + + /** + * 打开蓝牙 + * @param isFast true 直接打开蓝牙 false 提示用户打开 + */ + fun openBluetooth(context: Context, isFast: Boolean) { + if (!isEnable()) { + if (isFast) { + Log.d(Tag, "直接打开手机蓝牙") + bluetoothAdapter!!.enable() + } else { + context.startActivity(Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE)) + } + } else { + Log.d(Tag, "手机蓝牙状态已开") + } + } + + /** + * 本地蓝牙是否处于正在扫描状态 + * @return true false + */ + fun isDiscovery(): Boolean { + if (bluetoothAdapter == null) { + return false + } + return bluetoothAdapter!!.isDiscovering + } + + fun stopDiscoveryDevice() { + handler.removeCallbacks(stopScanRunnable) + if (bluetoothAdapter == null) { + return + } + bluetoothAdapter!!.stopLeScan(scanCallback) + } + + private val stopScanRunnable = Runnable { + if (onDeviceSearchListener != null) { + onDeviceSearchListener!!.onDiscoveryOutTime() //扫描超时回调 + } + //scanTime之后还没有扫描到设备,就停止扫描。 + stopDiscoveryDevice() + } + + //扫描设备回调 + private val scanCallback = object : BluetoothAdapter.LeScanCallback { + override fun onLeScan(device: BluetoothDevice?, rssi: Int, scanRecord: ByteArray?) { + if (device == null) return + if (device.name != null) { + Log.d(Tag, device.name + "-->" + device.address) + if (onDeviceSearchListener != null) { + onDeviceSearchListener!!.onDeviceFound(BlueToothBean(device, rssi)) //扫描到设备回调 + } + } + } + } + + fun startDiscoveryDevice(onDeviceSearchListener: OnDeviceSearchListener, scanTime: Long) { + if (bluetoothAdapter == null) { + return + } + this.onDeviceSearchListener = onDeviceSearchListener + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) { + bluetoothAdapter!!.startLeScan(scanCallback) + } else { + return + } + //设定最长扫描时间 + handler.postDelayed(stopScanRunnable, scanTime) + } + + fun connectBleDevice( + context: Context, bluetoothDevice: BluetoothDevice, + outTime: Long, serviceUUID: String, + readUUID: String, writeUUID: String, + onBleConnectListener: OnBleConnectListener + ): BluetoothGatt? { + if (isConnecting) { + Log.d(Tag, "connectBleDevice()-->isConnecting = true") + return null + } + this.curConnectDevice = bluetoothDevice + this.serviceUUID = UUID.fromString(serviceUUID) + this.readUUID = UUID.fromString(readUUID) + this.writeUUID = UUID.fromString(writeUUID) + this.onBleConnectListener = onBleConnectListener + Log.d(Tag, "开始准备连接:" + bluetoothDevice.name + "-->" + bluetoothDevice.address) + try { + bluetoothGatt = bluetoothDevice.connectGatt(context, false, bluetoothGattCallback) + bluetoothGatt!!.connect() + isConnecting = true + } catch (e: Exception) { + e.printStackTrace() + } + //设置连接超时时间10s + handler.postDelayed(connectOutTimeRunnable, outTime) + return bluetoothGatt + } + + private val bluetoothGattCallback = object : BluetoothGattCallback() { + override fun onConnectionStateChange(gatt: BluetoothGatt?, status: Int, newState: Int) { + super.onConnectionStateChange(gatt, status, newState) + val bluetoothDevice = gatt!!.device + Log.d(Tag, "连接的设备:" + bluetoothDevice.name + " " + bluetoothDevice.address) + isConnecting = true + //移除连接超时 + handler.removeCallbacks(connectOutTimeRunnable) + when (newState) { + BluetoothGatt.STATE_CONNECTING -> { + Log.d(Tag, "正在连接...") + if (onBleConnectListener != null) { + onBleConnectListener!!.onConnecting(gatt, bluetoothDevice) //正在连接回调 + } + } + BluetoothGatt.STATE_CONNECTED -> { + Log.d(Tag, "连接成功") + //连接成功去发现服务 + gatt.discoverServices() + //设置发现服务超时时间 + handler.postDelayed(serviceDiscoverOutTimeRunnable, MAX_CONNECT_TIME) + if (onBleConnectListener != null) { + onBleConnectListener!!.onConnectSuccess( + gatt, + bluetoothDevice, + status + ) //连接成功回调 + } + } + BluetoothGatt.STATE_DISCONNECTING -> { + Log.d(Tag, "正在断开...") + if (onBleConnectListener != null) { + onBleConnectListener!!.onDisConnecting(gatt, bluetoothDevice) //正在断开回调 + } + } + BluetoothGatt.STATE_DISCONNECTED -> { + Log.d(Tag, "断开连接status: $status") + gatt.close() + when (status) { + 133 -> { + //无法连接 + if (onBleConnectListener != null) { + gatt.close() + onBleConnectListener!!.onConnectFailure( + gatt, + bluetoothDevice, + "连接异常!", + status + ) //133连接异常 异常断开 + Log.d(Tag, "连接失败status:" + status + " " + bluetoothDevice.address) + } + } + 62 -> { + if (onBleConnectListener != null) { + gatt.close() + onBleConnectListener!!.onConnectFailure( + gatt, + bluetoothDevice, + "连接成功服务未发现断开!", + status + )//62没有发现服务 异常断开 + Log.d(Tag, "连接成功服务未发现断开status:$status") + } + } + 0 -> { + if (onBleConnectListener != null) { + onBleConnectListener!!.onDisConnectSuccess( + gatt, + bluetoothDevice, + status + ) //0正常断开 回调 + } + } + 8 -> {//因为距离远或者电池无法供电断开连接 + // 已经成功发现服务 + if (onBleConnectListener != null) { + onBleConnectListener!!.onDisConnectSuccess( + gatt, + bluetoothDevice, + status + ) //8断电断开 回调 + } + } + 34 -> { + if (onBleConnectListener != null) { + onBleConnectListener!!.onDisConnectSuccess( + gatt, + bluetoothDevice, + status + ) //34断开 + } + } + else -> {//其它断开连接 + if (onBleConnectListener != null) { + onBleConnectListener!!.onDisConnectSuccess( + gatt, + bluetoothDevice, + status + ) //其它断开 + } + } + } + } + } + } + + override fun onServicesDiscovered(gatt: BluetoothGatt?, status: Int) { + super.onServicesDiscovered(gatt, status) + //移除发现服务超时 + handler.removeCallbacks(serviceDiscoverOutTimeRunnable) + //配置服务信息 + if (setupService(gatt!!, serviceUUID, readUUID, writeUUID)) { + if (onBleConnectListener != null) { + onBleConnectListener!!.onServiceDiscoverySucceed( + gatt, + gatt.device, + status + ) //成功发现服务回调 + } + } else { + if (onBleConnectListener != null) { + onBleConnectListener!!.onServiceDiscoveryFailed( + gatt, + gatt.device, + "获取服务特征异常" + ) //发现服务失败回调 + } + } + } + + //读取蓝牙设备发出来的数据回调 + override fun onCharacteristicRead( + gatt: BluetoothGatt?, characteristic: BluetoothGattCharacteristic?, status: Int + ) { + super.onCharacteristicRead(gatt, characteristic, status) + Log.d(Tag, "读status: $status") + } + + //向蓝牙设备写入数据结果回调 + override fun onCharacteristicWrite( + gatt: BluetoothGatt?, + characteristic: BluetoothGattCharacteristic?, + status: Int + ) { + super.onCharacteristicWrite(gatt, characteristic, status) + if (characteristic!!.value == null) { + Log.e(Tag, "characteristic.getValue() == null"); + return + } + //将收到的字节数组转换成十六进制字符串 + when (status) { + BluetoothGatt.GATT_SUCCESS -> { + //写入成功 + if (onBleConnectListener != null) { + onBleConnectListener!!.onWriteSuccess( + gatt, + gatt!!.device, + characteristic.value + ) //写入成功回调 + } + } + BluetoothGatt.GATT_FAILURE -> { + //写入失败 + if (onBleConnectListener != null) { + onBleConnectListener!!.onWriteFailure( + gatt, + gatt!!.device, + characteristic.value, + "写入失败" + ) //写入失败回调 + } + } + BluetoothGatt.GATT_WRITE_NOT_PERMITTED -> { + Log.d(Tag, "没有权限") + } + } + } + + override fun onCharacteristicChanged( + gatt: BluetoothGatt?, characteristic: BluetoothGattCharacteristic? + ) { + super.onCharacteristicChanged(gatt, characteristic) + //接收数据 + Log.d(Tag, "收到数据:" + characteristic!!.value) + if (onBleConnectListener != null) { + onBleConnectListener!!.onReceiveMessage(gatt, characteristic) //接收数据回调 + } else { + Log.d(Tag, "onCharacteristicChanged-->onBleConnectListener == null: ") + } + } + + override fun onDescriptorRead( + gatt: BluetoothGatt?, descriptor: BluetoothGattDescriptor?, status: Int + ) { + super.onDescriptorRead(gatt, descriptor, status) + //开启监听成功,可以从设备读数据了 + Log.d(Tag, "onDescriptorRead开启监听成功") + } + + override fun onDescriptorWrite( + gatt: BluetoothGatt?, descriptor: BluetoothGattDescriptor?, status: Int + ) { + super.onDescriptorWrite(gatt, descriptor, status) + //开启监听成功,可以向设备写入命令了 + Log.d(Tag, "onDescriptorWrite开启监听成功") + } + + //蓝牙信号强度 + override fun onReadRemoteRssi(gatt: BluetoothGatt?, rssi: Int, status: Int) { + super.onReadRemoteRssi(gatt, rssi, status) + when (status) { + BluetoothGatt.GATT_SUCCESS -> { + Log.w(Tag, "读取RSSI值成功,RSSI值: $rssi ,status: $status") + if (onBleConnectListener != null) { + onBleConnectListener!!.onReadRssi(gatt, rssi, status) //成功读取连接的信号强度回调 + } + } + BluetoothGatt.GATT_FAILURE -> Log.w(Tag, "读取RSSI值失败,status: $status") + } + } + } + + private val connectOutTimeRunnable = Runnable { + if (bluetoothGatt == null) { + Log.d(Tag, "connectOutTimeRunnable-->bluetoothGatt == null") + return@Runnable + } + isConnecting = false + bluetoothGatt!!.disconnect() + //连接超时当作连接失败回调 + if (onBleConnectListener != null) { + onBleConnectListener!!.onConnectFailure( + bluetoothGatt, + curConnectDevice, + "连接超时", + -1 + ) //连接失败回调 + } + } + + private val serviceDiscoverOutTimeRunnable = Runnable { + if (bluetoothGatt == null) { + Log.d(Tag, "serviceDiscoverOutTimeRunnable-->bluetoothGatt == null"); + return@Runnable + } + isConnecting = false + bluetoothGatt!!.disconnect() + //发现服务超时当作连接失败回调 + if (onBleConnectListener != null) { + onBleConnectListener!!.onConnectFailure( + bluetoothGatt, + curConnectDevice, + "发现服务超时!", + -1 + ); //连接失败回调 + } + } + + private fun setupService( + bluetoothGatt: BluetoothGatt, serviceUUID: UUID, readUUID: UUID, writeUUID: UUID + ): Boolean { + var notifyCharacteristic: BluetoothGattCharacteristic? = null + bluetoothGatt.services.forEach { service -> + if (service.uuid == serviceUUID) { + bluetoothGattService = service + bluetoothGattService!!.characteristics.forEach { characteristic -> + val charaProp = characteristic.properties + if (characteristic.uuid == readUUID) { //读特征 + readCharacteristic = characteristic + } + if (characteristic.uuid == writeUUID) { //写特征 + writeCharacteristic = characteristic + } + if (charaProp and BluetoothGattCharacteristic.PROPERTY_NOTIFY > 0) { + val notifyServiceUUID = bluetoothGattService!!.uuid + val notifyCharacteristicUUID = characteristic.uuid + Log.d( + Tag, + "notifyCharacteristicUUID=$notifyCharacteristicUUID, notifyServiceUUID=$notifyServiceUUID" + ) + notifyCharacteristic = bluetoothGatt.getService(notifyServiceUUID) + .getCharacteristic(notifyCharacteristicUUID) + } + } + } + } + //打开读通知,打开的是notifyCharacteristic!!!,不然死活不走onCharacteristicChanged回调 + bluetoothGatt.setCharacteristicNotification(notifyCharacteristic, true) + //一定要重新设置 + for (descriptor in notifyCharacteristic!!.descriptors) { + descriptor.value = BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE + bluetoothGatt.writeDescriptor(descriptor) + } + //延迟2s,保证所有通知都能及时打开 + handler.postDelayed({ }, 2000) + return true + } + + fun sendCommand(cmd: ByteArray): Boolean { + if (writeCharacteristic == null) { + Log.d(Tag, "sendCommand(ByteArray)-->writeGattCharacteristic == null") + return false + } + if (bluetoothGatt == null) { + Log.d(Tag, "sendCommand(ByteArray)-->bluetoothGatt == null") + return false + } + val value = writeCharacteristic!!.setValue(cmd) + Log.d(Tag, "写特征设置值结果:$value") + return bluetoothGatt!!.writeCharacteristic(writeCharacteristic) + } + + fun disConnectDevice() { + if (bluetoothGatt == null) { + Log.d(Tag, "disConnectDevice(ByteArray)-->bluetoothGatt == null"); + return + } + bluetoothGatt!!.disconnect() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/birmm/inspect/utils/BluetoothStateBroadcastReceiver.kt b/app/src/main/java/com/casic/birmm/inspect/utils/BluetoothStateBroadcastReceiver.kt index b6b5281..9b87bcb 100644 --- a/app/src/main/java/com/casic/birmm/inspect/utils/BluetoothStateBroadcastReceiver.kt +++ b/app/src/main/java/com/casic/birmm/inspect/utils/BluetoothStateBroadcastReceiver.kt @@ -20,12 +20,6 @@ } } } - BluetoothDevice.ACTION_ACL_CONNECTED -> { - MapActivity.sendEmptyMessage(Constant.DEVICE_CONNECTED) - } - BluetoothDevice.ACTION_ACL_DISCONNECTED -> { - MapActivity.sendEmptyMessage(Constant.DEVICE_DISCONNECTED) - } } } } \ No newline at end of file diff --git a/app/src/main/java/com/casic/birmm/inspect/utils/Constant.kt b/app/src/main/java/com/casic/birmm/inspect/utils/Constant.kt index 507655b..55549ec 100644 --- a/app/src/main/java/com/casic/birmm/inspect/utils/Constant.kt +++ b/app/src/main/java/com/casic/birmm/inspect/utils/Constant.kt @@ -15,13 +15,15 @@ const val PAGE_LIMIT = 15 const val BLUETOOTH_ON = 20 const val BLUETOOTH_OFF = 21 - const val DEVICE_CONNECTED = 22 - const val DEVICE_CONNECT_FAIL = 23 - const val DEVICE_DISCONNECTED = 24 + const val CONNECT_SUCCESS = 22 + const val CONNECT_FAILURE = 23 + const val DISCONNECT_SUCCESS = 24 const val SEND_SUCCESS = 25 const val SEND_FAILURE = 26 const val RECEIVE_SUCCESS = 27 const val RECEIVE_FAILURE = 28 + const val DISCOVERY_DEVICE = 29 + const val DISCOVERY_OUT_TIME = 30 const val FIVE_YEARS = 5L * 365 * 60 * 60 * 24 * 1000L const val IMAGE_MINUS_SIZE = 100 * 1024 diff --git a/app/src/main/java/com/casic/birmm/inspect/utils/callback/OnBleConnectListener.kt b/app/src/main/java/com/casic/birmm/inspect/utils/callback/OnBleConnectListener.kt new file mode 100644 index 0000000..b0b05a0 --- /dev/null +++ b/app/src/main/java/com/casic/birmm/inspect/utils/callback/OnBleConnectListener.kt @@ -0,0 +1,52 @@ +package com.casic.birmm.inspect.utils.callback + +import android.bluetooth.BluetoothDevice +import android.bluetooth.BluetoothGatt +import android.bluetooth.BluetoothGattCharacteristic + + +interface OnBleConnectListener { + fun onConnecting(bluetoothGatt: BluetoothGatt?, bluetoothDevice: BluetoothDevice?) //正在连接 + + fun onConnectSuccess( + bluetoothGatt: BluetoothGatt?, bluetoothDevice: BluetoothDevice?, + status: Int + ) //连接成功 + + fun onConnectFailure( + bluetoothGatt: BluetoothGatt?, bluetoothDevice: BluetoothDevice?, + exception: String?, status: Int + ) //连接失败 + + fun onDisConnecting(bluetoothGatt: BluetoothGatt?, bluetoothDevice: BluetoothDevice?) //正在断开 + + fun onDisConnectSuccess( + bluetoothGatt: BluetoothGatt?, bluetoothDevice: BluetoothDevice?, status: Int + ) // 断开连接 + + + fun onServiceDiscoverySucceed( + bluetoothGatt: BluetoothGatt?, bluetoothDevice: BluetoothDevice?, status: Int + ) //发现服务成功 + + fun onServiceDiscoveryFailed( + bluetoothGatt: BluetoothGatt?, bluetoothDevice: BluetoothDevice?, msg: String? + ) //发现服务失败 + + fun onReceiveMessage( + bluetoothGatt: BluetoothGatt?, characteristic: BluetoothGattCharacteristic? + ) //收到消息 + + fun onReceiveError(errorMsg: String?) //接收数据出错 + + fun onWriteSuccess( + bluetoothGatt: BluetoothGatt?, bluetoothDevice: BluetoothDevice?, msg: ByteArray? + ) //写入成功 + + fun onWriteFailure( + bluetoothGatt: BluetoothGatt?, bluetoothDevice: BluetoothDevice?, + msg: ByteArray?, errorMsg: String? + ) //写入失败 + + fun onReadRssi(bluetoothGatt: BluetoothGatt?, rssi: Int, status: Int) //成功读取到连接信号强度 +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/birmm/inspect/utils/callback/OnDeviceSearchListener.kt b/app/src/main/java/com/casic/birmm/inspect/utils/callback/OnDeviceSearchListener.kt new file mode 100644 index 0000000..af4d430 --- /dev/null +++ b/app/src/main/java/com/casic/birmm/inspect/utils/callback/OnDeviceSearchListener.kt @@ -0,0 +1,8 @@ +package com.casic.birmm.inspect.utils.callback + +import com.casic.birmm.inspect.bean.BlueToothBean + +interface OnDeviceSearchListener { + fun onDeviceFound(blueToothBean: BlueToothBean) //搜索到设备 + fun onDiscoveryOutTime() //扫描超时 +} \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 5dd8db9..ff9af45 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -68,8 +68,6 @@ - - diff --git a/app/src/main/java/com/casic/birmm/inspect/bean/BlueToothBean.java b/app/src/main/java/com/casic/birmm/inspect/bean/BlueToothBean.java index 14424ec..c842129 100644 --- a/app/src/main/java/com/casic/birmm/inspect/bean/BlueToothBean.java +++ b/app/src/main/java/com/casic/birmm/inspect/bean/BlueToothBean.java @@ -1,27 +1,29 @@ package com.casic.birmm.inspect.bean; +import android.bluetooth.BluetoothDevice; + public class BlueToothBean { - private String blueToothName; - private String blueToothAddress; + private BluetoothDevice bluetoothDevice; //蓝牙设备 + private int rssi; //蓝牙信号 - public BlueToothBean(String blueToothName, String blueToothAddress) { - this.blueToothName = blueToothName; - this.blueToothAddress = blueToothAddress; + public BlueToothBean(BluetoothDevice bluetoothDevice, int rssi) { + this.bluetoothDevice = bluetoothDevice; + this.rssi = rssi; } - public String getBlueToothName() { - return blueToothName; + public BluetoothDevice getBluetoothDevice() { + return bluetoothDevice; } - public void setBlueToothName(String blueToothName) { - this.blueToothName = blueToothName; + public void setBluetoothDevice(BluetoothDevice bluetoothDevice) { + this.bluetoothDevice = bluetoothDevice; } - public String getBlueToothAddress() { - return blueToothAddress; + public int getRssi() { + return rssi; } - public void setBlueToothAddress(String blueToothAddress) { - this.blueToothAddress = blueToothAddress; + public void setRssi(int rssi) { + this.rssi = rssi; } } diff --git a/app/src/main/java/com/casic/birmm/inspect/extensions/ByteArray.kt b/app/src/main/java/com/casic/birmm/inspect/extensions/ByteArray.kt new file mode 100644 index 0000000..8b2ea6e --- /dev/null +++ b/app/src/main/java/com/casic/birmm/inspect/extensions/ByteArray.kt @@ -0,0 +1,59 @@ +package com.casic.birmm.inspect.extensions + +import java.util.* +import kotlin.collections.ArrayList +import kotlin.math.abs + +/** + * ByteArray扩展方法 + */ + +//ByteArray转16进制 +fun ByteArray.toHexString(hasSpace: Boolean = true) = this.joinToString("") { + (it.toInt() and 0xFF).toString(16).padStart( + 2, + '0' + ).toUpperCase(Locale.ROOT) + if (hasSpace) " " else "" +} + +//ByteArray转ascii码字符串 +fun ByteArray.toAsciiString(): String { + val builder = StringBuilder() + for (index in 1..12) { + builder.append(this[index].toInt().toChar()) + } + return builder.toString() +} + +//ByteArray转十进制字符串集合 +fun ByteArray.toDecStringList(): ArrayList { + val data: ArrayList = ArrayList() + /** + * 170,0,0,0,100,1,0,100,0,0,0,100,13,10 + * + * 170是数据标头AA + * 13,10是数据结束位 + * */ + //AA 00 00 00 64 01 00 64 00 00 00 64 0D 0A + //浓度:4字节 0-99999 + var potencyValue = 0 + for (index in 1..4) { + potencyValue += abs(this[index].toString(10).toInt()) + } + data.add(potencyValue.toString()) + //报警标志:1字节 0:未报警 1:报警 + data.add(this[5].toString(10)) + //报警值:2字节 0-65535 + var alarmValue = 0 + for (index in 6..7) { + alarmValue += abs(this[index].toString(10).toInt()) + } + data.add(alarmValue.toString()) + //5s内最大值: 4字节 0-99999 + var maxPotencyValue = 0 + for (index in 8..11) { + maxPotencyValue += abs(this[index].toString(10).toInt()) + } + data.add(maxPotencyValue.toString()) + return data +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/birmm/inspect/utils/BLEManager.kt b/app/src/main/java/com/casic/birmm/inspect/utils/BLEManager.kt new file mode 100644 index 0000000..a1fcddc --- /dev/null +++ b/app/src/main/java/com/casic/birmm/inspect/utils/BLEManager.kt @@ -0,0 +1,484 @@ +package com.casic.birmm.inspect.utils + +import android.bluetooth.* +import android.content.Context +import android.content.Intent +import android.os.Build +import android.os.Handler +import android.util.Log +import com.casic.birmm.inspect.bean.BlueToothBean +import com.casic.birmm.inspect.utils.callback.OnBleConnectListener +import com.casic.birmm.inspect.utils.callback.OnDeviceSearchListener +import java.util.* + + +/** + * 1、扫描设备 + * 2、配对设备 + * 3、解除设备配对 + * 4、连接设备 + * 6、发现服务 + * 7、打开读写功能 + * 8、数据通讯(发送数据、接收数据) + * 9、断开连接 + */ +object BLEManager { + private const val Tag = "BLEManager" + private const val MAX_CONNECT_TIME = 10000L//连接超时时间10s + private var context: Context? = null + private var bluetoothManager: BluetoothManager? = null + private var bluetoothAdapter: BluetoothAdapter? = null + private val handler = Handler() + private var onDeviceSearchListener: OnDeviceSearchListener? = null + private var isConnecting = false + private lateinit var curConnectDevice: BluetoothDevice + private lateinit var serviceUUID: UUID + private lateinit var readUUID: UUID + private lateinit var writeUUID: UUID + private var onBleConnectListener: OnBleConnectListener? = null + private var bluetoothGatt: BluetoothGatt? = null + private var bluetoothGattService: BluetoothGattService? = null + private var readCharacteristic: BluetoothGattCharacteristic? = null + private var writeCharacteristic: BluetoothGattCharacteristic? = null + + fun initBle(context: Context): Boolean { + this.context = context + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) { + bluetoothManager = + context.getSystemService(Context.BLUETOOTH_SERVICE) as BluetoothManager + if (bluetoothManager == null) { + return false + } + bluetoothAdapter = bluetoothManager!!.adapter + return bluetoothAdapter != null + } else { + return false + } + } + + fun isEnable(): Boolean { + if (bluetoothAdapter == null) { + return false + } + return bluetoothAdapter!!.isEnabled + } + + /** + * 打开蓝牙 + * @param isFast true 直接打开蓝牙 false 提示用户打开 + */ + fun openBluetooth(context: Context, isFast: Boolean) { + if (!isEnable()) { + if (isFast) { + Log.d(Tag, "直接打开手机蓝牙") + bluetoothAdapter!!.enable() + } else { + context.startActivity(Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE)) + } + } else { + Log.d(Tag, "手机蓝牙状态已开") + } + } + + /** + * 本地蓝牙是否处于正在扫描状态 + * @return true false + */ + fun isDiscovery(): Boolean { + if (bluetoothAdapter == null) { + return false + } + return bluetoothAdapter!!.isDiscovering + } + + fun stopDiscoveryDevice() { + handler.removeCallbacks(stopScanRunnable) + if (bluetoothAdapter == null) { + return + } + bluetoothAdapter!!.stopLeScan(scanCallback) + } + + private val stopScanRunnable = Runnable { + if (onDeviceSearchListener != null) { + onDeviceSearchListener!!.onDiscoveryOutTime() //扫描超时回调 + } + //scanTime之后还没有扫描到设备,就停止扫描。 + stopDiscoveryDevice() + } + + //扫描设备回调 + private val scanCallback = object : BluetoothAdapter.LeScanCallback { + override fun onLeScan(device: BluetoothDevice?, rssi: Int, scanRecord: ByteArray?) { + if (device == null) return + if (device.name != null) { + Log.d(Tag, device.name + "-->" + device.address) + if (onDeviceSearchListener != null) { + onDeviceSearchListener!!.onDeviceFound(BlueToothBean(device, rssi)) //扫描到设备回调 + } + } + } + } + + fun startDiscoveryDevice(onDeviceSearchListener: OnDeviceSearchListener, scanTime: Long) { + if (bluetoothAdapter == null) { + return + } + this.onDeviceSearchListener = onDeviceSearchListener + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) { + bluetoothAdapter!!.startLeScan(scanCallback) + } else { + return + } + //设定最长扫描时间 + handler.postDelayed(stopScanRunnable, scanTime) + } + + fun connectBleDevice( + context: Context, bluetoothDevice: BluetoothDevice, + outTime: Long, serviceUUID: String, + readUUID: String, writeUUID: String, + onBleConnectListener: OnBleConnectListener + ): BluetoothGatt? { + if (isConnecting) { + Log.d(Tag, "connectBleDevice()-->isConnecting = true") + return null + } + this.curConnectDevice = bluetoothDevice + this.serviceUUID = UUID.fromString(serviceUUID) + this.readUUID = UUID.fromString(readUUID) + this.writeUUID = UUID.fromString(writeUUID) + this.onBleConnectListener = onBleConnectListener + Log.d(Tag, "开始准备连接:" + bluetoothDevice.name + "-->" + bluetoothDevice.address) + try { + bluetoothGatt = bluetoothDevice.connectGatt(context, false, bluetoothGattCallback) + bluetoothGatt!!.connect() + isConnecting = true + } catch (e: Exception) { + e.printStackTrace() + } + //设置连接超时时间10s + handler.postDelayed(connectOutTimeRunnable, outTime) + return bluetoothGatt + } + + private val bluetoothGattCallback = object : BluetoothGattCallback() { + override fun onConnectionStateChange(gatt: BluetoothGatt?, status: Int, newState: Int) { + super.onConnectionStateChange(gatt, status, newState) + val bluetoothDevice = gatt!!.device + Log.d(Tag, "连接的设备:" + bluetoothDevice.name + " " + bluetoothDevice.address) + isConnecting = true + //移除连接超时 + handler.removeCallbacks(connectOutTimeRunnable) + when (newState) { + BluetoothGatt.STATE_CONNECTING -> { + Log.d(Tag, "正在连接...") + if (onBleConnectListener != null) { + onBleConnectListener!!.onConnecting(gatt, bluetoothDevice) //正在连接回调 + } + } + BluetoothGatt.STATE_CONNECTED -> { + Log.d(Tag, "连接成功") + //连接成功去发现服务 + gatt.discoverServices() + //设置发现服务超时时间 + handler.postDelayed(serviceDiscoverOutTimeRunnable, MAX_CONNECT_TIME) + if (onBleConnectListener != null) { + onBleConnectListener!!.onConnectSuccess( + gatt, + bluetoothDevice, + status + ) //连接成功回调 + } + } + BluetoothGatt.STATE_DISCONNECTING -> { + Log.d(Tag, "正在断开...") + if (onBleConnectListener != null) { + onBleConnectListener!!.onDisConnecting(gatt, bluetoothDevice) //正在断开回调 + } + } + BluetoothGatt.STATE_DISCONNECTED -> { + Log.d(Tag, "断开连接status: $status") + gatt.close() + when (status) { + 133 -> { + //无法连接 + if (onBleConnectListener != null) { + gatt.close() + onBleConnectListener!!.onConnectFailure( + gatt, + bluetoothDevice, + "连接异常!", + status + ) //133连接异常 异常断开 + Log.d(Tag, "连接失败status:" + status + " " + bluetoothDevice.address) + } + } + 62 -> { + if (onBleConnectListener != null) { + gatt.close() + onBleConnectListener!!.onConnectFailure( + gatt, + bluetoothDevice, + "连接成功服务未发现断开!", + status + )//62没有发现服务 异常断开 + Log.d(Tag, "连接成功服务未发现断开status:$status") + } + } + 0 -> { + if (onBleConnectListener != null) { + onBleConnectListener!!.onDisConnectSuccess( + gatt, + bluetoothDevice, + status + ) //0正常断开 回调 + } + } + 8 -> {//因为距离远或者电池无法供电断开连接 + // 已经成功发现服务 + if (onBleConnectListener != null) { + onBleConnectListener!!.onDisConnectSuccess( + gatt, + bluetoothDevice, + status + ) //8断电断开 回调 + } + } + 34 -> { + if (onBleConnectListener != null) { + onBleConnectListener!!.onDisConnectSuccess( + gatt, + bluetoothDevice, + status + ) //34断开 + } + } + else -> {//其它断开连接 + if (onBleConnectListener != null) { + onBleConnectListener!!.onDisConnectSuccess( + gatt, + bluetoothDevice, + status + ) //其它断开 + } + } + } + } + } + } + + override fun onServicesDiscovered(gatt: BluetoothGatt?, status: Int) { + super.onServicesDiscovered(gatt, status) + //移除发现服务超时 + handler.removeCallbacks(serviceDiscoverOutTimeRunnable) + //配置服务信息 + if (setupService(gatt!!, serviceUUID, readUUID, writeUUID)) { + if (onBleConnectListener != null) { + onBleConnectListener!!.onServiceDiscoverySucceed( + gatt, + gatt.device, + status + ) //成功发现服务回调 + } + } else { + if (onBleConnectListener != null) { + onBleConnectListener!!.onServiceDiscoveryFailed( + gatt, + gatt.device, + "获取服务特征异常" + ) //发现服务失败回调 + } + } + } + + //读取蓝牙设备发出来的数据回调 + override fun onCharacteristicRead( + gatt: BluetoothGatt?, characteristic: BluetoothGattCharacteristic?, status: Int + ) { + super.onCharacteristicRead(gatt, characteristic, status) + Log.d(Tag, "读status: $status") + } + + //向蓝牙设备写入数据结果回调 + override fun onCharacteristicWrite( + gatt: BluetoothGatt?, + characteristic: BluetoothGattCharacteristic?, + status: Int + ) { + super.onCharacteristicWrite(gatt, characteristic, status) + if (characteristic!!.value == null) { + Log.e(Tag, "characteristic.getValue() == null"); + return + } + //将收到的字节数组转换成十六进制字符串 + when (status) { + BluetoothGatt.GATT_SUCCESS -> { + //写入成功 + if (onBleConnectListener != null) { + onBleConnectListener!!.onWriteSuccess( + gatt, + gatt!!.device, + characteristic.value + ) //写入成功回调 + } + } + BluetoothGatt.GATT_FAILURE -> { + //写入失败 + if (onBleConnectListener != null) { + onBleConnectListener!!.onWriteFailure( + gatt, + gatt!!.device, + characteristic.value, + "写入失败" + ) //写入失败回调 + } + } + BluetoothGatt.GATT_WRITE_NOT_PERMITTED -> { + Log.d(Tag, "没有权限") + } + } + } + + override fun onCharacteristicChanged( + gatt: BluetoothGatt?, characteristic: BluetoothGattCharacteristic? + ) { + super.onCharacteristicChanged(gatt, characteristic) + //接收数据 + Log.d(Tag, "收到数据:" + characteristic!!.value) + if (onBleConnectListener != null) { + onBleConnectListener!!.onReceiveMessage(gatt, characteristic) //接收数据回调 + } else { + Log.d(Tag, "onCharacteristicChanged-->onBleConnectListener == null: ") + } + } + + override fun onDescriptorRead( + gatt: BluetoothGatt?, descriptor: BluetoothGattDescriptor?, status: Int + ) { + super.onDescriptorRead(gatt, descriptor, status) + //开启监听成功,可以从设备读数据了 + Log.d(Tag, "onDescriptorRead开启监听成功") + } + + override fun onDescriptorWrite( + gatt: BluetoothGatt?, descriptor: BluetoothGattDescriptor?, status: Int + ) { + super.onDescriptorWrite(gatt, descriptor, status) + //开启监听成功,可以向设备写入命令了 + Log.d(Tag, "onDescriptorWrite开启监听成功") + } + + //蓝牙信号强度 + override fun onReadRemoteRssi(gatt: BluetoothGatt?, rssi: Int, status: Int) { + super.onReadRemoteRssi(gatt, rssi, status) + when (status) { + BluetoothGatt.GATT_SUCCESS -> { + Log.w(Tag, "读取RSSI值成功,RSSI值: $rssi ,status: $status") + if (onBleConnectListener != null) { + onBleConnectListener!!.onReadRssi(gatt, rssi, status) //成功读取连接的信号强度回调 + } + } + BluetoothGatt.GATT_FAILURE -> Log.w(Tag, "读取RSSI值失败,status: $status") + } + } + } + + private val connectOutTimeRunnable = Runnable { + if (bluetoothGatt == null) { + Log.d(Tag, "connectOutTimeRunnable-->bluetoothGatt == null") + return@Runnable + } + isConnecting = false + bluetoothGatt!!.disconnect() + //连接超时当作连接失败回调 + if (onBleConnectListener != null) { + onBleConnectListener!!.onConnectFailure( + bluetoothGatt, + curConnectDevice, + "连接超时", + -1 + ) //连接失败回调 + } + } + + private val serviceDiscoverOutTimeRunnable = Runnable { + if (bluetoothGatt == null) { + Log.d(Tag, "serviceDiscoverOutTimeRunnable-->bluetoothGatt == null"); + return@Runnable + } + isConnecting = false + bluetoothGatt!!.disconnect() + //发现服务超时当作连接失败回调 + if (onBleConnectListener != null) { + onBleConnectListener!!.onConnectFailure( + bluetoothGatt, + curConnectDevice, + "发现服务超时!", + -1 + ); //连接失败回调 + } + } + + private fun setupService( + bluetoothGatt: BluetoothGatt, serviceUUID: UUID, readUUID: UUID, writeUUID: UUID + ): Boolean { + var notifyCharacteristic: BluetoothGattCharacteristic? = null + bluetoothGatt.services.forEach { service -> + if (service.uuid == serviceUUID) { + bluetoothGattService = service + bluetoothGattService!!.characteristics.forEach { characteristic -> + val charaProp = characteristic.properties + if (characteristic.uuid == readUUID) { //读特征 + readCharacteristic = characteristic + } + if (characteristic.uuid == writeUUID) { //写特征 + writeCharacteristic = characteristic + } + if (charaProp and BluetoothGattCharacteristic.PROPERTY_NOTIFY > 0) { + val notifyServiceUUID = bluetoothGattService!!.uuid + val notifyCharacteristicUUID = characteristic.uuid + Log.d( + Tag, + "notifyCharacteristicUUID=$notifyCharacteristicUUID, notifyServiceUUID=$notifyServiceUUID" + ) + notifyCharacteristic = bluetoothGatt.getService(notifyServiceUUID) + .getCharacteristic(notifyCharacteristicUUID) + } + } + } + } + //打开读通知,打开的是notifyCharacteristic!!!,不然死活不走onCharacteristicChanged回调 + bluetoothGatt.setCharacteristicNotification(notifyCharacteristic, true) + //一定要重新设置 + for (descriptor in notifyCharacteristic!!.descriptors) { + descriptor.value = BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE + bluetoothGatt.writeDescriptor(descriptor) + } + //延迟2s,保证所有通知都能及时打开 + handler.postDelayed({ }, 2000) + return true + } + + fun sendCommand(cmd: ByteArray): Boolean { + if (writeCharacteristic == null) { + Log.d(Tag, "sendCommand(ByteArray)-->writeGattCharacteristic == null") + return false + } + if (bluetoothGatt == null) { + Log.d(Tag, "sendCommand(ByteArray)-->bluetoothGatt == null") + return false + } + val value = writeCharacteristic!!.setValue(cmd) + Log.d(Tag, "写特征设置值结果:$value") + return bluetoothGatt!!.writeCharacteristic(writeCharacteristic) + } + + fun disConnectDevice() { + if (bluetoothGatt == null) { + Log.d(Tag, "disConnectDevice(ByteArray)-->bluetoothGatt == null"); + return + } + bluetoothGatt!!.disconnect() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/birmm/inspect/utils/BluetoothStateBroadcastReceiver.kt b/app/src/main/java/com/casic/birmm/inspect/utils/BluetoothStateBroadcastReceiver.kt index b6b5281..9b87bcb 100644 --- a/app/src/main/java/com/casic/birmm/inspect/utils/BluetoothStateBroadcastReceiver.kt +++ b/app/src/main/java/com/casic/birmm/inspect/utils/BluetoothStateBroadcastReceiver.kt @@ -20,12 +20,6 @@ } } } - BluetoothDevice.ACTION_ACL_CONNECTED -> { - MapActivity.sendEmptyMessage(Constant.DEVICE_CONNECTED) - } - BluetoothDevice.ACTION_ACL_DISCONNECTED -> { - MapActivity.sendEmptyMessage(Constant.DEVICE_DISCONNECTED) - } } } } \ No newline at end of file diff --git a/app/src/main/java/com/casic/birmm/inspect/utils/Constant.kt b/app/src/main/java/com/casic/birmm/inspect/utils/Constant.kt index 507655b..55549ec 100644 --- a/app/src/main/java/com/casic/birmm/inspect/utils/Constant.kt +++ b/app/src/main/java/com/casic/birmm/inspect/utils/Constant.kt @@ -15,13 +15,15 @@ const val PAGE_LIMIT = 15 const val BLUETOOTH_ON = 20 const val BLUETOOTH_OFF = 21 - const val DEVICE_CONNECTED = 22 - const val DEVICE_CONNECT_FAIL = 23 - const val DEVICE_DISCONNECTED = 24 + const val CONNECT_SUCCESS = 22 + const val CONNECT_FAILURE = 23 + const val DISCONNECT_SUCCESS = 24 const val SEND_SUCCESS = 25 const val SEND_FAILURE = 26 const val RECEIVE_SUCCESS = 27 const val RECEIVE_FAILURE = 28 + const val DISCOVERY_DEVICE = 29 + const val DISCOVERY_OUT_TIME = 30 const val FIVE_YEARS = 5L * 365 * 60 * 60 * 24 * 1000L const val IMAGE_MINUS_SIZE = 100 * 1024 diff --git a/app/src/main/java/com/casic/birmm/inspect/utils/callback/OnBleConnectListener.kt b/app/src/main/java/com/casic/birmm/inspect/utils/callback/OnBleConnectListener.kt new file mode 100644 index 0000000..b0b05a0 --- /dev/null +++ b/app/src/main/java/com/casic/birmm/inspect/utils/callback/OnBleConnectListener.kt @@ -0,0 +1,52 @@ +package com.casic.birmm.inspect.utils.callback + +import android.bluetooth.BluetoothDevice +import android.bluetooth.BluetoothGatt +import android.bluetooth.BluetoothGattCharacteristic + + +interface OnBleConnectListener { + fun onConnecting(bluetoothGatt: BluetoothGatt?, bluetoothDevice: BluetoothDevice?) //正在连接 + + fun onConnectSuccess( + bluetoothGatt: BluetoothGatt?, bluetoothDevice: BluetoothDevice?, + status: Int + ) //连接成功 + + fun onConnectFailure( + bluetoothGatt: BluetoothGatt?, bluetoothDevice: BluetoothDevice?, + exception: String?, status: Int + ) //连接失败 + + fun onDisConnecting(bluetoothGatt: BluetoothGatt?, bluetoothDevice: BluetoothDevice?) //正在断开 + + fun onDisConnectSuccess( + bluetoothGatt: BluetoothGatt?, bluetoothDevice: BluetoothDevice?, status: Int + ) // 断开连接 + + + fun onServiceDiscoverySucceed( + bluetoothGatt: BluetoothGatt?, bluetoothDevice: BluetoothDevice?, status: Int + ) //发现服务成功 + + fun onServiceDiscoveryFailed( + bluetoothGatt: BluetoothGatt?, bluetoothDevice: BluetoothDevice?, msg: String? + ) //发现服务失败 + + fun onReceiveMessage( + bluetoothGatt: BluetoothGatt?, characteristic: BluetoothGattCharacteristic? + ) //收到消息 + + fun onReceiveError(errorMsg: String?) //接收数据出错 + + fun onWriteSuccess( + bluetoothGatt: BluetoothGatt?, bluetoothDevice: BluetoothDevice?, msg: ByteArray? + ) //写入成功 + + fun onWriteFailure( + bluetoothGatt: BluetoothGatt?, bluetoothDevice: BluetoothDevice?, + msg: ByteArray?, errorMsg: String? + ) //写入失败 + + fun onReadRssi(bluetoothGatt: BluetoothGatt?, rssi: Int, status: Int) //成功读取到连接信号强度 +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/birmm/inspect/utils/callback/OnDeviceSearchListener.kt b/app/src/main/java/com/casic/birmm/inspect/utils/callback/OnDeviceSearchListener.kt new file mode 100644 index 0000000..af4d430 --- /dev/null +++ b/app/src/main/java/com/casic/birmm/inspect/utils/callback/OnDeviceSearchListener.kt @@ -0,0 +1,8 @@ +package com.casic.birmm.inspect.utils.callback + +import com.casic.birmm.inspect.bean.BlueToothBean + +interface OnDeviceSearchListener { + fun onDeviceFound(blueToothBean: BlueToothBean) //搜索到设备 + fun onDiscoveryOutTime() //扫描超时 +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/birmm/inspect/view/MapActivity.kt b/app/src/main/java/com/casic/birmm/inspect/view/MapActivity.kt index dab7f0c..9e21422 100644 --- a/app/src/main/java/com/casic/birmm/inspect/view/MapActivity.kt +++ b/app/src/main/java/com/casic/birmm/inspect/view/MapActivity.kt @@ -1,9 +1,9 @@ package com.casic.birmm.inspect.view -import android.bluetooth.BluetoothAdapter +import android.annotation.SuppressLint import android.bluetooth.BluetoothDevice -import android.content.BroadcastReceiver -import android.content.Context +import android.bluetooth.BluetoothGatt +import android.bluetooth.BluetoothGattCharacteristic import android.content.Intent import android.os.Bundle import android.os.Handler @@ -22,10 +22,14 @@ import com.casic.birmm.inspect.bean.InspectionBean import com.casic.birmm.inspect.extensions.id import com.casic.birmm.inspect.extensions.show +import com.casic.birmm.inspect.extensions.toAsciiString +import com.casic.birmm.inspect.extensions.toDecStringList import com.casic.birmm.inspect.model.TaskRecordModel import com.casic.birmm.inspect.model.UserInfoModel import com.casic.birmm.inspect.utils.* import com.casic.birmm.inspect.utils.callback.ILocationListener +import com.casic.birmm.inspect.utils.callback.OnBleConnectListener +import com.casic.birmm.inspect.utils.callback.OnDeviceSearchListener import com.casic.birmm.inspect.vm.TaskRecordViewModel import com.casic.birmm.inspect.widgets.InputDialog import com.casic.birmm.inspect.widgets.SingleChoiceDialog @@ -64,10 +68,9 @@ private var routes: MutableList = ArrayList()//路线集和 private var gson: Gson = Gson() private var isBluetoothOn = true - private var blueToothDevices: MutableList = ArrayList() private var blueToothBeans: MutableList = ArrayList()//搜索展示列表 - private var bluetoothAdapter: BluetoothAdapter? = null private var currentDevice: BluetoothDevice? = null// 当前蓝牙设备 + private var curConnectState = false init { weakReferenceHandler = WeakReferenceHandler(this) @@ -75,6 +78,7 @@ private class WeakReferenceHandler(activity: MapActivity) : Handler() { private val activity: WeakReference = WeakReference(activity) + @SuppressLint("SetTextI18n") override fun handleMessage(msg: Message) { val mapActivity = activity.get() ?: return when (msg.what) { @@ -88,27 +92,92 @@ mapActivity.bluetoothButton.setImageResource(R.drawable.ic_bluetooth_disabled) mapActivity.isBluetoothOn = false } - Constant.DEVICE_CONNECTED -> { - //TODO 设备已连接 - mapActivity.communicateWithDevice(Constant.ASK_DEV_CODE_COMMAND) + Constant.DISCOVERY_DEVICE -> { + val bean = msg.obj as BlueToothBean + if (mapActivity.blueToothBeans.size == 0) { + mapActivity.blueToothBeans.add(bean) + } else { + //0表示未添加到list的新设备,1表示已经扫描并添加到list的设备 + var judge = 0 + for (it in mapActivity.blueToothBeans) { + if (it.bluetoothDevice.address == bean.bluetoothDevice.address) { + judge = 1 + break + } + } + if (judge == 0) { + mapActivity.blueToothBeans.add(bean) + } + } } - Constant.DEVICE_CONNECT_FAIL -> { - //TODO 设备连接失败/超时 + Constant.DISCOVERY_OUT_TIME -> { + OtherUtils.dismissLoadingDialog() + val sheetBuilder = + QMUIBottomSheet.BottomListSheetBuilder(mapActivity) + sheetBuilder.setTitle("请选择要连接的设备") + mapActivity.blueToothBeans.forEach { + sheetBuilder.addItem(it.bluetoothDevice.name) + } + sheetBuilder.setGravityCenter(true).setAddCancelBtn(true) + .setOnSheetItemClickListener { dialog, _, position, _ -> + dialog.dismiss() + //连接点击的设备 + mapActivity.startConnectDevice( + mapActivity.blueToothBeans[position].bluetoothDevice + ) + }.build().show() } - Constant.DEVICE_DISCONNECTED -> { - //TODO 设备断开连接 + Constant.CONNECT_SUCCESS -> { + OtherUtils.dismissLoadingDialog() + mapActivity.curConnectState = true + BLEManager.sendCommand(Constant.ASK_DEV_CODE_COMMAND) + } + Constant.CONNECT_FAILURE -> { + mapActivity.curConnectState = false } Constant.SEND_SUCCESS -> { - //TODO 发送成功 + val sendSuccess = msg.obj as ByteArray + Log.d(Tag, "发送成功->sendSuccessBuffer: ${sendSuccess.toList()}") } Constant.SEND_FAILURE -> { - //TODO 发送失败 + val sendFail = msg.obj as ByteArray + Log.d(Tag, "发送失败->sendFailBuffer: ${sendFail.toList()}") } Constant.RECEIVE_SUCCESS -> { //TODO 接收成功 + val receiveByteArray = msg.obj as ByteArray + //根据返回值标头判断是设备编号还是数据值 + val firstByte = receiveByteArray[0] + if (firstByte == 0xAA.toByte()) { + //解析测量数据 + if (receiveByteArray.size == 14) { + val dataList = receiveByteArray.toDecStringList() + /** + * [100, 1, 100, 100] + * */ + mapActivity.currentValueView.text = dataList[0] + mapActivity.settingsValueView.text = dataList[2] + mapActivity.maxValueView.text = dataList[3] + } else { + Log.d(Tag, "设备返回值长度异常,无法解析") + } + } else { + //解析deviceCode + if (receiveByteArray.size >= 12) { + mapActivity.deviceStatusView.text = + "设备编号: ${receiveByteArray.toAsciiString()}" + BLEManager.sendCommand(Constant.OPEN_TRANSFER_COMMAND) + } else { + Log.d(Tag, "设备返回值长度异常,无法解析") + } + } } Constant.RECEIVE_FAILURE -> { - //TODO 接收失败 + val receiveString = msg.obj as String + Log.d(Tag, "接收失败->receiveString: $receiveString") + } + Constant.DISCONNECT_SUCCESS -> { + mapActivity.curConnectState = false } } } @@ -138,128 +207,123 @@ fun initData() { val userModelJson = SaveKeyValues.getValue(Constant.USER_OBJECT, "") as String userDataModel = gson.fromJson(userModelJson, UserInfoModel::class.java).data!! - //蓝牙搜索监听 - BroadcastManager.instance.addAction( - arrayOf( - BluetoothDevice.ACTION_FOUND, - BluetoothAdapter.ACTION_DISCOVERY_FINISHED - ), object : BroadcastReceiver() { - override fun onReceive(context: Context?, intent: Intent?) { - when (intent?.action) { - BluetoothDevice.ACTION_FOUND -> { - val device = - intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE) - lateinit var bean: BlueToothBean - device?.let { - bean = if (it.name == null || it.name == "") { - BlueToothBean("设备名未知", it.address) - } else { - BlueToothBean(device.name, device.address) - } - } - if (bean.blueToothName != "设备名未知") { - if (blueToothBeans.size == 0) { - blueToothBeans.add(bean) - blueToothDevices.add(device) - } else { - //0表示未添加到list的新设备,1表示已经扫描并添加到list的设备 - var judge = 0 - for (it in blueToothBeans) { - if (it.blueToothAddress == bean.blueToothAddress) { - judge = 1 - break - } - } - if (judge == 0) { - blueToothBeans.add(bean) - blueToothDevices.add(device) - } - } - } - } - BluetoothAdapter.ACTION_DISCOVERY_FINISHED -> { - OtherUtils.dismissLoadingDialog() - bluetoothButton.isEnabled = true - Log.d(Tag, gson.toJson(blueToothBeans)) - val sheetBuilder = - QMUIBottomSheet.BottomListSheetBuilder(this@MapActivity) - sheetBuilder.setTitle("请选择要连接的设备") - blueToothBeans.forEach { - sheetBuilder.addItem(it.blueToothName) - } - sheetBuilder.setGravityCenter(true).setAddCancelBtn(true) - .setOnSheetItemClickListener { dialog, _, position, _ -> - dialog.dismiss() - //连接点击的设备 - startConnectDevice( - blueToothBeans[position].blueToothName, - blueToothDevices[position] - ) - }.build().show() - } - } - } - }) + + if (BLEManager.initBle(this)) { + if (BLEManager.isEnable()) { + BLEManager.openBluetooth(this, false) + } + } else { + "该设备不支持低功耗蓝牙".show(this) + } } - private fun startConnectDevice(deviceName: String, bluetoothDevice: BluetoothDevice?) { - if (bluetoothDevice == null) { - Log.d(Tag, "startConnectDevice-->bluetoothDevice == null") - return + private fun startConnectDevice(device: BluetoothDevice) { + this.currentDevice = device + if (!curConnectState) { + OtherUtils.showLoadingDialog(this, "正在连接[${currentDevice!!.name}]...") + BLEManager.connectBleDevice( + this, currentDevice!!, 15000, + Constant.SERVICE_UUID, + Constant.READ_CHARACTERISTIC_UUID, + Constant.WRITE_CHARACTERISTIC_UUID, + onBleConnectListener + ) } - this.currentDevice = bluetoothDevice - if (bluetoothAdapter == null) { - Log.d(Tag, "startConnectDevice-->bluetoothAdapter == null") - return - } - OtherUtils.showLoadingDialog(this, "正在连接[$deviceName]...") - } -// private fun manageConnectSendReceiveData(bluetoothSocket: BluetoothSocket) { -// connectedThread = ConnectedThread(bluetoothSocket) -// connectedThread!!.start() -// connectedThread!!.setOnSendReceiveDataListener(object : -// ConnectedThread.OnSendReceiveDataListener { -// override fun onSendDataSuccess(data: ByteArray?) { -// val message = weakReferenceHandler.obtainMessage() -// message.what = Constant.SEND_SUCCESS -// message.obj = "发送数据成功,长度" + data!!.size + "->" + ConnectedThread.bytes2HexString( -// data, -// data.size -// ) -// weakReferenceHandler.sendMessage(message) -// } -// -// override fun onSendDataError(data: ByteArray?, errorMsg: String?) { -// val message = weakReferenceHandler.obtainMessage() -// message.what = Constant.SEND_FAILURE -// message.obj = "发送数据出错,长度" + data!!.size + "->" + ConnectedThread.bytes2HexString( -// data, -// data.size -// ) -// weakReferenceHandler.sendMessage(message) -// } -// -// override fun onReceiveDataSuccess(buffer: ByteArray?) { -// val message = weakReferenceHandler.obtainMessage() -// message.what = Constant.RECEIVE_SUCCESS -// message.obj = -// "成功接收数据,长度" + buffer!!.size + "->" + ConnectedThread.bytes2HexString( -// buffer, -// buffer.size -// ) -// weakReferenceHandler.sendMessage(message) -// } -// -// override fun onReceiveDataError(errorMsg: String?) { -// val message = weakReferenceHandler.obtainMessage() -// message.what = Constant.RECEIVE_FAILURE -// message.obj = "接收数据出错:$errorMsg" -// weakReferenceHandler.sendMessage(message) -// } -// }) -// } + private val onBleConnectListener = object : OnBleConnectListener { + override fun onConnecting( + bluetoothGatt: BluetoothGatt?, bluetoothDevice: BluetoothDevice? + ) { + } + + override fun onConnectSuccess( + bluetoothGatt: BluetoothGatt?, bluetoothDevice: BluetoothDevice?, status: Int + ) { + } + + override fun onConnectFailure( + bluetoothGatt: BluetoothGatt?, bluetoothDevice: BluetoothDevice?, + exception: String?, status: Int + ) { + val message = weakReferenceHandler.obtainMessage() + message.what = Constant.CONNECT_FAILURE + weakReferenceHandler.sendMessage(message) + } + + override fun onDisConnecting( + bluetoothGatt: BluetoothGatt?, bluetoothDevice: BluetoothDevice? + ) { + + } + + override fun onDisConnectSuccess( + bluetoothGatt: BluetoothGatt?, bluetoothDevice: BluetoothDevice?, + status: Int + ) { + val message = weakReferenceHandler.obtainMessage() + message.what = Constant.DISCONNECT_SUCCESS + message.obj = status + weakReferenceHandler.sendMessage(message) + } + + override fun onServiceDiscoverySucceed( + bluetoothGatt: BluetoothGatt?, bluetoothDevice: BluetoothDevice?, + status: Int + ) { + val message = weakReferenceHandler.obtainMessage() + message.what = Constant.CONNECT_SUCCESS + weakReferenceHandler.sendMessage(message) + } + + override fun onServiceDiscoveryFailed( + bluetoothGatt: BluetoothGatt?, bluetoothDevice: BluetoothDevice?, + msg: String? + ) { + val message = weakReferenceHandler.obtainMessage() + message.what = Constant.CONNECT_FAILURE + weakReferenceHandler.sendMessage(message) + } + + override fun onReceiveMessage( + bluetoothGatt: BluetoothGatt?, characteristic: BluetoothGattCharacteristic? + ) { + val message = weakReferenceHandler.obtainMessage() + message.what = Constant.RECEIVE_SUCCESS + message.obj = characteristic!!.value + weakReferenceHandler.sendMessage(message) + } + + override fun onReceiveError(errorMsg: String?) { + val message = weakReferenceHandler.obtainMessage() + message.what = Constant.RECEIVE_FAILURE + weakReferenceHandler.sendMessage(message) + } + + override fun onWriteSuccess( + bluetoothGatt: BluetoothGatt?, bluetoothDevice: BluetoothDevice?, + msg: ByteArray? + ) { + val message = weakReferenceHandler.obtainMessage() + message.what = Constant.SEND_SUCCESS + message.obj = msg + weakReferenceHandler.sendMessage(message) + } + + override fun onWriteFailure( + bluetoothGatt: BluetoothGatt?, bluetoothDevice: BluetoothDevice?, + msg: ByteArray?, errorMsg: String? + ) { + val message = weakReferenceHandler.obtainMessage() + message.what = Constant.SEND_FAILURE + message.obj = msg + weakReferenceHandler.sendMessage(message) + } + + override fun onReadRssi(bluetoothGatt: BluetoothGatt?, rssi: Int, status: Int) { + + } + } private fun initMap(savedInstanceState: Bundle?) { mapView.onCreate(savedInstanceState) @@ -319,47 +383,56 @@ } private fun menuButtonEvent() { - //判断蓝牙的状态 - bluetoothAdapter = BluetoothAdapter.getDefaultAdapter() - if (bluetoothAdapter == null) { - "该设备不支持蓝牙,无法连接".show(this) - return - } - if (bluetoothAdapter!!.isEnabled) { + //蓝牙按钮 + if (isBluetoothOn) { bluetoothButton.setImageResource(R.drawable.ic_bluetooth_enable) bluetoothButton.setOnClickListener { - if (currentDevice == null) { - //搜索蓝牙 - searchDevice() + if (curConnectState) { + //断开连接 + BLEManager.disConnectDevice() + currentValueView.text = "--" + settingsValueView.text = "--" + maxValueView.text = "--" + deviceStatusView.text = "设备编号:未连接" + "设备已断开连接".show(this) } else { - if (currentDevice!!.bondState == BluetoothDevice.BOND_NONE) { - searchDevice() - } else { - //断开连接 - currentValueView.text = "--" - settingsValueView.text = "--" - maxValueView.text = "--" - deviceStatusView.text = "设备编号:未连接" - "设备已断开连接".show(this) - } + searchDevice() } } } else { bluetoothButton.setImageResource(R.drawable.ic_bluetooth_disabled) - //打开 - startActivity(Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE)) + } + + //重新发送指令按钮 + refreshButton.setOnClickListener { + if (curConnectState) { + BLEManager.sendCommand(Constant.OPEN_TRANSFER_COMMAND) + } else { + "请先连接设备".show(this) + } } } private fun searchDevice() { //搜索蓝牙 - if (bluetoothAdapter!!.isDiscovering) { - //搜索中不让再次搜索 - bluetoothButton.isEnabled = false - return + if (BLEManager.isDiscovery()) {//当前正在搜索设备... + BLEManager.stopDiscoveryDevice() } OtherUtils.showLoadingDialog(this, "设备搜索中..."); - bluetoothAdapter!!.startDiscovery() + BLEManager.startDiscoveryDevice(object : OnDeviceSearchListener { + override fun onDeviceFound(blueToothBean: BlueToothBean) { + val message = weakReferenceHandler.obtainMessage() + message.what = Constant.DISCOVERY_DEVICE + message.obj = blueToothBean + weakReferenceHandler.sendMessage(message) + } + + override fun onDiscoveryOutTime() { + val message = weakReferenceHandler.obtainMessage() + message.what = Constant.DISCOVERY_OUT_TIME + weakReferenceHandler.sendMessage(message) + } + }, 15 * 1000) } private fun selectInspectMode() { @@ -434,17 +507,13 @@ } }) updateCurrentInspection() - // 和设备通信 - communicateWithDevice(Constant.OPEN_TRANSFER_COMMAND) } private fun updateCurrentInspection() { SaveKeyValues.putValue(Constant.INSPECTION_OBJECT, gson.toJson(inspectionBean)) } - private fun communicateWithDevice(cmd: ByteArray) { - - } + /***以下是地图生命周期管理************************************************************************/ override fun onResume() { super.onResume() @@ -465,5 +534,6 @@ cancel() super.onDestroy() mapView.onDestroy() + BroadcastManager.instance.destroy() } } \ No newline at end of file