diff --git a/app/build.gradle b/app/build.gradle index ae07527..5ad9504 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -116,4 +116,6 @@ implementation 'com.amap.api:3dmap:latest.integration' //高德地图搜索 implementation 'com.amap.api:search:8.1.0' + //蓝牙 + implementation 'com.github.Jasonchenlijian:FastBle:2.4.0' } \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle index ae07527..5ad9504 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -116,4 +116,6 @@ implementation 'com.amap.api:3dmap:latest.integration' //高德地图搜索 implementation 'com.amap.api:search:8.1.0' + //蓝牙 + implementation 'com.github.Jasonchenlijian:FastBle:2.4.0' } \ No newline at end of file diff --git a/app/src/main/java/com/casic/birmm/inspect/fragment/HomePageFragment.kt b/app/src/main/java/com/casic/birmm/inspect/fragment/HomePageFragment.kt index da55df2..e5252c1 100644 --- a/app/src/main/java/com/casic/birmm/inspect/fragment/HomePageFragment.kt +++ b/app/src/main/java/com/casic/birmm/inspect/fragment/HomePageFragment.kt @@ -1,14 +1,10 @@ package com.casic.birmm.inspect.fragment -import android.annotation.SuppressLint -import android.bluetooth.BluetoothDevice import android.bluetooth.BluetoothGatt import android.bluetooth.BluetoothGattCharacteristic import android.content.Context import android.graphics.Color import android.os.Bundle -import android.os.Handler -import android.os.Message import android.os.Vibrator import android.util.Log import android.view.LayoutInflater @@ -16,6 +12,7 @@ import android.widget.LinearLayout import androidx.core.text.isDigitsOnly import androidx.lifecycle.ViewModelProvider +import androidx.lifecycle.lifecycleScope import com.amap.api.location.AMapLocation import com.amap.api.maps.AMap import com.amap.api.maps.AMapOptions @@ -25,6 +22,7 @@ import com.amap.api.maps.model.MyLocationStyle import com.amap.api.maps.model.PolylineOptions import com.casic.birmm.inspect.R +import com.casic.birmm.inspect.base.BaseApplication import com.casic.birmm.inspect.callback.OnDistanceSearchListener import com.casic.birmm.inspect.databinding.FragmentMapInspectBinding import com.casic.birmm.inspect.extensions.addAll @@ -41,35 +39,37 @@ import com.casic.birmm.inspect.vm.EventViewModel import com.casic.birmm.inspect.vm.InspectionViewModel import com.casic.birmm.inspect.vm.RouteViewModel +import com.clj.fastble.BleManager +import com.clj.fastble.callback.BleGattCallback +import com.clj.fastble.callback.BleNotifyCallback +import com.clj.fastble.callback.BleScanCallback +import com.clj.fastble.callback.BleWriteCallback +import com.clj.fastble.data.BleDevice +import com.clj.fastble.exception.BleException import com.pengxh.kt.lite.base.KotlinBaseFragment +import com.pengxh.kt.lite.extensions.convertColor import com.pengxh.kt.lite.extensions.getStatusBarHeight import com.pengxh.kt.lite.extensions.navigatePageTo import com.pengxh.kt.lite.extensions.show import com.pengxh.kt.lite.extensions.timestampToCompleteDate import com.pengxh.kt.lite.extensions.timestampToDate import com.pengxh.kt.lite.utils.SaveKeyValues -import com.pengxh.kt.lite.utils.WeakReferenceHandler -import com.pengxh.kt.lite.utils.ble.BLEManager -import com.pengxh.kt.lite.utils.ble.BlueToothBean -import com.pengxh.kt.lite.utils.ble.OnBleConnectListener -import com.pengxh.kt.lite.utils.ble.OnDeviceDiscoveredListener import com.pengxh.kt.lite.vm.LoadState import com.pengxh.kt.lite.widget.dialog.AlertControlDialog import com.pengxh.kt.lite.widget.dialog.AlertInputDialog import com.pengxh.kt.lite.widget.dialog.BottomActionSheet -import com.qmuiteam.qmui.widget.dialog.QMUIBottomSheet +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.delay +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext import java.util.LinkedList import java.util.UUID -@SuppressLint("MissingPermission", "SetTextI18n") -class HomePageFragment : KotlinBaseFragment(), Handler.Callback { - - companion object { - lateinit var weakReferenceHandler: WeakReferenceHandler - } +class HomePageFragment : KotlinBaseFragment() { private val kTag = "HomePageFragment" - private val blueToothBeans: MutableList = ArrayList()//搜索展示列表 + private val bleManager by lazy { BleManager.getInstance() } + private val bluetoothDevices by lazy { java.util.ArrayList() } private val latLngs = LinkedList() private lateinit var userName: String private lateinit var vibrator: Vibrator @@ -77,14 +77,13 @@ private lateinit var inspectionViewModel: InspectionViewModel private lateinit var routeViewModel: RouteViewModel private lateinit var aMap: AMap + private lateinit var writeUuid: String + private lateinit var notifyUuid: String private var newModel: NewInspectionModel? = null//新建巡检数据结构模型 - private var isBluetoothOn = true - private var curConnectState = false - private var currentDevice: BluetoothDevice? = null// 当前蓝牙设备 + private var connectedDevice: BleDevice? = null private var isGeneratingTask = false private var alarmCount = 0 - private var connectedDeviceName = "" - private var isDataCommandOpened = false + private var connectedDeviceCode = "" override fun initViewBinding( inflater: LayoutInflater, container: ViewGroup? @@ -103,16 +102,8 @@ override fun initOnCreate(savedInstanceState: Bundle?) { vibrator = requireContext().getSystemService(Context.VIBRATOR_SERVICE) as Vibrator - weakReferenceHandler = WeakReferenceHandler(this) - //BLE - if (BLEManager.initBLE(requireContext())) { - if (!BLEManager.isBluetoothEnable()) { - BLEManager.openBluetooth(false) - } - } else { - "该设备不支持低功耗蓝牙".show(requireContext()) - } + bleManager.enableLog(true).setSplitWriteNum(16).init(BaseApplication.get()) //Map binding.mapView.onCreate(savedInstanceState) @@ -196,27 +187,10 @@ } is LoadState.Success -> { - //断开连接 - if (curConnectState) { - BLEManager.disConnectDevice() - } - binding.deviceStatusView.text = "设备编号:未连接" - binding.inspectNameView.text = "" - binding.inspectTimeView.text = "" - binding.currentValueView.text = "--" - binding.settingsValueView.text = "--" - binding.maxValueView.text = "--" - //按钮状态 binding.stopInspectButton.isEnabled = false binding.addInspectionButton.isEnabled = true - //清除缓存 - blueToothBeans.clear() - latLngs.clear() - SaveKeyValues.removeKey(LocaleConstant.DEVICE_CODE) - isDataCommandOpened = false - LoadingDialogHub.dismiss() "巡检记录保存成功".show(requireContext()) } @@ -279,7 +253,7 @@ position.toString(), newModel!!.id, newModel!!.name, - connectedDeviceName, + connectedDeviceCode, latLngs.last.longitude.toString(), latLngs.last.latitude.toString() ) @@ -311,261 +285,222 @@ } //蓝牙按钮 - if (isBluetoothOn) { - binding.bluetoothButton.setImageResource(R.drawable.ic_bluetooth_enable) - binding.bluetoothButton.setOnClickListener { - if (curConnectState) { - AlertControlDialog.Builder() - .setContext(requireContext()) - .setTitle("提示") - .setMessage("确定断开设备吗?") - .setNegativeButton("取消") - .setPositiveButton("确定") - .setOnDialogButtonClickListener(object : - AlertControlDialog.OnDialogButtonClickListener { - override fun onCancelClick() { - - } - - override fun onConfirmClick() { - //断开连接 - BLEManager.disConnectDevice() - binding.currentValueView.text = "--" - binding.settingsValueView.text = "--" - binding.maxValueView.text = "--" - binding.deviceStatusView.text = "设备编号:未连接" - isDataCommandOpened = false - "设备已断开连接".show(requireContext()) - } - }).build().show() - } else { - //搜索蓝牙 - if (!BLEManager.isBluetoothEnable()) { - BLEManager.openBluetooth(false) - return@setOnClickListener - } - if (BLEManager.isDiscovery()) {//当前正在搜索设备... - BLEManager.stopDiscoverDevice() - } - LoadingDialogHub.show(requireActivity(), "设备搜索中...") - BLEManager.startDiscoverDevice(object : OnDeviceDiscoveredListener { - override fun onDeviceFound(blueToothBean: BlueToothBean?) { - val message = weakReferenceHandler.obtainMessage() - message.what = LocaleConstant.DISCOVERY_DEVICE - message.obj = blueToothBean - weakReferenceHandler.sendMessage(message) - } - - override fun onDiscoveryTimeout() { - val message = weakReferenceHandler.obtainMessage() - message.what = LocaleConstant.DISCOVERY_OUT_TIME - weakReferenceHandler.sendMessage(message) - } - }, 3 * 1000) - } + binding.bluetoothButton.setOnClickListener { + if (!bleManager.isBlueEnable) { + bleManager.enableBluetooth() } - } else { - binding.bluetoothButton.setImageResource(R.drawable.ic_bluetooth_disabled) - } - //重新发送指令按钮 - binding.refreshButton.setOnClickListener { - if (curConnectState) { - BLEManager.sendCommand(LocaleConstant.OPEN_TRANSFER_COMMAND) + if (bleManager.isConnected(connectedDevice)) { + AlertControlDialog.Builder() + .setContext(requireContext()) + .setTitle("提示") + .setMessage("确定断开设备吗?") + .setNegativeButton("取消") + .setPositiveButton("确定") + .setOnDialogButtonClickListener(object : + AlertControlDialog.OnDialogButtonClickListener { + override fun onCancelClick() { + + } + + override fun onConfirmClick() { + lifecycleScope.launch(Dispatchers.IO) { + sendCommand(LocaleConstant.CLOSE_TRANSFER_COMMAND) + + delay(500) + + //关闭数据传送指令 + bleManager.disconnect(connectedDevice) + withContext(Dispatchers.Main) { + binding.currentValueView.text = "--" + binding.settingsValueView.text = "--" + binding.maxValueView.text = "--" + binding.deviceStatusView.text = "设备编号:未连接" + "设备已断开连接".show(requireContext()) + + //清除缓存 + bluetoothDevices.clear() + latLngs.clear() + aMap.clear()//清除原来的路线 + SaveKeyValues.removeKey(LocaleConstant.DEVICE_CODE) + } + } + } + }).build().show() } else { - "请先连接设备".show(requireContext()) + //搜索蓝牙 + bleManager.scan(object : BleScanCallback() { + override fun onScanStarted(success: Boolean) { + LoadingDialogHub.show(requireActivity(), "设备搜索中...") + } + + override fun onScanning(bleDevice: BleDevice) { + + } + + override fun onScanFinished(scanResultList: List) { + LoadingDialogHub.dismiss() + + scanResultList.forEach { + if (!it.name.isNullOrBlank()) { + bluetoothDevices.add(it) + } + } + showScanResult() + } + }) + } + + //重新发送指令按钮 + binding.retrySendButton.setOnClickListener { + if (bleManager.isConnected(connectedDevice)) { + sendCommand(LocaleConstant.OPEN_TRANSFER_COMMAND) + } else { + "请先连接设备".show(requireContext()) + } } } } - override fun handleMessage(msg: Message): Boolean { - when (msg.what) { - LocaleConstant.BLUETOOTH_ON -> { - "蓝牙已开启".show(requireContext()) + private fun sendCommand(command: ByteArray) { + bleManager.write(connectedDevice, LocaleConstant.UUIDS[2], writeUuid, command, + object : BleWriteCallback() { + override fun onWriteSuccess(current: Int, total: Int, justWrite: ByteArray?) { + Log.d(kTag, "onWriteSuccess: 指令下发成功") + } + + override fun onWriteFailure(exception: BleException?) { + "指令下发失败".show(requireContext()) + } + }) + } + + private fun showScanResult() { + val array = java.util.ArrayList() + for (it in bluetoothDevices) { + array.add(it.name) + } + + BottomActionSheet.Builder() + .setContext(requireContext()) + .setActionItemTitle(array) + .setItemTextColor(R.color.themeColor.convertColor(requireContext())) + .setOnActionSheetListener(object : BottomActionSheet.OnActionSheetListener { + override fun onActionItemClick(position: Int) { + //连接点击的设备 + startConnectDevice(bluetoothDevices[position]) + } + }).build().show() + } + + private fun startConnectDevice(device: BleDevice) { + // 当前蓝牙设备 + bleManager.connect(device, object : BleGattCallback() { + override fun onStartConnect() { + LoadingDialogHub.show(requireActivity(), "设备连接中...") + } + + override fun onConnectFail(bleDevice: BleDevice, exception: BleException) { + LoadingDialogHub.dismiss() + } + + override fun onConnectSuccess( + bleDevice: BleDevice, + gatt: BluetoothGatt, + status: Int + ) { + connectedDevice = bleDevice + notifyDeviceService(bleDevice, gatt) + } + + override fun onDisConnected( + isActiveDisConnected: Boolean, bleDevice: BleDevice, + gatt: BluetoothGatt, status: Int + ) { + LoadingDialogHub.dismiss() + connectedDevice = null binding.bluetoothButton.setImageResource(R.drawable.ic_bluetooth_enable) - isBluetoothOn = true } - - LocaleConstant.BLUETOOTH_OFF -> { - "蓝牙已关闭".show(requireContext()) - binding.bluetoothButton.setImageResource(R.drawable.ic_bluetooth_disabled) - isBluetoothOn = false - } - - LocaleConstant.DISCOVERY_DEVICE -> { - val bean = msg.obj as BlueToothBean - if (blueToothBeans.size == 0) { - blueToothBeans.add(bean) - } else { - //0表示未添加到list的新设备,1表示已经扫描并添加到list的设备 - var judge = 0 - for (it in blueToothBeans) { - if (it.bluetoothDevice.address == bean.bluetoothDevice.address) { - judge = 1 - break - } - } - if (judge == 0) { - blueToothBeans.add(bean) - } - } - } - - LocaleConstant.DISCOVERY_OUT_TIME -> { - LoadingDialogHub.dismiss() - if (blueToothBeans.size == 0) { - "无可用设备,请确认设备是否已经开启".show(requireContext()) - } else { - val sheetBuilder = QMUIBottomSheet.BottomListSheetBuilder(requireContext()) - sheetBuilder.setTitle("请选择要连接的设备") - blueToothBeans.forEach { - if (it.bluetoothDevice.name.isNotEmpty()) { - sheetBuilder.addItem(it.bluetoothDevice.name) - } - } - sheetBuilder.setGravityCenter(true).setAddCancelBtn(true) - .setOnSheetItemClickListener { dialog, _, position, _ -> - dialog.dismiss() - //连接点击的设备 - startConnectDevice(blueToothBeans[position].bluetoothDevice) - }.build().show() - } - } - - LocaleConstant.CONNECT_SUCCESS -> { - LoadingDialogHub.dismiss() - curConnectState = true - BLEManager.sendCommand(LocaleConstant.ASK_DEV_CODE_COMMAND) - } - - LocaleConstant.CONNECT_FAILURE -> curConnectState = false - LocaleConstant.SEND_SUCCESS -> Log.d( - kTag, "发送成功-> ${(msg.obj as ByteArray).toList()}" - ) - - LocaleConstant.SEND_FAILURE -> Log.d( - kTag, "发送失败-> ${(msg.obj as ByteArray).toList()}" - ) - - LocaleConstant.RECEIVE_SUCCESS -> { - val bytes = msg.obj as ByteArray - if (bytes.first() == 51.toByte() && bytes.size >= 14) { - //解析deviceCode - //[51, 51, 50, 48, 48, 48, 48, 49, 48, 48, 48, 50, 13, 10] - binding.deviceStatusView.text = "设备编号: ${bytes.toDeviceCode()}" - SaveKeyValues.putValue(LocaleConstant.DEVICE_CODE, bytes.toDeviceCode()) - if (!isDataCommandOpened) { - BLEManager.sendCommand(LocaleConstant.OPEN_TRANSFER_COMMAND) - isDataCommandOpened = true - } - } else if (bytes.first() == (-86).toByte() && bytes.size == 14) { - handleMethaneData(bytes) - } else { - //如果收到错误数据,就保存设备编号 - binding.deviceStatusView.text = "设备编号: $connectedDeviceName" - SaveKeyValues.putValue(LocaleConstant.DEVICE_CODE, connectedDeviceName) - if (!isDataCommandOpened) { - BLEManager.sendCommand(LocaleConstant.OPEN_TRANSFER_COMMAND) - isDataCommandOpened = true - } - } - } - - LocaleConstant.RECEIVE_FAILURE -> Log.d(kTag, "接收失败-> ${msg.obj as String}") - LocaleConstant.DISCONNECT_SUCCESS -> curConnectState = false - } - return true + }) } - private fun startConnectDevice(device: BluetoothDevice) { - this.currentDevice = device - if (!curConnectState) { - connectedDeviceName = currentDevice!!.name - LoadingDialogHub.show(requireActivity(), "正在连接[${connectedDeviceName}]...") - BLEManager.connectBleDevice( - requireContext(), currentDevice!!, 10000, - LocaleConstant.SERVICE_UUID, - LocaleConstant.READ_CHARACTERISTIC_UUID, - LocaleConstant.WRITE_CHARACTERISTIC_UUID, - onBleConnectListener - ) - } - } - - private val onBleConnectListener = object : OnBleConnectListener { - override fun onConnecting(bluetoothGatt: BluetoothGatt?) { - + private fun notifyDeviceService(bleDevice: BleDevice, gatt: BluetoothGatt) { + val gattService = gatt.getService(UUID.fromString(LocaleConstant.UUIDS[2])) + if (gattService == null) { + Log.d(kTag, "notifyDeviceService: gattService is null") + LoadingDialogHub.dismiss() + "连接失败,设备不支持低功耗蓝牙".show(requireContext()) + return } - override fun onConnectSuccess(bluetoothGatt: BluetoothGatt?, status: Int) { + gattService.characteristics.forEach { + //获取到相应的服务UUID和特征UUID + val uuid = it.uuid + val properties = it.properties + if (properties and BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE > 0 || + properties and BluetoothGattCharacteristic.PROPERTY_WRITE > 0 + ) { + Log.d(kTag, "uuid可写: $uuid") + writeUuid = uuid.toString() + it.writeType = BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT + } + + if (properties and BluetoothGattCharacteristic.PROPERTY_NOTIFY > 0 || + properties and BluetoothGattCharacteristic.PROPERTY_INDICATE > 0 + ) { + Log.d(kTag, "uuid可通知: $uuid") + notifyUuid = uuid.toString() + } } - override fun onConnectFailure( - bluetoothGatt: BluetoothGatt?, exception: String?, status: Int - ) { - val message = weakReferenceHandler.obtainMessage() - message.what = LocaleConstant.CONNECT_FAILURE - weakReferenceHandler.sendMessage(message) - } + bleManager.notify(bleDevice, LocaleConstant.UUIDS[2], notifyUuid, + object : BleNotifyCallback() { + override fun onNotifySuccess() { + LoadingDialogHub.dismiss() + "连接成功".show(requireContext()) + binding.bluetoothButton.setImageResource(R.drawable.ic_bluetooth_disabled) + lifecycleScope.launch(Dispatchers.IO) { + delay(500) + sendCommand(LocaleConstant.ASK_DEV_CODE_COMMAND) + } + } - override fun onDisConnecting(bluetoothGatt: BluetoothGatt?) { + override fun onNotifyFailure(exception: BleException) { + connectedDevice = null + } - } + override fun onCharacteristicChanged(data: ByteArray) { + // 打开通知后,设备发过来的数据 + if (data.first() == 51.toByte() && data.size >= 14) { + //[51, 57, 50, 48, 50, 52, 48, 49, 48, 48, 48, 54, 32, 0] + lifecycleScope.launch(Dispatchers.IO) { + val builder = StringBuilder() + for (index in 0..11) { + builder.append(data[index].toInt().toChar()) + } + withContext(Dispatchers.Main) { + connectedDeviceCode = data.toDeviceCode() + binding.deviceStatusView.text = "设备编号: $connectedDeviceCode" + SaveKeyValues.putValue( + LocaleConstant.DEVICE_CODE, connectedDeviceCode + ) + } - override fun onDisConnectSuccess(bluetoothGatt: BluetoothGatt?, status: Int) { - val message = weakReferenceHandler.obtainMessage() - message.what = LocaleConstant.DISCONNECT_SUCCESS - message.obj = status - weakReferenceHandler.sendMessage(message) - } + delay(500) - override fun onServiceDiscoverySucceed(bluetoothGatt: BluetoothGatt?, status: Int) { - val message = weakReferenceHandler.obtainMessage() - message.what = LocaleConstant.CONNECT_SUCCESS - weakReferenceHandler.sendMessage(message) - } - - override fun onServiceDiscoveryFailed(bluetoothGatt: BluetoothGatt?, msg: String?) { - val message = weakReferenceHandler.obtainMessage() - message.what = LocaleConstant.CONNECT_FAILURE - weakReferenceHandler.sendMessage(message) - } - - override fun onReceiveMessage( - bluetoothGatt: BluetoothGatt?, characteristic: BluetoothGattCharacteristic? - ) { - val message = weakReferenceHandler.obtainMessage() - message.what = LocaleConstant.RECEIVE_SUCCESS - message.obj = characteristic!!.value - weakReferenceHandler.sendMessage(message) - } - - override fun onReceiveError(errorMsg: String?) { - val message = weakReferenceHandler.obtainMessage() - message.what = LocaleConstant.RECEIVE_FAILURE - weakReferenceHandler.sendMessage(message) - } - - override fun onWriteSuccess(bluetoothGatt: BluetoothGatt?, msg: ByteArray?) { - val message = weakReferenceHandler.obtainMessage() - message.what = LocaleConstant.SEND_SUCCESS - message.obj = msg - weakReferenceHandler.sendMessage(message) - } - - override fun onWriteFailure( - bluetoothGatt: BluetoothGatt?, msg: ByteArray?, errorMsg: String? - ) { - val message = weakReferenceHandler.obtainMessage() - message.what = LocaleConstant.SEND_FAILURE - message.obj = msg - weakReferenceHandler.sendMessage(message) - } - - override fun onReadRssi(bluetoothGatt: BluetoothGatt?, rssi: Int, status: Int) { - - } + //发送数据传送指令 + sendCommand(LocaleConstant.OPEN_TRANSFER_COMMAND) + } + } else if (data.first() == (-86).toByte() && data.size >= 14) { + //[-86, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 13, 10, -86, 0, 0, 0, 0, 1] + handleMethaneData(data) + } else { + //如果收到错误数据,就保存设备编号 + Log.d(kTag, data.contentToString()) + } + } + }) } /** @@ -602,7 +537,7 @@ override fun onDistanceSearched(distance: String) { inspectionViewModel.addInspection( id = newModel!!.id, - deviceCode = connectedDeviceName, + deviceCode = connectedDeviceCode, name = newModel!!.name, startTime = newModel!!.startTime, endTime = System.currentTimeMillis().timestampToCompleteDate(), @@ -635,7 +570,8 @@ binding.settingsValueView.text = dataModel.alarmValue.toString() binding.maxValueView.text = dataModel.maxPotency.toString() //判断是否需要报警 - val isOpen = SaveKeyValues.getValue(LocaleConstant.SINGLE_OPEN_WARNING, false) as Boolean + val isOpen = + SaveKeyValues.getValue(LocaleConstant.SINGLE_OPEN_WARNING, false) as Boolean if (isOpen) { if (dataModel.potency >= dataModel.alarmValue) { //当前值大于设置值,需要报警 @@ -722,6 +658,8 @@ super.onDestroyView() LocationHub.get.stopLocation() SaveKeyValues.removeKey(LocaleConstant.DEVICE_CODE) + bleManager.disconnect(connectedDevice) + bleManager.destroy() } override fun onDestroy() { diff --git a/app/build.gradle b/app/build.gradle index ae07527..5ad9504 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -116,4 +116,6 @@ implementation 'com.amap.api:3dmap:latest.integration' //高德地图搜索 implementation 'com.amap.api:search:8.1.0' + //蓝牙 + implementation 'com.github.Jasonchenlijian:FastBle:2.4.0' } \ No newline at end of file diff --git a/app/src/main/java/com/casic/birmm/inspect/fragment/HomePageFragment.kt b/app/src/main/java/com/casic/birmm/inspect/fragment/HomePageFragment.kt index da55df2..e5252c1 100644 --- a/app/src/main/java/com/casic/birmm/inspect/fragment/HomePageFragment.kt +++ b/app/src/main/java/com/casic/birmm/inspect/fragment/HomePageFragment.kt @@ -1,14 +1,10 @@ package com.casic.birmm.inspect.fragment -import android.annotation.SuppressLint -import android.bluetooth.BluetoothDevice import android.bluetooth.BluetoothGatt import android.bluetooth.BluetoothGattCharacteristic import android.content.Context import android.graphics.Color import android.os.Bundle -import android.os.Handler -import android.os.Message import android.os.Vibrator import android.util.Log import android.view.LayoutInflater @@ -16,6 +12,7 @@ import android.widget.LinearLayout import androidx.core.text.isDigitsOnly import androidx.lifecycle.ViewModelProvider +import androidx.lifecycle.lifecycleScope import com.amap.api.location.AMapLocation import com.amap.api.maps.AMap import com.amap.api.maps.AMapOptions @@ -25,6 +22,7 @@ import com.amap.api.maps.model.MyLocationStyle import com.amap.api.maps.model.PolylineOptions import com.casic.birmm.inspect.R +import com.casic.birmm.inspect.base.BaseApplication import com.casic.birmm.inspect.callback.OnDistanceSearchListener import com.casic.birmm.inspect.databinding.FragmentMapInspectBinding import com.casic.birmm.inspect.extensions.addAll @@ -41,35 +39,37 @@ import com.casic.birmm.inspect.vm.EventViewModel import com.casic.birmm.inspect.vm.InspectionViewModel import com.casic.birmm.inspect.vm.RouteViewModel +import com.clj.fastble.BleManager +import com.clj.fastble.callback.BleGattCallback +import com.clj.fastble.callback.BleNotifyCallback +import com.clj.fastble.callback.BleScanCallback +import com.clj.fastble.callback.BleWriteCallback +import com.clj.fastble.data.BleDevice +import com.clj.fastble.exception.BleException import com.pengxh.kt.lite.base.KotlinBaseFragment +import com.pengxh.kt.lite.extensions.convertColor import com.pengxh.kt.lite.extensions.getStatusBarHeight import com.pengxh.kt.lite.extensions.navigatePageTo import com.pengxh.kt.lite.extensions.show import com.pengxh.kt.lite.extensions.timestampToCompleteDate import com.pengxh.kt.lite.extensions.timestampToDate import com.pengxh.kt.lite.utils.SaveKeyValues -import com.pengxh.kt.lite.utils.WeakReferenceHandler -import com.pengxh.kt.lite.utils.ble.BLEManager -import com.pengxh.kt.lite.utils.ble.BlueToothBean -import com.pengxh.kt.lite.utils.ble.OnBleConnectListener -import com.pengxh.kt.lite.utils.ble.OnDeviceDiscoveredListener import com.pengxh.kt.lite.vm.LoadState import com.pengxh.kt.lite.widget.dialog.AlertControlDialog import com.pengxh.kt.lite.widget.dialog.AlertInputDialog import com.pengxh.kt.lite.widget.dialog.BottomActionSheet -import com.qmuiteam.qmui.widget.dialog.QMUIBottomSheet +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.delay +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext import java.util.LinkedList import java.util.UUID -@SuppressLint("MissingPermission", "SetTextI18n") -class HomePageFragment : KotlinBaseFragment(), Handler.Callback { - - companion object { - lateinit var weakReferenceHandler: WeakReferenceHandler - } +class HomePageFragment : KotlinBaseFragment() { private val kTag = "HomePageFragment" - private val blueToothBeans: MutableList = ArrayList()//搜索展示列表 + private val bleManager by lazy { BleManager.getInstance() } + private val bluetoothDevices by lazy { java.util.ArrayList() } private val latLngs = LinkedList() private lateinit var userName: String private lateinit var vibrator: Vibrator @@ -77,14 +77,13 @@ private lateinit var inspectionViewModel: InspectionViewModel private lateinit var routeViewModel: RouteViewModel private lateinit var aMap: AMap + private lateinit var writeUuid: String + private lateinit var notifyUuid: String private var newModel: NewInspectionModel? = null//新建巡检数据结构模型 - private var isBluetoothOn = true - private var curConnectState = false - private var currentDevice: BluetoothDevice? = null// 当前蓝牙设备 + private var connectedDevice: BleDevice? = null private var isGeneratingTask = false private var alarmCount = 0 - private var connectedDeviceName = "" - private var isDataCommandOpened = false + private var connectedDeviceCode = "" override fun initViewBinding( inflater: LayoutInflater, container: ViewGroup? @@ -103,16 +102,8 @@ override fun initOnCreate(savedInstanceState: Bundle?) { vibrator = requireContext().getSystemService(Context.VIBRATOR_SERVICE) as Vibrator - weakReferenceHandler = WeakReferenceHandler(this) - //BLE - if (BLEManager.initBLE(requireContext())) { - if (!BLEManager.isBluetoothEnable()) { - BLEManager.openBluetooth(false) - } - } else { - "该设备不支持低功耗蓝牙".show(requireContext()) - } + bleManager.enableLog(true).setSplitWriteNum(16).init(BaseApplication.get()) //Map binding.mapView.onCreate(savedInstanceState) @@ -196,27 +187,10 @@ } is LoadState.Success -> { - //断开连接 - if (curConnectState) { - BLEManager.disConnectDevice() - } - binding.deviceStatusView.text = "设备编号:未连接" - binding.inspectNameView.text = "" - binding.inspectTimeView.text = "" - binding.currentValueView.text = "--" - binding.settingsValueView.text = "--" - binding.maxValueView.text = "--" - //按钮状态 binding.stopInspectButton.isEnabled = false binding.addInspectionButton.isEnabled = true - //清除缓存 - blueToothBeans.clear() - latLngs.clear() - SaveKeyValues.removeKey(LocaleConstant.DEVICE_CODE) - isDataCommandOpened = false - LoadingDialogHub.dismiss() "巡检记录保存成功".show(requireContext()) } @@ -279,7 +253,7 @@ position.toString(), newModel!!.id, newModel!!.name, - connectedDeviceName, + connectedDeviceCode, latLngs.last.longitude.toString(), latLngs.last.latitude.toString() ) @@ -311,261 +285,222 @@ } //蓝牙按钮 - if (isBluetoothOn) { - binding.bluetoothButton.setImageResource(R.drawable.ic_bluetooth_enable) - binding.bluetoothButton.setOnClickListener { - if (curConnectState) { - AlertControlDialog.Builder() - .setContext(requireContext()) - .setTitle("提示") - .setMessage("确定断开设备吗?") - .setNegativeButton("取消") - .setPositiveButton("确定") - .setOnDialogButtonClickListener(object : - AlertControlDialog.OnDialogButtonClickListener { - override fun onCancelClick() { - - } - - override fun onConfirmClick() { - //断开连接 - BLEManager.disConnectDevice() - binding.currentValueView.text = "--" - binding.settingsValueView.text = "--" - binding.maxValueView.text = "--" - binding.deviceStatusView.text = "设备编号:未连接" - isDataCommandOpened = false - "设备已断开连接".show(requireContext()) - } - }).build().show() - } else { - //搜索蓝牙 - if (!BLEManager.isBluetoothEnable()) { - BLEManager.openBluetooth(false) - return@setOnClickListener - } - if (BLEManager.isDiscovery()) {//当前正在搜索设备... - BLEManager.stopDiscoverDevice() - } - LoadingDialogHub.show(requireActivity(), "设备搜索中...") - BLEManager.startDiscoverDevice(object : OnDeviceDiscoveredListener { - override fun onDeviceFound(blueToothBean: BlueToothBean?) { - val message = weakReferenceHandler.obtainMessage() - message.what = LocaleConstant.DISCOVERY_DEVICE - message.obj = blueToothBean - weakReferenceHandler.sendMessage(message) - } - - override fun onDiscoveryTimeout() { - val message = weakReferenceHandler.obtainMessage() - message.what = LocaleConstant.DISCOVERY_OUT_TIME - weakReferenceHandler.sendMessage(message) - } - }, 3 * 1000) - } + binding.bluetoothButton.setOnClickListener { + if (!bleManager.isBlueEnable) { + bleManager.enableBluetooth() } - } else { - binding.bluetoothButton.setImageResource(R.drawable.ic_bluetooth_disabled) - } - //重新发送指令按钮 - binding.refreshButton.setOnClickListener { - if (curConnectState) { - BLEManager.sendCommand(LocaleConstant.OPEN_TRANSFER_COMMAND) + if (bleManager.isConnected(connectedDevice)) { + AlertControlDialog.Builder() + .setContext(requireContext()) + .setTitle("提示") + .setMessage("确定断开设备吗?") + .setNegativeButton("取消") + .setPositiveButton("确定") + .setOnDialogButtonClickListener(object : + AlertControlDialog.OnDialogButtonClickListener { + override fun onCancelClick() { + + } + + override fun onConfirmClick() { + lifecycleScope.launch(Dispatchers.IO) { + sendCommand(LocaleConstant.CLOSE_TRANSFER_COMMAND) + + delay(500) + + //关闭数据传送指令 + bleManager.disconnect(connectedDevice) + withContext(Dispatchers.Main) { + binding.currentValueView.text = "--" + binding.settingsValueView.text = "--" + binding.maxValueView.text = "--" + binding.deviceStatusView.text = "设备编号:未连接" + "设备已断开连接".show(requireContext()) + + //清除缓存 + bluetoothDevices.clear() + latLngs.clear() + aMap.clear()//清除原来的路线 + SaveKeyValues.removeKey(LocaleConstant.DEVICE_CODE) + } + } + } + }).build().show() } else { - "请先连接设备".show(requireContext()) + //搜索蓝牙 + bleManager.scan(object : BleScanCallback() { + override fun onScanStarted(success: Boolean) { + LoadingDialogHub.show(requireActivity(), "设备搜索中...") + } + + override fun onScanning(bleDevice: BleDevice) { + + } + + override fun onScanFinished(scanResultList: List) { + LoadingDialogHub.dismiss() + + scanResultList.forEach { + if (!it.name.isNullOrBlank()) { + bluetoothDevices.add(it) + } + } + showScanResult() + } + }) + } + + //重新发送指令按钮 + binding.retrySendButton.setOnClickListener { + if (bleManager.isConnected(connectedDevice)) { + sendCommand(LocaleConstant.OPEN_TRANSFER_COMMAND) + } else { + "请先连接设备".show(requireContext()) + } } } } - override fun handleMessage(msg: Message): Boolean { - when (msg.what) { - LocaleConstant.BLUETOOTH_ON -> { - "蓝牙已开启".show(requireContext()) + private fun sendCommand(command: ByteArray) { + bleManager.write(connectedDevice, LocaleConstant.UUIDS[2], writeUuid, command, + object : BleWriteCallback() { + override fun onWriteSuccess(current: Int, total: Int, justWrite: ByteArray?) { + Log.d(kTag, "onWriteSuccess: 指令下发成功") + } + + override fun onWriteFailure(exception: BleException?) { + "指令下发失败".show(requireContext()) + } + }) + } + + private fun showScanResult() { + val array = java.util.ArrayList() + for (it in bluetoothDevices) { + array.add(it.name) + } + + BottomActionSheet.Builder() + .setContext(requireContext()) + .setActionItemTitle(array) + .setItemTextColor(R.color.themeColor.convertColor(requireContext())) + .setOnActionSheetListener(object : BottomActionSheet.OnActionSheetListener { + override fun onActionItemClick(position: Int) { + //连接点击的设备 + startConnectDevice(bluetoothDevices[position]) + } + }).build().show() + } + + private fun startConnectDevice(device: BleDevice) { + // 当前蓝牙设备 + bleManager.connect(device, object : BleGattCallback() { + override fun onStartConnect() { + LoadingDialogHub.show(requireActivity(), "设备连接中...") + } + + override fun onConnectFail(bleDevice: BleDevice, exception: BleException) { + LoadingDialogHub.dismiss() + } + + override fun onConnectSuccess( + bleDevice: BleDevice, + gatt: BluetoothGatt, + status: Int + ) { + connectedDevice = bleDevice + notifyDeviceService(bleDevice, gatt) + } + + override fun onDisConnected( + isActiveDisConnected: Boolean, bleDevice: BleDevice, + gatt: BluetoothGatt, status: Int + ) { + LoadingDialogHub.dismiss() + connectedDevice = null binding.bluetoothButton.setImageResource(R.drawable.ic_bluetooth_enable) - isBluetoothOn = true } - - LocaleConstant.BLUETOOTH_OFF -> { - "蓝牙已关闭".show(requireContext()) - binding.bluetoothButton.setImageResource(R.drawable.ic_bluetooth_disabled) - isBluetoothOn = false - } - - LocaleConstant.DISCOVERY_DEVICE -> { - val bean = msg.obj as BlueToothBean - if (blueToothBeans.size == 0) { - blueToothBeans.add(bean) - } else { - //0表示未添加到list的新设备,1表示已经扫描并添加到list的设备 - var judge = 0 - for (it in blueToothBeans) { - if (it.bluetoothDevice.address == bean.bluetoothDevice.address) { - judge = 1 - break - } - } - if (judge == 0) { - blueToothBeans.add(bean) - } - } - } - - LocaleConstant.DISCOVERY_OUT_TIME -> { - LoadingDialogHub.dismiss() - if (blueToothBeans.size == 0) { - "无可用设备,请确认设备是否已经开启".show(requireContext()) - } else { - val sheetBuilder = QMUIBottomSheet.BottomListSheetBuilder(requireContext()) - sheetBuilder.setTitle("请选择要连接的设备") - blueToothBeans.forEach { - if (it.bluetoothDevice.name.isNotEmpty()) { - sheetBuilder.addItem(it.bluetoothDevice.name) - } - } - sheetBuilder.setGravityCenter(true).setAddCancelBtn(true) - .setOnSheetItemClickListener { dialog, _, position, _ -> - dialog.dismiss() - //连接点击的设备 - startConnectDevice(blueToothBeans[position].bluetoothDevice) - }.build().show() - } - } - - LocaleConstant.CONNECT_SUCCESS -> { - LoadingDialogHub.dismiss() - curConnectState = true - BLEManager.sendCommand(LocaleConstant.ASK_DEV_CODE_COMMAND) - } - - LocaleConstant.CONNECT_FAILURE -> curConnectState = false - LocaleConstant.SEND_SUCCESS -> Log.d( - kTag, "发送成功-> ${(msg.obj as ByteArray).toList()}" - ) - - LocaleConstant.SEND_FAILURE -> Log.d( - kTag, "发送失败-> ${(msg.obj as ByteArray).toList()}" - ) - - LocaleConstant.RECEIVE_SUCCESS -> { - val bytes = msg.obj as ByteArray - if (bytes.first() == 51.toByte() && bytes.size >= 14) { - //解析deviceCode - //[51, 51, 50, 48, 48, 48, 48, 49, 48, 48, 48, 50, 13, 10] - binding.deviceStatusView.text = "设备编号: ${bytes.toDeviceCode()}" - SaveKeyValues.putValue(LocaleConstant.DEVICE_CODE, bytes.toDeviceCode()) - if (!isDataCommandOpened) { - BLEManager.sendCommand(LocaleConstant.OPEN_TRANSFER_COMMAND) - isDataCommandOpened = true - } - } else if (bytes.first() == (-86).toByte() && bytes.size == 14) { - handleMethaneData(bytes) - } else { - //如果收到错误数据,就保存设备编号 - binding.deviceStatusView.text = "设备编号: $connectedDeviceName" - SaveKeyValues.putValue(LocaleConstant.DEVICE_CODE, connectedDeviceName) - if (!isDataCommandOpened) { - BLEManager.sendCommand(LocaleConstant.OPEN_TRANSFER_COMMAND) - isDataCommandOpened = true - } - } - } - - LocaleConstant.RECEIVE_FAILURE -> Log.d(kTag, "接收失败-> ${msg.obj as String}") - LocaleConstant.DISCONNECT_SUCCESS -> curConnectState = false - } - return true + }) } - private fun startConnectDevice(device: BluetoothDevice) { - this.currentDevice = device - if (!curConnectState) { - connectedDeviceName = currentDevice!!.name - LoadingDialogHub.show(requireActivity(), "正在连接[${connectedDeviceName}]...") - BLEManager.connectBleDevice( - requireContext(), currentDevice!!, 10000, - LocaleConstant.SERVICE_UUID, - LocaleConstant.READ_CHARACTERISTIC_UUID, - LocaleConstant.WRITE_CHARACTERISTIC_UUID, - onBleConnectListener - ) - } - } - - private val onBleConnectListener = object : OnBleConnectListener { - override fun onConnecting(bluetoothGatt: BluetoothGatt?) { - + private fun notifyDeviceService(bleDevice: BleDevice, gatt: BluetoothGatt) { + val gattService = gatt.getService(UUID.fromString(LocaleConstant.UUIDS[2])) + if (gattService == null) { + Log.d(kTag, "notifyDeviceService: gattService is null") + LoadingDialogHub.dismiss() + "连接失败,设备不支持低功耗蓝牙".show(requireContext()) + return } - override fun onConnectSuccess(bluetoothGatt: BluetoothGatt?, status: Int) { + gattService.characteristics.forEach { + //获取到相应的服务UUID和特征UUID + val uuid = it.uuid + val properties = it.properties + if (properties and BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE > 0 || + properties and BluetoothGattCharacteristic.PROPERTY_WRITE > 0 + ) { + Log.d(kTag, "uuid可写: $uuid") + writeUuid = uuid.toString() + it.writeType = BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT + } + + if (properties and BluetoothGattCharacteristic.PROPERTY_NOTIFY > 0 || + properties and BluetoothGattCharacteristic.PROPERTY_INDICATE > 0 + ) { + Log.d(kTag, "uuid可通知: $uuid") + notifyUuid = uuid.toString() + } } - override fun onConnectFailure( - bluetoothGatt: BluetoothGatt?, exception: String?, status: Int - ) { - val message = weakReferenceHandler.obtainMessage() - message.what = LocaleConstant.CONNECT_FAILURE - weakReferenceHandler.sendMessage(message) - } + bleManager.notify(bleDevice, LocaleConstant.UUIDS[2], notifyUuid, + object : BleNotifyCallback() { + override fun onNotifySuccess() { + LoadingDialogHub.dismiss() + "连接成功".show(requireContext()) + binding.bluetoothButton.setImageResource(R.drawable.ic_bluetooth_disabled) + lifecycleScope.launch(Dispatchers.IO) { + delay(500) + sendCommand(LocaleConstant.ASK_DEV_CODE_COMMAND) + } + } - override fun onDisConnecting(bluetoothGatt: BluetoothGatt?) { + override fun onNotifyFailure(exception: BleException) { + connectedDevice = null + } - } + override fun onCharacteristicChanged(data: ByteArray) { + // 打开通知后,设备发过来的数据 + if (data.first() == 51.toByte() && data.size >= 14) { + //[51, 57, 50, 48, 50, 52, 48, 49, 48, 48, 48, 54, 32, 0] + lifecycleScope.launch(Dispatchers.IO) { + val builder = StringBuilder() + for (index in 0..11) { + builder.append(data[index].toInt().toChar()) + } + withContext(Dispatchers.Main) { + connectedDeviceCode = data.toDeviceCode() + binding.deviceStatusView.text = "设备编号: $connectedDeviceCode" + SaveKeyValues.putValue( + LocaleConstant.DEVICE_CODE, connectedDeviceCode + ) + } - override fun onDisConnectSuccess(bluetoothGatt: BluetoothGatt?, status: Int) { - val message = weakReferenceHandler.obtainMessage() - message.what = LocaleConstant.DISCONNECT_SUCCESS - message.obj = status - weakReferenceHandler.sendMessage(message) - } + delay(500) - override fun onServiceDiscoverySucceed(bluetoothGatt: BluetoothGatt?, status: Int) { - val message = weakReferenceHandler.obtainMessage() - message.what = LocaleConstant.CONNECT_SUCCESS - weakReferenceHandler.sendMessage(message) - } - - override fun onServiceDiscoveryFailed(bluetoothGatt: BluetoothGatt?, msg: String?) { - val message = weakReferenceHandler.obtainMessage() - message.what = LocaleConstant.CONNECT_FAILURE - weakReferenceHandler.sendMessage(message) - } - - override fun onReceiveMessage( - bluetoothGatt: BluetoothGatt?, characteristic: BluetoothGattCharacteristic? - ) { - val message = weakReferenceHandler.obtainMessage() - message.what = LocaleConstant.RECEIVE_SUCCESS - message.obj = characteristic!!.value - weakReferenceHandler.sendMessage(message) - } - - override fun onReceiveError(errorMsg: String?) { - val message = weakReferenceHandler.obtainMessage() - message.what = LocaleConstant.RECEIVE_FAILURE - weakReferenceHandler.sendMessage(message) - } - - override fun onWriteSuccess(bluetoothGatt: BluetoothGatt?, msg: ByteArray?) { - val message = weakReferenceHandler.obtainMessage() - message.what = LocaleConstant.SEND_SUCCESS - message.obj = msg - weakReferenceHandler.sendMessage(message) - } - - override fun onWriteFailure( - bluetoothGatt: BluetoothGatt?, msg: ByteArray?, errorMsg: String? - ) { - val message = weakReferenceHandler.obtainMessage() - message.what = LocaleConstant.SEND_FAILURE - message.obj = msg - weakReferenceHandler.sendMessage(message) - } - - override fun onReadRssi(bluetoothGatt: BluetoothGatt?, rssi: Int, status: Int) { - - } + //发送数据传送指令 + sendCommand(LocaleConstant.OPEN_TRANSFER_COMMAND) + } + } else if (data.first() == (-86).toByte() && data.size >= 14) { + //[-86, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 13, 10, -86, 0, 0, 0, 0, 1] + handleMethaneData(data) + } else { + //如果收到错误数据,就保存设备编号 + Log.d(kTag, data.contentToString()) + } + } + }) } /** @@ -602,7 +537,7 @@ override fun onDistanceSearched(distance: String) { inspectionViewModel.addInspection( id = newModel!!.id, - deviceCode = connectedDeviceName, + deviceCode = connectedDeviceCode, name = newModel!!.name, startTime = newModel!!.startTime, endTime = System.currentTimeMillis().timestampToCompleteDate(), @@ -635,7 +570,8 @@ binding.settingsValueView.text = dataModel.alarmValue.toString() binding.maxValueView.text = dataModel.maxPotency.toString() //判断是否需要报警 - val isOpen = SaveKeyValues.getValue(LocaleConstant.SINGLE_OPEN_WARNING, false) as Boolean + val isOpen = + SaveKeyValues.getValue(LocaleConstant.SINGLE_OPEN_WARNING, false) as Boolean if (isOpen) { if (dataModel.potency >= dataModel.alarmValue) { //当前值大于设置值,需要报警 @@ -722,6 +658,8 @@ super.onDestroyView() LocationHub.get.stopLocation() SaveKeyValues.removeKey(LocaleConstant.DEVICE_CODE) + bleManager.disconnect(connectedDevice) + bleManager.destroy() } override fun onDestroy() { diff --git a/app/src/main/java/com/casic/birmm/inspect/single/fragment/HomePageFragment.kt b/app/src/main/java/com/casic/birmm/inspect/single/fragment/HomePageFragment.kt index c3130e2..a5ba495 100644 --- a/app/src/main/java/com/casic/birmm/inspect/single/fragment/HomePageFragment.kt +++ b/app/src/main/java/com/casic/birmm/inspect/single/fragment/HomePageFragment.kt @@ -1,14 +1,10 @@ package com.casic.birmm.inspect.single.fragment -import android.annotation.SuppressLint -import android.bluetooth.BluetoothDevice import android.bluetooth.BluetoothGatt import android.bluetooth.BluetoothGattCharacteristic import android.content.Context import android.graphics.Color import android.os.Bundle -import android.os.Handler -import android.os.Message import android.os.Vibrator import android.util.Log import android.view.LayoutInflater @@ -25,6 +21,7 @@ import com.amap.api.maps.model.MyLocationStyle import com.amap.api.maps.model.PolylineOptions import com.casic.birmm.inspect.R +import com.casic.birmm.inspect.base.BaseApplication import com.casic.birmm.inspect.databinding.SingleFragmentMapInspectBinding import com.casic.birmm.inspect.extensions.addAll import com.casic.birmm.inspect.extensions.id @@ -32,44 +29,51 @@ import com.casic.birmm.inspect.extensions.toDeviceCode import com.casic.birmm.inspect.model.NewInspectionModel import com.casic.birmm.inspect.single.view.NewEventActivity -import com.casic.birmm.inspect.utils.* +import com.casic.birmm.inspect.utils.DataBaseManager +import com.casic.birmm.inspect.utils.LoadingDialogHub +import com.casic.birmm.inspect.utils.LocaleConstant +import com.casic.birmm.inspect.utils.LocationHub +import com.casic.birmm.inspect.utils.SoundPoolHelper +import com.clj.fastble.BleManager +import com.clj.fastble.callback.BleGattCallback +import com.clj.fastble.callback.BleNotifyCallback +import com.clj.fastble.callback.BleScanCallback +import com.clj.fastble.callback.BleWriteCallback +import com.clj.fastble.data.BleDevice +import com.clj.fastble.exception.BleException import com.pengxh.kt.lite.base.KotlinBaseFragment -import com.pengxh.kt.lite.extensions.* +import com.pengxh.kt.lite.extensions.convertColor +import com.pengxh.kt.lite.extensions.getStatusBarHeight +import com.pengxh.kt.lite.extensions.navigatePageTo +import com.pengxh.kt.lite.extensions.show +import com.pengxh.kt.lite.extensions.timestampToCompleteDate +import com.pengxh.kt.lite.extensions.timestampToDate import com.pengxh.kt.lite.utils.SaveKeyValues -import com.pengxh.kt.lite.utils.WeakReferenceHandler -import com.pengxh.kt.lite.utils.ble.BLEManager -import com.pengxh.kt.lite.utils.ble.BlueToothBean -import com.pengxh.kt.lite.utils.ble.OnBleConnectListener -import com.pengxh.kt.lite.utils.ble.OnDeviceDiscoveredListener import com.pengxh.kt.lite.widget.dialog.AlertControlDialog import com.pengxh.kt.lite.widget.dialog.AlertInputDialog import com.pengxh.kt.lite.widget.dialog.BottomActionSheet -import com.qmuiteam.qmui.widget.dialog.QMUIBottomSheet import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.delay import kotlinx.coroutines.launch import kotlinx.coroutines.withContext -import java.util.* +import java.util.LinkedList +import java.util.UUID -@SuppressLint("MissingPermission") -class HomePageFragment : KotlinBaseFragment(), Handler.Callback { - - companion object { - lateinit var weakReferenceHandler: WeakReferenceHandler - } +class HomePageFragment : KotlinBaseFragment() { private val kTag = "HomePageFragment" - private val blueToothBeans: MutableList = ArrayList()//搜索展示列表 + private val bleManager by lazy { BleManager.getInstance() } + private val bluetoothDevices by lazy { ArrayList() } private val latLngs = LinkedList() private lateinit var vibrator: Vibrator private lateinit var aMap: AMap + private lateinit var writeUuid: String + private lateinit var notifyUuid: String private var newModel: NewInspectionModel? = null//新建巡检数据结构模型 - private var isBluetoothOn = true - private var curConnectState = false - private var currentDevice: BluetoothDevice? = null// 当前蓝牙设备 + private var connectedDevice: BleDevice? = null private var isGeneratingTask = false private var alarmCount = 0 - private var connectedDeviceName = "" - private var isDataCommandOpened = false + private var connectedDeviceCode = "" override fun initViewBinding( inflater: LayoutInflater, container: ViewGroup? @@ -88,15 +92,8 @@ override fun initOnCreate(savedInstanceState: Bundle?) { vibrator = requireContext().getSystemService(Context.VIBRATOR_SERVICE) as Vibrator - weakReferenceHandler = WeakReferenceHandler(this) - if (BLEManager.initBLE(requireContext())) { - if (!BLEManager.isBluetoothEnable()) { - BLEManager.openBluetooth(false) - } - } else { - "该设备不支持低功耗蓝牙".show(requireContext()) - } + bleManager.enableLog(true).setSplitWriteNum(16).init(BaseApplication.get()) binding.mapView.onCreate(savedInstanceState) aMap = binding.mapView.map @@ -212,7 +209,7 @@ position.toString(), newModel!!.id, newModel!!.name, - connectedDeviceName, + connectedDeviceCode, latLngs.last.longitude.toString(), latLngs.last.latitude.toString() ) @@ -244,287 +241,216 @@ } //蓝牙按钮 - if (isBluetoothOn) { - binding.bluetoothButton.setImageResource(R.drawable.ic_bluetooth_enable) - binding.bluetoothButton.setOnClickListener { - if (curConnectState) { - AlertControlDialog.Builder() - .setContext(requireContext()) - .setTitle("提示") - .setMessage("确定断开设备吗?") - .setNegativeButton("取消") - .setPositiveButton("确定") - .setOnDialogButtonClickListener(object : - AlertControlDialog.OnDialogButtonClickListener { - override fun onCancelClick() { - - } - - override fun onConfirmClick() { - //断开连接 - BLEManager.disConnectDevice() - binding.currentValueView.text = "--" - binding.settingsValueView.text = "--" - binding.maxValueView.text = "--" - binding.deviceStatusView.text = "设备编号:未连接" - isDataCommandOpened = false - "设备已断开连接".show(requireContext()) - } - }).build().show() - } else { - //搜索蓝牙 - if (!BLEManager.isBluetoothEnable()) { - BLEManager.openBluetooth(false) - return@setOnClickListener - } - if (BLEManager.isDiscovery()) {//当前正在搜索设备... - BLEManager.stopDiscoverDevice() - } - LoadingDialogHub.show(requireActivity(), "设备搜索中...") - BLEManager.startDiscoverDevice(object : OnDeviceDiscoveredListener { - override fun onDeviceFound(blueToothBean: BlueToothBean?) { - val message = weakReferenceHandler.obtainMessage() - message.what = LocaleConstant.DISCOVERY_DEVICE - message.obj = blueToothBean - weakReferenceHandler.sendMessage(message) - } - - override fun onDiscoveryTimeout() { - val message = weakReferenceHandler.obtainMessage() - message.what = LocaleConstant.DISCOVERY_OUT_TIME - weakReferenceHandler.sendMessage(message) - } - }, 3 * 1000) - } + binding.bluetoothButton.setOnClickListener { + if (!bleManager.isBlueEnable) { + bleManager.enableBluetooth() } - } else { - binding.bluetoothButton.setImageResource(R.drawable.ic_bluetooth_disabled) + + if (bleManager.isConnected(connectedDevice)) { + AlertControlDialog.Builder() + .setContext(requireContext()) + .setTitle("提示") + .setMessage("确定断开设备吗?") + .setNegativeButton("取消") + .setPositiveButton("确定") + .setOnDialogButtonClickListener(object : + AlertControlDialog.OnDialogButtonClickListener { + override fun onCancelClick() { + + } + + override fun onConfirmClick() { + lifecycleScope.launch(Dispatchers.IO) { + sendCommand(LocaleConstant.CLOSE_TRANSFER_COMMAND) + + delay(500) + + //关闭数据传送指令 + bleManager.disconnect(connectedDevice) + withContext(Dispatchers.Main) { + binding.currentValueView.text = "--" + binding.settingsValueView.text = "--" + binding.maxValueView.text = "--" + binding.deviceStatusView.text = "设备编号:未连接" + "设备已断开连接".show(requireContext()) + + //清除缓存 + bluetoothDevices.clear() + latLngs.clear() + aMap.clear()//清除原来的路线 + SaveKeyValues.removeKey(LocaleConstant.DEVICE_CODE) + } + } + } + }).build().show() + } else { + //搜索蓝牙 + bleManager.scan(object : BleScanCallback() { + override fun onScanStarted(success: Boolean) { + LoadingDialogHub.show(requireActivity(), "设备搜索中...") + } + + override fun onScanning(bleDevice: BleDevice) { + + } + + override fun onScanFinished(scanResultList: List) { + LoadingDialogHub.dismiss() + + scanResultList.forEach { + if (!it.name.isNullOrBlank()) { + bluetoothDevices.add(it) + } + } + showScanResult() + } + }) + } } //重新发送指令按钮 - binding.refreshButton.setOnClickListener { - if (curConnectState) { - BLEManager.sendCommand(LocaleConstant.OPEN_TRANSFER_COMMAND) + binding.retrySendButton.setOnClickListener { + if (bleManager.isConnected(connectedDevice)) { + sendCommand(LocaleConstant.OPEN_TRANSFER_COMMAND) } else { "请先连接设备".show(requireContext()) } } } - override fun handleMessage(msg: Message): Boolean { - when (msg.what) { - LocaleConstant.BLUETOOTH_ON -> { - "蓝牙已开启".show(requireContext()) + private fun sendCommand(command: ByteArray) { + bleManager.write(connectedDevice, LocaleConstant.UUIDS[2], writeUuid, command, + object : BleWriteCallback() { + override fun onWriteSuccess(current: Int, total: Int, justWrite: ByteArray?) { + Log.d(kTag, "onWriteSuccess: 指令下发成功") + } + + override fun onWriteFailure(exception: BleException?) { + "指令下发失败".show(requireContext()) + } + }) + } + + private fun showScanResult() { + val array = ArrayList() + for (it in bluetoothDevices) { + array.add(it.name) + } + + BottomActionSheet.Builder() + .setContext(requireContext()) + .setActionItemTitle(array) + .setItemTextColor(R.color.themeColor.convertColor(requireContext())) + .setOnActionSheetListener(object : BottomActionSheet.OnActionSheetListener { + override fun onActionItemClick(position: Int) { + //连接点击的设备 + startConnectDevice(bluetoothDevices[position]) + } + }).build().show() + } + + private fun startConnectDevice(device: BleDevice) { + // 当前蓝牙设备 + bleManager.connect(device, object : BleGattCallback() { + override fun onStartConnect() { + LoadingDialogHub.show(requireActivity(), "设备连接中...") + } + + override fun onConnectFail(bleDevice: BleDevice, exception: BleException) { + LoadingDialogHub.dismiss() + } + + override fun onConnectSuccess(bleDevice: BleDevice, gatt: BluetoothGatt, status: Int) { + connectedDevice = bleDevice + notifyDeviceService(bleDevice, gatt) + } + + override fun onDisConnected( + isActiveDisConnected: Boolean, bleDevice: BleDevice, + gatt: BluetoothGatt, status: Int + ) { + LoadingDialogHub.dismiss() + connectedDevice = null binding.bluetoothButton.setImageResource(R.drawable.ic_bluetooth_enable) - isBluetoothOn = true } - - LocaleConstant.BLUETOOTH_OFF -> { - "蓝牙已关闭".show(requireContext()) - binding.bluetoothButton.setImageResource(R.drawable.ic_bluetooth_disabled) - isBluetoothOn = false - } - - LocaleConstant.DISCOVERY_DEVICE -> { - val bean = msg.obj as BlueToothBean - if (blueToothBeans.size == 0) { - blueToothBeans.add(bean) - } else { - //0表示未添加到list的新设备,1表示已经扫描并添加到list的设备 - var judge = 0 - for (it in blueToothBeans) { - if (it.bluetoothDevice.address == bean.bluetoothDevice.address) { - judge = 1 - break - } - } - if (judge == 0) { - blueToothBeans.add(bean) - } - } - } - - LocaleConstant.DISCOVERY_OUT_TIME -> { - LoadingDialogHub.dismiss() - if (blueToothBeans.size == 0) { - "无可用设备,请确认设备是否已经开启".show(requireContext()) - } else { - val sheetBuilder = QMUIBottomSheet.BottomListSheetBuilder(requireContext()) - sheetBuilder.setTitle("请选择要连接的设备") - blueToothBeans.forEach { - if (it.bluetoothDevice.name.isNotEmpty()) { - sheetBuilder.addItem(it.bluetoothDevice.name) - } - } - sheetBuilder.setGravityCenter(true).setAddCancelBtn(true) - .setOnSheetItemClickListener { dialog, _, position, _ -> - dialog.dismiss() - //连接点击的设备 - startConnectDevice(blueToothBeans[position].bluetoothDevice) - }.build().show() - } - } - - LocaleConstant.CONNECT_SUCCESS -> { - LoadingDialogHub.dismiss() - curConnectState = true - BLEManager.sendCommand(LocaleConstant.ASK_DEV_CODE_COMMAND) - } - - LocaleConstant.CONNECT_FAILURE -> curConnectState = false - LocaleConstant.SEND_SUCCESS -> Log.d( - kTag, "发送成功-> ${(msg.obj as ByteArray).toList()}" - ) - - LocaleConstant.SEND_FAILURE -> Log.d( - kTag, "发送失败-> ${(msg.obj as ByteArray).toList()}" - ) - - LocaleConstant.RECEIVE_SUCCESS -> { - val bytes = msg.obj as ByteArray - if (bytes.first() == 51.toByte() && bytes.size >= 14) { - //解析deviceCode - //[51, 51, 50, 48, 48, 48, 48, 49, 48, 48, 48, 50, 13, 10] - binding.deviceStatusView.text = "设备编号: ${bytes.toDeviceCode()}" - SaveKeyValues.putValue(LocaleConstant.DEVICE_CODE, bytes.toDeviceCode()) - if (!isDataCommandOpened) { - BLEManager.sendCommand(LocaleConstant.OPEN_TRANSFER_COMMAND) - isDataCommandOpened = true - } - } else if (bytes.first() == (-86).toByte() && bytes.size == 14) { - handleMethaneData(bytes) - } else { - //如果收到错误数据,就保存设备编号 - binding.deviceStatusView.text = "设备编号: $connectedDeviceName" - SaveKeyValues.putValue(LocaleConstant.DEVICE_CODE, connectedDeviceName) - if (!isDataCommandOpened) { - BLEManager.sendCommand(LocaleConstant.OPEN_TRANSFER_COMMAND) - isDataCommandOpened = true - } - } - } - - LocaleConstant.RECEIVE_FAILURE -> Log.d(kTag, "接收失败-> ${msg.obj as String}") - LocaleConstant.DISCONNECT_SUCCESS -> curConnectState = false - LocaleConstant.STOP_TASK -> { - //断开连接 - if (curConnectState) { - BLEManager.disConnectDevice() - } - binding.deviceStatusView.text = "设备编号:未连接" - binding.inspectNameView.text = "" - binding.inspectTimeView.text = "" - binding.currentValueView.text = "--" - binding.settingsValueView.text = "--" - binding.maxValueView.text = "--" - - //按钮状态 - binding.stopInspectButton.isEnabled = false - binding.addInspectionButton.isEnabled = true - - //清除缓存 - blueToothBeans.clear() - latLngs.clear() - aMap.clear()//清除原来的路线 - SaveKeyValues.removeKey(LocaleConstant.DEVICE_CODE) - isDataCommandOpened = false - - LoadingDialogHub.dismiss() - "巡检记录保存成功".show(requireContext()) - } - } - return true + }) } - private fun startConnectDevice(device: BluetoothDevice) { - this.currentDevice = device - if (!curConnectState) { - connectedDeviceName = currentDevice!!.name - LoadingDialogHub.show(requireActivity(), "正在连接[${connectedDeviceName}]...") - BLEManager.connectBleDevice( - requireContext(), currentDevice!!, 10000, - LocaleConstant.SERVICE_UUID, - LocaleConstant.READ_CHARACTERISTIC_UUID, - LocaleConstant.WRITE_CHARACTERISTIC_UUID, - onBleConnectListener - ) - } - } - - private val onBleConnectListener = object : OnBleConnectListener { - override fun onConnecting(bluetoothGatt: BluetoothGatt?) { - + private fun notifyDeviceService(bleDevice: BleDevice, gatt: BluetoothGatt) { + val gattService = gatt.getService(UUID.fromString(LocaleConstant.UUIDS[2])) + if (gattService == null) { + Log.d(kTag, "notifyDeviceService: gattService is null") + LoadingDialogHub.dismiss() + "连接失败,设备不支持低功耗蓝牙".show(requireContext()) + return } - override fun onConnectSuccess(bluetoothGatt: BluetoothGatt?, status: Int) { + gattService.characteristics.forEach { + //获取到相应的服务UUID和特征UUID + val uuid = it.uuid + val properties = it.properties + if (properties and BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE > 0 || + properties and BluetoothGattCharacteristic.PROPERTY_WRITE > 0 + ) { + Log.d(kTag, "uuid可写: $uuid") + writeUuid = uuid.toString() + it.writeType = BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT + } + + if (properties and BluetoothGattCharacteristic.PROPERTY_NOTIFY > 0 || + properties and BluetoothGattCharacteristic.PROPERTY_INDICATE > 0 + ) { + Log.d(kTag, "uuid可通知: $uuid") + notifyUuid = uuid.toString() + } } - override fun onConnectFailure( - bluetoothGatt: BluetoothGatt?, exception: String?, status: Int - ) { - val message = weakReferenceHandler.obtainMessage() - message.what = LocaleConstant.CONNECT_FAILURE - weakReferenceHandler.sendMessage(message) - } + bleManager.notify(bleDevice, LocaleConstant.UUIDS[2], notifyUuid, + object : BleNotifyCallback() { + override fun onNotifySuccess() { + LoadingDialogHub.dismiss() + "连接成功".show(requireContext()) + binding.bluetoothButton.setImageResource(R.drawable.ic_bluetooth_disabled) + lifecycleScope.launch(Dispatchers.IO) { + delay(500) + sendCommand(LocaleConstant.ASK_DEV_CODE_COMMAND) + } + } - override fun onDisConnecting(bluetoothGatt: BluetoothGatt?) { + override fun onNotifyFailure(exception: BleException) { + connectedDevice = null + } - } + override fun onCharacteristicChanged(data: ByteArray) { + // 打开通知后,设备发过来的数据 + Log.d(kTag, data.contentToString()) + if (data.first() == 51.toByte() && data.size >= 14) { + //[51, 57, 50, 48, 50, 52, 48, 49, 48, 48, 48, 54, 32, 0] + lifecycleScope.launch(Dispatchers.IO) { + val builder = StringBuilder() + for (index in 0..11) { + builder.append(data[index].toInt().toChar()) + } + withContext(Dispatchers.Main) { + connectedDeviceCode = data.toDeviceCode() + binding.deviceStatusView.text = "设备编号: $connectedDeviceCode" + SaveKeyValues.putValue( + LocaleConstant.DEVICE_CODE, connectedDeviceCode + ) + } - override fun onDisConnectSuccess(bluetoothGatt: BluetoothGatt?, status: Int) { - val message = weakReferenceHandler.obtainMessage() - message.what = LocaleConstant.DISCONNECT_SUCCESS - message.obj = status - weakReferenceHandler.sendMessage(message) - } + delay(500) - override fun onServiceDiscoverySucceed(bluetoothGatt: BluetoothGatt?, status: Int) { - val message = weakReferenceHandler.obtainMessage() - message.what = LocaleConstant.CONNECT_SUCCESS - weakReferenceHandler.sendMessage(message) - } - - override fun onServiceDiscoveryFailed(bluetoothGatt: BluetoothGatt?, msg: String?) { - val message = weakReferenceHandler.obtainMessage() - message.what = LocaleConstant.CONNECT_FAILURE - weakReferenceHandler.sendMessage(message) - } - - override fun onReceiveMessage( - bluetoothGatt: BluetoothGatt?, characteristic: BluetoothGattCharacteristic? - ) { - val message = weakReferenceHandler.obtainMessage() - message.what = LocaleConstant.RECEIVE_SUCCESS - message.obj = characteristic!!.value - weakReferenceHandler.sendMessage(message) - } - - override fun onReceiveError(errorMsg: String?) { - val message = weakReferenceHandler.obtainMessage() - message.what = LocaleConstant.RECEIVE_FAILURE - weakReferenceHandler.sendMessage(message) - } - - override fun onWriteSuccess(bluetoothGatt: BluetoothGatt?, msg: ByteArray?) { - val message = weakReferenceHandler.obtainMessage() - message.what = LocaleConstant.SEND_SUCCESS - message.obj = msg - weakReferenceHandler.sendMessage(message) - } - - override fun onWriteFailure( - bluetoothGatt: BluetoothGatt?, msg: ByteArray?, errorMsg: String? - ) { - val message = weakReferenceHandler.obtainMessage() - message.what = LocaleConstant.SEND_FAILURE - message.obj = msg - weakReferenceHandler.sendMessage(message) - } - - override fun onReadRssi(bluetoothGatt: BluetoothGatt?, rssi: Int, status: Int) { - - } + //发送数据传送指令 + sendCommand(LocaleConstant.OPEN_TRANSFER_COMMAND) + } + } else if (data.first() == (-86).toByte() && data.size >= 14) { + //[-86, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 13, 10, -86, 0, 0, 0, 0, 1] + handleMethaneData(data) + } + } + }) } /** @@ -554,22 +480,28 @@ DataBaseManager.get.addInspectionRoute(newModel!!.id, latLngs[i]) } + + DataBaseManager.get.addInspection( + inspectionId = newModel!!.id, + deviceCode = connectedDeviceCode, + inspectionName = newModel!!.name, + startTime = newModel!!.startTime, + endTime = System.currentTimeMillis().timestampToCompleteDate(), + date = newModel!!.date, + startLng = latLngs.first.longitude, + startLat = latLngs.first.latitude, + newModel!!.startAddress, + endLng = latLngs.last.longitude, + endLat = latLngs.last.latitude, + newModel!!.endAddress, + routes = newModel!!.routes + ) } - DataBaseManager.get.addInspection( - inspectionId = newModel!!.id, - deviceCode = connectedDeviceName, - inspectionName = newModel!!.name, - startTime = newModel!!.startTime, - endTime = System.currentTimeMillis().timestampToCompleteDate(), - date = newModel!!.date, - startLng = latLngs.first.longitude, - startLat = latLngs.first.latitude, - newModel!!.startAddress, - endLng = latLngs.last.longitude, - endLat = latLngs.last.latitude, - newModel!!.endAddress, - routes = newModel!!.routes - ) + LoadingDialogHub.dismiss() + + //按钮状态 + binding.stopInspectButton.isEnabled = false + binding.addInspectionButton.isEnabled = true } } @@ -626,7 +558,7 @@ DataBaseManager.get.addEvent( taskId = UUID.randomUUID().toString(), inspectionId = newModel!!.id, - deviceCode = connectedDeviceName, + deviceCode = connectedDeviceCode, eventTitle = newModel!!.name, createTime = System.currentTimeMillis().timestampToCompleteDate(), type = "报警事件", @@ -679,6 +611,8 @@ super.onDestroyView() LocationHub.get.stopLocation() SaveKeyValues.removeKey(LocaleConstant.DEVICE_CODE) + bleManager.disconnect(connectedDevice) + bleManager.destroy() } override fun onDestroy() { diff --git a/app/build.gradle b/app/build.gradle index ae07527..5ad9504 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -116,4 +116,6 @@ implementation 'com.amap.api:3dmap:latest.integration' //高德地图搜索 implementation 'com.amap.api:search:8.1.0' + //蓝牙 + implementation 'com.github.Jasonchenlijian:FastBle:2.4.0' } \ No newline at end of file diff --git a/app/src/main/java/com/casic/birmm/inspect/fragment/HomePageFragment.kt b/app/src/main/java/com/casic/birmm/inspect/fragment/HomePageFragment.kt index da55df2..e5252c1 100644 --- a/app/src/main/java/com/casic/birmm/inspect/fragment/HomePageFragment.kt +++ b/app/src/main/java/com/casic/birmm/inspect/fragment/HomePageFragment.kt @@ -1,14 +1,10 @@ package com.casic.birmm.inspect.fragment -import android.annotation.SuppressLint -import android.bluetooth.BluetoothDevice import android.bluetooth.BluetoothGatt import android.bluetooth.BluetoothGattCharacteristic import android.content.Context import android.graphics.Color import android.os.Bundle -import android.os.Handler -import android.os.Message import android.os.Vibrator import android.util.Log import android.view.LayoutInflater @@ -16,6 +12,7 @@ import android.widget.LinearLayout import androidx.core.text.isDigitsOnly import androidx.lifecycle.ViewModelProvider +import androidx.lifecycle.lifecycleScope import com.amap.api.location.AMapLocation import com.amap.api.maps.AMap import com.amap.api.maps.AMapOptions @@ -25,6 +22,7 @@ import com.amap.api.maps.model.MyLocationStyle import com.amap.api.maps.model.PolylineOptions import com.casic.birmm.inspect.R +import com.casic.birmm.inspect.base.BaseApplication import com.casic.birmm.inspect.callback.OnDistanceSearchListener import com.casic.birmm.inspect.databinding.FragmentMapInspectBinding import com.casic.birmm.inspect.extensions.addAll @@ -41,35 +39,37 @@ import com.casic.birmm.inspect.vm.EventViewModel import com.casic.birmm.inspect.vm.InspectionViewModel import com.casic.birmm.inspect.vm.RouteViewModel +import com.clj.fastble.BleManager +import com.clj.fastble.callback.BleGattCallback +import com.clj.fastble.callback.BleNotifyCallback +import com.clj.fastble.callback.BleScanCallback +import com.clj.fastble.callback.BleWriteCallback +import com.clj.fastble.data.BleDevice +import com.clj.fastble.exception.BleException import com.pengxh.kt.lite.base.KotlinBaseFragment +import com.pengxh.kt.lite.extensions.convertColor import com.pengxh.kt.lite.extensions.getStatusBarHeight import com.pengxh.kt.lite.extensions.navigatePageTo import com.pengxh.kt.lite.extensions.show import com.pengxh.kt.lite.extensions.timestampToCompleteDate import com.pengxh.kt.lite.extensions.timestampToDate import com.pengxh.kt.lite.utils.SaveKeyValues -import com.pengxh.kt.lite.utils.WeakReferenceHandler -import com.pengxh.kt.lite.utils.ble.BLEManager -import com.pengxh.kt.lite.utils.ble.BlueToothBean -import com.pengxh.kt.lite.utils.ble.OnBleConnectListener -import com.pengxh.kt.lite.utils.ble.OnDeviceDiscoveredListener import com.pengxh.kt.lite.vm.LoadState import com.pengxh.kt.lite.widget.dialog.AlertControlDialog import com.pengxh.kt.lite.widget.dialog.AlertInputDialog import com.pengxh.kt.lite.widget.dialog.BottomActionSheet -import com.qmuiteam.qmui.widget.dialog.QMUIBottomSheet +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.delay +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext import java.util.LinkedList import java.util.UUID -@SuppressLint("MissingPermission", "SetTextI18n") -class HomePageFragment : KotlinBaseFragment(), Handler.Callback { - - companion object { - lateinit var weakReferenceHandler: WeakReferenceHandler - } +class HomePageFragment : KotlinBaseFragment() { private val kTag = "HomePageFragment" - private val blueToothBeans: MutableList = ArrayList()//搜索展示列表 + private val bleManager by lazy { BleManager.getInstance() } + private val bluetoothDevices by lazy { java.util.ArrayList() } private val latLngs = LinkedList() private lateinit var userName: String private lateinit var vibrator: Vibrator @@ -77,14 +77,13 @@ private lateinit var inspectionViewModel: InspectionViewModel private lateinit var routeViewModel: RouteViewModel private lateinit var aMap: AMap + private lateinit var writeUuid: String + private lateinit var notifyUuid: String private var newModel: NewInspectionModel? = null//新建巡检数据结构模型 - private var isBluetoothOn = true - private var curConnectState = false - private var currentDevice: BluetoothDevice? = null// 当前蓝牙设备 + private var connectedDevice: BleDevice? = null private var isGeneratingTask = false private var alarmCount = 0 - private var connectedDeviceName = "" - private var isDataCommandOpened = false + private var connectedDeviceCode = "" override fun initViewBinding( inflater: LayoutInflater, container: ViewGroup? @@ -103,16 +102,8 @@ override fun initOnCreate(savedInstanceState: Bundle?) { vibrator = requireContext().getSystemService(Context.VIBRATOR_SERVICE) as Vibrator - weakReferenceHandler = WeakReferenceHandler(this) - //BLE - if (BLEManager.initBLE(requireContext())) { - if (!BLEManager.isBluetoothEnable()) { - BLEManager.openBluetooth(false) - } - } else { - "该设备不支持低功耗蓝牙".show(requireContext()) - } + bleManager.enableLog(true).setSplitWriteNum(16).init(BaseApplication.get()) //Map binding.mapView.onCreate(savedInstanceState) @@ -196,27 +187,10 @@ } is LoadState.Success -> { - //断开连接 - if (curConnectState) { - BLEManager.disConnectDevice() - } - binding.deviceStatusView.text = "设备编号:未连接" - binding.inspectNameView.text = "" - binding.inspectTimeView.text = "" - binding.currentValueView.text = "--" - binding.settingsValueView.text = "--" - binding.maxValueView.text = "--" - //按钮状态 binding.stopInspectButton.isEnabled = false binding.addInspectionButton.isEnabled = true - //清除缓存 - blueToothBeans.clear() - latLngs.clear() - SaveKeyValues.removeKey(LocaleConstant.DEVICE_CODE) - isDataCommandOpened = false - LoadingDialogHub.dismiss() "巡检记录保存成功".show(requireContext()) } @@ -279,7 +253,7 @@ position.toString(), newModel!!.id, newModel!!.name, - connectedDeviceName, + connectedDeviceCode, latLngs.last.longitude.toString(), latLngs.last.latitude.toString() ) @@ -311,261 +285,222 @@ } //蓝牙按钮 - if (isBluetoothOn) { - binding.bluetoothButton.setImageResource(R.drawable.ic_bluetooth_enable) - binding.bluetoothButton.setOnClickListener { - if (curConnectState) { - AlertControlDialog.Builder() - .setContext(requireContext()) - .setTitle("提示") - .setMessage("确定断开设备吗?") - .setNegativeButton("取消") - .setPositiveButton("确定") - .setOnDialogButtonClickListener(object : - AlertControlDialog.OnDialogButtonClickListener { - override fun onCancelClick() { - - } - - override fun onConfirmClick() { - //断开连接 - BLEManager.disConnectDevice() - binding.currentValueView.text = "--" - binding.settingsValueView.text = "--" - binding.maxValueView.text = "--" - binding.deviceStatusView.text = "设备编号:未连接" - isDataCommandOpened = false - "设备已断开连接".show(requireContext()) - } - }).build().show() - } else { - //搜索蓝牙 - if (!BLEManager.isBluetoothEnable()) { - BLEManager.openBluetooth(false) - return@setOnClickListener - } - if (BLEManager.isDiscovery()) {//当前正在搜索设备... - BLEManager.stopDiscoverDevice() - } - LoadingDialogHub.show(requireActivity(), "设备搜索中...") - BLEManager.startDiscoverDevice(object : OnDeviceDiscoveredListener { - override fun onDeviceFound(blueToothBean: BlueToothBean?) { - val message = weakReferenceHandler.obtainMessage() - message.what = LocaleConstant.DISCOVERY_DEVICE - message.obj = blueToothBean - weakReferenceHandler.sendMessage(message) - } - - override fun onDiscoveryTimeout() { - val message = weakReferenceHandler.obtainMessage() - message.what = LocaleConstant.DISCOVERY_OUT_TIME - weakReferenceHandler.sendMessage(message) - } - }, 3 * 1000) - } + binding.bluetoothButton.setOnClickListener { + if (!bleManager.isBlueEnable) { + bleManager.enableBluetooth() } - } else { - binding.bluetoothButton.setImageResource(R.drawable.ic_bluetooth_disabled) - } - //重新发送指令按钮 - binding.refreshButton.setOnClickListener { - if (curConnectState) { - BLEManager.sendCommand(LocaleConstant.OPEN_TRANSFER_COMMAND) + if (bleManager.isConnected(connectedDevice)) { + AlertControlDialog.Builder() + .setContext(requireContext()) + .setTitle("提示") + .setMessage("确定断开设备吗?") + .setNegativeButton("取消") + .setPositiveButton("确定") + .setOnDialogButtonClickListener(object : + AlertControlDialog.OnDialogButtonClickListener { + override fun onCancelClick() { + + } + + override fun onConfirmClick() { + lifecycleScope.launch(Dispatchers.IO) { + sendCommand(LocaleConstant.CLOSE_TRANSFER_COMMAND) + + delay(500) + + //关闭数据传送指令 + bleManager.disconnect(connectedDevice) + withContext(Dispatchers.Main) { + binding.currentValueView.text = "--" + binding.settingsValueView.text = "--" + binding.maxValueView.text = "--" + binding.deviceStatusView.text = "设备编号:未连接" + "设备已断开连接".show(requireContext()) + + //清除缓存 + bluetoothDevices.clear() + latLngs.clear() + aMap.clear()//清除原来的路线 + SaveKeyValues.removeKey(LocaleConstant.DEVICE_CODE) + } + } + } + }).build().show() } else { - "请先连接设备".show(requireContext()) + //搜索蓝牙 + bleManager.scan(object : BleScanCallback() { + override fun onScanStarted(success: Boolean) { + LoadingDialogHub.show(requireActivity(), "设备搜索中...") + } + + override fun onScanning(bleDevice: BleDevice) { + + } + + override fun onScanFinished(scanResultList: List) { + LoadingDialogHub.dismiss() + + scanResultList.forEach { + if (!it.name.isNullOrBlank()) { + bluetoothDevices.add(it) + } + } + showScanResult() + } + }) + } + + //重新发送指令按钮 + binding.retrySendButton.setOnClickListener { + if (bleManager.isConnected(connectedDevice)) { + sendCommand(LocaleConstant.OPEN_TRANSFER_COMMAND) + } else { + "请先连接设备".show(requireContext()) + } } } } - override fun handleMessage(msg: Message): Boolean { - when (msg.what) { - LocaleConstant.BLUETOOTH_ON -> { - "蓝牙已开启".show(requireContext()) + private fun sendCommand(command: ByteArray) { + bleManager.write(connectedDevice, LocaleConstant.UUIDS[2], writeUuid, command, + object : BleWriteCallback() { + override fun onWriteSuccess(current: Int, total: Int, justWrite: ByteArray?) { + Log.d(kTag, "onWriteSuccess: 指令下发成功") + } + + override fun onWriteFailure(exception: BleException?) { + "指令下发失败".show(requireContext()) + } + }) + } + + private fun showScanResult() { + val array = java.util.ArrayList() + for (it in bluetoothDevices) { + array.add(it.name) + } + + BottomActionSheet.Builder() + .setContext(requireContext()) + .setActionItemTitle(array) + .setItemTextColor(R.color.themeColor.convertColor(requireContext())) + .setOnActionSheetListener(object : BottomActionSheet.OnActionSheetListener { + override fun onActionItemClick(position: Int) { + //连接点击的设备 + startConnectDevice(bluetoothDevices[position]) + } + }).build().show() + } + + private fun startConnectDevice(device: BleDevice) { + // 当前蓝牙设备 + bleManager.connect(device, object : BleGattCallback() { + override fun onStartConnect() { + LoadingDialogHub.show(requireActivity(), "设备连接中...") + } + + override fun onConnectFail(bleDevice: BleDevice, exception: BleException) { + LoadingDialogHub.dismiss() + } + + override fun onConnectSuccess( + bleDevice: BleDevice, + gatt: BluetoothGatt, + status: Int + ) { + connectedDevice = bleDevice + notifyDeviceService(bleDevice, gatt) + } + + override fun onDisConnected( + isActiveDisConnected: Boolean, bleDevice: BleDevice, + gatt: BluetoothGatt, status: Int + ) { + LoadingDialogHub.dismiss() + connectedDevice = null binding.bluetoothButton.setImageResource(R.drawable.ic_bluetooth_enable) - isBluetoothOn = true } - - LocaleConstant.BLUETOOTH_OFF -> { - "蓝牙已关闭".show(requireContext()) - binding.bluetoothButton.setImageResource(R.drawable.ic_bluetooth_disabled) - isBluetoothOn = false - } - - LocaleConstant.DISCOVERY_DEVICE -> { - val bean = msg.obj as BlueToothBean - if (blueToothBeans.size == 0) { - blueToothBeans.add(bean) - } else { - //0表示未添加到list的新设备,1表示已经扫描并添加到list的设备 - var judge = 0 - for (it in blueToothBeans) { - if (it.bluetoothDevice.address == bean.bluetoothDevice.address) { - judge = 1 - break - } - } - if (judge == 0) { - blueToothBeans.add(bean) - } - } - } - - LocaleConstant.DISCOVERY_OUT_TIME -> { - LoadingDialogHub.dismiss() - if (blueToothBeans.size == 0) { - "无可用设备,请确认设备是否已经开启".show(requireContext()) - } else { - val sheetBuilder = QMUIBottomSheet.BottomListSheetBuilder(requireContext()) - sheetBuilder.setTitle("请选择要连接的设备") - blueToothBeans.forEach { - if (it.bluetoothDevice.name.isNotEmpty()) { - sheetBuilder.addItem(it.bluetoothDevice.name) - } - } - sheetBuilder.setGravityCenter(true).setAddCancelBtn(true) - .setOnSheetItemClickListener { dialog, _, position, _ -> - dialog.dismiss() - //连接点击的设备 - startConnectDevice(blueToothBeans[position].bluetoothDevice) - }.build().show() - } - } - - LocaleConstant.CONNECT_SUCCESS -> { - LoadingDialogHub.dismiss() - curConnectState = true - BLEManager.sendCommand(LocaleConstant.ASK_DEV_CODE_COMMAND) - } - - LocaleConstant.CONNECT_FAILURE -> curConnectState = false - LocaleConstant.SEND_SUCCESS -> Log.d( - kTag, "发送成功-> ${(msg.obj as ByteArray).toList()}" - ) - - LocaleConstant.SEND_FAILURE -> Log.d( - kTag, "发送失败-> ${(msg.obj as ByteArray).toList()}" - ) - - LocaleConstant.RECEIVE_SUCCESS -> { - val bytes = msg.obj as ByteArray - if (bytes.first() == 51.toByte() && bytes.size >= 14) { - //解析deviceCode - //[51, 51, 50, 48, 48, 48, 48, 49, 48, 48, 48, 50, 13, 10] - binding.deviceStatusView.text = "设备编号: ${bytes.toDeviceCode()}" - SaveKeyValues.putValue(LocaleConstant.DEVICE_CODE, bytes.toDeviceCode()) - if (!isDataCommandOpened) { - BLEManager.sendCommand(LocaleConstant.OPEN_TRANSFER_COMMAND) - isDataCommandOpened = true - } - } else if (bytes.first() == (-86).toByte() && bytes.size == 14) { - handleMethaneData(bytes) - } else { - //如果收到错误数据,就保存设备编号 - binding.deviceStatusView.text = "设备编号: $connectedDeviceName" - SaveKeyValues.putValue(LocaleConstant.DEVICE_CODE, connectedDeviceName) - if (!isDataCommandOpened) { - BLEManager.sendCommand(LocaleConstant.OPEN_TRANSFER_COMMAND) - isDataCommandOpened = true - } - } - } - - LocaleConstant.RECEIVE_FAILURE -> Log.d(kTag, "接收失败-> ${msg.obj as String}") - LocaleConstant.DISCONNECT_SUCCESS -> curConnectState = false - } - return true + }) } - private fun startConnectDevice(device: BluetoothDevice) { - this.currentDevice = device - if (!curConnectState) { - connectedDeviceName = currentDevice!!.name - LoadingDialogHub.show(requireActivity(), "正在连接[${connectedDeviceName}]...") - BLEManager.connectBleDevice( - requireContext(), currentDevice!!, 10000, - LocaleConstant.SERVICE_UUID, - LocaleConstant.READ_CHARACTERISTIC_UUID, - LocaleConstant.WRITE_CHARACTERISTIC_UUID, - onBleConnectListener - ) - } - } - - private val onBleConnectListener = object : OnBleConnectListener { - override fun onConnecting(bluetoothGatt: BluetoothGatt?) { - + private fun notifyDeviceService(bleDevice: BleDevice, gatt: BluetoothGatt) { + val gattService = gatt.getService(UUID.fromString(LocaleConstant.UUIDS[2])) + if (gattService == null) { + Log.d(kTag, "notifyDeviceService: gattService is null") + LoadingDialogHub.dismiss() + "连接失败,设备不支持低功耗蓝牙".show(requireContext()) + return } - override fun onConnectSuccess(bluetoothGatt: BluetoothGatt?, status: Int) { + gattService.characteristics.forEach { + //获取到相应的服务UUID和特征UUID + val uuid = it.uuid + val properties = it.properties + if (properties and BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE > 0 || + properties and BluetoothGattCharacteristic.PROPERTY_WRITE > 0 + ) { + Log.d(kTag, "uuid可写: $uuid") + writeUuid = uuid.toString() + it.writeType = BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT + } + + if (properties and BluetoothGattCharacteristic.PROPERTY_NOTIFY > 0 || + properties and BluetoothGattCharacteristic.PROPERTY_INDICATE > 0 + ) { + Log.d(kTag, "uuid可通知: $uuid") + notifyUuid = uuid.toString() + } } - override fun onConnectFailure( - bluetoothGatt: BluetoothGatt?, exception: String?, status: Int - ) { - val message = weakReferenceHandler.obtainMessage() - message.what = LocaleConstant.CONNECT_FAILURE - weakReferenceHandler.sendMessage(message) - } + bleManager.notify(bleDevice, LocaleConstant.UUIDS[2], notifyUuid, + object : BleNotifyCallback() { + override fun onNotifySuccess() { + LoadingDialogHub.dismiss() + "连接成功".show(requireContext()) + binding.bluetoothButton.setImageResource(R.drawable.ic_bluetooth_disabled) + lifecycleScope.launch(Dispatchers.IO) { + delay(500) + sendCommand(LocaleConstant.ASK_DEV_CODE_COMMAND) + } + } - override fun onDisConnecting(bluetoothGatt: BluetoothGatt?) { + override fun onNotifyFailure(exception: BleException) { + connectedDevice = null + } - } + override fun onCharacteristicChanged(data: ByteArray) { + // 打开通知后,设备发过来的数据 + if (data.first() == 51.toByte() && data.size >= 14) { + //[51, 57, 50, 48, 50, 52, 48, 49, 48, 48, 48, 54, 32, 0] + lifecycleScope.launch(Dispatchers.IO) { + val builder = StringBuilder() + for (index in 0..11) { + builder.append(data[index].toInt().toChar()) + } + withContext(Dispatchers.Main) { + connectedDeviceCode = data.toDeviceCode() + binding.deviceStatusView.text = "设备编号: $connectedDeviceCode" + SaveKeyValues.putValue( + LocaleConstant.DEVICE_CODE, connectedDeviceCode + ) + } - override fun onDisConnectSuccess(bluetoothGatt: BluetoothGatt?, status: Int) { - val message = weakReferenceHandler.obtainMessage() - message.what = LocaleConstant.DISCONNECT_SUCCESS - message.obj = status - weakReferenceHandler.sendMessage(message) - } + delay(500) - override fun onServiceDiscoverySucceed(bluetoothGatt: BluetoothGatt?, status: Int) { - val message = weakReferenceHandler.obtainMessage() - message.what = LocaleConstant.CONNECT_SUCCESS - weakReferenceHandler.sendMessage(message) - } - - override fun onServiceDiscoveryFailed(bluetoothGatt: BluetoothGatt?, msg: String?) { - val message = weakReferenceHandler.obtainMessage() - message.what = LocaleConstant.CONNECT_FAILURE - weakReferenceHandler.sendMessage(message) - } - - override fun onReceiveMessage( - bluetoothGatt: BluetoothGatt?, characteristic: BluetoothGattCharacteristic? - ) { - val message = weakReferenceHandler.obtainMessage() - message.what = LocaleConstant.RECEIVE_SUCCESS - message.obj = characteristic!!.value - weakReferenceHandler.sendMessage(message) - } - - override fun onReceiveError(errorMsg: String?) { - val message = weakReferenceHandler.obtainMessage() - message.what = LocaleConstant.RECEIVE_FAILURE - weakReferenceHandler.sendMessage(message) - } - - override fun onWriteSuccess(bluetoothGatt: BluetoothGatt?, msg: ByteArray?) { - val message = weakReferenceHandler.obtainMessage() - message.what = LocaleConstant.SEND_SUCCESS - message.obj = msg - weakReferenceHandler.sendMessage(message) - } - - override fun onWriteFailure( - bluetoothGatt: BluetoothGatt?, msg: ByteArray?, errorMsg: String? - ) { - val message = weakReferenceHandler.obtainMessage() - message.what = LocaleConstant.SEND_FAILURE - message.obj = msg - weakReferenceHandler.sendMessage(message) - } - - override fun onReadRssi(bluetoothGatt: BluetoothGatt?, rssi: Int, status: Int) { - - } + //发送数据传送指令 + sendCommand(LocaleConstant.OPEN_TRANSFER_COMMAND) + } + } else if (data.first() == (-86).toByte() && data.size >= 14) { + //[-86, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 13, 10, -86, 0, 0, 0, 0, 1] + handleMethaneData(data) + } else { + //如果收到错误数据,就保存设备编号 + Log.d(kTag, data.contentToString()) + } + } + }) } /** @@ -602,7 +537,7 @@ override fun onDistanceSearched(distance: String) { inspectionViewModel.addInspection( id = newModel!!.id, - deviceCode = connectedDeviceName, + deviceCode = connectedDeviceCode, name = newModel!!.name, startTime = newModel!!.startTime, endTime = System.currentTimeMillis().timestampToCompleteDate(), @@ -635,7 +570,8 @@ binding.settingsValueView.text = dataModel.alarmValue.toString() binding.maxValueView.text = dataModel.maxPotency.toString() //判断是否需要报警 - val isOpen = SaveKeyValues.getValue(LocaleConstant.SINGLE_OPEN_WARNING, false) as Boolean + val isOpen = + SaveKeyValues.getValue(LocaleConstant.SINGLE_OPEN_WARNING, false) as Boolean if (isOpen) { if (dataModel.potency >= dataModel.alarmValue) { //当前值大于设置值,需要报警 @@ -722,6 +658,8 @@ super.onDestroyView() LocationHub.get.stopLocation() SaveKeyValues.removeKey(LocaleConstant.DEVICE_CODE) + bleManager.disconnect(connectedDevice) + bleManager.destroy() } override fun onDestroy() { diff --git a/app/src/main/java/com/casic/birmm/inspect/single/fragment/HomePageFragment.kt b/app/src/main/java/com/casic/birmm/inspect/single/fragment/HomePageFragment.kt index c3130e2..a5ba495 100644 --- a/app/src/main/java/com/casic/birmm/inspect/single/fragment/HomePageFragment.kt +++ b/app/src/main/java/com/casic/birmm/inspect/single/fragment/HomePageFragment.kt @@ -1,14 +1,10 @@ package com.casic.birmm.inspect.single.fragment -import android.annotation.SuppressLint -import android.bluetooth.BluetoothDevice import android.bluetooth.BluetoothGatt import android.bluetooth.BluetoothGattCharacteristic import android.content.Context import android.graphics.Color import android.os.Bundle -import android.os.Handler -import android.os.Message import android.os.Vibrator import android.util.Log import android.view.LayoutInflater @@ -25,6 +21,7 @@ import com.amap.api.maps.model.MyLocationStyle import com.amap.api.maps.model.PolylineOptions import com.casic.birmm.inspect.R +import com.casic.birmm.inspect.base.BaseApplication import com.casic.birmm.inspect.databinding.SingleFragmentMapInspectBinding import com.casic.birmm.inspect.extensions.addAll import com.casic.birmm.inspect.extensions.id @@ -32,44 +29,51 @@ import com.casic.birmm.inspect.extensions.toDeviceCode import com.casic.birmm.inspect.model.NewInspectionModel import com.casic.birmm.inspect.single.view.NewEventActivity -import com.casic.birmm.inspect.utils.* +import com.casic.birmm.inspect.utils.DataBaseManager +import com.casic.birmm.inspect.utils.LoadingDialogHub +import com.casic.birmm.inspect.utils.LocaleConstant +import com.casic.birmm.inspect.utils.LocationHub +import com.casic.birmm.inspect.utils.SoundPoolHelper +import com.clj.fastble.BleManager +import com.clj.fastble.callback.BleGattCallback +import com.clj.fastble.callback.BleNotifyCallback +import com.clj.fastble.callback.BleScanCallback +import com.clj.fastble.callback.BleWriteCallback +import com.clj.fastble.data.BleDevice +import com.clj.fastble.exception.BleException import com.pengxh.kt.lite.base.KotlinBaseFragment -import com.pengxh.kt.lite.extensions.* +import com.pengxh.kt.lite.extensions.convertColor +import com.pengxh.kt.lite.extensions.getStatusBarHeight +import com.pengxh.kt.lite.extensions.navigatePageTo +import com.pengxh.kt.lite.extensions.show +import com.pengxh.kt.lite.extensions.timestampToCompleteDate +import com.pengxh.kt.lite.extensions.timestampToDate import com.pengxh.kt.lite.utils.SaveKeyValues -import com.pengxh.kt.lite.utils.WeakReferenceHandler -import com.pengxh.kt.lite.utils.ble.BLEManager -import com.pengxh.kt.lite.utils.ble.BlueToothBean -import com.pengxh.kt.lite.utils.ble.OnBleConnectListener -import com.pengxh.kt.lite.utils.ble.OnDeviceDiscoveredListener import com.pengxh.kt.lite.widget.dialog.AlertControlDialog import com.pengxh.kt.lite.widget.dialog.AlertInputDialog import com.pengxh.kt.lite.widget.dialog.BottomActionSheet -import com.qmuiteam.qmui.widget.dialog.QMUIBottomSheet import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.delay import kotlinx.coroutines.launch import kotlinx.coroutines.withContext -import java.util.* +import java.util.LinkedList +import java.util.UUID -@SuppressLint("MissingPermission") -class HomePageFragment : KotlinBaseFragment(), Handler.Callback { - - companion object { - lateinit var weakReferenceHandler: WeakReferenceHandler - } +class HomePageFragment : KotlinBaseFragment() { private val kTag = "HomePageFragment" - private val blueToothBeans: MutableList = ArrayList()//搜索展示列表 + private val bleManager by lazy { BleManager.getInstance() } + private val bluetoothDevices by lazy { ArrayList() } private val latLngs = LinkedList() private lateinit var vibrator: Vibrator private lateinit var aMap: AMap + private lateinit var writeUuid: String + private lateinit var notifyUuid: String private var newModel: NewInspectionModel? = null//新建巡检数据结构模型 - private var isBluetoothOn = true - private var curConnectState = false - private var currentDevice: BluetoothDevice? = null// 当前蓝牙设备 + private var connectedDevice: BleDevice? = null private var isGeneratingTask = false private var alarmCount = 0 - private var connectedDeviceName = "" - private var isDataCommandOpened = false + private var connectedDeviceCode = "" override fun initViewBinding( inflater: LayoutInflater, container: ViewGroup? @@ -88,15 +92,8 @@ override fun initOnCreate(savedInstanceState: Bundle?) { vibrator = requireContext().getSystemService(Context.VIBRATOR_SERVICE) as Vibrator - weakReferenceHandler = WeakReferenceHandler(this) - if (BLEManager.initBLE(requireContext())) { - if (!BLEManager.isBluetoothEnable()) { - BLEManager.openBluetooth(false) - } - } else { - "该设备不支持低功耗蓝牙".show(requireContext()) - } + bleManager.enableLog(true).setSplitWriteNum(16).init(BaseApplication.get()) binding.mapView.onCreate(savedInstanceState) aMap = binding.mapView.map @@ -212,7 +209,7 @@ position.toString(), newModel!!.id, newModel!!.name, - connectedDeviceName, + connectedDeviceCode, latLngs.last.longitude.toString(), latLngs.last.latitude.toString() ) @@ -244,287 +241,216 @@ } //蓝牙按钮 - if (isBluetoothOn) { - binding.bluetoothButton.setImageResource(R.drawable.ic_bluetooth_enable) - binding.bluetoothButton.setOnClickListener { - if (curConnectState) { - AlertControlDialog.Builder() - .setContext(requireContext()) - .setTitle("提示") - .setMessage("确定断开设备吗?") - .setNegativeButton("取消") - .setPositiveButton("确定") - .setOnDialogButtonClickListener(object : - AlertControlDialog.OnDialogButtonClickListener { - override fun onCancelClick() { - - } - - override fun onConfirmClick() { - //断开连接 - BLEManager.disConnectDevice() - binding.currentValueView.text = "--" - binding.settingsValueView.text = "--" - binding.maxValueView.text = "--" - binding.deviceStatusView.text = "设备编号:未连接" - isDataCommandOpened = false - "设备已断开连接".show(requireContext()) - } - }).build().show() - } else { - //搜索蓝牙 - if (!BLEManager.isBluetoothEnable()) { - BLEManager.openBluetooth(false) - return@setOnClickListener - } - if (BLEManager.isDiscovery()) {//当前正在搜索设备... - BLEManager.stopDiscoverDevice() - } - LoadingDialogHub.show(requireActivity(), "设备搜索中...") - BLEManager.startDiscoverDevice(object : OnDeviceDiscoveredListener { - override fun onDeviceFound(blueToothBean: BlueToothBean?) { - val message = weakReferenceHandler.obtainMessage() - message.what = LocaleConstant.DISCOVERY_DEVICE - message.obj = blueToothBean - weakReferenceHandler.sendMessage(message) - } - - override fun onDiscoveryTimeout() { - val message = weakReferenceHandler.obtainMessage() - message.what = LocaleConstant.DISCOVERY_OUT_TIME - weakReferenceHandler.sendMessage(message) - } - }, 3 * 1000) - } + binding.bluetoothButton.setOnClickListener { + if (!bleManager.isBlueEnable) { + bleManager.enableBluetooth() } - } else { - binding.bluetoothButton.setImageResource(R.drawable.ic_bluetooth_disabled) + + if (bleManager.isConnected(connectedDevice)) { + AlertControlDialog.Builder() + .setContext(requireContext()) + .setTitle("提示") + .setMessage("确定断开设备吗?") + .setNegativeButton("取消") + .setPositiveButton("确定") + .setOnDialogButtonClickListener(object : + AlertControlDialog.OnDialogButtonClickListener { + override fun onCancelClick() { + + } + + override fun onConfirmClick() { + lifecycleScope.launch(Dispatchers.IO) { + sendCommand(LocaleConstant.CLOSE_TRANSFER_COMMAND) + + delay(500) + + //关闭数据传送指令 + bleManager.disconnect(connectedDevice) + withContext(Dispatchers.Main) { + binding.currentValueView.text = "--" + binding.settingsValueView.text = "--" + binding.maxValueView.text = "--" + binding.deviceStatusView.text = "设备编号:未连接" + "设备已断开连接".show(requireContext()) + + //清除缓存 + bluetoothDevices.clear() + latLngs.clear() + aMap.clear()//清除原来的路线 + SaveKeyValues.removeKey(LocaleConstant.DEVICE_CODE) + } + } + } + }).build().show() + } else { + //搜索蓝牙 + bleManager.scan(object : BleScanCallback() { + override fun onScanStarted(success: Boolean) { + LoadingDialogHub.show(requireActivity(), "设备搜索中...") + } + + override fun onScanning(bleDevice: BleDevice) { + + } + + override fun onScanFinished(scanResultList: List) { + LoadingDialogHub.dismiss() + + scanResultList.forEach { + if (!it.name.isNullOrBlank()) { + bluetoothDevices.add(it) + } + } + showScanResult() + } + }) + } } //重新发送指令按钮 - binding.refreshButton.setOnClickListener { - if (curConnectState) { - BLEManager.sendCommand(LocaleConstant.OPEN_TRANSFER_COMMAND) + binding.retrySendButton.setOnClickListener { + if (bleManager.isConnected(connectedDevice)) { + sendCommand(LocaleConstant.OPEN_TRANSFER_COMMAND) } else { "请先连接设备".show(requireContext()) } } } - override fun handleMessage(msg: Message): Boolean { - when (msg.what) { - LocaleConstant.BLUETOOTH_ON -> { - "蓝牙已开启".show(requireContext()) + private fun sendCommand(command: ByteArray) { + bleManager.write(connectedDevice, LocaleConstant.UUIDS[2], writeUuid, command, + object : BleWriteCallback() { + override fun onWriteSuccess(current: Int, total: Int, justWrite: ByteArray?) { + Log.d(kTag, "onWriteSuccess: 指令下发成功") + } + + override fun onWriteFailure(exception: BleException?) { + "指令下发失败".show(requireContext()) + } + }) + } + + private fun showScanResult() { + val array = ArrayList() + for (it in bluetoothDevices) { + array.add(it.name) + } + + BottomActionSheet.Builder() + .setContext(requireContext()) + .setActionItemTitle(array) + .setItemTextColor(R.color.themeColor.convertColor(requireContext())) + .setOnActionSheetListener(object : BottomActionSheet.OnActionSheetListener { + override fun onActionItemClick(position: Int) { + //连接点击的设备 + startConnectDevice(bluetoothDevices[position]) + } + }).build().show() + } + + private fun startConnectDevice(device: BleDevice) { + // 当前蓝牙设备 + bleManager.connect(device, object : BleGattCallback() { + override fun onStartConnect() { + LoadingDialogHub.show(requireActivity(), "设备连接中...") + } + + override fun onConnectFail(bleDevice: BleDevice, exception: BleException) { + LoadingDialogHub.dismiss() + } + + override fun onConnectSuccess(bleDevice: BleDevice, gatt: BluetoothGatt, status: Int) { + connectedDevice = bleDevice + notifyDeviceService(bleDevice, gatt) + } + + override fun onDisConnected( + isActiveDisConnected: Boolean, bleDevice: BleDevice, + gatt: BluetoothGatt, status: Int + ) { + LoadingDialogHub.dismiss() + connectedDevice = null binding.bluetoothButton.setImageResource(R.drawable.ic_bluetooth_enable) - isBluetoothOn = true } - - LocaleConstant.BLUETOOTH_OFF -> { - "蓝牙已关闭".show(requireContext()) - binding.bluetoothButton.setImageResource(R.drawable.ic_bluetooth_disabled) - isBluetoothOn = false - } - - LocaleConstant.DISCOVERY_DEVICE -> { - val bean = msg.obj as BlueToothBean - if (blueToothBeans.size == 0) { - blueToothBeans.add(bean) - } else { - //0表示未添加到list的新设备,1表示已经扫描并添加到list的设备 - var judge = 0 - for (it in blueToothBeans) { - if (it.bluetoothDevice.address == bean.bluetoothDevice.address) { - judge = 1 - break - } - } - if (judge == 0) { - blueToothBeans.add(bean) - } - } - } - - LocaleConstant.DISCOVERY_OUT_TIME -> { - LoadingDialogHub.dismiss() - if (blueToothBeans.size == 0) { - "无可用设备,请确认设备是否已经开启".show(requireContext()) - } else { - val sheetBuilder = QMUIBottomSheet.BottomListSheetBuilder(requireContext()) - sheetBuilder.setTitle("请选择要连接的设备") - blueToothBeans.forEach { - if (it.bluetoothDevice.name.isNotEmpty()) { - sheetBuilder.addItem(it.bluetoothDevice.name) - } - } - sheetBuilder.setGravityCenter(true).setAddCancelBtn(true) - .setOnSheetItemClickListener { dialog, _, position, _ -> - dialog.dismiss() - //连接点击的设备 - startConnectDevice(blueToothBeans[position].bluetoothDevice) - }.build().show() - } - } - - LocaleConstant.CONNECT_SUCCESS -> { - LoadingDialogHub.dismiss() - curConnectState = true - BLEManager.sendCommand(LocaleConstant.ASK_DEV_CODE_COMMAND) - } - - LocaleConstant.CONNECT_FAILURE -> curConnectState = false - LocaleConstant.SEND_SUCCESS -> Log.d( - kTag, "发送成功-> ${(msg.obj as ByteArray).toList()}" - ) - - LocaleConstant.SEND_FAILURE -> Log.d( - kTag, "发送失败-> ${(msg.obj as ByteArray).toList()}" - ) - - LocaleConstant.RECEIVE_SUCCESS -> { - val bytes = msg.obj as ByteArray - if (bytes.first() == 51.toByte() && bytes.size >= 14) { - //解析deviceCode - //[51, 51, 50, 48, 48, 48, 48, 49, 48, 48, 48, 50, 13, 10] - binding.deviceStatusView.text = "设备编号: ${bytes.toDeviceCode()}" - SaveKeyValues.putValue(LocaleConstant.DEVICE_CODE, bytes.toDeviceCode()) - if (!isDataCommandOpened) { - BLEManager.sendCommand(LocaleConstant.OPEN_TRANSFER_COMMAND) - isDataCommandOpened = true - } - } else if (bytes.first() == (-86).toByte() && bytes.size == 14) { - handleMethaneData(bytes) - } else { - //如果收到错误数据,就保存设备编号 - binding.deviceStatusView.text = "设备编号: $connectedDeviceName" - SaveKeyValues.putValue(LocaleConstant.DEVICE_CODE, connectedDeviceName) - if (!isDataCommandOpened) { - BLEManager.sendCommand(LocaleConstant.OPEN_TRANSFER_COMMAND) - isDataCommandOpened = true - } - } - } - - LocaleConstant.RECEIVE_FAILURE -> Log.d(kTag, "接收失败-> ${msg.obj as String}") - LocaleConstant.DISCONNECT_SUCCESS -> curConnectState = false - LocaleConstant.STOP_TASK -> { - //断开连接 - if (curConnectState) { - BLEManager.disConnectDevice() - } - binding.deviceStatusView.text = "设备编号:未连接" - binding.inspectNameView.text = "" - binding.inspectTimeView.text = "" - binding.currentValueView.text = "--" - binding.settingsValueView.text = "--" - binding.maxValueView.text = "--" - - //按钮状态 - binding.stopInspectButton.isEnabled = false - binding.addInspectionButton.isEnabled = true - - //清除缓存 - blueToothBeans.clear() - latLngs.clear() - aMap.clear()//清除原来的路线 - SaveKeyValues.removeKey(LocaleConstant.DEVICE_CODE) - isDataCommandOpened = false - - LoadingDialogHub.dismiss() - "巡检记录保存成功".show(requireContext()) - } - } - return true + }) } - private fun startConnectDevice(device: BluetoothDevice) { - this.currentDevice = device - if (!curConnectState) { - connectedDeviceName = currentDevice!!.name - LoadingDialogHub.show(requireActivity(), "正在连接[${connectedDeviceName}]...") - BLEManager.connectBleDevice( - requireContext(), currentDevice!!, 10000, - LocaleConstant.SERVICE_UUID, - LocaleConstant.READ_CHARACTERISTIC_UUID, - LocaleConstant.WRITE_CHARACTERISTIC_UUID, - onBleConnectListener - ) - } - } - - private val onBleConnectListener = object : OnBleConnectListener { - override fun onConnecting(bluetoothGatt: BluetoothGatt?) { - + private fun notifyDeviceService(bleDevice: BleDevice, gatt: BluetoothGatt) { + val gattService = gatt.getService(UUID.fromString(LocaleConstant.UUIDS[2])) + if (gattService == null) { + Log.d(kTag, "notifyDeviceService: gattService is null") + LoadingDialogHub.dismiss() + "连接失败,设备不支持低功耗蓝牙".show(requireContext()) + return } - override fun onConnectSuccess(bluetoothGatt: BluetoothGatt?, status: Int) { + gattService.characteristics.forEach { + //获取到相应的服务UUID和特征UUID + val uuid = it.uuid + val properties = it.properties + if (properties and BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE > 0 || + properties and BluetoothGattCharacteristic.PROPERTY_WRITE > 0 + ) { + Log.d(kTag, "uuid可写: $uuid") + writeUuid = uuid.toString() + it.writeType = BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT + } + + if (properties and BluetoothGattCharacteristic.PROPERTY_NOTIFY > 0 || + properties and BluetoothGattCharacteristic.PROPERTY_INDICATE > 0 + ) { + Log.d(kTag, "uuid可通知: $uuid") + notifyUuid = uuid.toString() + } } - override fun onConnectFailure( - bluetoothGatt: BluetoothGatt?, exception: String?, status: Int - ) { - val message = weakReferenceHandler.obtainMessage() - message.what = LocaleConstant.CONNECT_FAILURE - weakReferenceHandler.sendMessage(message) - } + bleManager.notify(bleDevice, LocaleConstant.UUIDS[2], notifyUuid, + object : BleNotifyCallback() { + override fun onNotifySuccess() { + LoadingDialogHub.dismiss() + "连接成功".show(requireContext()) + binding.bluetoothButton.setImageResource(R.drawable.ic_bluetooth_disabled) + lifecycleScope.launch(Dispatchers.IO) { + delay(500) + sendCommand(LocaleConstant.ASK_DEV_CODE_COMMAND) + } + } - override fun onDisConnecting(bluetoothGatt: BluetoothGatt?) { + override fun onNotifyFailure(exception: BleException) { + connectedDevice = null + } - } + override fun onCharacteristicChanged(data: ByteArray) { + // 打开通知后,设备发过来的数据 + Log.d(kTag, data.contentToString()) + if (data.first() == 51.toByte() && data.size >= 14) { + //[51, 57, 50, 48, 50, 52, 48, 49, 48, 48, 48, 54, 32, 0] + lifecycleScope.launch(Dispatchers.IO) { + val builder = StringBuilder() + for (index in 0..11) { + builder.append(data[index].toInt().toChar()) + } + withContext(Dispatchers.Main) { + connectedDeviceCode = data.toDeviceCode() + binding.deviceStatusView.text = "设备编号: $connectedDeviceCode" + SaveKeyValues.putValue( + LocaleConstant.DEVICE_CODE, connectedDeviceCode + ) + } - override fun onDisConnectSuccess(bluetoothGatt: BluetoothGatt?, status: Int) { - val message = weakReferenceHandler.obtainMessage() - message.what = LocaleConstant.DISCONNECT_SUCCESS - message.obj = status - weakReferenceHandler.sendMessage(message) - } + delay(500) - override fun onServiceDiscoverySucceed(bluetoothGatt: BluetoothGatt?, status: Int) { - val message = weakReferenceHandler.obtainMessage() - message.what = LocaleConstant.CONNECT_SUCCESS - weakReferenceHandler.sendMessage(message) - } - - override fun onServiceDiscoveryFailed(bluetoothGatt: BluetoothGatt?, msg: String?) { - val message = weakReferenceHandler.obtainMessage() - message.what = LocaleConstant.CONNECT_FAILURE - weakReferenceHandler.sendMessage(message) - } - - override fun onReceiveMessage( - bluetoothGatt: BluetoothGatt?, characteristic: BluetoothGattCharacteristic? - ) { - val message = weakReferenceHandler.obtainMessage() - message.what = LocaleConstant.RECEIVE_SUCCESS - message.obj = characteristic!!.value - weakReferenceHandler.sendMessage(message) - } - - override fun onReceiveError(errorMsg: String?) { - val message = weakReferenceHandler.obtainMessage() - message.what = LocaleConstant.RECEIVE_FAILURE - weakReferenceHandler.sendMessage(message) - } - - override fun onWriteSuccess(bluetoothGatt: BluetoothGatt?, msg: ByteArray?) { - val message = weakReferenceHandler.obtainMessage() - message.what = LocaleConstant.SEND_SUCCESS - message.obj = msg - weakReferenceHandler.sendMessage(message) - } - - override fun onWriteFailure( - bluetoothGatt: BluetoothGatt?, msg: ByteArray?, errorMsg: String? - ) { - val message = weakReferenceHandler.obtainMessage() - message.what = LocaleConstant.SEND_FAILURE - message.obj = msg - weakReferenceHandler.sendMessage(message) - } - - override fun onReadRssi(bluetoothGatt: BluetoothGatt?, rssi: Int, status: Int) { - - } + //发送数据传送指令 + sendCommand(LocaleConstant.OPEN_TRANSFER_COMMAND) + } + } else if (data.first() == (-86).toByte() && data.size >= 14) { + //[-86, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 13, 10, -86, 0, 0, 0, 0, 1] + handleMethaneData(data) + } + } + }) } /** @@ -554,22 +480,28 @@ DataBaseManager.get.addInspectionRoute(newModel!!.id, latLngs[i]) } + + DataBaseManager.get.addInspection( + inspectionId = newModel!!.id, + deviceCode = connectedDeviceCode, + inspectionName = newModel!!.name, + startTime = newModel!!.startTime, + endTime = System.currentTimeMillis().timestampToCompleteDate(), + date = newModel!!.date, + startLng = latLngs.first.longitude, + startLat = latLngs.first.latitude, + newModel!!.startAddress, + endLng = latLngs.last.longitude, + endLat = latLngs.last.latitude, + newModel!!.endAddress, + routes = newModel!!.routes + ) } - DataBaseManager.get.addInspection( - inspectionId = newModel!!.id, - deviceCode = connectedDeviceName, - inspectionName = newModel!!.name, - startTime = newModel!!.startTime, - endTime = System.currentTimeMillis().timestampToCompleteDate(), - date = newModel!!.date, - startLng = latLngs.first.longitude, - startLat = latLngs.first.latitude, - newModel!!.startAddress, - endLng = latLngs.last.longitude, - endLat = latLngs.last.latitude, - newModel!!.endAddress, - routes = newModel!!.routes - ) + LoadingDialogHub.dismiss() + + //按钮状态 + binding.stopInspectButton.isEnabled = false + binding.addInspectionButton.isEnabled = true } } @@ -626,7 +558,7 @@ DataBaseManager.get.addEvent( taskId = UUID.randomUUID().toString(), inspectionId = newModel!!.id, - deviceCode = connectedDeviceName, + deviceCode = connectedDeviceCode, eventTitle = newModel!!.name, createTime = System.currentTimeMillis().timestampToCompleteDate(), type = "报警事件", @@ -679,6 +611,8 @@ super.onDestroyView() LocationHub.get.stopLocation() SaveKeyValues.removeKey(LocaleConstant.DEVICE_CODE) + bleManager.disconnect(connectedDevice) + bleManager.destroy() } override fun onDestroy() { diff --git a/app/src/main/java/com/casic/birmm/inspect/utils/DataBaseManager.kt b/app/src/main/java/com/casic/birmm/inspect/utils/DataBaseManager.kt index 5464803..3a2cac1 100644 --- a/app/src/main/java/com/casic/birmm/inspect/utils/DataBaseManager.kt +++ b/app/src/main/java/com/casic/birmm/inspect/utils/DataBaseManager.kt @@ -10,7 +10,6 @@ import com.casic.birmm.inspect.greendao.InspectionLocalBeanDao import com.casic.birmm.inspect.greendao.RouteLocalBeanDao import com.casic.birmm.inspect.greendao.TaskEventLocalBeanDao -import com.casic.birmm.inspect.single.fragment.HomePageFragment import com.casic.birmm.inspect.single.fragment.QueryEventFragment import com.casic.birmm.inspect.single.fragment.QueryInspectionFragment @@ -73,10 +72,6 @@ QueryInspectionFragment.weakReferenceHandler.sendEmptyMessage( LocaleConstant.LOAD_INSPECTION ) - - HomePageFragment.weakReferenceHandler.sendEmptyMessage( - LocaleConstant.STOP_TASK - ) } } }) diff --git a/app/build.gradle b/app/build.gradle index ae07527..5ad9504 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -116,4 +116,6 @@ implementation 'com.amap.api:3dmap:latest.integration' //高德地图搜索 implementation 'com.amap.api:search:8.1.0' + //蓝牙 + implementation 'com.github.Jasonchenlijian:FastBle:2.4.0' } \ No newline at end of file diff --git a/app/src/main/java/com/casic/birmm/inspect/fragment/HomePageFragment.kt b/app/src/main/java/com/casic/birmm/inspect/fragment/HomePageFragment.kt index da55df2..e5252c1 100644 --- a/app/src/main/java/com/casic/birmm/inspect/fragment/HomePageFragment.kt +++ b/app/src/main/java/com/casic/birmm/inspect/fragment/HomePageFragment.kt @@ -1,14 +1,10 @@ package com.casic.birmm.inspect.fragment -import android.annotation.SuppressLint -import android.bluetooth.BluetoothDevice import android.bluetooth.BluetoothGatt import android.bluetooth.BluetoothGattCharacteristic import android.content.Context import android.graphics.Color import android.os.Bundle -import android.os.Handler -import android.os.Message import android.os.Vibrator import android.util.Log import android.view.LayoutInflater @@ -16,6 +12,7 @@ import android.widget.LinearLayout import androidx.core.text.isDigitsOnly import androidx.lifecycle.ViewModelProvider +import androidx.lifecycle.lifecycleScope import com.amap.api.location.AMapLocation import com.amap.api.maps.AMap import com.amap.api.maps.AMapOptions @@ -25,6 +22,7 @@ import com.amap.api.maps.model.MyLocationStyle import com.amap.api.maps.model.PolylineOptions import com.casic.birmm.inspect.R +import com.casic.birmm.inspect.base.BaseApplication import com.casic.birmm.inspect.callback.OnDistanceSearchListener import com.casic.birmm.inspect.databinding.FragmentMapInspectBinding import com.casic.birmm.inspect.extensions.addAll @@ -41,35 +39,37 @@ import com.casic.birmm.inspect.vm.EventViewModel import com.casic.birmm.inspect.vm.InspectionViewModel import com.casic.birmm.inspect.vm.RouteViewModel +import com.clj.fastble.BleManager +import com.clj.fastble.callback.BleGattCallback +import com.clj.fastble.callback.BleNotifyCallback +import com.clj.fastble.callback.BleScanCallback +import com.clj.fastble.callback.BleWriteCallback +import com.clj.fastble.data.BleDevice +import com.clj.fastble.exception.BleException import com.pengxh.kt.lite.base.KotlinBaseFragment +import com.pengxh.kt.lite.extensions.convertColor import com.pengxh.kt.lite.extensions.getStatusBarHeight import com.pengxh.kt.lite.extensions.navigatePageTo import com.pengxh.kt.lite.extensions.show import com.pengxh.kt.lite.extensions.timestampToCompleteDate import com.pengxh.kt.lite.extensions.timestampToDate import com.pengxh.kt.lite.utils.SaveKeyValues -import com.pengxh.kt.lite.utils.WeakReferenceHandler -import com.pengxh.kt.lite.utils.ble.BLEManager -import com.pengxh.kt.lite.utils.ble.BlueToothBean -import com.pengxh.kt.lite.utils.ble.OnBleConnectListener -import com.pengxh.kt.lite.utils.ble.OnDeviceDiscoveredListener import com.pengxh.kt.lite.vm.LoadState import com.pengxh.kt.lite.widget.dialog.AlertControlDialog import com.pengxh.kt.lite.widget.dialog.AlertInputDialog import com.pengxh.kt.lite.widget.dialog.BottomActionSheet -import com.qmuiteam.qmui.widget.dialog.QMUIBottomSheet +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.delay +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext import java.util.LinkedList import java.util.UUID -@SuppressLint("MissingPermission", "SetTextI18n") -class HomePageFragment : KotlinBaseFragment(), Handler.Callback { - - companion object { - lateinit var weakReferenceHandler: WeakReferenceHandler - } +class HomePageFragment : KotlinBaseFragment() { private val kTag = "HomePageFragment" - private val blueToothBeans: MutableList = ArrayList()//搜索展示列表 + private val bleManager by lazy { BleManager.getInstance() } + private val bluetoothDevices by lazy { java.util.ArrayList() } private val latLngs = LinkedList() private lateinit var userName: String private lateinit var vibrator: Vibrator @@ -77,14 +77,13 @@ private lateinit var inspectionViewModel: InspectionViewModel private lateinit var routeViewModel: RouteViewModel private lateinit var aMap: AMap + private lateinit var writeUuid: String + private lateinit var notifyUuid: String private var newModel: NewInspectionModel? = null//新建巡检数据结构模型 - private var isBluetoothOn = true - private var curConnectState = false - private var currentDevice: BluetoothDevice? = null// 当前蓝牙设备 + private var connectedDevice: BleDevice? = null private var isGeneratingTask = false private var alarmCount = 0 - private var connectedDeviceName = "" - private var isDataCommandOpened = false + private var connectedDeviceCode = "" override fun initViewBinding( inflater: LayoutInflater, container: ViewGroup? @@ -103,16 +102,8 @@ override fun initOnCreate(savedInstanceState: Bundle?) { vibrator = requireContext().getSystemService(Context.VIBRATOR_SERVICE) as Vibrator - weakReferenceHandler = WeakReferenceHandler(this) - //BLE - if (BLEManager.initBLE(requireContext())) { - if (!BLEManager.isBluetoothEnable()) { - BLEManager.openBluetooth(false) - } - } else { - "该设备不支持低功耗蓝牙".show(requireContext()) - } + bleManager.enableLog(true).setSplitWriteNum(16).init(BaseApplication.get()) //Map binding.mapView.onCreate(savedInstanceState) @@ -196,27 +187,10 @@ } is LoadState.Success -> { - //断开连接 - if (curConnectState) { - BLEManager.disConnectDevice() - } - binding.deviceStatusView.text = "设备编号:未连接" - binding.inspectNameView.text = "" - binding.inspectTimeView.text = "" - binding.currentValueView.text = "--" - binding.settingsValueView.text = "--" - binding.maxValueView.text = "--" - //按钮状态 binding.stopInspectButton.isEnabled = false binding.addInspectionButton.isEnabled = true - //清除缓存 - blueToothBeans.clear() - latLngs.clear() - SaveKeyValues.removeKey(LocaleConstant.DEVICE_CODE) - isDataCommandOpened = false - LoadingDialogHub.dismiss() "巡检记录保存成功".show(requireContext()) } @@ -279,7 +253,7 @@ position.toString(), newModel!!.id, newModel!!.name, - connectedDeviceName, + connectedDeviceCode, latLngs.last.longitude.toString(), latLngs.last.latitude.toString() ) @@ -311,261 +285,222 @@ } //蓝牙按钮 - if (isBluetoothOn) { - binding.bluetoothButton.setImageResource(R.drawable.ic_bluetooth_enable) - binding.bluetoothButton.setOnClickListener { - if (curConnectState) { - AlertControlDialog.Builder() - .setContext(requireContext()) - .setTitle("提示") - .setMessage("确定断开设备吗?") - .setNegativeButton("取消") - .setPositiveButton("确定") - .setOnDialogButtonClickListener(object : - AlertControlDialog.OnDialogButtonClickListener { - override fun onCancelClick() { - - } - - override fun onConfirmClick() { - //断开连接 - BLEManager.disConnectDevice() - binding.currentValueView.text = "--" - binding.settingsValueView.text = "--" - binding.maxValueView.text = "--" - binding.deviceStatusView.text = "设备编号:未连接" - isDataCommandOpened = false - "设备已断开连接".show(requireContext()) - } - }).build().show() - } else { - //搜索蓝牙 - if (!BLEManager.isBluetoothEnable()) { - BLEManager.openBluetooth(false) - return@setOnClickListener - } - if (BLEManager.isDiscovery()) {//当前正在搜索设备... - BLEManager.stopDiscoverDevice() - } - LoadingDialogHub.show(requireActivity(), "设备搜索中...") - BLEManager.startDiscoverDevice(object : OnDeviceDiscoveredListener { - override fun onDeviceFound(blueToothBean: BlueToothBean?) { - val message = weakReferenceHandler.obtainMessage() - message.what = LocaleConstant.DISCOVERY_DEVICE - message.obj = blueToothBean - weakReferenceHandler.sendMessage(message) - } - - override fun onDiscoveryTimeout() { - val message = weakReferenceHandler.obtainMessage() - message.what = LocaleConstant.DISCOVERY_OUT_TIME - weakReferenceHandler.sendMessage(message) - } - }, 3 * 1000) - } + binding.bluetoothButton.setOnClickListener { + if (!bleManager.isBlueEnable) { + bleManager.enableBluetooth() } - } else { - binding.bluetoothButton.setImageResource(R.drawable.ic_bluetooth_disabled) - } - //重新发送指令按钮 - binding.refreshButton.setOnClickListener { - if (curConnectState) { - BLEManager.sendCommand(LocaleConstant.OPEN_TRANSFER_COMMAND) + if (bleManager.isConnected(connectedDevice)) { + AlertControlDialog.Builder() + .setContext(requireContext()) + .setTitle("提示") + .setMessage("确定断开设备吗?") + .setNegativeButton("取消") + .setPositiveButton("确定") + .setOnDialogButtonClickListener(object : + AlertControlDialog.OnDialogButtonClickListener { + override fun onCancelClick() { + + } + + override fun onConfirmClick() { + lifecycleScope.launch(Dispatchers.IO) { + sendCommand(LocaleConstant.CLOSE_TRANSFER_COMMAND) + + delay(500) + + //关闭数据传送指令 + bleManager.disconnect(connectedDevice) + withContext(Dispatchers.Main) { + binding.currentValueView.text = "--" + binding.settingsValueView.text = "--" + binding.maxValueView.text = "--" + binding.deviceStatusView.text = "设备编号:未连接" + "设备已断开连接".show(requireContext()) + + //清除缓存 + bluetoothDevices.clear() + latLngs.clear() + aMap.clear()//清除原来的路线 + SaveKeyValues.removeKey(LocaleConstant.DEVICE_CODE) + } + } + } + }).build().show() } else { - "请先连接设备".show(requireContext()) + //搜索蓝牙 + bleManager.scan(object : BleScanCallback() { + override fun onScanStarted(success: Boolean) { + LoadingDialogHub.show(requireActivity(), "设备搜索中...") + } + + override fun onScanning(bleDevice: BleDevice) { + + } + + override fun onScanFinished(scanResultList: List) { + LoadingDialogHub.dismiss() + + scanResultList.forEach { + if (!it.name.isNullOrBlank()) { + bluetoothDevices.add(it) + } + } + showScanResult() + } + }) + } + + //重新发送指令按钮 + binding.retrySendButton.setOnClickListener { + if (bleManager.isConnected(connectedDevice)) { + sendCommand(LocaleConstant.OPEN_TRANSFER_COMMAND) + } else { + "请先连接设备".show(requireContext()) + } } } } - override fun handleMessage(msg: Message): Boolean { - when (msg.what) { - LocaleConstant.BLUETOOTH_ON -> { - "蓝牙已开启".show(requireContext()) + private fun sendCommand(command: ByteArray) { + bleManager.write(connectedDevice, LocaleConstant.UUIDS[2], writeUuid, command, + object : BleWriteCallback() { + override fun onWriteSuccess(current: Int, total: Int, justWrite: ByteArray?) { + Log.d(kTag, "onWriteSuccess: 指令下发成功") + } + + override fun onWriteFailure(exception: BleException?) { + "指令下发失败".show(requireContext()) + } + }) + } + + private fun showScanResult() { + val array = java.util.ArrayList() + for (it in bluetoothDevices) { + array.add(it.name) + } + + BottomActionSheet.Builder() + .setContext(requireContext()) + .setActionItemTitle(array) + .setItemTextColor(R.color.themeColor.convertColor(requireContext())) + .setOnActionSheetListener(object : BottomActionSheet.OnActionSheetListener { + override fun onActionItemClick(position: Int) { + //连接点击的设备 + startConnectDevice(bluetoothDevices[position]) + } + }).build().show() + } + + private fun startConnectDevice(device: BleDevice) { + // 当前蓝牙设备 + bleManager.connect(device, object : BleGattCallback() { + override fun onStartConnect() { + LoadingDialogHub.show(requireActivity(), "设备连接中...") + } + + override fun onConnectFail(bleDevice: BleDevice, exception: BleException) { + LoadingDialogHub.dismiss() + } + + override fun onConnectSuccess( + bleDevice: BleDevice, + gatt: BluetoothGatt, + status: Int + ) { + connectedDevice = bleDevice + notifyDeviceService(bleDevice, gatt) + } + + override fun onDisConnected( + isActiveDisConnected: Boolean, bleDevice: BleDevice, + gatt: BluetoothGatt, status: Int + ) { + LoadingDialogHub.dismiss() + connectedDevice = null binding.bluetoothButton.setImageResource(R.drawable.ic_bluetooth_enable) - isBluetoothOn = true } - - LocaleConstant.BLUETOOTH_OFF -> { - "蓝牙已关闭".show(requireContext()) - binding.bluetoothButton.setImageResource(R.drawable.ic_bluetooth_disabled) - isBluetoothOn = false - } - - LocaleConstant.DISCOVERY_DEVICE -> { - val bean = msg.obj as BlueToothBean - if (blueToothBeans.size == 0) { - blueToothBeans.add(bean) - } else { - //0表示未添加到list的新设备,1表示已经扫描并添加到list的设备 - var judge = 0 - for (it in blueToothBeans) { - if (it.bluetoothDevice.address == bean.bluetoothDevice.address) { - judge = 1 - break - } - } - if (judge == 0) { - blueToothBeans.add(bean) - } - } - } - - LocaleConstant.DISCOVERY_OUT_TIME -> { - LoadingDialogHub.dismiss() - if (blueToothBeans.size == 0) { - "无可用设备,请确认设备是否已经开启".show(requireContext()) - } else { - val sheetBuilder = QMUIBottomSheet.BottomListSheetBuilder(requireContext()) - sheetBuilder.setTitle("请选择要连接的设备") - blueToothBeans.forEach { - if (it.bluetoothDevice.name.isNotEmpty()) { - sheetBuilder.addItem(it.bluetoothDevice.name) - } - } - sheetBuilder.setGravityCenter(true).setAddCancelBtn(true) - .setOnSheetItemClickListener { dialog, _, position, _ -> - dialog.dismiss() - //连接点击的设备 - startConnectDevice(blueToothBeans[position].bluetoothDevice) - }.build().show() - } - } - - LocaleConstant.CONNECT_SUCCESS -> { - LoadingDialogHub.dismiss() - curConnectState = true - BLEManager.sendCommand(LocaleConstant.ASK_DEV_CODE_COMMAND) - } - - LocaleConstant.CONNECT_FAILURE -> curConnectState = false - LocaleConstant.SEND_SUCCESS -> Log.d( - kTag, "发送成功-> ${(msg.obj as ByteArray).toList()}" - ) - - LocaleConstant.SEND_FAILURE -> Log.d( - kTag, "发送失败-> ${(msg.obj as ByteArray).toList()}" - ) - - LocaleConstant.RECEIVE_SUCCESS -> { - val bytes = msg.obj as ByteArray - if (bytes.first() == 51.toByte() && bytes.size >= 14) { - //解析deviceCode - //[51, 51, 50, 48, 48, 48, 48, 49, 48, 48, 48, 50, 13, 10] - binding.deviceStatusView.text = "设备编号: ${bytes.toDeviceCode()}" - SaveKeyValues.putValue(LocaleConstant.DEVICE_CODE, bytes.toDeviceCode()) - if (!isDataCommandOpened) { - BLEManager.sendCommand(LocaleConstant.OPEN_TRANSFER_COMMAND) - isDataCommandOpened = true - } - } else if (bytes.first() == (-86).toByte() && bytes.size == 14) { - handleMethaneData(bytes) - } else { - //如果收到错误数据,就保存设备编号 - binding.deviceStatusView.text = "设备编号: $connectedDeviceName" - SaveKeyValues.putValue(LocaleConstant.DEVICE_CODE, connectedDeviceName) - if (!isDataCommandOpened) { - BLEManager.sendCommand(LocaleConstant.OPEN_TRANSFER_COMMAND) - isDataCommandOpened = true - } - } - } - - LocaleConstant.RECEIVE_FAILURE -> Log.d(kTag, "接收失败-> ${msg.obj as String}") - LocaleConstant.DISCONNECT_SUCCESS -> curConnectState = false - } - return true + }) } - private fun startConnectDevice(device: BluetoothDevice) { - this.currentDevice = device - if (!curConnectState) { - connectedDeviceName = currentDevice!!.name - LoadingDialogHub.show(requireActivity(), "正在连接[${connectedDeviceName}]...") - BLEManager.connectBleDevice( - requireContext(), currentDevice!!, 10000, - LocaleConstant.SERVICE_UUID, - LocaleConstant.READ_CHARACTERISTIC_UUID, - LocaleConstant.WRITE_CHARACTERISTIC_UUID, - onBleConnectListener - ) - } - } - - private val onBleConnectListener = object : OnBleConnectListener { - override fun onConnecting(bluetoothGatt: BluetoothGatt?) { - + private fun notifyDeviceService(bleDevice: BleDevice, gatt: BluetoothGatt) { + val gattService = gatt.getService(UUID.fromString(LocaleConstant.UUIDS[2])) + if (gattService == null) { + Log.d(kTag, "notifyDeviceService: gattService is null") + LoadingDialogHub.dismiss() + "连接失败,设备不支持低功耗蓝牙".show(requireContext()) + return } - override fun onConnectSuccess(bluetoothGatt: BluetoothGatt?, status: Int) { + gattService.characteristics.forEach { + //获取到相应的服务UUID和特征UUID + val uuid = it.uuid + val properties = it.properties + if (properties and BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE > 0 || + properties and BluetoothGattCharacteristic.PROPERTY_WRITE > 0 + ) { + Log.d(kTag, "uuid可写: $uuid") + writeUuid = uuid.toString() + it.writeType = BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT + } + + if (properties and BluetoothGattCharacteristic.PROPERTY_NOTIFY > 0 || + properties and BluetoothGattCharacteristic.PROPERTY_INDICATE > 0 + ) { + Log.d(kTag, "uuid可通知: $uuid") + notifyUuid = uuid.toString() + } } - override fun onConnectFailure( - bluetoothGatt: BluetoothGatt?, exception: String?, status: Int - ) { - val message = weakReferenceHandler.obtainMessage() - message.what = LocaleConstant.CONNECT_FAILURE - weakReferenceHandler.sendMessage(message) - } + bleManager.notify(bleDevice, LocaleConstant.UUIDS[2], notifyUuid, + object : BleNotifyCallback() { + override fun onNotifySuccess() { + LoadingDialogHub.dismiss() + "连接成功".show(requireContext()) + binding.bluetoothButton.setImageResource(R.drawable.ic_bluetooth_disabled) + lifecycleScope.launch(Dispatchers.IO) { + delay(500) + sendCommand(LocaleConstant.ASK_DEV_CODE_COMMAND) + } + } - override fun onDisConnecting(bluetoothGatt: BluetoothGatt?) { + override fun onNotifyFailure(exception: BleException) { + connectedDevice = null + } - } + override fun onCharacteristicChanged(data: ByteArray) { + // 打开通知后,设备发过来的数据 + if (data.first() == 51.toByte() && data.size >= 14) { + //[51, 57, 50, 48, 50, 52, 48, 49, 48, 48, 48, 54, 32, 0] + lifecycleScope.launch(Dispatchers.IO) { + val builder = StringBuilder() + for (index in 0..11) { + builder.append(data[index].toInt().toChar()) + } + withContext(Dispatchers.Main) { + connectedDeviceCode = data.toDeviceCode() + binding.deviceStatusView.text = "设备编号: $connectedDeviceCode" + SaveKeyValues.putValue( + LocaleConstant.DEVICE_CODE, connectedDeviceCode + ) + } - override fun onDisConnectSuccess(bluetoothGatt: BluetoothGatt?, status: Int) { - val message = weakReferenceHandler.obtainMessage() - message.what = LocaleConstant.DISCONNECT_SUCCESS - message.obj = status - weakReferenceHandler.sendMessage(message) - } + delay(500) - override fun onServiceDiscoverySucceed(bluetoothGatt: BluetoothGatt?, status: Int) { - val message = weakReferenceHandler.obtainMessage() - message.what = LocaleConstant.CONNECT_SUCCESS - weakReferenceHandler.sendMessage(message) - } - - override fun onServiceDiscoveryFailed(bluetoothGatt: BluetoothGatt?, msg: String?) { - val message = weakReferenceHandler.obtainMessage() - message.what = LocaleConstant.CONNECT_FAILURE - weakReferenceHandler.sendMessage(message) - } - - override fun onReceiveMessage( - bluetoothGatt: BluetoothGatt?, characteristic: BluetoothGattCharacteristic? - ) { - val message = weakReferenceHandler.obtainMessage() - message.what = LocaleConstant.RECEIVE_SUCCESS - message.obj = characteristic!!.value - weakReferenceHandler.sendMessage(message) - } - - override fun onReceiveError(errorMsg: String?) { - val message = weakReferenceHandler.obtainMessage() - message.what = LocaleConstant.RECEIVE_FAILURE - weakReferenceHandler.sendMessage(message) - } - - override fun onWriteSuccess(bluetoothGatt: BluetoothGatt?, msg: ByteArray?) { - val message = weakReferenceHandler.obtainMessage() - message.what = LocaleConstant.SEND_SUCCESS - message.obj = msg - weakReferenceHandler.sendMessage(message) - } - - override fun onWriteFailure( - bluetoothGatt: BluetoothGatt?, msg: ByteArray?, errorMsg: String? - ) { - val message = weakReferenceHandler.obtainMessage() - message.what = LocaleConstant.SEND_FAILURE - message.obj = msg - weakReferenceHandler.sendMessage(message) - } - - override fun onReadRssi(bluetoothGatt: BluetoothGatt?, rssi: Int, status: Int) { - - } + //发送数据传送指令 + sendCommand(LocaleConstant.OPEN_TRANSFER_COMMAND) + } + } else if (data.first() == (-86).toByte() && data.size >= 14) { + //[-86, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 13, 10, -86, 0, 0, 0, 0, 1] + handleMethaneData(data) + } else { + //如果收到错误数据,就保存设备编号 + Log.d(kTag, data.contentToString()) + } + } + }) } /** @@ -602,7 +537,7 @@ override fun onDistanceSearched(distance: String) { inspectionViewModel.addInspection( id = newModel!!.id, - deviceCode = connectedDeviceName, + deviceCode = connectedDeviceCode, name = newModel!!.name, startTime = newModel!!.startTime, endTime = System.currentTimeMillis().timestampToCompleteDate(), @@ -635,7 +570,8 @@ binding.settingsValueView.text = dataModel.alarmValue.toString() binding.maxValueView.text = dataModel.maxPotency.toString() //判断是否需要报警 - val isOpen = SaveKeyValues.getValue(LocaleConstant.SINGLE_OPEN_WARNING, false) as Boolean + val isOpen = + SaveKeyValues.getValue(LocaleConstant.SINGLE_OPEN_WARNING, false) as Boolean if (isOpen) { if (dataModel.potency >= dataModel.alarmValue) { //当前值大于设置值,需要报警 @@ -722,6 +658,8 @@ super.onDestroyView() LocationHub.get.stopLocation() SaveKeyValues.removeKey(LocaleConstant.DEVICE_CODE) + bleManager.disconnect(connectedDevice) + bleManager.destroy() } override fun onDestroy() { diff --git a/app/src/main/java/com/casic/birmm/inspect/single/fragment/HomePageFragment.kt b/app/src/main/java/com/casic/birmm/inspect/single/fragment/HomePageFragment.kt index c3130e2..a5ba495 100644 --- a/app/src/main/java/com/casic/birmm/inspect/single/fragment/HomePageFragment.kt +++ b/app/src/main/java/com/casic/birmm/inspect/single/fragment/HomePageFragment.kt @@ -1,14 +1,10 @@ package com.casic.birmm.inspect.single.fragment -import android.annotation.SuppressLint -import android.bluetooth.BluetoothDevice import android.bluetooth.BluetoothGatt import android.bluetooth.BluetoothGattCharacteristic import android.content.Context import android.graphics.Color import android.os.Bundle -import android.os.Handler -import android.os.Message import android.os.Vibrator import android.util.Log import android.view.LayoutInflater @@ -25,6 +21,7 @@ import com.amap.api.maps.model.MyLocationStyle import com.amap.api.maps.model.PolylineOptions import com.casic.birmm.inspect.R +import com.casic.birmm.inspect.base.BaseApplication import com.casic.birmm.inspect.databinding.SingleFragmentMapInspectBinding import com.casic.birmm.inspect.extensions.addAll import com.casic.birmm.inspect.extensions.id @@ -32,44 +29,51 @@ import com.casic.birmm.inspect.extensions.toDeviceCode import com.casic.birmm.inspect.model.NewInspectionModel import com.casic.birmm.inspect.single.view.NewEventActivity -import com.casic.birmm.inspect.utils.* +import com.casic.birmm.inspect.utils.DataBaseManager +import com.casic.birmm.inspect.utils.LoadingDialogHub +import com.casic.birmm.inspect.utils.LocaleConstant +import com.casic.birmm.inspect.utils.LocationHub +import com.casic.birmm.inspect.utils.SoundPoolHelper +import com.clj.fastble.BleManager +import com.clj.fastble.callback.BleGattCallback +import com.clj.fastble.callback.BleNotifyCallback +import com.clj.fastble.callback.BleScanCallback +import com.clj.fastble.callback.BleWriteCallback +import com.clj.fastble.data.BleDevice +import com.clj.fastble.exception.BleException import com.pengxh.kt.lite.base.KotlinBaseFragment -import com.pengxh.kt.lite.extensions.* +import com.pengxh.kt.lite.extensions.convertColor +import com.pengxh.kt.lite.extensions.getStatusBarHeight +import com.pengxh.kt.lite.extensions.navigatePageTo +import com.pengxh.kt.lite.extensions.show +import com.pengxh.kt.lite.extensions.timestampToCompleteDate +import com.pengxh.kt.lite.extensions.timestampToDate import com.pengxh.kt.lite.utils.SaveKeyValues -import com.pengxh.kt.lite.utils.WeakReferenceHandler -import com.pengxh.kt.lite.utils.ble.BLEManager -import com.pengxh.kt.lite.utils.ble.BlueToothBean -import com.pengxh.kt.lite.utils.ble.OnBleConnectListener -import com.pengxh.kt.lite.utils.ble.OnDeviceDiscoveredListener import com.pengxh.kt.lite.widget.dialog.AlertControlDialog import com.pengxh.kt.lite.widget.dialog.AlertInputDialog import com.pengxh.kt.lite.widget.dialog.BottomActionSheet -import com.qmuiteam.qmui.widget.dialog.QMUIBottomSheet import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.delay import kotlinx.coroutines.launch import kotlinx.coroutines.withContext -import java.util.* +import java.util.LinkedList +import java.util.UUID -@SuppressLint("MissingPermission") -class HomePageFragment : KotlinBaseFragment(), Handler.Callback { - - companion object { - lateinit var weakReferenceHandler: WeakReferenceHandler - } +class HomePageFragment : KotlinBaseFragment() { private val kTag = "HomePageFragment" - private val blueToothBeans: MutableList = ArrayList()//搜索展示列表 + private val bleManager by lazy { BleManager.getInstance() } + private val bluetoothDevices by lazy { ArrayList() } private val latLngs = LinkedList() private lateinit var vibrator: Vibrator private lateinit var aMap: AMap + private lateinit var writeUuid: String + private lateinit var notifyUuid: String private var newModel: NewInspectionModel? = null//新建巡检数据结构模型 - private var isBluetoothOn = true - private var curConnectState = false - private var currentDevice: BluetoothDevice? = null// 当前蓝牙设备 + private var connectedDevice: BleDevice? = null private var isGeneratingTask = false private var alarmCount = 0 - private var connectedDeviceName = "" - private var isDataCommandOpened = false + private var connectedDeviceCode = "" override fun initViewBinding( inflater: LayoutInflater, container: ViewGroup? @@ -88,15 +92,8 @@ override fun initOnCreate(savedInstanceState: Bundle?) { vibrator = requireContext().getSystemService(Context.VIBRATOR_SERVICE) as Vibrator - weakReferenceHandler = WeakReferenceHandler(this) - if (BLEManager.initBLE(requireContext())) { - if (!BLEManager.isBluetoothEnable()) { - BLEManager.openBluetooth(false) - } - } else { - "该设备不支持低功耗蓝牙".show(requireContext()) - } + bleManager.enableLog(true).setSplitWriteNum(16).init(BaseApplication.get()) binding.mapView.onCreate(savedInstanceState) aMap = binding.mapView.map @@ -212,7 +209,7 @@ position.toString(), newModel!!.id, newModel!!.name, - connectedDeviceName, + connectedDeviceCode, latLngs.last.longitude.toString(), latLngs.last.latitude.toString() ) @@ -244,287 +241,216 @@ } //蓝牙按钮 - if (isBluetoothOn) { - binding.bluetoothButton.setImageResource(R.drawable.ic_bluetooth_enable) - binding.bluetoothButton.setOnClickListener { - if (curConnectState) { - AlertControlDialog.Builder() - .setContext(requireContext()) - .setTitle("提示") - .setMessage("确定断开设备吗?") - .setNegativeButton("取消") - .setPositiveButton("确定") - .setOnDialogButtonClickListener(object : - AlertControlDialog.OnDialogButtonClickListener { - override fun onCancelClick() { - - } - - override fun onConfirmClick() { - //断开连接 - BLEManager.disConnectDevice() - binding.currentValueView.text = "--" - binding.settingsValueView.text = "--" - binding.maxValueView.text = "--" - binding.deviceStatusView.text = "设备编号:未连接" - isDataCommandOpened = false - "设备已断开连接".show(requireContext()) - } - }).build().show() - } else { - //搜索蓝牙 - if (!BLEManager.isBluetoothEnable()) { - BLEManager.openBluetooth(false) - return@setOnClickListener - } - if (BLEManager.isDiscovery()) {//当前正在搜索设备... - BLEManager.stopDiscoverDevice() - } - LoadingDialogHub.show(requireActivity(), "设备搜索中...") - BLEManager.startDiscoverDevice(object : OnDeviceDiscoveredListener { - override fun onDeviceFound(blueToothBean: BlueToothBean?) { - val message = weakReferenceHandler.obtainMessage() - message.what = LocaleConstant.DISCOVERY_DEVICE - message.obj = blueToothBean - weakReferenceHandler.sendMessage(message) - } - - override fun onDiscoveryTimeout() { - val message = weakReferenceHandler.obtainMessage() - message.what = LocaleConstant.DISCOVERY_OUT_TIME - weakReferenceHandler.sendMessage(message) - } - }, 3 * 1000) - } + binding.bluetoothButton.setOnClickListener { + if (!bleManager.isBlueEnable) { + bleManager.enableBluetooth() } - } else { - binding.bluetoothButton.setImageResource(R.drawable.ic_bluetooth_disabled) + + if (bleManager.isConnected(connectedDevice)) { + AlertControlDialog.Builder() + .setContext(requireContext()) + .setTitle("提示") + .setMessage("确定断开设备吗?") + .setNegativeButton("取消") + .setPositiveButton("确定") + .setOnDialogButtonClickListener(object : + AlertControlDialog.OnDialogButtonClickListener { + override fun onCancelClick() { + + } + + override fun onConfirmClick() { + lifecycleScope.launch(Dispatchers.IO) { + sendCommand(LocaleConstant.CLOSE_TRANSFER_COMMAND) + + delay(500) + + //关闭数据传送指令 + bleManager.disconnect(connectedDevice) + withContext(Dispatchers.Main) { + binding.currentValueView.text = "--" + binding.settingsValueView.text = "--" + binding.maxValueView.text = "--" + binding.deviceStatusView.text = "设备编号:未连接" + "设备已断开连接".show(requireContext()) + + //清除缓存 + bluetoothDevices.clear() + latLngs.clear() + aMap.clear()//清除原来的路线 + SaveKeyValues.removeKey(LocaleConstant.DEVICE_CODE) + } + } + } + }).build().show() + } else { + //搜索蓝牙 + bleManager.scan(object : BleScanCallback() { + override fun onScanStarted(success: Boolean) { + LoadingDialogHub.show(requireActivity(), "设备搜索中...") + } + + override fun onScanning(bleDevice: BleDevice) { + + } + + override fun onScanFinished(scanResultList: List) { + LoadingDialogHub.dismiss() + + scanResultList.forEach { + if (!it.name.isNullOrBlank()) { + bluetoothDevices.add(it) + } + } + showScanResult() + } + }) + } } //重新发送指令按钮 - binding.refreshButton.setOnClickListener { - if (curConnectState) { - BLEManager.sendCommand(LocaleConstant.OPEN_TRANSFER_COMMAND) + binding.retrySendButton.setOnClickListener { + if (bleManager.isConnected(connectedDevice)) { + sendCommand(LocaleConstant.OPEN_TRANSFER_COMMAND) } else { "请先连接设备".show(requireContext()) } } } - override fun handleMessage(msg: Message): Boolean { - when (msg.what) { - LocaleConstant.BLUETOOTH_ON -> { - "蓝牙已开启".show(requireContext()) + private fun sendCommand(command: ByteArray) { + bleManager.write(connectedDevice, LocaleConstant.UUIDS[2], writeUuid, command, + object : BleWriteCallback() { + override fun onWriteSuccess(current: Int, total: Int, justWrite: ByteArray?) { + Log.d(kTag, "onWriteSuccess: 指令下发成功") + } + + override fun onWriteFailure(exception: BleException?) { + "指令下发失败".show(requireContext()) + } + }) + } + + private fun showScanResult() { + val array = ArrayList() + for (it in bluetoothDevices) { + array.add(it.name) + } + + BottomActionSheet.Builder() + .setContext(requireContext()) + .setActionItemTitle(array) + .setItemTextColor(R.color.themeColor.convertColor(requireContext())) + .setOnActionSheetListener(object : BottomActionSheet.OnActionSheetListener { + override fun onActionItemClick(position: Int) { + //连接点击的设备 + startConnectDevice(bluetoothDevices[position]) + } + }).build().show() + } + + private fun startConnectDevice(device: BleDevice) { + // 当前蓝牙设备 + bleManager.connect(device, object : BleGattCallback() { + override fun onStartConnect() { + LoadingDialogHub.show(requireActivity(), "设备连接中...") + } + + override fun onConnectFail(bleDevice: BleDevice, exception: BleException) { + LoadingDialogHub.dismiss() + } + + override fun onConnectSuccess(bleDevice: BleDevice, gatt: BluetoothGatt, status: Int) { + connectedDevice = bleDevice + notifyDeviceService(bleDevice, gatt) + } + + override fun onDisConnected( + isActiveDisConnected: Boolean, bleDevice: BleDevice, + gatt: BluetoothGatt, status: Int + ) { + LoadingDialogHub.dismiss() + connectedDevice = null binding.bluetoothButton.setImageResource(R.drawable.ic_bluetooth_enable) - isBluetoothOn = true } - - LocaleConstant.BLUETOOTH_OFF -> { - "蓝牙已关闭".show(requireContext()) - binding.bluetoothButton.setImageResource(R.drawable.ic_bluetooth_disabled) - isBluetoothOn = false - } - - LocaleConstant.DISCOVERY_DEVICE -> { - val bean = msg.obj as BlueToothBean - if (blueToothBeans.size == 0) { - blueToothBeans.add(bean) - } else { - //0表示未添加到list的新设备,1表示已经扫描并添加到list的设备 - var judge = 0 - for (it in blueToothBeans) { - if (it.bluetoothDevice.address == bean.bluetoothDevice.address) { - judge = 1 - break - } - } - if (judge == 0) { - blueToothBeans.add(bean) - } - } - } - - LocaleConstant.DISCOVERY_OUT_TIME -> { - LoadingDialogHub.dismiss() - if (blueToothBeans.size == 0) { - "无可用设备,请确认设备是否已经开启".show(requireContext()) - } else { - val sheetBuilder = QMUIBottomSheet.BottomListSheetBuilder(requireContext()) - sheetBuilder.setTitle("请选择要连接的设备") - blueToothBeans.forEach { - if (it.bluetoothDevice.name.isNotEmpty()) { - sheetBuilder.addItem(it.bluetoothDevice.name) - } - } - sheetBuilder.setGravityCenter(true).setAddCancelBtn(true) - .setOnSheetItemClickListener { dialog, _, position, _ -> - dialog.dismiss() - //连接点击的设备 - startConnectDevice(blueToothBeans[position].bluetoothDevice) - }.build().show() - } - } - - LocaleConstant.CONNECT_SUCCESS -> { - LoadingDialogHub.dismiss() - curConnectState = true - BLEManager.sendCommand(LocaleConstant.ASK_DEV_CODE_COMMAND) - } - - LocaleConstant.CONNECT_FAILURE -> curConnectState = false - LocaleConstant.SEND_SUCCESS -> Log.d( - kTag, "发送成功-> ${(msg.obj as ByteArray).toList()}" - ) - - LocaleConstant.SEND_FAILURE -> Log.d( - kTag, "发送失败-> ${(msg.obj as ByteArray).toList()}" - ) - - LocaleConstant.RECEIVE_SUCCESS -> { - val bytes = msg.obj as ByteArray - if (bytes.first() == 51.toByte() && bytes.size >= 14) { - //解析deviceCode - //[51, 51, 50, 48, 48, 48, 48, 49, 48, 48, 48, 50, 13, 10] - binding.deviceStatusView.text = "设备编号: ${bytes.toDeviceCode()}" - SaveKeyValues.putValue(LocaleConstant.DEVICE_CODE, bytes.toDeviceCode()) - if (!isDataCommandOpened) { - BLEManager.sendCommand(LocaleConstant.OPEN_TRANSFER_COMMAND) - isDataCommandOpened = true - } - } else if (bytes.first() == (-86).toByte() && bytes.size == 14) { - handleMethaneData(bytes) - } else { - //如果收到错误数据,就保存设备编号 - binding.deviceStatusView.text = "设备编号: $connectedDeviceName" - SaveKeyValues.putValue(LocaleConstant.DEVICE_CODE, connectedDeviceName) - if (!isDataCommandOpened) { - BLEManager.sendCommand(LocaleConstant.OPEN_TRANSFER_COMMAND) - isDataCommandOpened = true - } - } - } - - LocaleConstant.RECEIVE_FAILURE -> Log.d(kTag, "接收失败-> ${msg.obj as String}") - LocaleConstant.DISCONNECT_SUCCESS -> curConnectState = false - LocaleConstant.STOP_TASK -> { - //断开连接 - if (curConnectState) { - BLEManager.disConnectDevice() - } - binding.deviceStatusView.text = "设备编号:未连接" - binding.inspectNameView.text = "" - binding.inspectTimeView.text = "" - binding.currentValueView.text = "--" - binding.settingsValueView.text = "--" - binding.maxValueView.text = "--" - - //按钮状态 - binding.stopInspectButton.isEnabled = false - binding.addInspectionButton.isEnabled = true - - //清除缓存 - blueToothBeans.clear() - latLngs.clear() - aMap.clear()//清除原来的路线 - SaveKeyValues.removeKey(LocaleConstant.DEVICE_CODE) - isDataCommandOpened = false - - LoadingDialogHub.dismiss() - "巡检记录保存成功".show(requireContext()) - } - } - return true + }) } - private fun startConnectDevice(device: BluetoothDevice) { - this.currentDevice = device - if (!curConnectState) { - connectedDeviceName = currentDevice!!.name - LoadingDialogHub.show(requireActivity(), "正在连接[${connectedDeviceName}]...") - BLEManager.connectBleDevice( - requireContext(), currentDevice!!, 10000, - LocaleConstant.SERVICE_UUID, - LocaleConstant.READ_CHARACTERISTIC_UUID, - LocaleConstant.WRITE_CHARACTERISTIC_UUID, - onBleConnectListener - ) - } - } - - private val onBleConnectListener = object : OnBleConnectListener { - override fun onConnecting(bluetoothGatt: BluetoothGatt?) { - + private fun notifyDeviceService(bleDevice: BleDevice, gatt: BluetoothGatt) { + val gattService = gatt.getService(UUID.fromString(LocaleConstant.UUIDS[2])) + if (gattService == null) { + Log.d(kTag, "notifyDeviceService: gattService is null") + LoadingDialogHub.dismiss() + "连接失败,设备不支持低功耗蓝牙".show(requireContext()) + return } - override fun onConnectSuccess(bluetoothGatt: BluetoothGatt?, status: Int) { + gattService.characteristics.forEach { + //获取到相应的服务UUID和特征UUID + val uuid = it.uuid + val properties = it.properties + if (properties and BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE > 0 || + properties and BluetoothGattCharacteristic.PROPERTY_WRITE > 0 + ) { + Log.d(kTag, "uuid可写: $uuid") + writeUuid = uuid.toString() + it.writeType = BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT + } + + if (properties and BluetoothGattCharacteristic.PROPERTY_NOTIFY > 0 || + properties and BluetoothGattCharacteristic.PROPERTY_INDICATE > 0 + ) { + Log.d(kTag, "uuid可通知: $uuid") + notifyUuid = uuid.toString() + } } - override fun onConnectFailure( - bluetoothGatt: BluetoothGatt?, exception: String?, status: Int - ) { - val message = weakReferenceHandler.obtainMessage() - message.what = LocaleConstant.CONNECT_FAILURE - weakReferenceHandler.sendMessage(message) - } + bleManager.notify(bleDevice, LocaleConstant.UUIDS[2], notifyUuid, + object : BleNotifyCallback() { + override fun onNotifySuccess() { + LoadingDialogHub.dismiss() + "连接成功".show(requireContext()) + binding.bluetoothButton.setImageResource(R.drawable.ic_bluetooth_disabled) + lifecycleScope.launch(Dispatchers.IO) { + delay(500) + sendCommand(LocaleConstant.ASK_DEV_CODE_COMMAND) + } + } - override fun onDisConnecting(bluetoothGatt: BluetoothGatt?) { + override fun onNotifyFailure(exception: BleException) { + connectedDevice = null + } - } + override fun onCharacteristicChanged(data: ByteArray) { + // 打开通知后,设备发过来的数据 + Log.d(kTag, data.contentToString()) + if (data.first() == 51.toByte() && data.size >= 14) { + //[51, 57, 50, 48, 50, 52, 48, 49, 48, 48, 48, 54, 32, 0] + lifecycleScope.launch(Dispatchers.IO) { + val builder = StringBuilder() + for (index in 0..11) { + builder.append(data[index].toInt().toChar()) + } + withContext(Dispatchers.Main) { + connectedDeviceCode = data.toDeviceCode() + binding.deviceStatusView.text = "设备编号: $connectedDeviceCode" + SaveKeyValues.putValue( + LocaleConstant.DEVICE_CODE, connectedDeviceCode + ) + } - override fun onDisConnectSuccess(bluetoothGatt: BluetoothGatt?, status: Int) { - val message = weakReferenceHandler.obtainMessage() - message.what = LocaleConstant.DISCONNECT_SUCCESS - message.obj = status - weakReferenceHandler.sendMessage(message) - } + delay(500) - override fun onServiceDiscoverySucceed(bluetoothGatt: BluetoothGatt?, status: Int) { - val message = weakReferenceHandler.obtainMessage() - message.what = LocaleConstant.CONNECT_SUCCESS - weakReferenceHandler.sendMessage(message) - } - - override fun onServiceDiscoveryFailed(bluetoothGatt: BluetoothGatt?, msg: String?) { - val message = weakReferenceHandler.obtainMessage() - message.what = LocaleConstant.CONNECT_FAILURE - weakReferenceHandler.sendMessage(message) - } - - override fun onReceiveMessage( - bluetoothGatt: BluetoothGatt?, characteristic: BluetoothGattCharacteristic? - ) { - val message = weakReferenceHandler.obtainMessage() - message.what = LocaleConstant.RECEIVE_SUCCESS - message.obj = characteristic!!.value - weakReferenceHandler.sendMessage(message) - } - - override fun onReceiveError(errorMsg: String?) { - val message = weakReferenceHandler.obtainMessage() - message.what = LocaleConstant.RECEIVE_FAILURE - weakReferenceHandler.sendMessage(message) - } - - override fun onWriteSuccess(bluetoothGatt: BluetoothGatt?, msg: ByteArray?) { - val message = weakReferenceHandler.obtainMessage() - message.what = LocaleConstant.SEND_SUCCESS - message.obj = msg - weakReferenceHandler.sendMessage(message) - } - - override fun onWriteFailure( - bluetoothGatt: BluetoothGatt?, msg: ByteArray?, errorMsg: String? - ) { - val message = weakReferenceHandler.obtainMessage() - message.what = LocaleConstant.SEND_FAILURE - message.obj = msg - weakReferenceHandler.sendMessage(message) - } - - override fun onReadRssi(bluetoothGatt: BluetoothGatt?, rssi: Int, status: Int) { - - } + //发送数据传送指令 + sendCommand(LocaleConstant.OPEN_TRANSFER_COMMAND) + } + } else if (data.first() == (-86).toByte() && data.size >= 14) { + //[-86, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 13, 10, -86, 0, 0, 0, 0, 1] + handleMethaneData(data) + } + } + }) } /** @@ -554,22 +480,28 @@ DataBaseManager.get.addInspectionRoute(newModel!!.id, latLngs[i]) } + + DataBaseManager.get.addInspection( + inspectionId = newModel!!.id, + deviceCode = connectedDeviceCode, + inspectionName = newModel!!.name, + startTime = newModel!!.startTime, + endTime = System.currentTimeMillis().timestampToCompleteDate(), + date = newModel!!.date, + startLng = latLngs.first.longitude, + startLat = latLngs.first.latitude, + newModel!!.startAddress, + endLng = latLngs.last.longitude, + endLat = latLngs.last.latitude, + newModel!!.endAddress, + routes = newModel!!.routes + ) } - DataBaseManager.get.addInspection( - inspectionId = newModel!!.id, - deviceCode = connectedDeviceName, - inspectionName = newModel!!.name, - startTime = newModel!!.startTime, - endTime = System.currentTimeMillis().timestampToCompleteDate(), - date = newModel!!.date, - startLng = latLngs.first.longitude, - startLat = latLngs.first.latitude, - newModel!!.startAddress, - endLng = latLngs.last.longitude, - endLat = latLngs.last.latitude, - newModel!!.endAddress, - routes = newModel!!.routes - ) + LoadingDialogHub.dismiss() + + //按钮状态 + binding.stopInspectButton.isEnabled = false + binding.addInspectionButton.isEnabled = true } } @@ -626,7 +558,7 @@ DataBaseManager.get.addEvent( taskId = UUID.randomUUID().toString(), inspectionId = newModel!!.id, - deviceCode = connectedDeviceName, + deviceCode = connectedDeviceCode, eventTitle = newModel!!.name, createTime = System.currentTimeMillis().timestampToCompleteDate(), type = "报警事件", @@ -679,6 +611,8 @@ super.onDestroyView() LocationHub.get.stopLocation() SaveKeyValues.removeKey(LocaleConstant.DEVICE_CODE) + bleManager.disconnect(connectedDevice) + bleManager.destroy() } override fun onDestroy() { diff --git a/app/src/main/java/com/casic/birmm/inspect/utils/DataBaseManager.kt b/app/src/main/java/com/casic/birmm/inspect/utils/DataBaseManager.kt index 5464803..3a2cac1 100644 --- a/app/src/main/java/com/casic/birmm/inspect/utils/DataBaseManager.kt +++ b/app/src/main/java/com/casic/birmm/inspect/utils/DataBaseManager.kt @@ -10,7 +10,6 @@ import com.casic.birmm.inspect.greendao.InspectionLocalBeanDao import com.casic.birmm.inspect.greendao.RouteLocalBeanDao import com.casic.birmm.inspect.greendao.TaskEventLocalBeanDao -import com.casic.birmm.inspect.single.fragment.HomePageFragment import com.casic.birmm.inspect.single.fragment.QueryEventFragment import com.casic.birmm.inspect.single.fragment.QueryInspectionFragment @@ -73,10 +72,6 @@ QueryInspectionFragment.weakReferenceHandler.sendEmptyMessage( LocaleConstant.LOAD_INSPECTION ) - - HomePageFragment.weakReferenceHandler.sendEmptyMessage( - LocaleConstant.STOP_TASK - ) } } }) diff --git a/app/src/main/java/com/casic/birmm/inspect/utils/LocaleConstant.kt b/app/src/main/java/com/casic/birmm/inspect/utils/LocaleConstant.kt index 36fca8e..0fc27b6 100644 --- a/app/src/main/java/com/casic/birmm/inspect/utils/LocaleConstant.kt +++ b/app/src/main/java/com/casic/birmm/inspect/utils/LocaleConstant.kt @@ -71,8 +71,35 @@ Manifest.permission.WRITE_EXTERNAL_STORAGE ) } - val ASK_DEV_CODE_COMMAND = byteArrayOf(0x01, 0x0D, 0x0A)// 查询设备编号命令 + + //主服务UUID + val UUIDS = listOf( + "00001801-0000-1000-8000-00805f9b34fb", + "00001800-0000-1000-8000-00805f9b34fb", + "0003cdd0-0000-1000-8000-00805f9b0131" + ) + + //可通知不可写 + val SUB_0_UUIDS = listOf( + "00002a05-0000-1000-8000-00805f9b34fb" + ) + + //可写不可通知 + val SUB_1_UUIDS = listOf( + "00002a00-0000-1000-8000-00805f9b34fb", + "00002a01-0000-1000-8000-00805f9b34fb", + "00002a04-0000-1000-8000-00805f9b34fb" + ) + + //可写可通知 + val SUB_2_UUIDS = listOf( + "0003cdd1-0000-1000-8000-00805f9b0131",//可通知 + "0003cdd2-0000-1000-8000-00805f9b0131"//可写 + ) + + val ASK_DEV_CODE_COMMAND = byteArrayOf(0x01, 0x0D, 0x0A) // 查询设备编号命令 val OPEN_TRANSFER_COMMAND = byteArrayOf(0x02, 0x0D, 0x0A) // 开启数据发送命令 + val CLOSE_TRANSFER_COMMAND = byteArrayOf(0x03, 0x0D, 0x0A) // 关闭数据发送命令 /** * ============================================================================================= @@ -116,9 +143,6 @@ const val USER_OBJECT = "userObject" const val OPEN_WARNING = "isOpenWarning" const val AUTO_RECORD = "isRecordLog" - const val SERVICE_UUID = "0003cdd0-0000-1000-8000-00805f9b0131"//连接设备的UUID - const val WRITE_CHARACTERISTIC_UUID = "0003cdd2-0000-1000-8000-00805f9b0131"//写数据特征值UUID - const val READ_CHARACTERISTIC_UUID = "0003cdd1-0000-1000-8000-00805f9b0131"//读数据特征值UUID const val SINGLE_OPEN_WARNING = "isSingleOpenWarning" const val SINGLE_AUTO_RECORD = "isSingleRecordLog" diff --git a/app/build.gradle b/app/build.gradle index ae07527..5ad9504 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -116,4 +116,6 @@ implementation 'com.amap.api:3dmap:latest.integration' //高德地图搜索 implementation 'com.amap.api:search:8.1.0' + //蓝牙 + implementation 'com.github.Jasonchenlijian:FastBle:2.4.0' } \ No newline at end of file diff --git a/app/src/main/java/com/casic/birmm/inspect/fragment/HomePageFragment.kt b/app/src/main/java/com/casic/birmm/inspect/fragment/HomePageFragment.kt index da55df2..e5252c1 100644 --- a/app/src/main/java/com/casic/birmm/inspect/fragment/HomePageFragment.kt +++ b/app/src/main/java/com/casic/birmm/inspect/fragment/HomePageFragment.kt @@ -1,14 +1,10 @@ package com.casic.birmm.inspect.fragment -import android.annotation.SuppressLint -import android.bluetooth.BluetoothDevice import android.bluetooth.BluetoothGatt import android.bluetooth.BluetoothGattCharacteristic import android.content.Context import android.graphics.Color import android.os.Bundle -import android.os.Handler -import android.os.Message import android.os.Vibrator import android.util.Log import android.view.LayoutInflater @@ -16,6 +12,7 @@ import android.widget.LinearLayout import androidx.core.text.isDigitsOnly import androidx.lifecycle.ViewModelProvider +import androidx.lifecycle.lifecycleScope import com.amap.api.location.AMapLocation import com.amap.api.maps.AMap import com.amap.api.maps.AMapOptions @@ -25,6 +22,7 @@ import com.amap.api.maps.model.MyLocationStyle import com.amap.api.maps.model.PolylineOptions import com.casic.birmm.inspect.R +import com.casic.birmm.inspect.base.BaseApplication import com.casic.birmm.inspect.callback.OnDistanceSearchListener import com.casic.birmm.inspect.databinding.FragmentMapInspectBinding import com.casic.birmm.inspect.extensions.addAll @@ -41,35 +39,37 @@ import com.casic.birmm.inspect.vm.EventViewModel import com.casic.birmm.inspect.vm.InspectionViewModel import com.casic.birmm.inspect.vm.RouteViewModel +import com.clj.fastble.BleManager +import com.clj.fastble.callback.BleGattCallback +import com.clj.fastble.callback.BleNotifyCallback +import com.clj.fastble.callback.BleScanCallback +import com.clj.fastble.callback.BleWriteCallback +import com.clj.fastble.data.BleDevice +import com.clj.fastble.exception.BleException import com.pengxh.kt.lite.base.KotlinBaseFragment +import com.pengxh.kt.lite.extensions.convertColor import com.pengxh.kt.lite.extensions.getStatusBarHeight import com.pengxh.kt.lite.extensions.navigatePageTo import com.pengxh.kt.lite.extensions.show import com.pengxh.kt.lite.extensions.timestampToCompleteDate import com.pengxh.kt.lite.extensions.timestampToDate import com.pengxh.kt.lite.utils.SaveKeyValues -import com.pengxh.kt.lite.utils.WeakReferenceHandler -import com.pengxh.kt.lite.utils.ble.BLEManager -import com.pengxh.kt.lite.utils.ble.BlueToothBean -import com.pengxh.kt.lite.utils.ble.OnBleConnectListener -import com.pengxh.kt.lite.utils.ble.OnDeviceDiscoveredListener import com.pengxh.kt.lite.vm.LoadState import com.pengxh.kt.lite.widget.dialog.AlertControlDialog import com.pengxh.kt.lite.widget.dialog.AlertInputDialog import com.pengxh.kt.lite.widget.dialog.BottomActionSheet -import com.qmuiteam.qmui.widget.dialog.QMUIBottomSheet +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.delay +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext import java.util.LinkedList import java.util.UUID -@SuppressLint("MissingPermission", "SetTextI18n") -class HomePageFragment : KotlinBaseFragment(), Handler.Callback { - - companion object { - lateinit var weakReferenceHandler: WeakReferenceHandler - } +class HomePageFragment : KotlinBaseFragment() { private val kTag = "HomePageFragment" - private val blueToothBeans: MutableList = ArrayList()//搜索展示列表 + private val bleManager by lazy { BleManager.getInstance() } + private val bluetoothDevices by lazy { java.util.ArrayList() } private val latLngs = LinkedList() private lateinit var userName: String private lateinit var vibrator: Vibrator @@ -77,14 +77,13 @@ private lateinit var inspectionViewModel: InspectionViewModel private lateinit var routeViewModel: RouteViewModel private lateinit var aMap: AMap + private lateinit var writeUuid: String + private lateinit var notifyUuid: String private var newModel: NewInspectionModel? = null//新建巡检数据结构模型 - private var isBluetoothOn = true - private var curConnectState = false - private var currentDevice: BluetoothDevice? = null// 当前蓝牙设备 + private var connectedDevice: BleDevice? = null private var isGeneratingTask = false private var alarmCount = 0 - private var connectedDeviceName = "" - private var isDataCommandOpened = false + private var connectedDeviceCode = "" override fun initViewBinding( inflater: LayoutInflater, container: ViewGroup? @@ -103,16 +102,8 @@ override fun initOnCreate(savedInstanceState: Bundle?) { vibrator = requireContext().getSystemService(Context.VIBRATOR_SERVICE) as Vibrator - weakReferenceHandler = WeakReferenceHandler(this) - //BLE - if (BLEManager.initBLE(requireContext())) { - if (!BLEManager.isBluetoothEnable()) { - BLEManager.openBluetooth(false) - } - } else { - "该设备不支持低功耗蓝牙".show(requireContext()) - } + bleManager.enableLog(true).setSplitWriteNum(16).init(BaseApplication.get()) //Map binding.mapView.onCreate(savedInstanceState) @@ -196,27 +187,10 @@ } is LoadState.Success -> { - //断开连接 - if (curConnectState) { - BLEManager.disConnectDevice() - } - binding.deviceStatusView.text = "设备编号:未连接" - binding.inspectNameView.text = "" - binding.inspectTimeView.text = "" - binding.currentValueView.text = "--" - binding.settingsValueView.text = "--" - binding.maxValueView.text = "--" - //按钮状态 binding.stopInspectButton.isEnabled = false binding.addInspectionButton.isEnabled = true - //清除缓存 - blueToothBeans.clear() - latLngs.clear() - SaveKeyValues.removeKey(LocaleConstant.DEVICE_CODE) - isDataCommandOpened = false - LoadingDialogHub.dismiss() "巡检记录保存成功".show(requireContext()) } @@ -279,7 +253,7 @@ position.toString(), newModel!!.id, newModel!!.name, - connectedDeviceName, + connectedDeviceCode, latLngs.last.longitude.toString(), latLngs.last.latitude.toString() ) @@ -311,261 +285,222 @@ } //蓝牙按钮 - if (isBluetoothOn) { - binding.bluetoothButton.setImageResource(R.drawable.ic_bluetooth_enable) - binding.bluetoothButton.setOnClickListener { - if (curConnectState) { - AlertControlDialog.Builder() - .setContext(requireContext()) - .setTitle("提示") - .setMessage("确定断开设备吗?") - .setNegativeButton("取消") - .setPositiveButton("确定") - .setOnDialogButtonClickListener(object : - AlertControlDialog.OnDialogButtonClickListener { - override fun onCancelClick() { - - } - - override fun onConfirmClick() { - //断开连接 - BLEManager.disConnectDevice() - binding.currentValueView.text = "--" - binding.settingsValueView.text = "--" - binding.maxValueView.text = "--" - binding.deviceStatusView.text = "设备编号:未连接" - isDataCommandOpened = false - "设备已断开连接".show(requireContext()) - } - }).build().show() - } else { - //搜索蓝牙 - if (!BLEManager.isBluetoothEnable()) { - BLEManager.openBluetooth(false) - return@setOnClickListener - } - if (BLEManager.isDiscovery()) {//当前正在搜索设备... - BLEManager.stopDiscoverDevice() - } - LoadingDialogHub.show(requireActivity(), "设备搜索中...") - BLEManager.startDiscoverDevice(object : OnDeviceDiscoveredListener { - override fun onDeviceFound(blueToothBean: BlueToothBean?) { - val message = weakReferenceHandler.obtainMessage() - message.what = LocaleConstant.DISCOVERY_DEVICE - message.obj = blueToothBean - weakReferenceHandler.sendMessage(message) - } - - override fun onDiscoveryTimeout() { - val message = weakReferenceHandler.obtainMessage() - message.what = LocaleConstant.DISCOVERY_OUT_TIME - weakReferenceHandler.sendMessage(message) - } - }, 3 * 1000) - } + binding.bluetoothButton.setOnClickListener { + if (!bleManager.isBlueEnable) { + bleManager.enableBluetooth() } - } else { - binding.bluetoothButton.setImageResource(R.drawable.ic_bluetooth_disabled) - } - //重新发送指令按钮 - binding.refreshButton.setOnClickListener { - if (curConnectState) { - BLEManager.sendCommand(LocaleConstant.OPEN_TRANSFER_COMMAND) + if (bleManager.isConnected(connectedDevice)) { + AlertControlDialog.Builder() + .setContext(requireContext()) + .setTitle("提示") + .setMessage("确定断开设备吗?") + .setNegativeButton("取消") + .setPositiveButton("确定") + .setOnDialogButtonClickListener(object : + AlertControlDialog.OnDialogButtonClickListener { + override fun onCancelClick() { + + } + + override fun onConfirmClick() { + lifecycleScope.launch(Dispatchers.IO) { + sendCommand(LocaleConstant.CLOSE_TRANSFER_COMMAND) + + delay(500) + + //关闭数据传送指令 + bleManager.disconnect(connectedDevice) + withContext(Dispatchers.Main) { + binding.currentValueView.text = "--" + binding.settingsValueView.text = "--" + binding.maxValueView.text = "--" + binding.deviceStatusView.text = "设备编号:未连接" + "设备已断开连接".show(requireContext()) + + //清除缓存 + bluetoothDevices.clear() + latLngs.clear() + aMap.clear()//清除原来的路线 + SaveKeyValues.removeKey(LocaleConstant.DEVICE_CODE) + } + } + } + }).build().show() } else { - "请先连接设备".show(requireContext()) + //搜索蓝牙 + bleManager.scan(object : BleScanCallback() { + override fun onScanStarted(success: Boolean) { + LoadingDialogHub.show(requireActivity(), "设备搜索中...") + } + + override fun onScanning(bleDevice: BleDevice) { + + } + + override fun onScanFinished(scanResultList: List) { + LoadingDialogHub.dismiss() + + scanResultList.forEach { + if (!it.name.isNullOrBlank()) { + bluetoothDevices.add(it) + } + } + showScanResult() + } + }) + } + + //重新发送指令按钮 + binding.retrySendButton.setOnClickListener { + if (bleManager.isConnected(connectedDevice)) { + sendCommand(LocaleConstant.OPEN_TRANSFER_COMMAND) + } else { + "请先连接设备".show(requireContext()) + } } } } - override fun handleMessage(msg: Message): Boolean { - when (msg.what) { - LocaleConstant.BLUETOOTH_ON -> { - "蓝牙已开启".show(requireContext()) + private fun sendCommand(command: ByteArray) { + bleManager.write(connectedDevice, LocaleConstant.UUIDS[2], writeUuid, command, + object : BleWriteCallback() { + override fun onWriteSuccess(current: Int, total: Int, justWrite: ByteArray?) { + Log.d(kTag, "onWriteSuccess: 指令下发成功") + } + + override fun onWriteFailure(exception: BleException?) { + "指令下发失败".show(requireContext()) + } + }) + } + + private fun showScanResult() { + val array = java.util.ArrayList() + for (it in bluetoothDevices) { + array.add(it.name) + } + + BottomActionSheet.Builder() + .setContext(requireContext()) + .setActionItemTitle(array) + .setItemTextColor(R.color.themeColor.convertColor(requireContext())) + .setOnActionSheetListener(object : BottomActionSheet.OnActionSheetListener { + override fun onActionItemClick(position: Int) { + //连接点击的设备 + startConnectDevice(bluetoothDevices[position]) + } + }).build().show() + } + + private fun startConnectDevice(device: BleDevice) { + // 当前蓝牙设备 + bleManager.connect(device, object : BleGattCallback() { + override fun onStartConnect() { + LoadingDialogHub.show(requireActivity(), "设备连接中...") + } + + override fun onConnectFail(bleDevice: BleDevice, exception: BleException) { + LoadingDialogHub.dismiss() + } + + override fun onConnectSuccess( + bleDevice: BleDevice, + gatt: BluetoothGatt, + status: Int + ) { + connectedDevice = bleDevice + notifyDeviceService(bleDevice, gatt) + } + + override fun onDisConnected( + isActiveDisConnected: Boolean, bleDevice: BleDevice, + gatt: BluetoothGatt, status: Int + ) { + LoadingDialogHub.dismiss() + connectedDevice = null binding.bluetoothButton.setImageResource(R.drawable.ic_bluetooth_enable) - isBluetoothOn = true } - - LocaleConstant.BLUETOOTH_OFF -> { - "蓝牙已关闭".show(requireContext()) - binding.bluetoothButton.setImageResource(R.drawable.ic_bluetooth_disabled) - isBluetoothOn = false - } - - LocaleConstant.DISCOVERY_DEVICE -> { - val bean = msg.obj as BlueToothBean - if (blueToothBeans.size == 0) { - blueToothBeans.add(bean) - } else { - //0表示未添加到list的新设备,1表示已经扫描并添加到list的设备 - var judge = 0 - for (it in blueToothBeans) { - if (it.bluetoothDevice.address == bean.bluetoothDevice.address) { - judge = 1 - break - } - } - if (judge == 0) { - blueToothBeans.add(bean) - } - } - } - - LocaleConstant.DISCOVERY_OUT_TIME -> { - LoadingDialogHub.dismiss() - if (blueToothBeans.size == 0) { - "无可用设备,请确认设备是否已经开启".show(requireContext()) - } else { - val sheetBuilder = QMUIBottomSheet.BottomListSheetBuilder(requireContext()) - sheetBuilder.setTitle("请选择要连接的设备") - blueToothBeans.forEach { - if (it.bluetoothDevice.name.isNotEmpty()) { - sheetBuilder.addItem(it.bluetoothDevice.name) - } - } - sheetBuilder.setGravityCenter(true).setAddCancelBtn(true) - .setOnSheetItemClickListener { dialog, _, position, _ -> - dialog.dismiss() - //连接点击的设备 - startConnectDevice(blueToothBeans[position].bluetoothDevice) - }.build().show() - } - } - - LocaleConstant.CONNECT_SUCCESS -> { - LoadingDialogHub.dismiss() - curConnectState = true - BLEManager.sendCommand(LocaleConstant.ASK_DEV_CODE_COMMAND) - } - - LocaleConstant.CONNECT_FAILURE -> curConnectState = false - LocaleConstant.SEND_SUCCESS -> Log.d( - kTag, "发送成功-> ${(msg.obj as ByteArray).toList()}" - ) - - LocaleConstant.SEND_FAILURE -> Log.d( - kTag, "发送失败-> ${(msg.obj as ByteArray).toList()}" - ) - - LocaleConstant.RECEIVE_SUCCESS -> { - val bytes = msg.obj as ByteArray - if (bytes.first() == 51.toByte() && bytes.size >= 14) { - //解析deviceCode - //[51, 51, 50, 48, 48, 48, 48, 49, 48, 48, 48, 50, 13, 10] - binding.deviceStatusView.text = "设备编号: ${bytes.toDeviceCode()}" - SaveKeyValues.putValue(LocaleConstant.DEVICE_CODE, bytes.toDeviceCode()) - if (!isDataCommandOpened) { - BLEManager.sendCommand(LocaleConstant.OPEN_TRANSFER_COMMAND) - isDataCommandOpened = true - } - } else if (bytes.first() == (-86).toByte() && bytes.size == 14) { - handleMethaneData(bytes) - } else { - //如果收到错误数据,就保存设备编号 - binding.deviceStatusView.text = "设备编号: $connectedDeviceName" - SaveKeyValues.putValue(LocaleConstant.DEVICE_CODE, connectedDeviceName) - if (!isDataCommandOpened) { - BLEManager.sendCommand(LocaleConstant.OPEN_TRANSFER_COMMAND) - isDataCommandOpened = true - } - } - } - - LocaleConstant.RECEIVE_FAILURE -> Log.d(kTag, "接收失败-> ${msg.obj as String}") - LocaleConstant.DISCONNECT_SUCCESS -> curConnectState = false - } - return true + }) } - private fun startConnectDevice(device: BluetoothDevice) { - this.currentDevice = device - if (!curConnectState) { - connectedDeviceName = currentDevice!!.name - LoadingDialogHub.show(requireActivity(), "正在连接[${connectedDeviceName}]...") - BLEManager.connectBleDevice( - requireContext(), currentDevice!!, 10000, - LocaleConstant.SERVICE_UUID, - LocaleConstant.READ_CHARACTERISTIC_UUID, - LocaleConstant.WRITE_CHARACTERISTIC_UUID, - onBleConnectListener - ) - } - } - - private val onBleConnectListener = object : OnBleConnectListener { - override fun onConnecting(bluetoothGatt: BluetoothGatt?) { - + private fun notifyDeviceService(bleDevice: BleDevice, gatt: BluetoothGatt) { + val gattService = gatt.getService(UUID.fromString(LocaleConstant.UUIDS[2])) + if (gattService == null) { + Log.d(kTag, "notifyDeviceService: gattService is null") + LoadingDialogHub.dismiss() + "连接失败,设备不支持低功耗蓝牙".show(requireContext()) + return } - override fun onConnectSuccess(bluetoothGatt: BluetoothGatt?, status: Int) { + gattService.characteristics.forEach { + //获取到相应的服务UUID和特征UUID + val uuid = it.uuid + val properties = it.properties + if (properties and BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE > 0 || + properties and BluetoothGattCharacteristic.PROPERTY_WRITE > 0 + ) { + Log.d(kTag, "uuid可写: $uuid") + writeUuid = uuid.toString() + it.writeType = BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT + } + + if (properties and BluetoothGattCharacteristic.PROPERTY_NOTIFY > 0 || + properties and BluetoothGattCharacteristic.PROPERTY_INDICATE > 0 + ) { + Log.d(kTag, "uuid可通知: $uuid") + notifyUuid = uuid.toString() + } } - override fun onConnectFailure( - bluetoothGatt: BluetoothGatt?, exception: String?, status: Int - ) { - val message = weakReferenceHandler.obtainMessage() - message.what = LocaleConstant.CONNECT_FAILURE - weakReferenceHandler.sendMessage(message) - } + bleManager.notify(bleDevice, LocaleConstant.UUIDS[2], notifyUuid, + object : BleNotifyCallback() { + override fun onNotifySuccess() { + LoadingDialogHub.dismiss() + "连接成功".show(requireContext()) + binding.bluetoothButton.setImageResource(R.drawable.ic_bluetooth_disabled) + lifecycleScope.launch(Dispatchers.IO) { + delay(500) + sendCommand(LocaleConstant.ASK_DEV_CODE_COMMAND) + } + } - override fun onDisConnecting(bluetoothGatt: BluetoothGatt?) { + override fun onNotifyFailure(exception: BleException) { + connectedDevice = null + } - } + override fun onCharacteristicChanged(data: ByteArray) { + // 打开通知后,设备发过来的数据 + if (data.first() == 51.toByte() && data.size >= 14) { + //[51, 57, 50, 48, 50, 52, 48, 49, 48, 48, 48, 54, 32, 0] + lifecycleScope.launch(Dispatchers.IO) { + val builder = StringBuilder() + for (index in 0..11) { + builder.append(data[index].toInt().toChar()) + } + withContext(Dispatchers.Main) { + connectedDeviceCode = data.toDeviceCode() + binding.deviceStatusView.text = "设备编号: $connectedDeviceCode" + SaveKeyValues.putValue( + LocaleConstant.DEVICE_CODE, connectedDeviceCode + ) + } - override fun onDisConnectSuccess(bluetoothGatt: BluetoothGatt?, status: Int) { - val message = weakReferenceHandler.obtainMessage() - message.what = LocaleConstant.DISCONNECT_SUCCESS - message.obj = status - weakReferenceHandler.sendMessage(message) - } + delay(500) - override fun onServiceDiscoverySucceed(bluetoothGatt: BluetoothGatt?, status: Int) { - val message = weakReferenceHandler.obtainMessage() - message.what = LocaleConstant.CONNECT_SUCCESS - weakReferenceHandler.sendMessage(message) - } - - override fun onServiceDiscoveryFailed(bluetoothGatt: BluetoothGatt?, msg: String?) { - val message = weakReferenceHandler.obtainMessage() - message.what = LocaleConstant.CONNECT_FAILURE - weakReferenceHandler.sendMessage(message) - } - - override fun onReceiveMessage( - bluetoothGatt: BluetoothGatt?, characteristic: BluetoothGattCharacteristic? - ) { - val message = weakReferenceHandler.obtainMessage() - message.what = LocaleConstant.RECEIVE_SUCCESS - message.obj = characteristic!!.value - weakReferenceHandler.sendMessage(message) - } - - override fun onReceiveError(errorMsg: String?) { - val message = weakReferenceHandler.obtainMessage() - message.what = LocaleConstant.RECEIVE_FAILURE - weakReferenceHandler.sendMessage(message) - } - - override fun onWriteSuccess(bluetoothGatt: BluetoothGatt?, msg: ByteArray?) { - val message = weakReferenceHandler.obtainMessage() - message.what = LocaleConstant.SEND_SUCCESS - message.obj = msg - weakReferenceHandler.sendMessage(message) - } - - override fun onWriteFailure( - bluetoothGatt: BluetoothGatt?, msg: ByteArray?, errorMsg: String? - ) { - val message = weakReferenceHandler.obtainMessage() - message.what = LocaleConstant.SEND_FAILURE - message.obj = msg - weakReferenceHandler.sendMessage(message) - } - - override fun onReadRssi(bluetoothGatt: BluetoothGatt?, rssi: Int, status: Int) { - - } + //发送数据传送指令 + sendCommand(LocaleConstant.OPEN_TRANSFER_COMMAND) + } + } else if (data.first() == (-86).toByte() && data.size >= 14) { + //[-86, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 13, 10, -86, 0, 0, 0, 0, 1] + handleMethaneData(data) + } else { + //如果收到错误数据,就保存设备编号 + Log.d(kTag, data.contentToString()) + } + } + }) } /** @@ -602,7 +537,7 @@ override fun onDistanceSearched(distance: String) { inspectionViewModel.addInspection( id = newModel!!.id, - deviceCode = connectedDeviceName, + deviceCode = connectedDeviceCode, name = newModel!!.name, startTime = newModel!!.startTime, endTime = System.currentTimeMillis().timestampToCompleteDate(), @@ -635,7 +570,8 @@ binding.settingsValueView.text = dataModel.alarmValue.toString() binding.maxValueView.text = dataModel.maxPotency.toString() //判断是否需要报警 - val isOpen = SaveKeyValues.getValue(LocaleConstant.SINGLE_OPEN_WARNING, false) as Boolean + val isOpen = + SaveKeyValues.getValue(LocaleConstant.SINGLE_OPEN_WARNING, false) as Boolean if (isOpen) { if (dataModel.potency >= dataModel.alarmValue) { //当前值大于设置值,需要报警 @@ -722,6 +658,8 @@ super.onDestroyView() LocationHub.get.stopLocation() SaveKeyValues.removeKey(LocaleConstant.DEVICE_CODE) + bleManager.disconnect(connectedDevice) + bleManager.destroy() } override fun onDestroy() { diff --git a/app/src/main/java/com/casic/birmm/inspect/single/fragment/HomePageFragment.kt b/app/src/main/java/com/casic/birmm/inspect/single/fragment/HomePageFragment.kt index c3130e2..a5ba495 100644 --- a/app/src/main/java/com/casic/birmm/inspect/single/fragment/HomePageFragment.kt +++ b/app/src/main/java/com/casic/birmm/inspect/single/fragment/HomePageFragment.kt @@ -1,14 +1,10 @@ package com.casic.birmm.inspect.single.fragment -import android.annotation.SuppressLint -import android.bluetooth.BluetoothDevice import android.bluetooth.BluetoothGatt import android.bluetooth.BluetoothGattCharacteristic import android.content.Context import android.graphics.Color import android.os.Bundle -import android.os.Handler -import android.os.Message import android.os.Vibrator import android.util.Log import android.view.LayoutInflater @@ -25,6 +21,7 @@ import com.amap.api.maps.model.MyLocationStyle import com.amap.api.maps.model.PolylineOptions import com.casic.birmm.inspect.R +import com.casic.birmm.inspect.base.BaseApplication import com.casic.birmm.inspect.databinding.SingleFragmentMapInspectBinding import com.casic.birmm.inspect.extensions.addAll import com.casic.birmm.inspect.extensions.id @@ -32,44 +29,51 @@ import com.casic.birmm.inspect.extensions.toDeviceCode import com.casic.birmm.inspect.model.NewInspectionModel import com.casic.birmm.inspect.single.view.NewEventActivity -import com.casic.birmm.inspect.utils.* +import com.casic.birmm.inspect.utils.DataBaseManager +import com.casic.birmm.inspect.utils.LoadingDialogHub +import com.casic.birmm.inspect.utils.LocaleConstant +import com.casic.birmm.inspect.utils.LocationHub +import com.casic.birmm.inspect.utils.SoundPoolHelper +import com.clj.fastble.BleManager +import com.clj.fastble.callback.BleGattCallback +import com.clj.fastble.callback.BleNotifyCallback +import com.clj.fastble.callback.BleScanCallback +import com.clj.fastble.callback.BleWriteCallback +import com.clj.fastble.data.BleDevice +import com.clj.fastble.exception.BleException import com.pengxh.kt.lite.base.KotlinBaseFragment -import com.pengxh.kt.lite.extensions.* +import com.pengxh.kt.lite.extensions.convertColor +import com.pengxh.kt.lite.extensions.getStatusBarHeight +import com.pengxh.kt.lite.extensions.navigatePageTo +import com.pengxh.kt.lite.extensions.show +import com.pengxh.kt.lite.extensions.timestampToCompleteDate +import com.pengxh.kt.lite.extensions.timestampToDate import com.pengxh.kt.lite.utils.SaveKeyValues -import com.pengxh.kt.lite.utils.WeakReferenceHandler -import com.pengxh.kt.lite.utils.ble.BLEManager -import com.pengxh.kt.lite.utils.ble.BlueToothBean -import com.pengxh.kt.lite.utils.ble.OnBleConnectListener -import com.pengxh.kt.lite.utils.ble.OnDeviceDiscoveredListener import com.pengxh.kt.lite.widget.dialog.AlertControlDialog import com.pengxh.kt.lite.widget.dialog.AlertInputDialog import com.pengxh.kt.lite.widget.dialog.BottomActionSheet -import com.qmuiteam.qmui.widget.dialog.QMUIBottomSheet import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.delay import kotlinx.coroutines.launch import kotlinx.coroutines.withContext -import java.util.* +import java.util.LinkedList +import java.util.UUID -@SuppressLint("MissingPermission") -class HomePageFragment : KotlinBaseFragment(), Handler.Callback { - - companion object { - lateinit var weakReferenceHandler: WeakReferenceHandler - } +class HomePageFragment : KotlinBaseFragment() { private val kTag = "HomePageFragment" - private val blueToothBeans: MutableList = ArrayList()//搜索展示列表 + private val bleManager by lazy { BleManager.getInstance() } + private val bluetoothDevices by lazy { ArrayList() } private val latLngs = LinkedList() private lateinit var vibrator: Vibrator private lateinit var aMap: AMap + private lateinit var writeUuid: String + private lateinit var notifyUuid: String private var newModel: NewInspectionModel? = null//新建巡检数据结构模型 - private var isBluetoothOn = true - private var curConnectState = false - private var currentDevice: BluetoothDevice? = null// 当前蓝牙设备 + private var connectedDevice: BleDevice? = null private var isGeneratingTask = false private var alarmCount = 0 - private var connectedDeviceName = "" - private var isDataCommandOpened = false + private var connectedDeviceCode = "" override fun initViewBinding( inflater: LayoutInflater, container: ViewGroup? @@ -88,15 +92,8 @@ override fun initOnCreate(savedInstanceState: Bundle?) { vibrator = requireContext().getSystemService(Context.VIBRATOR_SERVICE) as Vibrator - weakReferenceHandler = WeakReferenceHandler(this) - if (BLEManager.initBLE(requireContext())) { - if (!BLEManager.isBluetoothEnable()) { - BLEManager.openBluetooth(false) - } - } else { - "该设备不支持低功耗蓝牙".show(requireContext()) - } + bleManager.enableLog(true).setSplitWriteNum(16).init(BaseApplication.get()) binding.mapView.onCreate(savedInstanceState) aMap = binding.mapView.map @@ -212,7 +209,7 @@ position.toString(), newModel!!.id, newModel!!.name, - connectedDeviceName, + connectedDeviceCode, latLngs.last.longitude.toString(), latLngs.last.latitude.toString() ) @@ -244,287 +241,216 @@ } //蓝牙按钮 - if (isBluetoothOn) { - binding.bluetoothButton.setImageResource(R.drawable.ic_bluetooth_enable) - binding.bluetoothButton.setOnClickListener { - if (curConnectState) { - AlertControlDialog.Builder() - .setContext(requireContext()) - .setTitle("提示") - .setMessage("确定断开设备吗?") - .setNegativeButton("取消") - .setPositiveButton("确定") - .setOnDialogButtonClickListener(object : - AlertControlDialog.OnDialogButtonClickListener { - override fun onCancelClick() { - - } - - override fun onConfirmClick() { - //断开连接 - BLEManager.disConnectDevice() - binding.currentValueView.text = "--" - binding.settingsValueView.text = "--" - binding.maxValueView.text = "--" - binding.deviceStatusView.text = "设备编号:未连接" - isDataCommandOpened = false - "设备已断开连接".show(requireContext()) - } - }).build().show() - } else { - //搜索蓝牙 - if (!BLEManager.isBluetoothEnable()) { - BLEManager.openBluetooth(false) - return@setOnClickListener - } - if (BLEManager.isDiscovery()) {//当前正在搜索设备... - BLEManager.stopDiscoverDevice() - } - LoadingDialogHub.show(requireActivity(), "设备搜索中...") - BLEManager.startDiscoverDevice(object : OnDeviceDiscoveredListener { - override fun onDeviceFound(blueToothBean: BlueToothBean?) { - val message = weakReferenceHandler.obtainMessage() - message.what = LocaleConstant.DISCOVERY_DEVICE - message.obj = blueToothBean - weakReferenceHandler.sendMessage(message) - } - - override fun onDiscoveryTimeout() { - val message = weakReferenceHandler.obtainMessage() - message.what = LocaleConstant.DISCOVERY_OUT_TIME - weakReferenceHandler.sendMessage(message) - } - }, 3 * 1000) - } + binding.bluetoothButton.setOnClickListener { + if (!bleManager.isBlueEnable) { + bleManager.enableBluetooth() } - } else { - binding.bluetoothButton.setImageResource(R.drawable.ic_bluetooth_disabled) + + if (bleManager.isConnected(connectedDevice)) { + AlertControlDialog.Builder() + .setContext(requireContext()) + .setTitle("提示") + .setMessage("确定断开设备吗?") + .setNegativeButton("取消") + .setPositiveButton("确定") + .setOnDialogButtonClickListener(object : + AlertControlDialog.OnDialogButtonClickListener { + override fun onCancelClick() { + + } + + override fun onConfirmClick() { + lifecycleScope.launch(Dispatchers.IO) { + sendCommand(LocaleConstant.CLOSE_TRANSFER_COMMAND) + + delay(500) + + //关闭数据传送指令 + bleManager.disconnect(connectedDevice) + withContext(Dispatchers.Main) { + binding.currentValueView.text = "--" + binding.settingsValueView.text = "--" + binding.maxValueView.text = "--" + binding.deviceStatusView.text = "设备编号:未连接" + "设备已断开连接".show(requireContext()) + + //清除缓存 + bluetoothDevices.clear() + latLngs.clear() + aMap.clear()//清除原来的路线 + SaveKeyValues.removeKey(LocaleConstant.DEVICE_CODE) + } + } + } + }).build().show() + } else { + //搜索蓝牙 + bleManager.scan(object : BleScanCallback() { + override fun onScanStarted(success: Boolean) { + LoadingDialogHub.show(requireActivity(), "设备搜索中...") + } + + override fun onScanning(bleDevice: BleDevice) { + + } + + override fun onScanFinished(scanResultList: List) { + LoadingDialogHub.dismiss() + + scanResultList.forEach { + if (!it.name.isNullOrBlank()) { + bluetoothDevices.add(it) + } + } + showScanResult() + } + }) + } } //重新发送指令按钮 - binding.refreshButton.setOnClickListener { - if (curConnectState) { - BLEManager.sendCommand(LocaleConstant.OPEN_TRANSFER_COMMAND) + binding.retrySendButton.setOnClickListener { + if (bleManager.isConnected(connectedDevice)) { + sendCommand(LocaleConstant.OPEN_TRANSFER_COMMAND) } else { "请先连接设备".show(requireContext()) } } } - override fun handleMessage(msg: Message): Boolean { - when (msg.what) { - LocaleConstant.BLUETOOTH_ON -> { - "蓝牙已开启".show(requireContext()) + private fun sendCommand(command: ByteArray) { + bleManager.write(connectedDevice, LocaleConstant.UUIDS[2], writeUuid, command, + object : BleWriteCallback() { + override fun onWriteSuccess(current: Int, total: Int, justWrite: ByteArray?) { + Log.d(kTag, "onWriteSuccess: 指令下发成功") + } + + override fun onWriteFailure(exception: BleException?) { + "指令下发失败".show(requireContext()) + } + }) + } + + private fun showScanResult() { + val array = ArrayList() + for (it in bluetoothDevices) { + array.add(it.name) + } + + BottomActionSheet.Builder() + .setContext(requireContext()) + .setActionItemTitle(array) + .setItemTextColor(R.color.themeColor.convertColor(requireContext())) + .setOnActionSheetListener(object : BottomActionSheet.OnActionSheetListener { + override fun onActionItemClick(position: Int) { + //连接点击的设备 + startConnectDevice(bluetoothDevices[position]) + } + }).build().show() + } + + private fun startConnectDevice(device: BleDevice) { + // 当前蓝牙设备 + bleManager.connect(device, object : BleGattCallback() { + override fun onStartConnect() { + LoadingDialogHub.show(requireActivity(), "设备连接中...") + } + + override fun onConnectFail(bleDevice: BleDevice, exception: BleException) { + LoadingDialogHub.dismiss() + } + + override fun onConnectSuccess(bleDevice: BleDevice, gatt: BluetoothGatt, status: Int) { + connectedDevice = bleDevice + notifyDeviceService(bleDevice, gatt) + } + + override fun onDisConnected( + isActiveDisConnected: Boolean, bleDevice: BleDevice, + gatt: BluetoothGatt, status: Int + ) { + LoadingDialogHub.dismiss() + connectedDevice = null binding.bluetoothButton.setImageResource(R.drawable.ic_bluetooth_enable) - isBluetoothOn = true } - - LocaleConstant.BLUETOOTH_OFF -> { - "蓝牙已关闭".show(requireContext()) - binding.bluetoothButton.setImageResource(R.drawable.ic_bluetooth_disabled) - isBluetoothOn = false - } - - LocaleConstant.DISCOVERY_DEVICE -> { - val bean = msg.obj as BlueToothBean - if (blueToothBeans.size == 0) { - blueToothBeans.add(bean) - } else { - //0表示未添加到list的新设备,1表示已经扫描并添加到list的设备 - var judge = 0 - for (it in blueToothBeans) { - if (it.bluetoothDevice.address == bean.bluetoothDevice.address) { - judge = 1 - break - } - } - if (judge == 0) { - blueToothBeans.add(bean) - } - } - } - - LocaleConstant.DISCOVERY_OUT_TIME -> { - LoadingDialogHub.dismiss() - if (blueToothBeans.size == 0) { - "无可用设备,请确认设备是否已经开启".show(requireContext()) - } else { - val sheetBuilder = QMUIBottomSheet.BottomListSheetBuilder(requireContext()) - sheetBuilder.setTitle("请选择要连接的设备") - blueToothBeans.forEach { - if (it.bluetoothDevice.name.isNotEmpty()) { - sheetBuilder.addItem(it.bluetoothDevice.name) - } - } - sheetBuilder.setGravityCenter(true).setAddCancelBtn(true) - .setOnSheetItemClickListener { dialog, _, position, _ -> - dialog.dismiss() - //连接点击的设备 - startConnectDevice(blueToothBeans[position].bluetoothDevice) - }.build().show() - } - } - - LocaleConstant.CONNECT_SUCCESS -> { - LoadingDialogHub.dismiss() - curConnectState = true - BLEManager.sendCommand(LocaleConstant.ASK_DEV_CODE_COMMAND) - } - - LocaleConstant.CONNECT_FAILURE -> curConnectState = false - LocaleConstant.SEND_SUCCESS -> Log.d( - kTag, "发送成功-> ${(msg.obj as ByteArray).toList()}" - ) - - LocaleConstant.SEND_FAILURE -> Log.d( - kTag, "发送失败-> ${(msg.obj as ByteArray).toList()}" - ) - - LocaleConstant.RECEIVE_SUCCESS -> { - val bytes = msg.obj as ByteArray - if (bytes.first() == 51.toByte() && bytes.size >= 14) { - //解析deviceCode - //[51, 51, 50, 48, 48, 48, 48, 49, 48, 48, 48, 50, 13, 10] - binding.deviceStatusView.text = "设备编号: ${bytes.toDeviceCode()}" - SaveKeyValues.putValue(LocaleConstant.DEVICE_CODE, bytes.toDeviceCode()) - if (!isDataCommandOpened) { - BLEManager.sendCommand(LocaleConstant.OPEN_TRANSFER_COMMAND) - isDataCommandOpened = true - } - } else if (bytes.first() == (-86).toByte() && bytes.size == 14) { - handleMethaneData(bytes) - } else { - //如果收到错误数据,就保存设备编号 - binding.deviceStatusView.text = "设备编号: $connectedDeviceName" - SaveKeyValues.putValue(LocaleConstant.DEVICE_CODE, connectedDeviceName) - if (!isDataCommandOpened) { - BLEManager.sendCommand(LocaleConstant.OPEN_TRANSFER_COMMAND) - isDataCommandOpened = true - } - } - } - - LocaleConstant.RECEIVE_FAILURE -> Log.d(kTag, "接收失败-> ${msg.obj as String}") - LocaleConstant.DISCONNECT_SUCCESS -> curConnectState = false - LocaleConstant.STOP_TASK -> { - //断开连接 - if (curConnectState) { - BLEManager.disConnectDevice() - } - binding.deviceStatusView.text = "设备编号:未连接" - binding.inspectNameView.text = "" - binding.inspectTimeView.text = "" - binding.currentValueView.text = "--" - binding.settingsValueView.text = "--" - binding.maxValueView.text = "--" - - //按钮状态 - binding.stopInspectButton.isEnabled = false - binding.addInspectionButton.isEnabled = true - - //清除缓存 - blueToothBeans.clear() - latLngs.clear() - aMap.clear()//清除原来的路线 - SaveKeyValues.removeKey(LocaleConstant.DEVICE_CODE) - isDataCommandOpened = false - - LoadingDialogHub.dismiss() - "巡检记录保存成功".show(requireContext()) - } - } - return true + }) } - private fun startConnectDevice(device: BluetoothDevice) { - this.currentDevice = device - if (!curConnectState) { - connectedDeviceName = currentDevice!!.name - LoadingDialogHub.show(requireActivity(), "正在连接[${connectedDeviceName}]...") - BLEManager.connectBleDevice( - requireContext(), currentDevice!!, 10000, - LocaleConstant.SERVICE_UUID, - LocaleConstant.READ_CHARACTERISTIC_UUID, - LocaleConstant.WRITE_CHARACTERISTIC_UUID, - onBleConnectListener - ) - } - } - - private val onBleConnectListener = object : OnBleConnectListener { - override fun onConnecting(bluetoothGatt: BluetoothGatt?) { - + private fun notifyDeviceService(bleDevice: BleDevice, gatt: BluetoothGatt) { + val gattService = gatt.getService(UUID.fromString(LocaleConstant.UUIDS[2])) + if (gattService == null) { + Log.d(kTag, "notifyDeviceService: gattService is null") + LoadingDialogHub.dismiss() + "连接失败,设备不支持低功耗蓝牙".show(requireContext()) + return } - override fun onConnectSuccess(bluetoothGatt: BluetoothGatt?, status: Int) { + gattService.characteristics.forEach { + //获取到相应的服务UUID和特征UUID + val uuid = it.uuid + val properties = it.properties + if (properties and BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE > 0 || + properties and BluetoothGattCharacteristic.PROPERTY_WRITE > 0 + ) { + Log.d(kTag, "uuid可写: $uuid") + writeUuid = uuid.toString() + it.writeType = BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT + } + + if (properties and BluetoothGattCharacteristic.PROPERTY_NOTIFY > 0 || + properties and BluetoothGattCharacteristic.PROPERTY_INDICATE > 0 + ) { + Log.d(kTag, "uuid可通知: $uuid") + notifyUuid = uuid.toString() + } } - override fun onConnectFailure( - bluetoothGatt: BluetoothGatt?, exception: String?, status: Int - ) { - val message = weakReferenceHandler.obtainMessage() - message.what = LocaleConstant.CONNECT_FAILURE - weakReferenceHandler.sendMessage(message) - } + bleManager.notify(bleDevice, LocaleConstant.UUIDS[2], notifyUuid, + object : BleNotifyCallback() { + override fun onNotifySuccess() { + LoadingDialogHub.dismiss() + "连接成功".show(requireContext()) + binding.bluetoothButton.setImageResource(R.drawable.ic_bluetooth_disabled) + lifecycleScope.launch(Dispatchers.IO) { + delay(500) + sendCommand(LocaleConstant.ASK_DEV_CODE_COMMAND) + } + } - override fun onDisConnecting(bluetoothGatt: BluetoothGatt?) { + override fun onNotifyFailure(exception: BleException) { + connectedDevice = null + } - } + override fun onCharacteristicChanged(data: ByteArray) { + // 打开通知后,设备发过来的数据 + Log.d(kTag, data.contentToString()) + if (data.first() == 51.toByte() && data.size >= 14) { + //[51, 57, 50, 48, 50, 52, 48, 49, 48, 48, 48, 54, 32, 0] + lifecycleScope.launch(Dispatchers.IO) { + val builder = StringBuilder() + for (index in 0..11) { + builder.append(data[index].toInt().toChar()) + } + withContext(Dispatchers.Main) { + connectedDeviceCode = data.toDeviceCode() + binding.deviceStatusView.text = "设备编号: $connectedDeviceCode" + SaveKeyValues.putValue( + LocaleConstant.DEVICE_CODE, connectedDeviceCode + ) + } - override fun onDisConnectSuccess(bluetoothGatt: BluetoothGatt?, status: Int) { - val message = weakReferenceHandler.obtainMessage() - message.what = LocaleConstant.DISCONNECT_SUCCESS - message.obj = status - weakReferenceHandler.sendMessage(message) - } + delay(500) - override fun onServiceDiscoverySucceed(bluetoothGatt: BluetoothGatt?, status: Int) { - val message = weakReferenceHandler.obtainMessage() - message.what = LocaleConstant.CONNECT_SUCCESS - weakReferenceHandler.sendMessage(message) - } - - override fun onServiceDiscoveryFailed(bluetoothGatt: BluetoothGatt?, msg: String?) { - val message = weakReferenceHandler.obtainMessage() - message.what = LocaleConstant.CONNECT_FAILURE - weakReferenceHandler.sendMessage(message) - } - - override fun onReceiveMessage( - bluetoothGatt: BluetoothGatt?, characteristic: BluetoothGattCharacteristic? - ) { - val message = weakReferenceHandler.obtainMessage() - message.what = LocaleConstant.RECEIVE_SUCCESS - message.obj = characteristic!!.value - weakReferenceHandler.sendMessage(message) - } - - override fun onReceiveError(errorMsg: String?) { - val message = weakReferenceHandler.obtainMessage() - message.what = LocaleConstant.RECEIVE_FAILURE - weakReferenceHandler.sendMessage(message) - } - - override fun onWriteSuccess(bluetoothGatt: BluetoothGatt?, msg: ByteArray?) { - val message = weakReferenceHandler.obtainMessage() - message.what = LocaleConstant.SEND_SUCCESS - message.obj = msg - weakReferenceHandler.sendMessage(message) - } - - override fun onWriteFailure( - bluetoothGatt: BluetoothGatt?, msg: ByteArray?, errorMsg: String? - ) { - val message = weakReferenceHandler.obtainMessage() - message.what = LocaleConstant.SEND_FAILURE - message.obj = msg - weakReferenceHandler.sendMessage(message) - } - - override fun onReadRssi(bluetoothGatt: BluetoothGatt?, rssi: Int, status: Int) { - - } + //发送数据传送指令 + sendCommand(LocaleConstant.OPEN_TRANSFER_COMMAND) + } + } else if (data.first() == (-86).toByte() && data.size >= 14) { + //[-86, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 13, 10, -86, 0, 0, 0, 0, 1] + handleMethaneData(data) + } + } + }) } /** @@ -554,22 +480,28 @@ DataBaseManager.get.addInspectionRoute(newModel!!.id, latLngs[i]) } + + DataBaseManager.get.addInspection( + inspectionId = newModel!!.id, + deviceCode = connectedDeviceCode, + inspectionName = newModel!!.name, + startTime = newModel!!.startTime, + endTime = System.currentTimeMillis().timestampToCompleteDate(), + date = newModel!!.date, + startLng = latLngs.first.longitude, + startLat = latLngs.first.latitude, + newModel!!.startAddress, + endLng = latLngs.last.longitude, + endLat = latLngs.last.latitude, + newModel!!.endAddress, + routes = newModel!!.routes + ) } - DataBaseManager.get.addInspection( - inspectionId = newModel!!.id, - deviceCode = connectedDeviceName, - inspectionName = newModel!!.name, - startTime = newModel!!.startTime, - endTime = System.currentTimeMillis().timestampToCompleteDate(), - date = newModel!!.date, - startLng = latLngs.first.longitude, - startLat = latLngs.first.latitude, - newModel!!.startAddress, - endLng = latLngs.last.longitude, - endLat = latLngs.last.latitude, - newModel!!.endAddress, - routes = newModel!!.routes - ) + LoadingDialogHub.dismiss() + + //按钮状态 + binding.stopInspectButton.isEnabled = false + binding.addInspectionButton.isEnabled = true } } @@ -626,7 +558,7 @@ DataBaseManager.get.addEvent( taskId = UUID.randomUUID().toString(), inspectionId = newModel!!.id, - deviceCode = connectedDeviceName, + deviceCode = connectedDeviceCode, eventTitle = newModel!!.name, createTime = System.currentTimeMillis().timestampToCompleteDate(), type = "报警事件", @@ -679,6 +611,8 @@ super.onDestroyView() LocationHub.get.stopLocation() SaveKeyValues.removeKey(LocaleConstant.DEVICE_CODE) + bleManager.disconnect(connectedDevice) + bleManager.destroy() } override fun onDestroy() { diff --git a/app/src/main/java/com/casic/birmm/inspect/utils/DataBaseManager.kt b/app/src/main/java/com/casic/birmm/inspect/utils/DataBaseManager.kt index 5464803..3a2cac1 100644 --- a/app/src/main/java/com/casic/birmm/inspect/utils/DataBaseManager.kt +++ b/app/src/main/java/com/casic/birmm/inspect/utils/DataBaseManager.kt @@ -10,7 +10,6 @@ import com.casic.birmm.inspect.greendao.InspectionLocalBeanDao import com.casic.birmm.inspect.greendao.RouteLocalBeanDao import com.casic.birmm.inspect.greendao.TaskEventLocalBeanDao -import com.casic.birmm.inspect.single.fragment.HomePageFragment import com.casic.birmm.inspect.single.fragment.QueryEventFragment import com.casic.birmm.inspect.single.fragment.QueryInspectionFragment @@ -73,10 +72,6 @@ QueryInspectionFragment.weakReferenceHandler.sendEmptyMessage( LocaleConstant.LOAD_INSPECTION ) - - HomePageFragment.weakReferenceHandler.sendEmptyMessage( - LocaleConstant.STOP_TASK - ) } } }) diff --git a/app/src/main/java/com/casic/birmm/inspect/utils/LocaleConstant.kt b/app/src/main/java/com/casic/birmm/inspect/utils/LocaleConstant.kt index 36fca8e..0fc27b6 100644 --- a/app/src/main/java/com/casic/birmm/inspect/utils/LocaleConstant.kt +++ b/app/src/main/java/com/casic/birmm/inspect/utils/LocaleConstant.kt @@ -71,8 +71,35 @@ Manifest.permission.WRITE_EXTERNAL_STORAGE ) } - val ASK_DEV_CODE_COMMAND = byteArrayOf(0x01, 0x0D, 0x0A)// 查询设备编号命令 + + //主服务UUID + val UUIDS = listOf( + "00001801-0000-1000-8000-00805f9b34fb", + "00001800-0000-1000-8000-00805f9b34fb", + "0003cdd0-0000-1000-8000-00805f9b0131" + ) + + //可通知不可写 + val SUB_0_UUIDS = listOf( + "00002a05-0000-1000-8000-00805f9b34fb" + ) + + //可写不可通知 + val SUB_1_UUIDS = listOf( + "00002a00-0000-1000-8000-00805f9b34fb", + "00002a01-0000-1000-8000-00805f9b34fb", + "00002a04-0000-1000-8000-00805f9b34fb" + ) + + //可写可通知 + val SUB_2_UUIDS = listOf( + "0003cdd1-0000-1000-8000-00805f9b0131",//可通知 + "0003cdd2-0000-1000-8000-00805f9b0131"//可写 + ) + + val ASK_DEV_CODE_COMMAND = byteArrayOf(0x01, 0x0D, 0x0A) // 查询设备编号命令 val OPEN_TRANSFER_COMMAND = byteArrayOf(0x02, 0x0D, 0x0A) // 开启数据发送命令 + val CLOSE_TRANSFER_COMMAND = byteArrayOf(0x03, 0x0D, 0x0A) // 关闭数据发送命令 /** * ============================================================================================= @@ -116,9 +143,6 @@ const val USER_OBJECT = "userObject" const val OPEN_WARNING = "isOpenWarning" const val AUTO_RECORD = "isRecordLog" - const val SERVICE_UUID = "0003cdd0-0000-1000-8000-00805f9b0131"//连接设备的UUID - const val WRITE_CHARACTERISTIC_UUID = "0003cdd2-0000-1000-8000-00805f9b0131"//写数据特征值UUID - const val READ_CHARACTERISTIC_UUID = "0003cdd1-0000-1000-8000-00805f9b0131"//读数据特征值UUID const val SINGLE_OPEN_WARNING = "isSingleOpenWarning" const val SINGLE_AUTO_RECORD = "isSingleRecordLog" diff --git a/app/src/main/res/layout/fragment_map_inspect.xml b/app/src/main/res/layout/fragment_map_inspect.xml index 9d86b40..964f207 100644 --- a/app/src/main/res/layout/fragment_map_inspect.xml +++ b/app/src/main/res/layout/fragment_map_inspect.xml @@ -88,7 +88,7 @@ android:src="@drawable/ic_bluetooth" /> diff --git a/app/build.gradle b/app/build.gradle index ae07527..5ad9504 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -116,4 +116,6 @@ implementation 'com.amap.api:3dmap:latest.integration' //高德地图搜索 implementation 'com.amap.api:search:8.1.0' + //蓝牙 + implementation 'com.github.Jasonchenlijian:FastBle:2.4.0' } \ No newline at end of file diff --git a/app/src/main/java/com/casic/birmm/inspect/fragment/HomePageFragment.kt b/app/src/main/java/com/casic/birmm/inspect/fragment/HomePageFragment.kt index da55df2..e5252c1 100644 --- a/app/src/main/java/com/casic/birmm/inspect/fragment/HomePageFragment.kt +++ b/app/src/main/java/com/casic/birmm/inspect/fragment/HomePageFragment.kt @@ -1,14 +1,10 @@ package com.casic.birmm.inspect.fragment -import android.annotation.SuppressLint -import android.bluetooth.BluetoothDevice import android.bluetooth.BluetoothGatt import android.bluetooth.BluetoothGattCharacteristic import android.content.Context import android.graphics.Color import android.os.Bundle -import android.os.Handler -import android.os.Message import android.os.Vibrator import android.util.Log import android.view.LayoutInflater @@ -16,6 +12,7 @@ import android.widget.LinearLayout import androidx.core.text.isDigitsOnly import androidx.lifecycle.ViewModelProvider +import androidx.lifecycle.lifecycleScope import com.amap.api.location.AMapLocation import com.amap.api.maps.AMap import com.amap.api.maps.AMapOptions @@ -25,6 +22,7 @@ import com.amap.api.maps.model.MyLocationStyle import com.amap.api.maps.model.PolylineOptions import com.casic.birmm.inspect.R +import com.casic.birmm.inspect.base.BaseApplication import com.casic.birmm.inspect.callback.OnDistanceSearchListener import com.casic.birmm.inspect.databinding.FragmentMapInspectBinding import com.casic.birmm.inspect.extensions.addAll @@ -41,35 +39,37 @@ import com.casic.birmm.inspect.vm.EventViewModel import com.casic.birmm.inspect.vm.InspectionViewModel import com.casic.birmm.inspect.vm.RouteViewModel +import com.clj.fastble.BleManager +import com.clj.fastble.callback.BleGattCallback +import com.clj.fastble.callback.BleNotifyCallback +import com.clj.fastble.callback.BleScanCallback +import com.clj.fastble.callback.BleWriteCallback +import com.clj.fastble.data.BleDevice +import com.clj.fastble.exception.BleException import com.pengxh.kt.lite.base.KotlinBaseFragment +import com.pengxh.kt.lite.extensions.convertColor import com.pengxh.kt.lite.extensions.getStatusBarHeight import com.pengxh.kt.lite.extensions.navigatePageTo import com.pengxh.kt.lite.extensions.show import com.pengxh.kt.lite.extensions.timestampToCompleteDate import com.pengxh.kt.lite.extensions.timestampToDate import com.pengxh.kt.lite.utils.SaveKeyValues -import com.pengxh.kt.lite.utils.WeakReferenceHandler -import com.pengxh.kt.lite.utils.ble.BLEManager -import com.pengxh.kt.lite.utils.ble.BlueToothBean -import com.pengxh.kt.lite.utils.ble.OnBleConnectListener -import com.pengxh.kt.lite.utils.ble.OnDeviceDiscoveredListener import com.pengxh.kt.lite.vm.LoadState import com.pengxh.kt.lite.widget.dialog.AlertControlDialog import com.pengxh.kt.lite.widget.dialog.AlertInputDialog import com.pengxh.kt.lite.widget.dialog.BottomActionSheet -import com.qmuiteam.qmui.widget.dialog.QMUIBottomSheet +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.delay +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext import java.util.LinkedList import java.util.UUID -@SuppressLint("MissingPermission", "SetTextI18n") -class HomePageFragment : KotlinBaseFragment(), Handler.Callback { - - companion object { - lateinit var weakReferenceHandler: WeakReferenceHandler - } +class HomePageFragment : KotlinBaseFragment() { private val kTag = "HomePageFragment" - private val blueToothBeans: MutableList = ArrayList()//搜索展示列表 + private val bleManager by lazy { BleManager.getInstance() } + private val bluetoothDevices by lazy { java.util.ArrayList() } private val latLngs = LinkedList() private lateinit var userName: String private lateinit var vibrator: Vibrator @@ -77,14 +77,13 @@ private lateinit var inspectionViewModel: InspectionViewModel private lateinit var routeViewModel: RouteViewModel private lateinit var aMap: AMap + private lateinit var writeUuid: String + private lateinit var notifyUuid: String private var newModel: NewInspectionModel? = null//新建巡检数据结构模型 - private var isBluetoothOn = true - private var curConnectState = false - private var currentDevice: BluetoothDevice? = null// 当前蓝牙设备 + private var connectedDevice: BleDevice? = null private var isGeneratingTask = false private var alarmCount = 0 - private var connectedDeviceName = "" - private var isDataCommandOpened = false + private var connectedDeviceCode = "" override fun initViewBinding( inflater: LayoutInflater, container: ViewGroup? @@ -103,16 +102,8 @@ override fun initOnCreate(savedInstanceState: Bundle?) { vibrator = requireContext().getSystemService(Context.VIBRATOR_SERVICE) as Vibrator - weakReferenceHandler = WeakReferenceHandler(this) - //BLE - if (BLEManager.initBLE(requireContext())) { - if (!BLEManager.isBluetoothEnable()) { - BLEManager.openBluetooth(false) - } - } else { - "该设备不支持低功耗蓝牙".show(requireContext()) - } + bleManager.enableLog(true).setSplitWriteNum(16).init(BaseApplication.get()) //Map binding.mapView.onCreate(savedInstanceState) @@ -196,27 +187,10 @@ } is LoadState.Success -> { - //断开连接 - if (curConnectState) { - BLEManager.disConnectDevice() - } - binding.deviceStatusView.text = "设备编号:未连接" - binding.inspectNameView.text = "" - binding.inspectTimeView.text = "" - binding.currentValueView.text = "--" - binding.settingsValueView.text = "--" - binding.maxValueView.text = "--" - //按钮状态 binding.stopInspectButton.isEnabled = false binding.addInspectionButton.isEnabled = true - //清除缓存 - blueToothBeans.clear() - latLngs.clear() - SaveKeyValues.removeKey(LocaleConstant.DEVICE_CODE) - isDataCommandOpened = false - LoadingDialogHub.dismiss() "巡检记录保存成功".show(requireContext()) } @@ -279,7 +253,7 @@ position.toString(), newModel!!.id, newModel!!.name, - connectedDeviceName, + connectedDeviceCode, latLngs.last.longitude.toString(), latLngs.last.latitude.toString() ) @@ -311,261 +285,222 @@ } //蓝牙按钮 - if (isBluetoothOn) { - binding.bluetoothButton.setImageResource(R.drawable.ic_bluetooth_enable) - binding.bluetoothButton.setOnClickListener { - if (curConnectState) { - AlertControlDialog.Builder() - .setContext(requireContext()) - .setTitle("提示") - .setMessage("确定断开设备吗?") - .setNegativeButton("取消") - .setPositiveButton("确定") - .setOnDialogButtonClickListener(object : - AlertControlDialog.OnDialogButtonClickListener { - override fun onCancelClick() { - - } - - override fun onConfirmClick() { - //断开连接 - BLEManager.disConnectDevice() - binding.currentValueView.text = "--" - binding.settingsValueView.text = "--" - binding.maxValueView.text = "--" - binding.deviceStatusView.text = "设备编号:未连接" - isDataCommandOpened = false - "设备已断开连接".show(requireContext()) - } - }).build().show() - } else { - //搜索蓝牙 - if (!BLEManager.isBluetoothEnable()) { - BLEManager.openBluetooth(false) - return@setOnClickListener - } - if (BLEManager.isDiscovery()) {//当前正在搜索设备... - BLEManager.stopDiscoverDevice() - } - LoadingDialogHub.show(requireActivity(), "设备搜索中...") - BLEManager.startDiscoverDevice(object : OnDeviceDiscoveredListener { - override fun onDeviceFound(blueToothBean: BlueToothBean?) { - val message = weakReferenceHandler.obtainMessage() - message.what = LocaleConstant.DISCOVERY_DEVICE - message.obj = blueToothBean - weakReferenceHandler.sendMessage(message) - } - - override fun onDiscoveryTimeout() { - val message = weakReferenceHandler.obtainMessage() - message.what = LocaleConstant.DISCOVERY_OUT_TIME - weakReferenceHandler.sendMessage(message) - } - }, 3 * 1000) - } + binding.bluetoothButton.setOnClickListener { + if (!bleManager.isBlueEnable) { + bleManager.enableBluetooth() } - } else { - binding.bluetoothButton.setImageResource(R.drawable.ic_bluetooth_disabled) - } - //重新发送指令按钮 - binding.refreshButton.setOnClickListener { - if (curConnectState) { - BLEManager.sendCommand(LocaleConstant.OPEN_TRANSFER_COMMAND) + if (bleManager.isConnected(connectedDevice)) { + AlertControlDialog.Builder() + .setContext(requireContext()) + .setTitle("提示") + .setMessage("确定断开设备吗?") + .setNegativeButton("取消") + .setPositiveButton("确定") + .setOnDialogButtonClickListener(object : + AlertControlDialog.OnDialogButtonClickListener { + override fun onCancelClick() { + + } + + override fun onConfirmClick() { + lifecycleScope.launch(Dispatchers.IO) { + sendCommand(LocaleConstant.CLOSE_TRANSFER_COMMAND) + + delay(500) + + //关闭数据传送指令 + bleManager.disconnect(connectedDevice) + withContext(Dispatchers.Main) { + binding.currentValueView.text = "--" + binding.settingsValueView.text = "--" + binding.maxValueView.text = "--" + binding.deviceStatusView.text = "设备编号:未连接" + "设备已断开连接".show(requireContext()) + + //清除缓存 + bluetoothDevices.clear() + latLngs.clear() + aMap.clear()//清除原来的路线 + SaveKeyValues.removeKey(LocaleConstant.DEVICE_CODE) + } + } + } + }).build().show() } else { - "请先连接设备".show(requireContext()) + //搜索蓝牙 + bleManager.scan(object : BleScanCallback() { + override fun onScanStarted(success: Boolean) { + LoadingDialogHub.show(requireActivity(), "设备搜索中...") + } + + override fun onScanning(bleDevice: BleDevice) { + + } + + override fun onScanFinished(scanResultList: List) { + LoadingDialogHub.dismiss() + + scanResultList.forEach { + if (!it.name.isNullOrBlank()) { + bluetoothDevices.add(it) + } + } + showScanResult() + } + }) + } + + //重新发送指令按钮 + binding.retrySendButton.setOnClickListener { + if (bleManager.isConnected(connectedDevice)) { + sendCommand(LocaleConstant.OPEN_TRANSFER_COMMAND) + } else { + "请先连接设备".show(requireContext()) + } } } } - override fun handleMessage(msg: Message): Boolean { - when (msg.what) { - LocaleConstant.BLUETOOTH_ON -> { - "蓝牙已开启".show(requireContext()) + private fun sendCommand(command: ByteArray) { + bleManager.write(connectedDevice, LocaleConstant.UUIDS[2], writeUuid, command, + object : BleWriteCallback() { + override fun onWriteSuccess(current: Int, total: Int, justWrite: ByteArray?) { + Log.d(kTag, "onWriteSuccess: 指令下发成功") + } + + override fun onWriteFailure(exception: BleException?) { + "指令下发失败".show(requireContext()) + } + }) + } + + private fun showScanResult() { + val array = java.util.ArrayList() + for (it in bluetoothDevices) { + array.add(it.name) + } + + BottomActionSheet.Builder() + .setContext(requireContext()) + .setActionItemTitle(array) + .setItemTextColor(R.color.themeColor.convertColor(requireContext())) + .setOnActionSheetListener(object : BottomActionSheet.OnActionSheetListener { + override fun onActionItemClick(position: Int) { + //连接点击的设备 + startConnectDevice(bluetoothDevices[position]) + } + }).build().show() + } + + private fun startConnectDevice(device: BleDevice) { + // 当前蓝牙设备 + bleManager.connect(device, object : BleGattCallback() { + override fun onStartConnect() { + LoadingDialogHub.show(requireActivity(), "设备连接中...") + } + + override fun onConnectFail(bleDevice: BleDevice, exception: BleException) { + LoadingDialogHub.dismiss() + } + + override fun onConnectSuccess( + bleDevice: BleDevice, + gatt: BluetoothGatt, + status: Int + ) { + connectedDevice = bleDevice + notifyDeviceService(bleDevice, gatt) + } + + override fun onDisConnected( + isActiveDisConnected: Boolean, bleDevice: BleDevice, + gatt: BluetoothGatt, status: Int + ) { + LoadingDialogHub.dismiss() + connectedDevice = null binding.bluetoothButton.setImageResource(R.drawable.ic_bluetooth_enable) - isBluetoothOn = true } - - LocaleConstant.BLUETOOTH_OFF -> { - "蓝牙已关闭".show(requireContext()) - binding.bluetoothButton.setImageResource(R.drawable.ic_bluetooth_disabled) - isBluetoothOn = false - } - - LocaleConstant.DISCOVERY_DEVICE -> { - val bean = msg.obj as BlueToothBean - if (blueToothBeans.size == 0) { - blueToothBeans.add(bean) - } else { - //0表示未添加到list的新设备,1表示已经扫描并添加到list的设备 - var judge = 0 - for (it in blueToothBeans) { - if (it.bluetoothDevice.address == bean.bluetoothDevice.address) { - judge = 1 - break - } - } - if (judge == 0) { - blueToothBeans.add(bean) - } - } - } - - LocaleConstant.DISCOVERY_OUT_TIME -> { - LoadingDialogHub.dismiss() - if (blueToothBeans.size == 0) { - "无可用设备,请确认设备是否已经开启".show(requireContext()) - } else { - val sheetBuilder = QMUIBottomSheet.BottomListSheetBuilder(requireContext()) - sheetBuilder.setTitle("请选择要连接的设备") - blueToothBeans.forEach { - if (it.bluetoothDevice.name.isNotEmpty()) { - sheetBuilder.addItem(it.bluetoothDevice.name) - } - } - sheetBuilder.setGravityCenter(true).setAddCancelBtn(true) - .setOnSheetItemClickListener { dialog, _, position, _ -> - dialog.dismiss() - //连接点击的设备 - startConnectDevice(blueToothBeans[position].bluetoothDevice) - }.build().show() - } - } - - LocaleConstant.CONNECT_SUCCESS -> { - LoadingDialogHub.dismiss() - curConnectState = true - BLEManager.sendCommand(LocaleConstant.ASK_DEV_CODE_COMMAND) - } - - LocaleConstant.CONNECT_FAILURE -> curConnectState = false - LocaleConstant.SEND_SUCCESS -> Log.d( - kTag, "发送成功-> ${(msg.obj as ByteArray).toList()}" - ) - - LocaleConstant.SEND_FAILURE -> Log.d( - kTag, "发送失败-> ${(msg.obj as ByteArray).toList()}" - ) - - LocaleConstant.RECEIVE_SUCCESS -> { - val bytes = msg.obj as ByteArray - if (bytes.first() == 51.toByte() && bytes.size >= 14) { - //解析deviceCode - //[51, 51, 50, 48, 48, 48, 48, 49, 48, 48, 48, 50, 13, 10] - binding.deviceStatusView.text = "设备编号: ${bytes.toDeviceCode()}" - SaveKeyValues.putValue(LocaleConstant.DEVICE_CODE, bytes.toDeviceCode()) - if (!isDataCommandOpened) { - BLEManager.sendCommand(LocaleConstant.OPEN_TRANSFER_COMMAND) - isDataCommandOpened = true - } - } else if (bytes.first() == (-86).toByte() && bytes.size == 14) { - handleMethaneData(bytes) - } else { - //如果收到错误数据,就保存设备编号 - binding.deviceStatusView.text = "设备编号: $connectedDeviceName" - SaveKeyValues.putValue(LocaleConstant.DEVICE_CODE, connectedDeviceName) - if (!isDataCommandOpened) { - BLEManager.sendCommand(LocaleConstant.OPEN_TRANSFER_COMMAND) - isDataCommandOpened = true - } - } - } - - LocaleConstant.RECEIVE_FAILURE -> Log.d(kTag, "接收失败-> ${msg.obj as String}") - LocaleConstant.DISCONNECT_SUCCESS -> curConnectState = false - } - return true + }) } - private fun startConnectDevice(device: BluetoothDevice) { - this.currentDevice = device - if (!curConnectState) { - connectedDeviceName = currentDevice!!.name - LoadingDialogHub.show(requireActivity(), "正在连接[${connectedDeviceName}]...") - BLEManager.connectBleDevice( - requireContext(), currentDevice!!, 10000, - LocaleConstant.SERVICE_UUID, - LocaleConstant.READ_CHARACTERISTIC_UUID, - LocaleConstant.WRITE_CHARACTERISTIC_UUID, - onBleConnectListener - ) - } - } - - private val onBleConnectListener = object : OnBleConnectListener { - override fun onConnecting(bluetoothGatt: BluetoothGatt?) { - + private fun notifyDeviceService(bleDevice: BleDevice, gatt: BluetoothGatt) { + val gattService = gatt.getService(UUID.fromString(LocaleConstant.UUIDS[2])) + if (gattService == null) { + Log.d(kTag, "notifyDeviceService: gattService is null") + LoadingDialogHub.dismiss() + "连接失败,设备不支持低功耗蓝牙".show(requireContext()) + return } - override fun onConnectSuccess(bluetoothGatt: BluetoothGatt?, status: Int) { + gattService.characteristics.forEach { + //获取到相应的服务UUID和特征UUID + val uuid = it.uuid + val properties = it.properties + if (properties and BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE > 0 || + properties and BluetoothGattCharacteristic.PROPERTY_WRITE > 0 + ) { + Log.d(kTag, "uuid可写: $uuid") + writeUuid = uuid.toString() + it.writeType = BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT + } + + if (properties and BluetoothGattCharacteristic.PROPERTY_NOTIFY > 0 || + properties and BluetoothGattCharacteristic.PROPERTY_INDICATE > 0 + ) { + Log.d(kTag, "uuid可通知: $uuid") + notifyUuid = uuid.toString() + } } - override fun onConnectFailure( - bluetoothGatt: BluetoothGatt?, exception: String?, status: Int - ) { - val message = weakReferenceHandler.obtainMessage() - message.what = LocaleConstant.CONNECT_FAILURE - weakReferenceHandler.sendMessage(message) - } + bleManager.notify(bleDevice, LocaleConstant.UUIDS[2], notifyUuid, + object : BleNotifyCallback() { + override fun onNotifySuccess() { + LoadingDialogHub.dismiss() + "连接成功".show(requireContext()) + binding.bluetoothButton.setImageResource(R.drawable.ic_bluetooth_disabled) + lifecycleScope.launch(Dispatchers.IO) { + delay(500) + sendCommand(LocaleConstant.ASK_DEV_CODE_COMMAND) + } + } - override fun onDisConnecting(bluetoothGatt: BluetoothGatt?) { + override fun onNotifyFailure(exception: BleException) { + connectedDevice = null + } - } + override fun onCharacteristicChanged(data: ByteArray) { + // 打开通知后,设备发过来的数据 + if (data.first() == 51.toByte() && data.size >= 14) { + //[51, 57, 50, 48, 50, 52, 48, 49, 48, 48, 48, 54, 32, 0] + lifecycleScope.launch(Dispatchers.IO) { + val builder = StringBuilder() + for (index in 0..11) { + builder.append(data[index].toInt().toChar()) + } + withContext(Dispatchers.Main) { + connectedDeviceCode = data.toDeviceCode() + binding.deviceStatusView.text = "设备编号: $connectedDeviceCode" + SaveKeyValues.putValue( + LocaleConstant.DEVICE_CODE, connectedDeviceCode + ) + } - override fun onDisConnectSuccess(bluetoothGatt: BluetoothGatt?, status: Int) { - val message = weakReferenceHandler.obtainMessage() - message.what = LocaleConstant.DISCONNECT_SUCCESS - message.obj = status - weakReferenceHandler.sendMessage(message) - } + delay(500) - override fun onServiceDiscoverySucceed(bluetoothGatt: BluetoothGatt?, status: Int) { - val message = weakReferenceHandler.obtainMessage() - message.what = LocaleConstant.CONNECT_SUCCESS - weakReferenceHandler.sendMessage(message) - } - - override fun onServiceDiscoveryFailed(bluetoothGatt: BluetoothGatt?, msg: String?) { - val message = weakReferenceHandler.obtainMessage() - message.what = LocaleConstant.CONNECT_FAILURE - weakReferenceHandler.sendMessage(message) - } - - override fun onReceiveMessage( - bluetoothGatt: BluetoothGatt?, characteristic: BluetoothGattCharacteristic? - ) { - val message = weakReferenceHandler.obtainMessage() - message.what = LocaleConstant.RECEIVE_SUCCESS - message.obj = characteristic!!.value - weakReferenceHandler.sendMessage(message) - } - - override fun onReceiveError(errorMsg: String?) { - val message = weakReferenceHandler.obtainMessage() - message.what = LocaleConstant.RECEIVE_FAILURE - weakReferenceHandler.sendMessage(message) - } - - override fun onWriteSuccess(bluetoothGatt: BluetoothGatt?, msg: ByteArray?) { - val message = weakReferenceHandler.obtainMessage() - message.what = LocaleConstant.SEND_SUCCESS - message.obj = msg - weakReferenceHandler.sendMessage(message) - } - - override fun onWriteFailure( - bluetoothGatt: BluetoothGatt?, msg: ByteArray?, errorMsg: String? - ) { - val message = weakReferenceHandler.obtainMessage() - message.what = LocaleConstant.SEND_FAILURE - message.obj = msg - weakReferenceHandler.sendMessage(message) - } - - override fun onReadRssi(bluetoothGatt: BluetoothGatt?, rssi: Int, status: Int) { - - } + //发送数据传送指令 + sendCommand(LocaleConstant.OPEN_TRANSFER_COMMAND) + } + } else if (data.first() == (-86).toByte() && data.size >= 14) { + //[-86, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 13, 10, -86, 0, 0, 0, 0, 1] + handleMethaneData(data) + } else { + //如果收到错误数据,就保存设备编号 + Log.d(kTag, data.contentToString()) + } + } + }) } /** @@ -602,7 +537,7 @@ override fun onDistanceSearched(distance: String) { inspectionViewModel.addInspection( id = newModel!!.id, - deviceCode = connectedDeviceName, + deviceCode = connectedDeviceCode, name = newModel!!.name, startTime = newModel!!.startTime, endTime = System.currentTimeMillis().timestampToCompleteDate(), @@ -635,7 +570,8 @@ binding.settingsValueView.text = dataModel.alarmValue.toString() binding.maxValueView.text = dataModel.maxPotency.toString() //判断是否需要报警 - val isOpen = SaveKeyValues.getValue(LocaleConstant.SINGLE_OPEN_WARNING, false) as Boolean + val isOpen = + SaveKeyValues.getValue(LocaleConstant.SINGLE_OPEN_WARNING, false) as Boolean if (isOpen) { if (dataModel.potency >= dataModel.alarmValue) { //当前值大于设置值,需要报警 @@ -722,6 +658,8 @@ super.onDestroyView() LocationHub.get.stopLocation() SaveKeyValues.removeKey(LocaleConstant.DEVICE_CODE) + bleManager.disconnect(connectedDevice) + bleManager.destroy() } override fun onDestroy() { diff --git a/app/src/main/java/com/casic/birmm/inspect/single/fragment/HomePageFragment.kt b/app/src/main/java/com/casic/birmm/inspect/single/fragment/HomePageFragment.kt index c3130e2..a5ba495 100644 --- a/app/src/main/java/com/casic/birmm/inspect/single/fragment/HomePageFragment.kt +++ b/app/src/main/java/com/casic/birmm/inspect/single/fragment/HomePageFragment.kt @@ -1,14 +1,10 @@ package com.casic.birmm.inspect.single.fragment -import android.annotation.SuppressLint -import android.bluetooth.BluetoothDevice import android.bluetooth.BluetoothGatt import android.bluetooth.BluetoothGattCharacteristic import android.content.Context import android.graphics.Color import android.os.Bundle -import android.os.Handler -import android.os.Message import android.os.Vibrator import android.util.Log import android.view.LayoutInflater @@ -25,6 +21,7 @@ import com.amap.api.maps.model.MyLocationStyle import com.amap.api.maps.model.PolylineOptions import com.casic.birmm.inspect.R +import com.casic.birmm.inspect.base.BaseApplication import com.casic.birmm.inspect.databinding.SingleFragmentMapInspectBinding import com.casic.birmm.inspect.extensions.addAll import com.casic.birmm.inspect.extensions.id @@ -32,44 +29,51 @@ import com.casic.birmm.inspect.extensions.toDeviceCode import com.casic.birmm.inspect.model.NewInspectionModel import com.casic.birmm.inspect.single.view.NewEventActivity -import com.casic.birmm.inspect.utils.* +import com.casic.birmm.inspect.utils.DataBaseManager +import com.casic.birmm.inspect.utils.LoadingDialogHub +import com.casic.birmm.inspect.utils.LocaleConstant +import com.casic.birmm.inspect.utils.LocationHub +import com.casic.birmm.inspect.utils.SoundPoolHelper +import com.clj.fastble.BleManager +import com.clj.fastble.callback.BleGattCallback +import com.clj.fastble.callback.BleNotifyCallback +import com.clj.fastble.callback.BleScanCallback +import com.clj.fastble.callback.BleWriteCallback +import com.clj.fastble.data.BleDevice +import com.clj.fastble.exception.BleException import com.pengxh.kt.lite.base.KotlinBaseFragment -import com.pengxh.kt.lite.extensions.* +import com.pengxh.kt.lite.extensions.convertColor +import com.pengxh.kt.lite.extensions.getStatusBarHeight +import com.pengxh.kt.lite.extensions.navigatePageTo +import com.pengxh.kt.lite.extensions.show +import com.pengxh.kt.lite.extensions.timestampToCompleteDate +import com.pengxh.kt.lite.extensions.timestampToDate import com.pengxh.kt.lite.utils.SaveKeyValues -import com.pengxh.kt.lite.utils.WeakReferenceHandler -import com.pengxh.kt.lite.utils.ble.BLEManager -import com.pengxh.kt.lite.utils.ble.BlueToothBean -import com.pengxh.kt.lite.utils.ble.OnBleConnectListener -import com.pengxh.kt.lite.utils.ble.OnDeviceDiscoveredListener import com.pengxh.kt.lite.widget.dialog.AlertControlDialog import com.pengxh.kt.lite.widget.dialog.AlertInputDialog import com.pengxh.kt.lite.widget.dialog.BottomActionSheet -import com.qmuiteam.qmui.widget.dialog.QMUIBottomSheet import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.delay import kotlinx.coroutines.launch import kotlinx.coroutines.withContext -import java.util.* +import java.util.LinkedList +import java.util.UUID -@SuppressLint("MissingPermission") -class HomePageFragment : KotlinBaseFragment(), Handler.Callback { - - companion object { - lateinit var weakReferenceHandler: WeakReferenceHandler - } +class HomePageFragment : KotlinBaseFragment() { private val kTag = "HomePageFragment" - private val blueToothBeans: MutableList = ArrayList()//搜索展示列表 + private val bleManager by lazy { BleManager.getInstance() } + private val bluetoothDevices by lazy { ArrayList() } private val latLngs = LinkedList() private lateinit var vibrator: Vibrator private lateinit var aMap: AMap + private lateinit var writeUuid: String + private lateinit var notifyUuid: String private var newModel: NewInspectionModel? = null//新建巡检数据结构模型 - private var isBluetoothOn = true - private var curConnectState = false - private var currentDevice: BluetoothDevice? = null// 当前蓝牙设备 + private var connectedDevice: BleDevice? = null private var isGeneratingTask = false private var alarmCount = 0 - private var connectedDeviceName = "" - private var isDataCommandOpened = false + private var connectedDeviceCode = "" override fun initViewBinding( inflater: LayoutInflater, container: ViewGroup? @@ -88,15 +92,8 @@ override fun initOnCreate(savedInstanceState: Bundle?) { vibrator = requireContext().getSystemService(Context.VIBRATOR_SERVICE) as Vibrator - weakReferenceHandler = WeakReferenceHandler(this) - if (BLEManager.initBLE(requireContext())) { - if (!BLEManager.isBluetoothEnable()) { - BLEManager.openBluetooth(false) - } - } else { - "该设备不支持低功耗蓝牙".show(requireContext()) - } + bleManager.enableLog(true).setSplitWriteNum(16).init(BaseApplication.get()) binding.mapView.onCreate(savedInstanceState) aMap = binding.mapView.map @@ -212,7 +209,7 @@ position.toString(), newModel!!.id, newModel!!.name, - connectedDeviceName, + connectedDeviceCode, latLngs.last.longitude.toString(), latLngs.last.latitude.toString() ) @@ -244,287 +241,216 @@ } //蓝牙按钮 - if (isBluetoothOn) { - binding.bluetoothButton.setImageResource(R.drawable.ic_bluetooth_enable) - binding.bluetoothButton.setOnClickListener { - if (curConnectState) { - AlertControlDialog.Builder() - .setContext(requireContext()) - .setTitle("提示") - .setMessage("确定断开设备吗?") - .setNegativeButton("取消") - .setPositiveButton("确定") - .setOnDialogButtonClickListener(object : - AlertControlDialog.OnDialogButtonClickListener { - override fun onCancelClick() { - - } - - override fun onConfirmClick() { - //断开连接 - BLEManager.disConnectDevice() - binding.currentValueView.text = "--" - binding.settingsValueView.text = "--" - binding.maxValueView.text = "--" - binding.deviceStatusView.text = "设备编号:未连接" - isDataCommandOpened = false - "设备已断开连接".show(requireContext()) - } - }).build().show() - } else { - //搜索蓝牙 - if (!BLEManager.isBluetoothEnable()) { - BLEManager.openBluetooth(false) - return@setOnClickListener - } - if (BLEManager.isDiscovery()) {//当前正在搜索设备... - BLEManager.stopDiscoverDevice() - } - LoadingDialogHub.show(requireActivity(), "设备搜索中...") - BLEManager.startDiscoverDevice(object : OnDeviceDiscoveredListener { - override fun onDeviceFound(blueToothBean: BlueToothBean?) { - val message = weakReferenceHandler.obtainMessage() - message.what = LocaleConstant.DISCOVERY_DEVICE - message.obj = blueToothBean - weakReferenceHandler.sendMessage(message) - } - - override fun onDiscoveryTimeout() { - val message = weakReferenceHandler.obtainMessage() - message.what = LocaleConstant.DISCOVERY_OUT_TIME - weakReferenceHandler.sendMessage(message) - } - }, 3 * 1000) - } + binding.bluetoothButton.setOnClickListener { + if (!bleManager.isBlueEnable) { + bleManager.enableBluetooth() } - } else { - binding.bluetoothButton.setImageResource(R.drawable.ic_bluetooth_disabled) + + if (bleManager.isConnected(connectedDevice)) { + AlertControlDialog.Builder() + .setContext(requireContext()) + .setTitle("提示") + .setMessage("确定断开设备吗?") + .setNegativeButton("取消") + .setPositiveButton("确定") + .setOnDialogButtonClickListener(object : + AlertControlDialog.OnDialogButtonClickListener { + override fun onCancelClick() { + + } + + override fun onConfirmClick() { + lifecycleScope.launch(Dispatchers.IO) { + sendCommand(LocaleConstant.CLOSE_TRANSFER_COMMAND) + + delay(500) + + //关闭数据传送指令 + bleManager.disconnect(connectedDevice) + withContext(Dispatchers.Main) { + binding.currentValueView.text = "--" + binding.settingsValueView.text = "--" + binding.maxValueView.text = "--" + binding.deviceStatusView.text = "设备编号:未连接" + "设备已断开连接".show(requireContext()) + + //清除缓存 + bluetoothDevices.clear() + latLngs.clear() + aMap.clear()//清除原来的路线 + SaveKeyValues.removeKey(LocaleConstant.DEVICE_CODE) + } + } + } + }).build().show() + } else { + //搜索蓝牙 + bleManager.scan(object : BleScanCallback() { + override fun onScanStarted(success: Boolean) { + LoadingDialogHub.show(requireActivity(), "设备搜索中...") + } + + override fun onScanning(bleDevice: BleDevice) { + + } + + override fun onScanFinished(scanResultList: List) { + LoadingDialogHub.dismiss() + + scanResultList.forEach { + if (!it.name.isNullOrBlank()) { + bluetoothDevices.add(it) + } + } + showScanResult() + } + }) + } } //重新发送指令按钮 - binding.refreshButton.setOnClickListener { - if (curConnectState) { - BLEManager.sendCommand(LocaleConstant.OPEN_TRANSFER_COMMAND) + binding.retrySendButton.setOnClickListener { + if (bleManager.isConnected(connectedDevice)) { + sendCommand(LocaleConstant.OPEN_TRANSFER_COMMAND) } else { "请先连接设备".show(requireContext()) } } } - override fun handleMessage(msg: Message): Boolean { - when (msg.what) { - LocaleConstant.BLUETOOTH_ON -> { - "蓝牙已开启".show(requireContext()) + private fun sendCommand(command: ByteArray) { + bleManager.write(connectedDevice, LocaleConstant.UUIDS[2], writeUuid, command, + object : BleWriteCallback() { + override fun onWriteSuccess(current: Int, total: Int, justWrite: ByteArray?) { + Log.d(kTag, "onWriteSuccess: 指令下发成功") + } + + override fun onWriteFailure(exception: BleException?) { + "指令下发失败".show(requireContext()) + } + }) + } + + private fun showScanResult() { + val array = ArrayList() + for (it in bluetoothDevices) { + array.add(it.name) + } + + BottomActionSheet.Builder() + .setContext(requireContext()) + .setActionItemTitle(array) + .setItemTextColor(R.color.themeColor.convertColor(requireContext())) + .setOnActionSheetListener(object : BottomActionSheet.OnActionSheetListener { + override fun onActionItemClick(position: Int) { + //连接点击的设备 + startConnectDevice(bluetoothDevices[position]) + } + }).build().show() + } + + private fun startConnectDevice(device: BleDevice) { + // 当前蓝牙设备 + bleManager.connect(device, object : BleGattCallback() { + override fun onStartConnect() { + LoadingDialogHub.show(requireActivity(), "设备连接中...") + } + + override fun onConnectFail(bleDevice: BleDevice, exception: BleException) { + LoadingDialogHub.dismiss() + } + + override fun onConnectSuccess(bleDevice: BleDevice, gatt: BluetoothGatt, status: Int) { + connectedDevice = bleDevice + notifyDeviceService(bleDevice, gatt) + } + + override fun onDisConnected( + isActiveDisConnected: Boolean, bleDevice: BleDevice, + gatt: BluetoothGatt, status: Int + ) { + LoadingDialogHub.dismiss() + connectedDevice = null binding.bluetoothButton.setImageResource(R.drawable.ic_bluetooth_enable) - isBluetoothOn = true } - - LocaleConstant.BLUETOOTH_OFF -> { - "蓝牙已关闭".show(requireContext()) - binding.bluetoothButton.setImageResource(R.drawable.ic_bluetooth_disabled) - isBluetoothOn = false - } - - LocaleConstant.DISCOVERY_DEVICE -> { - val bean = msg.obj as BlueToothBean - if (blueToothBeans.size == 0) { - blueToothBeans.add(bean) - } else { - //0表示未添加到list的新设备,1表示已经扫描并添加到list的设备 - var judge = 0 - for (it in blueToothBeans) { - if (it.bluetoothDevice.address == bean.bluetoothDevice.address) { - judge = 1 - break - } - } - if (judge == 0) { - blueToothBeans.add(bean) - } - } - } - - LocaleConstant.DISCOVERY_OUT_TIME -> { - LoadingDialogHub.dismiss() - if (blueToothBeans.size == 0) { - "无可用设备,请确认设备是否已经开启".show(requireContext()) - } else { - val sheetBuilder = QMUIBottomSheet.BottomListSheetBuilder(requireContext()) - sheetBuilder.setTitle("请选择要连接的设备") - blueToothBeans.forEach { - if (it.bluetoothDevice.name.isNotEmpty()) { - sheetBuilder.addItem(it.bluetoothDevice.name) - } - } - sheetBuilder.setGravityCenter(true).setAddCancelBtn(true) - .setOnSheetItemClickListener { dialog, _, position, _ -> - dialog.dismiss() - //连接点击的设备 - startConnectDevice(blueToothBeans[position].bluetoothDevice) - }.build().show() - } - } - - LocaleConstant.CONNECT_SUCCESS -> { - LoadingDialogHub.dismiss() - curConnectState = true - BLEManager.sendCommand(LocaleConstant.ASK_DEV_CODE_COMMAND) - } - - LocaleConstant.CONNECT_FAILURE -> curConnectState = false - LocaleConstant.SEND_SUCCESS -> Log.d( - kTag, "发送成功-> ${(msg.obj as ByteArray).toList()}" - ) - - LocaleConstant.SEND_FAILURE -> Log.d( - kTag, "发送失败-> ${(msg.obj as ByteArray).toList()}" - ) - - LocaleConstant.RECEIVE_SUCCESS -> { - val bytes = msg.obj as ByteArray - if (bytes.first() == 51.toByte() && bytes.size >= 14) { - //解析deviceCode - //[51, 51, 50, 48, 48, 48, 48, 49, 48, 48, 48, 50, 13, 10] - binding.deviceStatusView.text = "设备编号: ${bytes.toDeviceCode()}" - SaveKeyValues.putValue(LocaleConstant.DEVICE_CODE, bytes.toDeviceCode()) - if (!isDataCommandOpened) { - BLEManager.sendCommand(LocaleConstant.OPEN_TRANSFER_COMMAND) - isDataCommandOpened = true - } - } else if (bytes.first() == (-86).toByte() && bytes.size == 14) { - handleMethaneData(bytes) - } else { - //如果收到错误数据,就保存设备编号 - binding.deviceStatusView.text = "设备编号: $connectedDeviceName" - SaveKeyValues.putValue(LocaleConstant.DEVICE_CODE, connectedDeviceName) - if (!isDataCommandOpened) { - BLEManager.sendCommand(LocaleConstant.OPEN_TRANSFER_COMMAND) - isDataCommandOpened = true - } - } - } - - LocaleConstant.RECEIVE_FAILURE -> Log.d(kTag, "接收失败-> ${msg.obj as String}") - LocaleConstant.DISCONNECT_SUCCESS -> curConnectState = false - LocaleConstant.STOP_TASK -> { - //断开连接 - if (curConnectState) { - BLEManager.disConnectDevice() - } - binding.deviceStatusView.text = "设备编号:未连接" - binding.inspectNameView.text = "" - binding.inspectTimeView.text = "" - binding.currentValueView.text = "--" - binding.settingsValueView.text = "--" - binding.maxValueView.text = "--" - - //按钮状态 - binding.stopInspectButton.isEnabled = false - binding.addInspectionButton.isEnabled = true - - //清除缓存 - blueToothBeans.clear() - latLngs.clear() - aMap.clear()//清除原来的路线 - SaveKeyValues.removeKey(LocaleConstant.DEVICE_CODE) - isDataCommandOpened = false - - LoadingDialogHub.dismiss() - "巡检记录保存成功".show(requireContext()) - } - } - return true + }) } - private fun startConnectDevice(device: BluetoothDevice) { - this.currentDevice = device - if (!curConnectState) { - connectedDeviceName = currentDevice!!.name - LoadingDialogHub.show(requireActivity(), "正在连接[${connectedDeviceName}]...") - BLEManager.connectBleDevice( - requireContext(), currentDevice!!, 10000, - LocaleConstant.SERVICE_UUID, - LocaleConstant.READ_CHARACTERISTIC_UUID, - LocaleConstant.WRITE_CHARACTERISTIC_UUID, - onBleConnectListener - ) - } - } - - private val onBleConnectListener = object : OnBleConnectListener { - override fun onConnecting(bluetoothGatt: BluetoothGatt?) { - + private fun notifyDeviceService(bleDevice: BleDevice, gatt: BluetoothGatt) { + val gattService = gatt.getService(UUID.fromString(LocaleConstant.UUIDS[2])) + if (gattService == null) { + Log.d(kTag, "notifyDeviceService: gattService is null") + LoadingDialogHub.dismiss() + "连接失败,设备不支持低功耗蓝牙".show(requireContext()) + return } - override fun onConnectSuccess(bluetoothGatt: BluetoothGatt?, status: Int) { + gattService.characteristics.forEach { + //获取到相应的服务UUID和特征UUID + val uuid = it.uuid + val properties = it.properties + if (properties and BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE > 0 || + properties and BluetoothGattCharacteristic.PROPERTY_WRITE > 0 + ) { + Log.d(kTag, "uuid可写: $uuid") + writeUuid = uuid.toString() + it.writeType = BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT + } + + if (properties and BluetoothGattCharacteristic.PROPERTY_NOTIFY > 0 || + properties and BluetoothGattCharacteristic.PROPERTY_INDICATE > 0 + ) { + Log.d(kTag, "uuid可通知: $uuid") + notifyUuid = uuid.toString() + } } - override fun onConnectFailure( - bluetoothGatt: BluetoothGatt?, exception: String?, status: Int - ) { - val message = weakReferenceHandler.obtainMessage() - message.what = LocaleConstant.CONNECT_FAILURE - weakReferenceHandler.sendMessage(message) - } + bleManager.notify(bleDevice, LocaleConstant.UUIDS[2], notifyUuid, + object : BleNotifyCallback() { + override fun onNotifySuccess() { + LoadingDialogHub.dismiss() + "连接成功".show(requireContext()) + binding.bluetoothButton.setImageResource(R.drawable.ic_bluetooth_disabled) + lifecycleScope.launch(Dispatchers.IO) { + delay(500) + sendCommand(LocaleConstant.ASK_DEV_CODE_COMMAND) + } + } - override fun onDisConnecting(bluetoothGatt: BluetoothGatt?) { + override fun onNotifyFailure(exception: BleException) { + connectedDevice = null + } - } + override fun onCharacteristicChanged(data: ByteArray) { + // 打开通知后,设备发过来的数据 + Log.d(kTag, data.contentToString()) + if (data.first() == 51.toByte() && data.size >= 14) { + //[51, 57, 50, 48, 50, 52, 48, 49, 48, 48, 48, 54, 32, 0] + lifecycleScope.launch(Dispatchers.IO) { + val builder = StringBuilder() + for (index in 0..11) { + builder.append(data[index].toInt().toChar()) + } + withContext(Dispatchers.Main) { + connectedDeviceCode = data.toDeviceCode() + binding.deviceStatusView.text = "设备编号: $connectedDeviceCode" + SaveKeyValues.putValue( + LocaleConstant.DEVICE_CODE, connectedDeviceCode + ) + } - override fun onDisConnectSuccess(bluetoothGatt: BluetoothGatt?, status: Int) { - val message = weakReferenceHandler.obtainMessage() - message.what = LocaleConstant.DISCONNECT_SUCCESS - message.obj = status - weakReferenceHandler.sendMessage(message) - } + delay(500) - override fun onServiceDiscoverySucceed(bluetoothGatt: BluetoothGatt?, status: Int) { - val message = weakReferenceHandler.obtainMessage() - message.what = LocaleConstant.CONNECT_SUCCESS - weakReferenceHandler.sendMessage(message) - } - - override fun onServiceDiscoveryFailed(bluetoothGatt: BluetoothGatt?, msg: String?) { - val message = weakReferenceHandler.obtainMessage() - message.what = LocaleConstant.CONNECT_FAILURE - weakReferenceHandler.sendMessage(message) - } - - override fun onReceiveMessage( - bluetoothGatt: BluetoothGatt?, characteristic: BluetoothGattCharacteristic? - ) { - val message = weakReferenceHandler.obtainMessage() - message.what = LocaleConstant.RECEIVE_SUCCESS - message.obj = characteristic!!.value - weakReferenceHandler.sendMessage(message) - } - - override fun onReceiveError(errorMsg: String?) { - val message = weakReferenceHandler.obtainMessage() - message.what = LocaleConstant.RECEIVE_FAILURE - weakReferenceHandler.sendMessage(message) - } - - override fun onWriteSuccess(bluetoothGatt: BluetoothGatt?, msg: ByteArray?) { - val message = weakReferenceHandler.obtainMessage() - message.what = LocaleConstant.SEND_SUCCESS - message.obj = msg - weakReferenceHandler.sendMessage(message) - } - - override fun onWriteFailure( - bluetoothGatt: BluetoothGatt?, msg: ByteArray?, errorMsg: String? - ) { - val message = weakReferenceHandler.obtainMessage() - message.what = LocaleConstant.SEND_FAILURE - message.obj = msg - weakReferenceHandler.sendMessage(message) - } - - override fun onReadRssi(bluetoothGatt: BluetoothGatt?, rssi: Int, status: Int) { - - } + //发送数据传送指令 + sendCommand(LocaleConstant.OPEN_TRANSFER_COMMAND) + } + } else if (data.first() == (-86).toByte() && data.size >= 14) { + //[-86, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 13, 10, -86, 0, 0, 0, 0, 1] + handleMethaneData(data) + } + } + }) } /** @@ -554,22 +480,28 @@ DataBaseManager.get.addInspectionRoute(newModel!!.id, latLngs[i]) } + + DataBaseManager.get.addInspection( + inspectionId = newModel!!.id, + deviceCode = connectedDeviceCode, + inspectionName = newModel!!.name, + startTime = newModel!!.startTime, + endTime = System.currentTimeMillis().timestampToCompleteDate(), + date = newModel!!.date, + startLng = latLngs.first.longitude, + startLat = latLngs.first.latitude, + newModel!!.startAddress, + endLng = latLngs.last.longitude, + endLat = latLngs.last.latitude, + newModel!!.endAddress, + routes = newModel!!.routes + ) } - DataBaseManager.get.addInspection( - inspectionId = newModel!!.id, - deviceCode = connectedDeviceName, - inspectionName = newModel!!.name, - startTime = newModel!!.startTime, - endTime = System.currentTimeMillis().timestampToCompleteDate(), - date = newModel!!.date, - startLng = latLngs.first.longitude, - startLat = latLngs.first.latitude, - newModel!!.startAddress, - endLng = latLngs.last.longitude, - endLat = latLngs.last.latitude, - newModel!!.endAddress, - routes = newModel!!.routes - ) + LoadingDialogHub.dismiss() + + //按钮状态 + binding.stopInspectButton.isEnabled = false + binding.addInspectionButton.isEnabled = true } } @@ -626,7 +558,7 @@ DataBaseManager.get.addEvent( taskId = UUID.randomUUID().toString(), inspectionId = newModel!!.id, - deviceCode = connectedDeviceName, + deviceCode = connectedDeviceCode, eventTitle = newModel!!.name, createTime = System.currentTimeMillis().timestampToCompleteDate(), type = "报警事件", @@ -679,6 +611,8 @@ super.onDestroyView() LocationHub.get.stopLocation() SaveKeyValues.removeKey(LocaleConstant.DEVICE_CODE) + bleManager.disconnect(connectedDevice) + bleManager.destroy() } override fun onDestroy() { diff --git a/app/src/main/java/com/casic/birmm/inspect/utils/DataBaseManager.kt b/app/src/main/java/com/casic/birmm/inspect/utils/DataBaseManager.kt index 5464803..3a2cac1 100644 --- a/app/src/main/java/com/casic/birmm/inspect/utils/DataBaseManager.kt +++ b/app/src/main/java/com/casic/birmm/inspect/utils/DataBaseManager.kt @@ -10,7 +10,6 @@ import com.casic.birmm.inspect.greendao.InspectionLocalBeanDao import com.casic.birmm.inspect.greendao.RouteLocalBeanDao import com.casic.birmm.inspect.greendao.TaskEventLocalBeanDao -import com.casic.birmm.inspect.single.fragment.HomePageFragment import com.casic.birmm.inspect.single.fragment.QueryEventFragment import com.casic.birmm.inspect.single.fragment.QueryInspectionFragment @@ -73,10 +72,6 @@ QueryInspectionFragment.weakReferenceHandler.sendEmptyMessage( LocaleConstant.LOAD_INSPECTION ) - - HomePageFragment.weakReferenceHandler.sendEmptyMessage( - LocaleConstant.STOP_TASK - ) } } }) diff --git a/app/src/main/java/com/casic/birmm/inspect/utils/LocaleConstant.kt b/app/src/main/java/com/casic/birmm/inspect/utils/LocaleConstant.kt index 36fca8e..0fc27b6 100644 --- a/app/src/main/java/com/casic/birmm/inspect/utils/LocaleConstant.kt +++ b/app/src/main/java/com/casic/birmm/inspect/utils/LocaleConstant.kt @@ -71,8 +71,35 @@ Manifest.permission.WRITE_EXTERNAL_STORAGE ) } - val ASK_DEV_CODE_COMMAND = byteArrayOf(0x01, 0x0D, 0x0A)// 查询设备编号命令 + + //主服务UUID + val UUIDS = listOf( + "00001801-0000-1000-8000-00805f9b34fb", + "00001800-0000-1000-8000-00805f9b34fb", + "0003cdd0-0000-1000-8000-00805f9b0131" + ) + + //可通知不可写 + val SUB_0_UUIDS = listOf( + "00002a05-0000-1000-8000-00805f9b34fb" + ) + + //可写不可通知 + val SUB_1_UUIDS = listOf( + "00002a00-0000-1000-8000-00805f9b34fb", + "00002a01-0000-1000-8000-00805f9b34fb", + "00002a04-0000-1000-8000-00805f9b34fb" + ) + + //可写可通知 + val SUB_2_UUIDS = listOf( + "0003cdd1-0000-1000-8000-00805f9b0131",//可通知 + "0003cdd2-0000-1000-8000-00805f9b0131"//可写 + ) + + val ASK_DEV_CODE_COMMAND = byteArrayOf(0x01, 0x0D, 0x0A) // 查询设备编号命令 val OPEN_TRANSFER_COMMAND = byteArrayOf(0x02, 0x0D, 0x0A) // 开启数据发送命令 + val CLOSE_TRANSFER_COMMAND = byteArrayOf(0x03, 0x0D, 0x0A) // 关闭数据发送命令 /** * ============================================================================================= @@ -116,9 +143,6 @@ const val USER_OBJECT = "userObject" const val OPEN_WARNING = "isOpenWarning" const val AUTO_RECORD = "isRecordLog" - const val SERVICE_UUID = "0003cdd0-0000-1000-8000-00805f9b0131"//连接设备的UUID - const val WRITE_CHARACTERISTIC_UUID = "0003cdd2-0000-1000-8000-00805f9b0131"//写数据特征值UUID - const val READ_CHARACTERISTIC_UUID = "0003cdd1-0000-1000-8000-00805f9b0131"//读数据特征值UUID const val SINGLE_OPEN_WARNING = "isSingleOpenWarning" const val SINGLE_AUTO_RECORD = "isSingleRecordLog" diff --git a/app/src/main/res/layout/fragment_map_inspect.xml b/app/src/main/res/layout/fragment_map_inspect.xml index 9d86b40..964f207 100644 --- a/app/src/main/res/layout/fragment_map_inspect.xml +++ b/app/src/main/res/layout/fragment_map_inspect.xml @@ -88,7 +88,7 @@ android:src="@drawable/ic_bluetooth" /> diff --git a/app/src/main/res/layout/single_fragment_map_inspect.xml b/app/src/main/res/layout/single_fragment_map_inspect.xml index 9d86b40..9749bf6 100644 --- a/app/src/main/res/layout/single_fragment_map_inspect.xml +++ b/app/src/main/res/layout/single_fragment_map_inspect.xml @@ -88,7 +88,7 @@ android:src="@drawable/ic_bluetooth" /> @@ -115,6 +115,7 @@ @@ -147,6 +148,7 @@ @@ -179,6 +181,7 @@