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:
@@ -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 호출
|
||||||
|
|||||||
+39
-15
@@ -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)
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+5
-1
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user