fix: device name display

This commit is contained in:
玖叁 2024-12-27 15:56:28 +08:00
parent c86ef81c4a
commit e168f08aa4
3 changed files with 134 additions and 38 deletions

View File

@ -61,7 +61,7 @@ fun SettingsPage(viewModel: CarViewModel) {
@Composable @Composable
fun SettingsList(viewModel: CarViewModel) { fun SettingsList(viewModel: CarViewModel) {
val context = LocalContext.current val context = LocalContext.current
var debugMode by remember { mutableStateOf(DebugWindowManager.isShowing()) } var debugMode by remember { mutableStateOf(DebugWindowManager.isShowing()) }
val onDebugModeChange = { enable: Boolean -> val onDebugModeChange = { enable: Boolean ->
@ -79,16 +79,7 @@ fun SettingsList(viewModel: CarViewModel) {
SettingConnectDeviceItem(viewModel) SettingConnectDeviceItem(viewModel)
} }
item { item {
ListItem( SettingDeviceNameItem(viewModel)
headlineContent = { Text("设备名称") },
supportingContent = { Text("当前名称: Name") },
trailingContent = {
Icon(
ImageVector.vectorResource(R.drawable.arrow_right_24),
contentDescription = "设备名称"
)
}
)
} }
item { item {
ListItem( ListItem(
@ -179,4 +170,20 @@ fun SettingConnectDeviceItem(viewModel: CarViewModel) {
} }
) )
} }
} }
@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 = "设备名称"
)
}
)
}

View File

@ -41,6 +41,8 @@ interface BluetoothRepository {
fun observeLogs(): Flow<List<LogEntry>> fun observeLogs(): Flow<List<LogEntry>>
fun clearLogs() fun clearLogs()
val connectionState: StateFlow<ConnectionState> val connectionState: StateFlow<ConnectionState>
val deviceName: StateFlow<String?>
val deviceAddress: StateFlow<String?>
} }
// 添加连接状态枚举 // 添加连接状态枚举
@ -55,6 +57,9 @@ val serviceUUID = "6E400001-B5A3-F393-E0A9-E50E24DCCA9E"
val rxCharUUID = "6E400002-B5A3-F393-E0A9-E50E24DCCA9E" val rxCharUUID = "6E400002-B5A3-F393-E0A9-E50E24DCCA9E"
val txCharUUID = "6E400003-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( class BluetoothRepositoryImpl(
private val context: Context, private val context: Context,
@ -80,6 +85,12 @@ class BluetoothRepositoryImpl(
private val _connectionState = MutableStateFlow(ConnectionState.DISCONNECTED) private val _connectionState = MutableStateFlow(ConnectionState.DISCONNECTED)
override val connectionState: StateFlow<ConnectionState> = _connectionState.asStateFlow() override val connectionState: StateFlow<ConnectionState> = _connectionState.asStateFlow()
private val _deviceName = MutableStateFlow<String?>(null)
override val deviceName: StateFlow<String?> = _deviceName.asStateFlow()
private val _deviceAddress = MutableStateFlow<String?>(null)
override val deviceAddress: StateFlow<String?> = _deviceAddress.asStateFlow()
private val scanCallback = object : ScanCallback() { private val scanCallback = object : ScanCallback() {
override fun onScanResult(callbackType: Int, result: ScanResult) { override fun onScanResult(callbackType: Int, result: ScanResult) {
val bleDevice = BleDevice( val bleDevice = BleDevice(
@ -132,7 +143,10 @@ class BluetoothRepositoryImpl(
} }
@SuppressLint("MissingPermission") @SuppressLint("MissingPermission")
fun connectToDevice(device: BluetoothDevice, callback: ((Boolean) -> Unit)? = null) { fun connectToDevice(
device: BluetoothDevice,
callback: ((Boolean, BluetoothDevice?) -> Unit)? = null
) {
_connectionState.value = ConnectionState.CONNECTING _connectionState.value = ConnectionState.CONNECTING
device.connectGatt( device.connectGatt(
context, context,
@ -164,21 +178,23 @@ class BluetoothRepositoryImpl(
} }
} }
if (callback != null) { if (callback != null) {
callback(true) callback(true, gatt.device)
} }
_deviceAddress.value = gatt.device.address
_deviceName.value = gatt.device.name ?: "未知设备"
} }
BluetoothProfile.STATE_CONNECTING -> { BluetoothProfile.STATE_CONNECTING -> {
_connectionState.value = ConnectionState.CONNECTING _connectionState.value = ConnectionState.CONNECTING
if (callback != null) { if (callback != null) {
callback(false) callback(false, null)
} }
} }
BluetoothProfile.STATE_DISCONNECTING -> { BluetoothProfile.STATE_DISCONNECTING -> {
_connectionState.value = ConnectionState.DISCONNECTING _connectionState.value = ConnectionState.DISCONNECTING
if (callback != null) { if (callback != null) {
callback(false) callback(false, null)
} }
} }
@ -187,8 +203,10 @@ class BluetoothRepositoryImpl(
rxCharacteristic = null rxCharacteristic = null
_connectionState.value = ConnectionState.DISCONNECTED _connectionState.value = ConnectionState.DISCONNECTED
if (callback != null) { 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) { override fun onServicesDiscovered(gatt: BluetoothGatt?, status: Int) {
super.onServicesDiscovered(gatt, status) super.onServicesDiscovered(gatt, status)
if (status == BluetoothGatt.GATT_SUCCESS) { 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 -> gatt?.getService(UUID.fromString(serviceUUID))?.let { service ->
bluetoothGatt = gatt bluetoothGatt = gatt
rxCharacteristic = 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( override fun onCharacteristicChanged(
gatt: BluetoothGatt, gatt: BluetoothGatt,
characteristic: BluetoothGattCharacteristic, characteristic: BluetoothGattCharacteristic,
@ -222,6 +263,11 @@ class BluetoothRepositoryImpl(
if (characteristic.uuid == UUID.fromString(txCharUUID)) { if (characteristic.uuid == UUID.fromString(txCharUUID)) {
onReceivePacket(value) onReceivePacket(value)
} }
if (characteristic.uuid == UUID.fromString(deviceNameCharUUID)) {
val deviceName = String(value)
_deviceName.value = deviceName
_deviceAddress.value = gatt.device.address
}
} }
}, },
TRANSPORT_LE TRANSPORT_LE
@ -237,8 +283,8 @@ class BluetoothRepositoryImpl(
context.getSystemService(Context.BLUETOOTH_SERVICE) as BluetoothManager context.getSystemService(Context.BLUETOOTH_SERVICE) as BluetoothManager
val device = bluetoothManager.adapter.getRemoteDevice(address) val device = bluetoothManager.adapter.getRemoteDevice(address)
if (callback != null) { if (callback != null) {
connectToDevice(device) { isConnected -> connectToDevice(device) { isConnected, deviceDetail ->
callback(isConnected, device) callback(isConnected, deviceDetail ?: device)
} }
} else { } else {
connectToDevice(device) 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") @SuppressLint("MissingPermission")
override fun sendCommand(command: ByteArray) { override fun sendCommand(command: ByteArray) {
rxCharacteristic?.let { characteristic -> rxCharacteristic?.let { characteristic ->
@ -321,7 +389,8 @@ class BluetoothRepositoryImpl(
private fun createInfraredState(irPinCount: Int, irData: UByte) = InfraredState( private fun createInfraredState(irPinCount: Int, irData: UByte) = InfraredState(
enable = true, 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 -> bluetoothGatt?.let { gatt ->
_connectionState.value = ConnectionState.DISCONNECTING _connectionState.value = ConnectionState.DISCONNECTING
gatt.disconnect() gatt.disconnect()
gatt.close() // 重要:需要调用 close() 释放资源
bluetoothGatt = null
rxCharacteristic = null
_deviceName.value = null
_deviceAddress.value = null
_connectionState.value = ConnectionState.DISCONNECTED
} }
} }
} }

View File

@ -21,6 +21,7 @@ import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import android.view.WindowManager import android.view.WindowManager
import kotlinx.coroutines.flow.combine
// 用例 // 用例
@ -82,20 +83,14 @@ class CarViewModel(
fun startScan() = (repository as BluetoothRepositoryImpl).startScan() fun startScan() = (repository as BluetoothRepositoryImpl).startScan()
fun stopScan() = (repository as BluetoothRepositoryImpl).stopScan() fun stopScan() = (repository as BluetoothRepositoryImpl).stopScan()
@SuppressLint("MissingPermission")
fun connectToDevice(device: BluetoothDevice) { fun connectToDevice(device: BluetoothDevice) {
(repository as BluetoothRepositoryImpl).connectToDevice(device) { isConnected -> (repository as BluetoothRepositoryImpl).connectToDevice(device)
if (isConnected) {
updateDeviceInfo(device)
}
}
} }
@SuppressLint("MissingPermission")
fun connectToDeviceByAddress(address: String) { fun connectToDeviceByAddress(address: String) {
(repository as BluetoothRepositoryImpl).connectToDeviceByAddress(address) { isConnected, device -> (repository as BluetoothRepositoryImpl).connectToDeviceByAddress(address)
if (isConnected) {
updateDeviceInfo(device)
}
}
} }
val carState: StateFlow<CarState> = repository.observeCarState() val carState: StateFlow<CarState> = repository.observeCarState()
@ -116,24 +111,43 @@ class CarViewModel(
initialValue = ConnectionState.DISCONNECTED initialValue = ConnectionState.DISCONNECTED
) )
// 添加断开连接方法
fun disconnect() {
(repository as BluetoothRepositoryImpl).disconnect()
}
init { init {
// 监听连接状态变化
viewModelScope.launch { viewModelScope.launch {
// 监听连接状态变化
connectionState.collect { state -> connectionState.collect { state ->
if (state == ConnectionState.DISCONNECTED) { when (state) {
// 断开连接时清空设备信息 ConnectionState.DISCONNECTED -> {
_connectionInfoState.value = ConnectionInfoState() // 断开连接时清空设备信息
_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") private fun updateDeviceInfo(deviceName: String?, deviceAddress: String) {
fun updateDeviceInfo(device: BluetoothDevice) {
_connectionInfoState.value = ConnectionInfoState( _connectionInfoState.value = ConnectionInfoState(
name = device.name ?: "未知设备", name = deviceName ?: "未知设备 (${deviceAddress})",
address = device.address, address = deviceAddress,
version = "001" version = "001"
) )
} }