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:
+15
-60
@@ -59,10 +59,10 @@ fun PlacementGuideView(appState: AppState) {
|
|||||||
val analyzer = remember { PiezoEchoAnalyzer.shared }
|
val analyzer = remember { PiezoEchoAnalyzer.shared }
|
||||||
|
|
||||||
var isScanning by remember { mutableStateOf(false) }
|
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 channelDetected = remember { mutableStateListOf(false, false, false, false, false, false) }
|
||||||
val channelUrineLen = remember { mutableStateListOf(0, 0, 0, 0, 0, 0) }
|
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 directionIcon by remember { mutableStateOf("") }
|
||||||
var placementScore by remember { mutableIntStateOf(0) }
|
var placementScore by remember { mutableIntStateOf(0) }
|
||||||
var scanCount by remember { mutableIntStateOf(0) }
|
var scanCount by remember { mutableIntStateOf(0) }
|
||||||
@@ -82,7 +82,7 @@ fun PlacementGuideView(appState: AppState) {
|
|||||||
isLocked = false
|
isLocked = false
|
||||||
scanCount = 0
|
scanCount = 0
|
||||||
placementScore = 0
|
placementScore = 0
|
||||||
directionHint = "치골 위에 센서를 올려놓으세요"
|
directionHint = "센서를 부착해주세요"
|
||||||
directionIcon = ""
|
directionIcon = ""
|
||||||
for (i in 0 until 6) { channelDetected[i] = false; channelUrineLen[i] = 0 }
|
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(
|
Button(
|
||||||
onClick = {
|
onClick = {
|
||||||
if (isContinuousMode) {
|
isContinuousMode = false
|
||||||
isContinuousMode = false
|
appState.placementComplete()
|
||||||
} else {
|
|
||||||
isContinuousMode = true
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|
enabled = isLocked || detectedCount >= 4,
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.height(52.dp),
|
.height(52.dp),
|
||||||
shape = RoundedCornerShape(14.dp),
|
shape = RoundedCornerShape(14.dp),
|
||||||
colors = ButtonDefaults.buttonColors(
|
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(
|
Icon(Icons.Filled.CheckCircle, contentDescription = null, tint = Color.White)
|
||||||
text = when {
|
Spacer(modifier = Modifier.width(8.dp))
|
||||||
isContinuousMode -> "Stop Scanning"
|
Text("Placement Complete", fontSize = 16.sp, fontWeight = FontWeight.Bold, color = Color.White)
|
||||||
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
|
|
||||||
)
|
|
||||||
Spacer(modifier = Modifier.width(8.dp))
|
|
||||||
Text(
|
|
||||||
text = "Start Measurement",
|
|
||||||
fontSize = 16.sp,
|
|
||||||
fontWeight = FontWeight.Bold,
|
|
||||||
color = Color.White
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Skip button
|
// Skip button
|
||||||
Spacer(modifier = Modifier.height(12.dp))
|
Spacer(modifier = Modifier.height(8.dp))
|
||||||
Button(
|
TextButton(onClick = { isContinuousMode = false; appState.placementComplete() }) {
|
||||||
onClick = { appState.placementComplete() },
|
|
||||||
modifier = Modifier
|
|
||||||
.fillMaxWidth()
|
|
||||||
.height(48.dp),
|
|
||||||
shape = RoundedCornerShape(14.dp),
|
|
||||||
colors = ButtonDefaults.buttonColors(
|
|
||||||
containerColor = MlSecondaryText.copy(alpha = 0.15f)
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
Text(
|
Text(
|
||||||
text = "Skip",
|
text = "Skip",
|
||||||
fontSize = 15.sp,
|
fontSize = 15.sp,
|
||||||
@@ -1036,7 +991,7 @@ private fun computePlacementGuide(
|
|||||||
val lrMax = maxOf(len4, len5, 1)
|
val lrMax = maxOf(len4, len5, 1)
|
||||||
val lrSymmetry = 1.0 - kotlin.math.abs(len4 - len5).toDouble() / lrMax
|
val lrSymmetry = 1.0 - kotlin.math.abs(len4 - len5).toDouble() / lrMax
|
||||||
|
|
||||||
if (lrSymmetry >= 0.7 || (ch4 && ch5)) {
|
if (lrSymmetry >= 0.7) {
|
||||||
return PlacementGuideResult(
|
return PlacementGuideResult(
|
||||||
"좌우 대칭 OK! 최종 확인 중...",
|
"좌우 대칭 OK! 최종 확인 중...",
|
||||||
"checkmark.circle", 60,
|
"checkmark.circle", 60,
|
||||||
|
|||||||
Reference in New Issue
Block a user