feat(firmware): 이미 연결된 기기 있으면 스캔 생략 · 바로 재연결 (cloud-mvp 19a1c12 이식)

이전 fix (47317b3) 는 진입 시 disconnect 만 했지만 사용자가 여전히
Scan → Connect 를 수동으로 눌러야 했음. UX 낭비.

이제:
1. 진입 → 이미 연결된 기기 체크 (BleManager.isConnected)
2. BluetoothDevice + name 캡처
3. 메인 앱 disconnect (프로브 광고 재개)
4. 1.5s 대기 (BLE stack 정리)
5. DfuManager.connectDirectly(device, name) — 스캔 생략
6. 사용자는 CONNECTED 상태에서 "Start DFU" 만 누르면 됨

BleManager.kt (demo-final path): currentBluetoothDevice getter 추가
DfuManager.kt: connectDirectly(device, name)
FirmwareUpdateView.kt: LaunchedEffect 개편 — 조건 분기 · delay · auto-connect

연결 없이 진입하면 기존 스캔 flow 유지.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
2026-08-03 15:28:23 +09:00
parent 47317b3c5d
commit 33c4e94d07
3 changed files with 35 additions and 6 deletions
@@ -114,6 +114,13 @@ class BleManager private constructor(private val context: Context) {
val batteryLevel = mutableStateOf(0) val batteryLevel = mutableStateOf(0)
val bluetoothEnabled = mutableStateOf(bluetoothAdapter?.isEnabled == true) val bluetoothEnabled = mutableStateOf(bluetoothAdapter?.isEnabled == true)
val isServiceReady = mutableStateOf(false) val isServiceReady = mutableStateOf(false)
/** 현재 GATT 연결된 BluetoothDevice · 없으면 null. Firmware DFU 진입 시
* 스캔 생략하고 바로 이 기기로 재연결하는 경로에서 사용. */
val currentBluetoothDevice: android.bluetooth.BluetoothDevice?
@android.annotation.SuppressLint("MissingPermission")
get() = bluetoothGatt?.device
val isBonded: Boolean val isBonded: Boolean
@android.annotation.SuppressLint("MissingPermission") @android.annotation.SuppressLint("MissingPermission")
get() = bluetoothGatt?.device?.bondState == android.bluetooth.BluetoothDevice.BOND_BONDED get() = bluetoothGatt?.device?.bondState == android.bluetooth.BluetoothDevice.BOND_BONDED
@@ -195,6 +195,16 @@ class DfuManager(context: Context) {
).also { it.connect(device.device) } ).also { it.connect(device.device) }
} }
/**
* 스캔 생략 · 이미 알고 있는 기기에 바로 연결 (2026-08-03).
* 메인 앱이 이미 프로브에 연결됐던 상태에서 FirmwareUpdate 진입할 때 사용.
*/
fun connectDirectly(device: BluetoothDevice, name: String) {
val scanned = ScannedDevice(device = device, name = name, rssi = 0, inDfuMode = false)
log("skip scan — using previously connected device: $name")
connect(scanned)
}
fun disconnect() { fun disconnect() {
closeNus() closeNus()
if (phase.value == DfuPhase.CONNECTED || phase.value == DfuPhase.CONNECTING) { if (phase.value == DfuPhase.CONNECTED || phase.value == DfuPhase.CONNECTING) {
@@ -91,13 +91,25 @@ fun FirmwareUpdateScreen(onBack: () -> Unit, modifier: Modifier = Modifier) {
) { result -> granted = result.values.all { it } } ) { result -> granted = result.values.all { it } }
LaunchedEffect(Unit) { LaunchedEffect(Unit) {
// 진입 시 메인 앱 BLE 연결 해제 — 프로브가 연결 중이면 광고 중단되어
// DfuManager 스캔에서 안 보이는 문제 해결 (2026-08-03).
runCatching {
com.medithings.vesiscan.ble.BleManager.getInstance(context).disconnect()
}
manager.loadFirmware() manager.loadFirmware()
if (!granted) launcher.launch(perms) if (!granted) {
launcher.launch(perms)
return@LaunchedEffect
}
// 이미 메인 앱이 연결된 기기가 있으면 스캔 생략 · 자동 재연결로 UX 단축 (2026-08-03).
val bleManager = com.medithings.vesiscan.ble.BleManager.getInstance(context)
val preConnectedDevice = if (bleManager.isConnected.value) bleManager.currentBluetoothDevice else null
val preConnectedName = bleManager.connectedDeviceName.value
// 메인 앱 BLE 연결 해제 — 프로브가 연결 중이면 광고 중단되어 스캔 안 보임.
runCatching { bleManager.disconnect() }
// 이미 알던 기기가 있으면 스캔 없이 바로 재연결 시도.
if (preConnectedDevice != null && preConnectedName.isNotEmpty()) {
kotlinx.coroutines.delay(1500) // BLE stack 정리 · 재광고 대기
manager.connectDirectly(preConnectedDevice, preConnectedName)
}
} }
DisposableEffect(Unit) { onDispose { manager.shutdown() } } DisposableEffect(Unit) { onDispose { manager.shutdown() } }