ble: 펌웨어 구버전 경고 배너 (< VBTFW0118)

새 펌웨어(VBTFW0118+) 와 코드의 BLE throttle/MTU 가정이 맞도록
정렬됨. 더 옛 펌웨어가 연결된 경우 측정이 안 됨에도 사용자
입장에선 원인을 알 수 없어 endless loading 으로 보임 — 그
혼동을 막기 위한 명시적 경고.

- BleManager.MIN_FIRMWARE_BUILD = 118 상수
- BleManager.parseFirmwareBuild("VBTFW0116") → 116
- BleManager.isFirmwareOutdated: 파싱한 빌드 < 118 일 때 true
- 신규 FirmwareWarningBanner 컴포저블 (주황 경고색)
- PlacementGuide / PiezoMonitoring 상단에 노출

펌웨어 미수신 상태(아직 mid? 응답 전)는 false 로 취급 — false
positive 방지.
This commit is contained in:
2026-06-11 15:21:04 +09:00
parent 7be3f0b096
commit 1492030333
4 changed files with 81 additions and 0 deletions
@@ -28,6 +28,8 @@ class BleManager private constructor(private val context: Context) {
companion object {
private const val TAG = "BleManager"
/** 정상 측정에 필요한 최소 펌웨어 빌드 번호 (mtb? throttle/MTU 가정과 일치). */
const val MIN_FIRMWARE_BUILD = 118
private inline fun logd(msg: () -> String) { if (BuildConfig.DEBUG) Log.d(TAG, msg()) }
private inline fun logw(msg: () -> String) { if (BuildConfig.DEBUG) Log.w(TAG, msg()) }
private inline fun loge(msg: () -> String) { if (BuildConfig.DEBUG) Log.e(TAG, msg()) }
@@ -81,6 +83,20 @@ class BleManager private constructor(private val context: Context) {
val firmwareVersion = mutableStateOf("") // "VBTFW0116"
val hardwareVersion = mutableStateOf("") // "VBTHW0100"
val serialNumber = mutableStateOf("") // "VBT26040001"
/** "VBTFW0116" → 116, 형식 안 맞으면 null. */
fun parseFirmwareBuild(raw: String): Int? {
val m = Regex("(\\d+)$").find(raw) ?: return null
return m.groupValues[1].toIntOrNull()
}
/** 펌웨어 버전이 MIN_FIRMWARE_BUILD 미만이면 true. 모르거나(아직 미수신) 형식 안 맞으면 false. */
val isFirmwareOutdated: Boolean
get() {
val n = parseFirmwareBuild(firmwareVersion.value) ?: return false
return n < MIN_FIRMWARE_BUILD
}
val batteryLevel = mutableStateOf(0)
val bluetoothEnabled = mutableStateOf(bluetoothAdapter?.isEnabled == true)
val isServiceReady = mutableStateOf(false)
@@ -0,0 +1,59 @@
package com.example.medilightv2android.ui.components
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Warning
import androidx.compose.material3.Icon
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.example.medilightv2android.ble.BleManager
/**
* 펌웨어 구버전 경고 배너 — PlacementGuide / PiezoMonitoring 상단에 노출.
* isFirmwareOutdated 가 true 일 때만 렌더. 사용자가 측정 안 됨 원인을 즉시 파악하도록.
*/
@Composable
fun FirmwareWarningBanner(bleManager: BleManager, modifier: Modifier = Modifier) {
val fw by bleManager.firmwareVersion
if (!bleManager.isFirmwareOutdated) return
Row(
modifier = modifier
.fillMaxWidth()
.padding(horizontal = 16.dp, vertical = 6.dp)
.clip(RoundedCornerShape(10.dp))
.background(Color(0xFFFF9500))
.padding(horizontal = 12.dp, vertical = 8.dp),
verticalAlignment = Alignment.CenterVertically
) {
Icon(Icons.Default.Warning, "Warning", tint = Color.White)
Spacer(modifier = Modifier.width(10.dp))
Column(modifier = Modifier.weight(1f)) {
Text(
"펌웨어 업데이트 필요",
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)
)
}
}
}
@@ -947,6 +947,9 @@ fun PiezoMonitoringView(appState: AppState) {
// BLE 끊김 banner는 사내 테스트 피드백으로 제거됨 — 백그라운드 자동 재연결만 유지.
// 펌웨어 구버전 경고
com.example.medilightv2android.ui.components.FirmwareWarningBanner(bleManager)
val scrollState = rememberScrollState()
LaunchedEffect(showSettings) {
if (showSettings) scrollState.animateScrollTo(0)
@@ -191,6 +191,9 @@ fun PlacementGuideView(appState: AppState) {
}
}
// 펌웨어 구버전 경고
com.example.medilightv2android.ui.components.FirmwareWarningBanner(bleManager)
Box(modifier = Modifier.fillMaxSize()) {
Column(
modifier = Modifier