feat(piezo): 도넛 위 status chip 을 posture 아이콘+텍스트로 교체
- urgency message Text 자리에 자세 chip (아이콘 + 라벨) 배치 - 색·아이콘·라벨이 currentPosture 에 따라 Crossfade 300ms 로 부드럽게 전환 - top bar 의 작은 inline posture Row 제거 (중복 표시 방지) - UNKNOWN 상태 가시성 ↑ : tint #616161 + 라벨 'Detecting' (회색 투명+'—' 대비) - Raw BLE 녹화 버튼은 dev 모드에서만 노출 (legacy 유지) Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
+90
-87
@@ -1115,68 +1115,40 @@ fun PiezoMonitoringView(appState: AppState) {
|
||||
|
||||
Spacer(modifier = Modifier.width(8.dp))
|
||||
|
||||
// Posture icon (Phase 1) — flowchart-based 자세 분류 결과 표시.
|
||||
// SUPINE/SITTING/STANDING/UNKNOWN 에 따라 아이콘+색 변경.
|
||||
val postureIcon = when (currentPosture) {
|
||||
com.example.medilightv2android.managers.Posture.LYING -> Icons.Default.Hotel
|
||||
com.example.medilightv2android.managers.Posture.NON_LYING -> Icons.Default.AccessibilityNew
|
||||
com.example.medilightv2android.managers.Posture.WALKING -> Icons.Default.DirectionsWalk
|
||||
com.example.medilightv2android.managers.Posture.UNKNOWN -> Icons.Default.HelpOutline
|
||||
}
|
||||
val postureTint = when (currentPosture) {
|
||||
com.example.medilightv2android.managers.Posture.LYING -> Color(0xFF2196F3)
|
||||
com.example.medilightv2android.managers.Posture.NON_LYING -> Color(0xFF4CAF50)
|
||||
com.example.medilightv2android.managers.Posture.WALKING -> Color(0xFFD32F2F)
|
||||
com.example.medilightv2android.managers.Posture.UNKNOWN -> Color.Gray.copy(alpha = 0.5f)
|
||||
}
|
||||
val postureLabel = when (currentPosture) {
|
||||
com.example.medilightv2android.managers.Posture.LYING -> "Lying"
|
||||
com.example.medilightv2android.managers.Posture.NON_LYING -> "Upright"
|
||||
com.example.medilightv2android.managers.Posture.WALKING -> "Walking"
|
||||
com.example.medilightv2android.managers.Posture.UNKNOWN -> "—"
|
||||
}
|
||||
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||
Icon(
|
||||
postureIcon, contentDescription = postureLabel,
|
||||
tint = postureTint, modifier = Modifier.size(20.dp),
|
||||
)
|
||||
Spacer(modifier = Modifier.width(3.dp))
|
||||
Text(postureLabel, fontSize = (11 * fontScale).sp,
|
||||
fontWeight = FontWeight.SemiBold, color = postureTint)
|
||||
}
|
||||
Spacer(modifier = Modifier.width(8.dp))
|
||||
|
||||
// Raw BLE capture toggle — IMU/packet 디버그용 (Downloads/VesiScan_RawBLE/)
|
||||
var rawBleCapturing by remember {
|
||||
mutableStateOf(com.example.medilightv2android.services.RawBleLogger.isCapturing)
|
||||
// Dev mode 에서만 노출 (legacy 유지) — 일반 사용자에겐 숨김.
|
||||
if (appState.isDevMode) {
|
||||
var rawBleCapturing by remember {
|
||||
mutableStateOf(com.example.medilightv2android.services.RawBleLogger.isCapturing)
|
||||
}
|
||||
IconButton(
|
||||
onClick = {
|
||||
if (rawBleCapturing) {
|
||||
val f = com.example.medilightv2android.services.RawBleLogger.stopCapture()
|
||||
val n = com.example.medilightv2android.services.RawBleLogger.count
|
||||
rawBleCapturing = false
|
||||
android.widget.Toast.makeText(context,
|
||||
"Raw BLE: $n packets saved to ${f?.name}",
|
||||
android.widget.Toast.LENGTH_LONG).show()
|
||||
} else {
|
||||
val f = com.example.medilightv2android.services.RawBleLogger.startCapture()
|
||||
rawBleCapturing = true
|
||||
android.widget.Toast.makeText(context,
|
||||
"Raw BLE capture started → ${f.name}",
|
||||
android.widget.Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
},
|
||||
modifier = Modifier.size(28.dp)
|
||||
) {
|
||||
Icon(
|
||||
if (rawBleCapturing) Icons.Default.RadioButtonChecked else Icons.Default.FiberManualRecord,
|
||||
contentDescription = if (rawBleCapturing) "Stop raw BLE capture" else "Start raw BLE capture",
|
||||
tint = if (rawBleCapturing) Color.Red else Color.Gray.copy(alpha = 0.5f),
|
||||
modifier = Modifier.size(20.dp)
|
||||
)
|
||||
}
|
||||
Spacer(modifier = Modifier.width(4.dp))
|
||||
}
|
||||
IconButton(
|
||||
onClick = {
|
||||
if (rawBleCapturing) {
|
||||
val f = com.example.medilightv2android.services.RawBleLogger.stopCapture()
|
||||
val n = com.example.medilightv2android.services.RawBleLogger.count
|
||||
rawBleCapturing = false
|
||||
android.widget.Toast.makeText(context,
|
||||
"Raw BLE: $n packets saved to ${f?.name}",
|
||||
android.widget.Toast.LENGTH_LONG).show()
|
||||
} else {
|
||||
val f = com.example.medilightv2android.services.RawBleLogger.startCapture()
|
||||
rawBleCapturing = true
|
||||
android.widget.Toast.makeText(context,
|
||||
"Raw BLE capture started → ${f.name}",
|
||||
android.widget.Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
},
|
||||
modifier = Modifier.size(28.dp)
|
||||
) {
|
||||
Icon(
|
||||
if (rawBleCapturing) Icons.Default.RadioButtonChecked else Icons.Default.FiberManualRecord,
|
||||
contentDescription = if (rawBleCapturing) "Stop raw BLE capture" else "Start raw BLE capture",
|
||||
tint = if (rawBleCapturing) Color.Red else Color.Gray.copy(alpha = 0.5f),
|
||||
modifier = Modifier.size(20.dp)
|
||||
)
|
||||
}
|
||||
Spacer(modifier = Modifier.width(4.dp))
|
||||
|
||||
// Catheter count (탭하면 추가 팝업)
|
||||
var showAddCatheter by remember { mutableStateOf(false) }
|
||||
@@ -1276,33 +1248,64 @@ fun PiezoMonitoringView(appState: AppState) {
|
||||
Arrangement.spacedBy(nonDevSpacing)
|
||||
) {
|
||||
|
||||
// Status badge — 임상 모드에선 측정 라벨, 일반 모드에선 urgency 메시지.
|
||||
// 빌드 114: dev 모드 한정 탭 시 더미 측정값 주입
|
||||
val clinicalSession = com.example.medilightv2android.services.ClinicalSessionStore.currentSession
|
||||
val statusText = if (clinicalSession != null)
|
||||
"${clinicalSession.posture.label} · ${clinicalSession.step.label}"
|
||||
else urgency.message
|
||||
val statusColor = urgency.accentColor
|
||||
Text(
|
||||
text = statusText,
|
||||
fontSize = (14 * fontScale).sp,
|
||||
fontWeight = FontWeight.Bold,
|
||||
color = statusColor,
|
||||
modifier = Modifier
|
||||
.clip(RoundedCornerShape(20.dp))
|
||||
.background(statusColor.copy(alpha = 0.15f))
|
||||
.then(if (appState.isDevMode) Modifier.clickable {
|
||||
val maxVol = appState.piezoSettings.maxVolume
|
||||
val dummy = (60..(maxVol - 50).coerceAtLeast(70)).random().toDouble()
|
||||
lastVolumeMl = dummy
|
||||
maxVolumeMl = maxOf(maxVolumeMl, dummy)
|
||||
displayMaxVolumeMl = maxVolumeMl
|
||||
val pct = (dummy / maxVol.toDouble()).coerceAtMost(1.0)
|
||||
val level = (pct * BladderLevel.MAX_LEVEL).toInt().coerceIn(0, BladderLevel.MAX_LEVEL)
|
||||
appState.bladderLevel = appState.bladderLevel.copy(currentLevel = level)
|
||||
} else Modifier)
|
||||
.padding(horizontal = 20.dp, vertical = 8.dp)
|
||||
)
|
||||
// Status chip — 자세 아이콘+텍스트 (urgency message 자리에 배치).
|
||||
// 빌드 114: dev 모드 한정 탭 시 더미 측정값 주입.
|
||||
// 색/아이콘/라벨은 currentPosture 에 따라 변하며 Crossfade 300ms 로 부드럽게 전환.
|
||||
androidx.compose.animation.Crossfade(
|
||||
targetState = currentPosture,
|
||||
animationSpec = androidx.compose.animation.core.tween(300),
|
||||
label = "posture-chip"
|
||||
) { posture ->
|
||||
val icon = when (posture) {
|
||||
com.example.medilightv2android.managers.Posture.LYING -> Icons.Default.Hotel
|
||||
com.example.medilightv2android.managers.Posture.NON_LYING -> Icons.Default.AccessibilityNew
|
||||
com.example.medilightv2android.managers.Posture.WALKING -> Icons.Default.DirectionsWalk
|
||||
com.example.medilightv2android.managers.Posture.UNKNOWN -> Icons.Default.HelpOutline
|
||||
}
|
||||
val tint = when (posture) {
|
||||
com.example.medilightv2android.managers.Posture.LYING -> Color(0xFF2196F3)
|
||||
com.example.medilightv2android.managers.Posture.NON_LYING -> Color(0xFF4CAF50)
|
||||
com.example.medilightv2android.managers.Posture.WALKING -> Color(0xFFD32F2F)
|
||||
com.example.medilightv2android.managers.Posture.UNKNOWN -> Color(0xFF616161) // 진한 회색 — 가시성 확보
|
||||
}
|
||||
val label = when (posture) {
|
||||
com.example.medilightv2android.managers.Posture.LYING -> "Lying"
|
||||
com.example.medilightv2android.managers.Posture.NON_LYING -> "Upright"
|
||||
com.example.medilightv2android.managers.Posture.WALKING -> "Walking"
|
||||
com.example.medilightv2android.managers.Posture.UNKNOWN -> "Detecting"
|
||||
}
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
modifier = Modifier
|
||||
.clip(RoundedCornerShape(20.dp))
|
||||
.background(tint.copy(alpha = 0.15f))
|
||||
.then(if (appState.isDevMode) Modifier.clickable {
|
||||
val maxVol = appState.piezoSettings.maxVolume
|
||||
val dummy = (60..(maxVol - 50).coerceAtLeast(70)).random().toDouble()
|
||||
lastVolumeMl = dummy
|
||||
maxVolumeMl = maxOf(maxVolumeMl, dummy)
|
||||
displayMaxVolumeMl = maxVolumeMl
|
||||
val pct = (dummy / maxVol.toDouble()).coerceAtMost(1.0)
|
||||
val lvl = (pct * BladderLevel.MAX_LEVEL).toInt().coerceIn(0, BladderLevel.MAX_LEVEL)
|
||||
appState.bladderLevel = appState.bladderLevel.copy(currentLevel = lvl)
|
||||
} else Modifier)
|
||||
.padding(horizontal = 20.dp, vertical = 8.dp)
|
||||
) {
|
||||
Icon(
|
||||
icon,
|
||||
contentDescription = label,
|
||||
tint = tint,
|
||||
modifier = Modifier.size(22.dp)
|
||||
)
|
||||
Spacer(modifier = Modifier.width(6.dp))
|
||||
Text(
|
||||
label,
|
||||
fontSize = (16 * fontScale).sp,
|
||||
fontWeight = FontWeight.Bold,
|
||||
color = tint
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// Donut + Bladdy sizing — 회의 결정: dev/일반 통일.
|
||||
val baseDonutSize = donutFitSize
|
||||
|
||||
Reference in New Issue
Block a user