From e168f08aa4ac71819ab05ff5ae80653557217a0c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=96=E5=8F=81?= Date: Fri, 27 Dec 2024 15:56:28 +0800 Subject: [PATCH] fix: device name display --- .../icu/fur93/esp32_car/page/SettingsPage.kt | 31 ++++--- .../repository/BluetoothRepository.kt | 91 +++++++++++++++++-- .../fur93/esp32_car/viewmodel/CarViewModel.kt | 50 ++++++---- 3 files changed, 134 insertions(+), 38 deletions(-) diff --git a/app/src/main/java/icu/fur93/esp32_car/page/SettingsPage.kt b/app/src/main/java/icu/fur93/esp32_car/page/SettingsPage.kt index 9cb41d7..ed3cf0d 100644 --- a/app/src/main/java/icu/fur93/esp32_car/page/SettingsPage.kt +++ b/app/src/main/java/icu/fur93/esp32_car/page/SettingsPage.kt @@ -61,7 +61,7 @@ fun SettingsPage(viewModel: CarViewModel) { @Composable fun SettingsList(viewModel: CarViewModel) { val context = LocalContext.current - + var debugMode by remember { mutableStateOf(DebugWindowManager.isShowing()) } val onDebugModeChange = { enable: Boolean -> @@ -79,16 +79,7 @@ fun SettingsList(viewModel: CarViewModel) { SettingConnectDeviceItem(viewModel) } item { - ListItem( - headlineContent = { Text("设备名称") }, - supportingContent = { Text("当前名称: Name") }, - trailingContent = { - Icon( - ImageVector.vectorResource(R.drawable.arrow_right_24), - contentDescription = "设备名称" - ) - } - ) + SettingDeviceNameItem(viewModel) } item { ListItem( @@ -179,4 +170,20 @@ fun SettingConnectDeviceItem(viewModel: CarViewModel) { } ) } -} \ No newline at end of file +} + +@Composable +fun SettingDeviceNameItem(viewModel: CarViewModel) { + val connectionInfoState by viewModel.connectionInfoState.collectAsState() + + ListItem( + headlineContent = { Text("设备名称") }, + supportingContent = { Text("当前名称: ${connectionInfoState.name}") }, + trailingContent = { + Icon( + ImageVector.vectorResource(R.drawable.arrow_right_24), + contentDescription = "设备名称" + ) + } + ) +} diff --git a/app/src/main/java/icu/fur93/esp32_car/repository/BluetoothRepository.kt b/app/src/main/java/icu/fur93/esp32_car/repository/BluetoothRepository.kt index 6bcb280..e5e82c2 100644 --- a/app/src/main/java/icu/fur93/esp32_car/repository/BluetoothRepository.kt +++ b/app/src/main/java/icu/fur93/esp32_car/repository/BluetoothRepository.kt @@ -41,6 +41,8 @@ interface BluetoothRepository { fun observeLogs(): Flow> fun clearLogs() val connectionState: StateFlow + val deviceName: StateFlow + val deviceAddress: StateFlow } // 添加连接状态枚举 @@ -55,6 +57,9 @@ val serviceUUID = "6E400001-B5A3-F393-E0A9-E50E24DCCA9E" val rxCharUUID = "6E400002-B5A3-F393-E0A9-E50E24DCCA9E" val txCharUUID = "6E400003-B5A3-F393-E0A9-E50E24DCCA9E" +val deviceInfoServiceUUID = "0000180a-0000-1000-8000-00805f9b34fb" +val deviceNameCharUUID = "00002a00-0000-1000-8000-00805f9b34fb" + // 蓝牙通信实现 class BluetoothRepositoryImpl( private val context: Context, @@ -80,6 +85,12 @@ class BluetoothRepositoryImpl( private val _connectionState = MutableStateFlow(ConnectionState.DISCONNECTED) override val connectionState: StateFlow = _connectionState.asStateFlow() + private val _deviceName = MutableStateFlow(null) + override val deviceName: StateFlow = _deviceName.asStateFlow() + + private val _deviceAddress = MutableStateFlow(null) + override val deviceAddress: StateFlow = _deviceAddress.asStateFlow() + private val scanCallback = object : ScanCallback() { override fun onScanResult(callbackType: Int, result: ScanResult) { val bleDevice = BleDevice( @@ -132,7 +143,10 @@ class BluetoothRepositoryImpl( } @SuppressLint("MissingPermission") - fun connectToDevice(device: BluetoothDevice, callback: ((Boolean) -> Unit)? = null) { + fun connectToDevice( + device: BluetoothDevice, + callback: ((Boolean, BluetoothDevice?) -> Unit)? = null + ) { _connectionState.value = ConnectionState.CONNECTING device.connectGatt( context, @@ -164,21 +178,23 @@ class BluetoothRepositoryImpl( } } if (callback != null) { - callback(true) + callback(true, gatt.device) } + _deviceAddress.value = gatt.device.address + _deviceName.value = gatt.device.name ?: "未知设备" } BluetoothProfile.STATE_CONNECTING -> { _connectionState.value = ConnectionState.CONNECTING if (callback != null) { - callback(false) + callback(false, null) } } BluetoothProfile.STATE_DISCONNECTING -> { _connectionState.value = ConnectionState.DISCONNECTING if (callback != null) { - callback(false) + callback(false, null) } } @@ -187,8 +203,10 @@ class BluetoothRepositoryImpl( rxCharacteristic = null _connectionState.value = ConnectionState.DISCONNECTED if (callback != null) { - callback(false) + callback(false, null) } + _deviceName.value = null + _deviceAddress.value = null } } } @@ -196,6 +214,15 @@ class BluetoothRepositoryImpl( override fun onServicesDiscovered(gatt: BluetoothGatt?, status: Int) { super.onServicesDiscovered(gatt, status) if (status == BluetoothGatt.GATT_SUCCESS) { + // 读取设备名称 + val genericService = + gatt?.getService(UUID.fromString("00001800-0000-1000-8000-00805f9b34fb")) + val deviceNameChar = + genericService?.getCharacteristic(UUID.fromString("00002a00-0000-1000-8000-00805f9b34fb")) + if (deviceNameChar != null) { + gatt.readCharacteristic(deviceNameChar) + } + gatt?.getService(UUID.fromString(serviceUUID))?.let { service -> bluetoothGatt = gatt rxCharacteristic = @@ -213,6 +240,20 @@ class BluetoothRepositoryImpl( } } + override fun onCharacteristicRead( + gatt: BluetoothGatt, + characteristic: BluetoothGattCharacteristic, + value: ByteArray, + status: Int + ) { + super.onCharacteristicRead(gatt, characteristic, value, status) + if (characteristic.uuid == UUID.fromString(deviceNameCharUUID)) { + val deviceName = String(value) + _deviceName.value = deviceName + Log.d("DeviceName", "Device name: $deviceName") + } + } + override fun onCharacteristicChanged( gatt: BluetoothGatt, characteristic: BluetoothGattCharacteristic, @@ -222,6 +263,11 @@ class BluetoothRepositoryImpl( if (characteristic.uuid == UUID.fromString(txCharUUID)) { onReceivePacket(value) } + if (characteristic.uuid == UUID.fromString(deviceNameCharUUID)) { + val deviceName = String(value) + _deviceName.value = deviceName + _deviceAddress.value = gatt.device.address + } } }, TRANSPORT_LE @@ -237,8 +283,8 @@ class BluetoothRepositoryImpl( context.getSystemService(Context.BLUETOOTH_SERVICE) as BluetoothManager val device = bluetoothManager.adapter.getRemoteDevice(address) if (callback != null) { - connectToDevice(device) { isConnected -> - callback(isConnected, device) + connectToDevice(device) { isConnected, deviceDetail -> + callback(isConnected, deviceDetail ?: device) } } else { connectToDevice(device) @@ -246,6 +292,28 @@ class BluetoothRepositoryImpl( } + @SuppressLint("MissingPermission") + private fun setupDeviceNameNotification(gatt: BluetoothGatt) { + val service = gatt.getService(UUID.fromString(deviceInfoServiceUUID)) + val characteristic = service?.getCharacteristic(UUID.fromString(deviceNameCharUUID)) + + characteristic?.let { char -> + // 启用通知 + gatt.setCharacteristicNotification(char, true) + + // 获取 descriptor 并写入通知使能值 + val descriptor = + char.getDescriptor(UUID.fromString("00002902-0000-1000-8000-00805f9b34fb")) + descriptor?.let { + it.value = BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE + gatt.writeDescriptor(it) + } + + // 同时也读取一次当前值 + gatt.readCharacteristic(char) + } + } + @SuppressLint("MissingPermission") override fun sendCommand(command: ByteArray) { rxCharacteristic?.let { characteristic -> @@ -321,7 +389,8 @@ class BluetoothRepositoryImpl( private fun createInfraredState(irPinCount: Int, irData: UByte) = InfraredState( enable = true, - statusList = irData.toString(2).padStart(irPinCount, '0').reversed().map { if (it == '1') 1u else 0u } + statusList = irData.toString(2).padStart(irPinCount, '0').reversed() + .map { if (it == '1') 1u else 0u } ) // 断开连接方法 @@ -330,6 +399,12 @@ class BluetoothRepositoryImpl( bluetoothGatt?.let { gatt -> _connectionState.value = ConnectionState.DISCONNECTING gatt.disconnect() + gatt.close() // 重要:需要调用 close() 释放资源 + bluetoothGatt = null + rxCharacteristic = null + _deviceName.value = null + _deviceAddress.value = null + _connectionState.value = ConnectionState.DISCONNECTED } } } \ No newline at end of file diff --git a/app/src/main/java/icu/fur93/esp32_car/viewmodel/CarViewModel.kt b/app/src/main/java/icu/fur93/esp32_car/viewmodel/CarViewModel.kt index f523e32..efe7c10 100644 --- a/app/src/main/java/icu/fur93/esp32_car/viewmodel/CarViewModel.kt +++ b/app/src/main/java/icu/fur93/esp32_car/viewmodel/CarViewModel.kt @@ -21,6 +21,7 @@ import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.launch import android.view.WindowManager +import kotlinx.coroutines.flow.combine // 用例 @@ -82,20 +83,14 @@ class CarViewModel( fun startScan() = (repository as BluetoothRepositoryImpl).startScan() fun stopScan() = (repository as BluetoothRepositoryImpl).stopScan() + @SuppressLint("MissingPermission") fun connectToDevice(device: BluetoothDevice) { - (repository as BluetoothRepositoryImpl).connectToDevice(device) { isConnected -> - if (isConnected) { - updateDeviceInfo(device) - } - } + (repository as BluetoothRepositoryImpl).connectToDevice(device) } + @SuppressLint("MissingPermission") fun connectToDeviceByAddress(address: String) { - (repository as BluetoothRepositoryImpl).connectToDeviceByAddress(address) { isConnected, device -> - if (isConnected) { - updateDeviceInfo(device) - } - } + (repository as BluetoothRepositoryImpl).connectToDeviceByAddress(address) } val carState: StateFlow = repository.observeCarState() @@ -116,24 +111,43 @@ class CarViewModel( initialValue = ConnectionState.DISCONNECTED ) + // 添加断开连接方法 + fun disconnect() { + (repository as BluetoothRepositoryImpl).disconnect() + } + init { - // 监听连接状态变化 viewModelScope.launch { + // 监听连接状态变化 connectionState.collect { state -> - if (state == ConnectionState.DISCONNECTED) { - // 断开连接时清空设备信息 - _connectionInfoState.value = ConnectionInfoState() + when (state) { + ConnectionState.DISCONNECTED -> { + // 断开连接时清空设备信息 + _connectionInfoState.value = ConnectionInfoState() + } + else -> { + // 其他状态处理... + } + } + } + } + + viewModelScope.launch { + repository.deviceName.combine(repository.deviceAddress) { name, address -> + Pair(name, address) + }.collect { (name, address) -> + if (name != null && address != null) { + updateDeviceInfo(name, address) } } } } // 更新设备信息的方法 - @SuppressLint("MissingPermission") - fun updateDeviceInfo(device: BluetoothDevice) { + private fun updateDeviceInfo(deviceName: String?, deviceAddress: String) { _connectionInfoState.value = ConnectionInfoState( - name = device.name ?: "未知设备", - address = device.address, + name = deviceName ?: "未知设备 (${deviceAddress})", + address = deviceAddress, version = "001" ) }