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))
|
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/)
|
// Raw BLE capture toggle — IMU/packet 디버그용 (Downloads/VesiScan_RawBLE/)
|
||||||
var rawBleCapturing by remember {
|
// Dev mode 에서만 노출 (legacy 유지) — 일반 사용자에겐 숨김.
|
||||||
mutableStateOf(com.example.medilightv2android.services.RawBleLogger.isCapturing)
|
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 (탭하면 추가 팝업)
|
// Catheter count (탭하면 추가 팝업)
|
||||||
var showAddCatheter by remember { mutableStateOf(false) }
|
var showAddCatheter by remember { mutableStateOf(false) }
|
||||||
@@ -1276,33 +1248,64 @@ fun PiezoMonitoringView(appState: AppState) {
|
|||||||
Arrangement.spacedBy(nonDevSpacing)
|
Arrangement.spacedBy(nonDevSpacing)
|
||||||
) {
|
) {
|
||||||
|
|
||||||
// Status badge — 임상 모드에선 측정 라벨, 일반 모드에선 urgency 메시지.
|
// Status chip — 자세 아이콘+텍스트 (urgency message 자리에 배치).
|
||||||
// 빌드 114: dev 모드 한정 탭 시 더미 측정값 주입
|
// 빌드 114: dev 모드 한정 탭 시 더미 측정값 주입.
|
||||||
val clinicalSession = com.example.medilightv2android.services.ClinicalSessionStore.currentSession
|
// 색/아이콘/라벨은 currentPosture 에 따라 변하며 Crossfade 300ms 로 부드럽게 전환.
|
||||||
val statusText = if (clinicalSession != null)
|
androidx.compose.animation.Crossfade(
|
||||||
"${clinicalSession.posture.label} · ${clinicalSession.step.label}"
|
targetState = currentPosture,
|
||||||
else urgency.message
|
animationSpec = androidx.compose.animation.core.tween(300),
|
||||||
val statusColor = urgency.accentColor
|
label = "posture-chip"
|
||||||
Text(
|
) { posture ->
|
||||||
text = statusText,
|
val icon = when (posture) {
|
||||||
fontSize = (14 * fontScale).sp,
|
com.example.medilightv2android.managers.Posture.LYING -> Icons.Default.Hotel
|
||||||
fontWeight = FontWeight.Bold,
|
com.example.medilightv2android.managers.Posture.NON_LYING -> Icons.Default.AccessibilityNew
|
||||||
color = statusColor,
|
com.example.medilightv2android.managers.Posture.WALKING -> Icons.Default.DirectionsWalk
|
||||||
modifier = Modifier
|
com.example.medilightv2android.managers.Posture.UNKNOWN -> Icons.Default.HelpOutline
|
||||||
.clip(RoundedCornerShape(20.dp))
|
}
|
||||||
.background(statusColor.copy(alpha = 0.15f))
|
val tint = when (posture) {
|
||||||
.then(if (appState.isDevMode) Modifier.clickable {
|
com.example.medilightv2android.managers.Posture.LYING -> Color(0xFF2196F3)
|
||||||
val maxVol = appState.piezoSettings.maxVolume
|
com.example.medilightv2android.managers.Posture.NON_LYING -> Color(0xFF4CAF50)
|
||||||
val dummy = (60..(maxVol - 50).coerceAtLeast(70)).random().toDouble()
|
com.example.medilightv2android.managers.Posture.WALKING -> Color(0xFFD32F2F)
|
||||||
lastVolumeMl = dummy
|
com.example.medilightv2android.managers.Posture.UNKNOWN -> Color(0xFF616161) // 진한 회색 — 가시성 확보
|
||||||
maxVolumeMl = maxOf(maxVolumeMl, dummy)
|
}
|
||||||
displayMaxVolumeMl = maxVolumeMl
|
val label = when (posture) {
|
||||||
val pct = (dummy / maxVol.toDouble()).coerceAtMost(1.0)
|
com.example.medilightv2android.managers.Posture.LYING -> "Lying"
|
||||||
val level = (pct * BladderLevel.MAX_LEVEL).toInt().coerceIn(0, BladderLevel.MAX_LEVEL)
|
com.example.medilightv2android.managers.Posture.NON_LYING -> "Upright"
|
||||||
appState.bladderLevel = appState.bladderLevel.copy(currentLevel = level)
|
com.example.medilightv2android.managers.Posture.WALKING -> "Walking"
|
||||||
} else Modifier)
|
com.example.medilightv2android.managers.Posture.UNKNOWN -> "Detecting"
|
||||||
.padding(horizontal = 20.dp, vertical = 8.dp)
|
}
|
||||||
)
|
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/일반 통일.
|
// Donut + Bladdy sizing — 회의 결정: dev/일반 통일.
|
||||||
val baseDonutSize = donutFitSize
|
val baseDonutSize = donutFitSize
|
||||||
|
|||||||
Reference in New Issue
Block a user