ble: 일반 사용자는 KnownDeviceStore 저장 0개 + 펌웨어 미수신 경고

다중 기기 페어링 저장은 Clinical 흐름 전용.
일반 사용자는 매 세션 fresh scan + pair — 저장 기능 자체 미노출.

- BleManager.connect 성공 시: ClinicalSessionStore.inClinicalFlow
  체크 → false 면 KnownDeviceStore.add() skip
- DeviceScanView: 같은 조건으로 Saved Devices 섹션 노출 X.
  knownDevices = emptyList() → 기존 단일 디바이스 리스트 UX

+ FirmwareWarningBanner 케이스 추가:
  연결 후 8초간 mid? 응답 미수신 → 빨강 경고
  "펌웨어 응답 없음 — 펌웨어 호환 문제 가능"
  사용자가 정확한 원인 파악 가능 (구버전 != 응답 자체 안 옴).
This commit is contained in:
2026-06-11 15:34:09 +09:00
parent 1492030333
commit f85b5cfbed
3 changed files with 53 additions and 21 deletions
@@ -869,11 +869,15 @@ class BleManager private constructor(private val context: Context) {
cancelReconnectTimer() cancelReconnectTimer()
debugLogger.connected(connectedDeviceName.value, gatt.device.address) debugLogger.connected(connectedDeviceName.value, gatt.device.address)
try { // Clinical 흐름에서만 KnownDeviceStore 영속화. 일반 사용자는
com.example.medilightv2android.services.KnownDeviceStore.add( // 매 세션 fresh scan + pair — 저장 0개.
context, gatt.device.address, connectedDeviceName.value if (com.example.medilightv2android.services.ClinicalSessionStore.inClinicalFlow) {
) try {
} catch (_: Exception) {} com.example.medilightv2android.services.KnownDeviceStore.add(
context, gatt.device.address, connectedDeviceName.value
)
} catch (_: Exception) {}
}
// MTU 247 요청 (reb: 208B 패킷 드롭 방지) // MTU 247 요청 (reb: 208B 패킷 드롭 방지)
// onMtuChanged 콜백에서 discoverServices 호출 // onMtuChanged 콜백에서 discoverServices 호출
@@ -13,7 +13,11 @@ import androidx.compose.material.icons.filled.Warning
import androidx.compose.material3.Icon import androidx.compose.material3.Icon
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip import androidx.compose.ui.draw.clip
@@ -22,38 +26,58 @@ import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp import androidx.compose.ui.unit.sp
import com.example.medilightv2android.ble.BleManager import com.example.medilightv2android.ble.BleManager
import kotlinx.coroutines.delay
/** /**
* 펌웨어 구버전 경고 배너 — PlacementGuide / PiezoMonitoring 상단에 노출. * 펌웨어 상태 배너 — PlacementGuide / PiezoMonitoring 상단에 노출.
* isFirmwareOutdated 가 true 일 때만 렌더. 사용자가 측정 안 됨 원인을 즉시 파악하도록. *
* 표시 케이스:
* (1) 펌웨어 구버전 (build < MIN_FIRMWARE_BUILD) — 주황 경고
* (2) 연결 후 8초 넘게 mid? 응답 미수신 — 빨강 경고 (펌웨어가 mid 미지원)
* (3) 정상 또는 미연결 — 안 보임
*/ */
@Composable @Composable
fun FirmwareWarningBanner(bleManager: BleManager, modifier: Modifier = Modifier) { fun FirmwareWarningBanner(bleManager: BleManager, modifier: Modifier = Modifier) {
val fw by bleManager.firmwareVersion val fw by bleManager.firmwareVersion
if (!bleManager.isFirmwareOutdated) return val isConnected by bleManager.isConnected
// (2) 연결됐는데 일정 시간 동안 펌웨어 미수신 추적
var timedOut by remember { mutableStateOf(false) }
LaunchedEffect(isConnected, fw) {
timedOut = false
if (isConnected && fw.isEmpty()) {
delay(8_000)
if (bleManager.isConnected.value && bleManager.firmwareVersion.value.isEmpty()) {
timedOut = true
}
}
}
val outdated = bleManager.isFirmwareOutdated
if (!outdated && !timedOut) return
val bgColor = if (outdated) Color(0xFFFF9500) else Color(0xFFD32F2F)
val title = if (outdated) "펌웨어 업데이트 필요" else "펌웨어 응답 없음"
val subtitle = if (outdated) {
"현재: $fw · 필요: VBTFW${"%04d".format(BleManager.MIN_FIRMWARE_BUILD)} 이상. 측정이 정상 동작하지 않을 수 있습니다."
} else {
"연결됐으나 mid? 응답 미수신. 펌웨어 호환 문제 가능 — 측정 안 될 수 있음."
}
Row( Row(
modifier = modifier modifier = modifier
.fillMaxWidth() .fillMaxWidth()
.padding(horizontal = 16.dp, vertical = 6.dp) .padding(horizontal = 16.dp, vertical = 6.dp)
.clip(RoundedCornerShape(10.dp)) .clip(RoundedCornerShape(10.dp))
.background(Color(0xFFFF9500)) .background(bgColor)
.padding(horizontal = 12.dp, vertical = 8.dp), .padding(horizontal = 12.dp, vertical = 8.dp),
verticalAlignment = Alignment.CenterVertically verticalAlignment = Alignment.CenterVertically
) { ) {
Icon(Icons.Default.Warning, "Warning", tint = Color.White) Icon(Icons.Default.Warning, "Warning", tint = Color.White)
Spacer(modifier = Modifier.width(10.dp)) Spacer(modifier = Modifier.width(10.dp))
Column(modifier = Modifier.weight(1f)) { Column(modifier = Modifier.weight(1f)) {
Text( Text(title, fontSize = 13.sp, fontWeight = FontWeight.Bold, color = Color.White)
"펌웨어 업데이트 필요", Text(subtitle, fontSize = 11.sp, color = Color.White.copy(alpha = 0.9f))
fontSize = 13.sp,
fontWeight = FontWeight.Bold,
color = Color.White
)
Text(
"현재: $fw · 필요: VBTFW${"%04d".format(BleManager.MIN_FIRMWARE_BUILD)} 이상. 측정이 정상 동작하지 않을 수 있습니다.",
fontSize = 11.sp,
color = Color.White.copy(alpha = 0.9f)
)
} }
} }
} }
@@ -52,7 +52,11 @@ fun DeviceScanView(appState: AppState) {
var connectingDeviceAddress by remember { mutableStateOf<String?>(null) } var connectingDeviceAddress by remember { mutableStateOf<String?>(null) }
var permissionsGranted by remember { mutableStateOf(false) } var permissionsGranted by remember { mutableStateOf(false) }
var knownDevices by remember { mutableStateOf(KnownDeviceStore.getAll(context)) } // Clinical 흐름에서만 다중 기기 저장/표시 — 일반 사용자는 fresh scan & pair.
val isClinicalFlow = com.example.medilightv2android.services.ClinicalSessionStore.inClinicalFlow
var knownDevices by remember {
mutableStateOf(if (isClinicalFlow) KnownDeviceStore.getAll(context) else emptyList())
}
var pendingDelete by remember { mutableStateOf<KnownDevice?>(null) } var pendingDelete by remember { mutableStateOf<KnownDevice?>(null) }
val knownAddresses = knownDevices.map { it.address }.toSet() val knownAddresses = knownDevices.map { it.address }.toSet()
val newDevices = bleManager.discoveredDevices val newDevices = bleManager.discoveredDevices