UX: Placement 자동스캔 + Stop/Resume 삭제 + Placement Complete

Placement:
- 화면 진입 시 자동 스캔 시작 (isContinuousMode=true)
- 초기 힌트: "센서를 부착해주세요"
- Stop/Resume Scanning 버튼 삭제
- Start Measurement → "Placement Complete" (Green 또는 4+ch 감지 시 활성화)
- Skip 버튼 TextButton으로 유지

Algorithm:
- LATERAL 조건: (ch4&&ch5) OR 제거 → lrSymmetry >= 0.7 필수

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-30 14:43:36 +09:00
parent e528bc6761
commit 288efaafc9
@@ -59,10 +59,10 @@ fun PlacementGuideView(appState: AppState) {
val analyzer = remember { PiezoEchoAnalyzer.shared }
var isScanning by remember { mutableStateOf(false) }
var isContinuousMode by remember { mutableStateOf(false) }
var isContinuousMode by remember { mutableStateOf(true) }
val channelDetected = remember { mutableStateListOf(false, false, false, false, false, false) }
val channelUrineLen = remember { mutableStateListOf(0, 0, 0, 0, 0, 0) }
var directionHint by remember { mutableStateOf("치골 위에 센서를 올려놓으세요") }
var directionHint by remember { mutableStateOf("센서를 부착해주세요") }
var directionIcon by remember { mutableStateOf("") }
var placementScore by remember { mutableIntStateOf(0) }
var scanCount by remember { mutableIntStateOf(0) }
@@ -82,7 +82,7 @@ fun PlacementGuideView(appState: AppState) {
isLocked = false
scanCount = 0
placementScore = 0
directionHint = "치골 위에 센서를 올려놓으세요"
directionHint = "센서를 부착해주세요"
directionIcon = ""
for (i in 0 until 6) { channelDetected[i] = false; channelUrineLen[i] = 0 }
}
@@ -573,75 +573,30 @@ fun PlacementGuideView(appState: AppState) {
}
}
// Start/Stop button
// Placement Complete button
Button(
onClick = {
if (isContinuousMode) {
isContinuousMode = false
} else {
isContinuousMode = true
}
appState.placementComplete()
},
enabled = isLocked || detectedCount >= 4,
modifier = Modifier
.fillMaxWidth()
.height(52.dp),
shape = RoundedCornerShape(14.dp),
colors = ButtonDefaults.buttonColors(
containerColor = if (isContinuousMode) Color.Red else MlPrimary
containerColor = if (isLocked) Color(0xFF4CAF50) else MlPrimary,
disabledContainerColor = Color.Gray.copy(alpha = 0.3f)
)
) {
Text(
text = when {
isContinuousMode -> "Stop Scanning"
scanCount == 0 -> "Start Scanning"
else -> "Resume Scanning"
},
fontSize = 16.sp,
fontWeight = FontWeight.SemiBold,
color = Color.White
)
}
// Start Measurement button (when 4+ channels detected)
if (isLocked || detectedCount >= 4) {
Spacer(modifier = Modifier.height(12.dp))
Button(
onClick = { appState.placementComplete() },
modifier = Modifier
.fillMaxWidth()
.height(52.dp),
shape = RoundedCornerShape(14.dp),
colors = ButtonDefaults.buttonColors(
containerColor = Color(0xFF4CAF50)
)
) {
Icon(
Icons.Filled.PlayArrow,
contentDescription = null,
tint = Color.White
)
Icon(Icons.Filled.CheckCircle, contentDescription = null, tint = Color.White)
Spacer(modifier = Modifier.width(8.dp))
Text(
text = "Start Measurement",
fontSize = 16.sp,
fontWeight = FontWeight.Bold,
color = Color.White
)
}
Text("Placement Complete", fontSize = 16.sp, fontWeight = FontWeight.Bold, color = Color.White)
}
// Skip button
Spacer(modifier = Modifier.height(12.dp))
Button(
onClick = { appState.placementComplete() },
modifier = Modifier
.fillMaxWidth()
.height(48.dp),
shape = RoundedCornerShape(14.dp),
colors = ButtonDefaults.buttonColors(
containerColor = MlSecondaryText.copy(alpha = 0.15f)
)
) {
Spacer(modifier = Modifier.height(8.dp))
TextButton(onClick = { isContinuousMode = false; appState.placementComplete() }) {
Text(
text = "Skip",
fontSize = 15.sp,
@@ -1036,7 +991,7 @@ private fun computePlacementGuide(
val lrMax = maxOf(len4, len5, 1)
val lrSymmetry = 1.0 - kotlin.math.abs(len4 - len5).toDouble() / lrMax
if (lrSymmetry >= 0.7 || (ch4 && ch5)) {
if (lrSymmetry >= 0.7) {
return PlacementGuideResult(
"좌우 대칭 OK! 최종 확인 중...",
"checkmark.circle", 60,