UI text: revised 문서 반영 — 전체 텍스트 통일

[Placement]
- Alignment Complete → Start Scanning
- sensor → VesiScan (초기 안내)
- Raise/Lower the sensor → Slide up/down
- Move left/right → Slide left/right
- Fine-tune → Optimizing, Checking LR → Centering
- Optimal position! → In position!
- Position lost/detached → Restart above pubic bone.
- Past optimum → Too far, Lost signal → Signal lost

[Donut]
- Void/Catheterize → Voided/Catheterized
- Auto → Auto Scan, Stop → Stop Scan, Spot → Single Scan
- Voiding recorded → Recorded
- 한글 다이얼로그 → 영어 (Disconnect and remove pairing.)
- Sensor Position Issue → Position lost, Go to Alignment → Realign

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-11 13:58:27 +09:00
parent 75b10c2f77
commit ddd2f79380
2 changed files with 47 additions and 47 deletions
@@ -949,7 +949,7 @@ fun PiezoMonitoringView(appState: AppState) {
lastVolumeMl = 0.0 lastVolumeMl = 0.0
maxVolumeMl = 0.0 maxVolumeMl = 0.0
resetMeasurement() resetMeasurement()
android.widget.Toast.makeText(context, "Voiding recorded", android.widget.Toast.LENGTH_SHORT).show() android.widget.Toast.makeText(context, "Recorded", android.widget.Toast.LENGTH_SHORT).show()
}, },
enabled = !isSpotInProgress && !isAutoMeasuring, enabled = !isSpotInProgress && !isAutoMeasuring,
modifier = Modifier.weight(1.3f).height(btnHeight), modifier = Modifier.weight(1.3f).height(btnHeight),
@@ -960,8 +960,8 @@ fun PiezoMonitoringView(appState: AppState) {
) )
) { ) {
Column(horizontalAlignment = Alignment.CenterHorizontally) { Column(horizontalAlignment = Alignment.CenterHorizontally) {
Text("Void /", fontSize = 13.sp, fontWeight = FontWeight.SemiBold, color = MlPrimary) Text("Voided", fontSize = 13.sp, fontWeight = FontWeight.SemiBold, color = MlPrimary)
Text("Catheterization", fontSize = 13.sp, fontWeight = FontWeight.SemiBold, color = MlPrimary) Text("Catheterized", fontSize = 13.sp, fontWeight = FontWeight.SemiBold, color = MlPrimary)
} }
} }
@@ -979,9 +979,9 @@ fun PiezoMonitoringView(appState: AppState) {
) )
) { ) {
if (isAutoMeasuring) { if (isAutoMeasuring) {
Text("Stop", fontSize = 14.sp, fontWeight = FontWeight.SemiBold, color = Color.White) Text("Stop Scan", fontSize = 14.sp, fontWeight = FontWeight.SemiBold, color = Color.White)
} else { } else {
Text("Automatic", fontSize = 14.sp, fontWeight = FontWeight.SemiBold, color = Color.White) Text("Auto Scan", fontSize = 14.sp, fontWeight = FontWeight.SemiBold, color = Color.White)
} }
} }
@@ -1032,7 +1032,7 @@ fun PiezoMonitoringView(appState: AppState) {
if (isSpotInProgress) { if (isSpotInProgress) {
CircularProgressIndicator(color = Color.White, modifier = Modifier.size(18.dp), strokeWidth = 2.dp) CircularProgressIndicator(color = Color.White, modifier = Modifier.size(18.dp), strokeWidth = 2.dp)
} else { } else {
Text("Spot", fontSize = 14.sp, fontWeight = FontWeight.SemiBold, color = Color.White) Text("Single Scan", fontSize = 14.sp, fontWeight = FontWeight.SemiBold, color = Color.White)
} }
} }
} }
@@ -1286,7 +1286,7 @@ fun PiezoMonitoringView(appState: AppState) {
AlertDialog( AlertDialog(
onDismissRequest = { showDisconnectConfirm = false }, onDismissRequest = { showDisconnectConfirm = false },
title = { Text("Delete Pairing") }, title = { Text("Delete Pairing") },
text = { Text("기기 연결을 해제하고 페어링 정보를 삭제합니다.") }, text = { Text("Disconnect and remove pairing.") },
confirmButton = { confirmButton = {
TextButton(onClick = { TextButton(onClick = {
showDisconnectConfirm = false; showSettings = false showDisconnectConfirm = false; showSettings = false
@@ -1328,14 +1328,14 @@ fun PiezoMonitoringView(appState: AppState) {
if (showRealignPrompt) { if (showRealignPrompt) {
AlertDialog( AlertDialog(
onDismissRequest = { showRealignPrompt = false }, onDismissRequest = { showRealignPrompt = false },
title = { Text("Sensor Position Issue") }, title = { Text("Position lost") },
text = { Text("The sensor may have shifted from the optimal position. Please re-run Sensor Alignment to ensure accurate measurements.") }, text = { Text("Sensor may have shifted. Restart from alignment.") },
confirmButton = { confirmButton = {
Button(onClick = { Button(onClick = {
showRealignPrompt = false showRealignPrompt = false
autoFailCount = 0 autoFailCount = 0
appState.currentScreen = com.example.medilightv2android.AppScreen.PLACEMENT_GUIDE appState.currentScreen = com.example.medilightv2android.AppScreen.PLACEMENT_GUIDE
}) { Text("Go to Alignment") } }) { Text("Realign") }
}, },
dismissButton = { dismissButton = {
TextButton(onClick = { showRealignPrompt = false; autoFailCount = 0 }) { TextButton(onClick = { showRealignPrompt = false; autoFailCount = 0 }) {
@@ -120,7 +120,7 @@ fun PlacementGuideView(appState: AppState) {
lastHintChangeMs = 0L lastHintChangeMs = 0L
lastGuidePhase = PlacementPhase.VERTICAL lastGuidePhase = PlacementPhase.VERTICAL
phaseHoldScans = 0 phaseHoldScans = 0
directionHint = "Place the sensor above the pubic bone, then press Start" directionHint = "Place VesiScan above the pubic bone, then press Start"
directionIcon = "" directionIcon = ""
lastCv = Double.NaN lastCv = Double.NaN
for (i in 0 until 6) { channelDetected[i] = false; channelUrineLen[i] = 0 } for (i in 0 until 6) { channelDetected[i] = false; channelUrineLen[i] = 0 }
@@ -225,8 +225,8 @@ fun PlacementGuideView(appState: AppState) {
val displayHint = buildString { val displayHint = buildString {
val hint = directionHint.ifEmpty { " " } val hint = directionHint.ifEmpty { " " }
append(hint) append(hint)
if (hint.startsWith("Raise") || hint.startsWith("Lower") || hint.startsWith("Move") || if (hint.startsWith("Slide") || hint.startsWith("Optimizing") ||
hint.startsWith("Fine-tune") || hint.startsWith("Checking") || hint.startsWith("Adjust")) { hint.startsWith("Centering") || hint.startsWith("Adjust")) {
append(dots) append(dots)
} }
if (appState.isDevMode && !lastCv.isNaN()) { if (appState.isDevMode && !lastCv.isNaN()) {
@@ -602,7 +602,7 @@ fun PlacementGuideView(appState: AppState) {
val detachResult = if (fullCh.size >= 6) DetachmentDetection.detect(buffers) else null val detachResult = if (fullCh.size >= 6) DetachmentDetection.detect(buffers) else null
if (detachResult != null && detachResult.isDetached) { if (detachResult != null && detachResult.isDetached) {
directionHint = "Sensor detached. Place above pubic bone, then press Start" directionHint = "Sensor detached. Restart above pubic bone."
directionIcon = "" // 화살표 없음 directionIcon = "" // 화살표 없음
placementScore = 0 placementScore = 0
isContinuousMode = false isContinuousMode = false
@@ -690,7 +690,7 @@ fun PlacementGuideView(appState: AppState) {
// GREEN hold 중이면 힌트 강제 유지 // GREEN hold 중이면 힌트 강제 유지
if (greenHeld) { if (greenHeld) {
directionHint = "Optimal position!" directionHint = "In position!"
directionIcon = "checkmark.circle" directionIcon = "checkmark.circle"
placementPhase = PlacementPhase.GREEN placementPhase = PlacementPhase.GREEN
if (!guideResult.cv.isNaN()) lastCv = guideResult.cv if (!guideResult.cv.isNaN()) lastCv = guideResult.cv
@@ -707,7 +707,7 @@ fun PlacementGuideView(appState: AppState) {
greenFailCount++ greenFailCount++
if (greenFailCount < 3) { if (greenFailCount < 3) {
// 아직 이탈 안 함 — GREEN 유지 // 아직 이탈 안 함 — GREEN 유지
directionHint = "Optimal position!" directionHint = "In position!"
directionIcon = "checkmark.circle" directionIcon = "checkmark.circle"
placementPhase = PlacementPhase.GREEN placementPhase = PlacementPhase.GREEN
} else { } else {
@@ -716,7 +716,7 @@ fun PlacementGuideView(appState: AppState) {
greenLockedAtMs = 0L greenLockedAtMs = 0L
greenFailCount = 0 greenFailCount = 0
if (lastLedMode != 5) { bleManager.sendLedMode(5); lastLedMode = 5 } if (lastLedMode != 5) { bleManager.sendLedMode(5); lastLedMode = 5 }
directionHint = "Position lost. Place sensor above pubic bone, then press Start" directionHint = "Position lost. Restart above pubic bone."
directionIcon = "" // 화살표 없음 directionIcon = "" // 화살표 없음
placementScore = 10 placementScore = 10
placementPhase = PlacementPhase.VERTICAL placementPhase = PlacementPhase.VERTICAL
@@ -810,7 +810,7 @@ fun PlacementGuideView(appState: AppState) {
} }
} }
// Alignment Complete button (스캔 진행 중에만) // Start Scanning button (스캔 진행 중에만)
if (!waitingForStart) Button( if (!waitingForStart) Button(
onClick = { onClick = {
isContinuousMode = false isContinuousMode = false
@@ -828,7 +828,7 @@ fun PlacementGuideView(appState: AppState) {
) { ) {
Icon(Icons.Filled.CheckCircle, contentDescription = null, tint = Color.White) Icon(Icons.Filled.CheckCircle, contentDescription = null, tint = Color.White)
Spacer(modifier = Modifier.width(8.dp)) Spacer(modifier = Modifier.width(8.dp))
Text("Alignment Complete", fontSize = 16.sp, fontWeight = FontWeight.Bold, color = Color.White) Text("Start Scanning", fontSize = 16.sp, fontWeight = FontWeight.Bold, color = Color.White)
} }
// Skip button // Skip button
@@ -1234,7 +1234,7 @@ private fun computePlacementGuide(
val detectedCount = detected.count { it } val detectedCount = detected.count { it }
if (detectedCount == 0) { if (detectedCount == 0) {
return PlacementGuideResult("Raise the sensor up", "arrow.up", 0, return PlacementGuideResult("Slide up", "arrow.up", 0,
phase = PlacementPhase.VERTICAL, nextPhase = PlacementPhase.VERTICAL) phase = PlacementPhase.VERTICAL, nextPhase = PlacementPhase.VERTICAL)
} }
@@ -1259,9 +1259,9 @@ private fun computePlacementGuide(
// Phase 1: nothing detected — start instruction // Phase 1: nothing detected — start instruction
if (centerDetected == 0) { if (centerDetected == 0) {
val hint = if (vs.everSawAllFour) val hint = if (vs.everSawAllFour)
"Lost signal — slide back up ↑" "Signal lost — slide up ↑"
else else
"Place sensor near navel, slide slowly down ↓" "Start near navel, slide down slowly ↓"
val icon = if (vs.everSawAllFour) "arrow.up" else "arrow.down" val icon = if (vs.everSawAllFour) "arrow.up" else "arrow.down"
return PlacementGuideResult(hint, icon, 5, return PlacementGuideResult(hint, icon, 5,
phase = PlacementPhase.VERTICAL, nextPhase = PlacementPhase.VERTICAL) phase = PlacementPhase.VERTICAL, nextPhase = PlacementPhase.VERTICAL)
@@ -1270,7 +1270,7 @@ private fun computePlacementGuide(
// Phase 4: was at all-4, now lost a channel → overshoot (boundary) // Phase 4: was at all-4, now lost a channel → overshoot (boundary)
if (vs.everSawAllFour && centerDetected < prev && centerDetected < 4) { if (vs.everSawAllFour && centerDetected < prev && centerDetected < 4) {
vs.boundaryHit = true vs.boundaryHit = true
return PlacementGuideResult("Past optimum — slide back up ↑", "arrow.up", 50, return PlacementGuideResult("Too far — slide up ↑", "arrow.up", 50,
phase = PlacementPhase.VERTICAL, nextPhase = PlacementPhase.VERTICAL) phase = PlacementPhase.VERTICAL, nextPhase = PlacementPhase.VERTICAL)
} }
@@ -1290,9 +1290,9 @@ private fun computePlacementGuide(
phase = PlacementPhase.VERTICAL, nextPhase = PlacementPhase.GREEN) phase = PlacementPhase.VERTICAL, nextPhase = PlacementPhase.GREEN)
} else { } else {
val lrHint = when { val lrHint = when {
l4 > 0 && l5 > 0 && l4 > l5 -> "Move left ←" l4 > 0 && l5 > 0 && l4 > l5 -> "Slide left ←"
l4 > 0 && l5 > 0 && l5 > l4 -> "Move right →" l4 > 0 && l5 > 0 && l5 > l4 -> "Slide right →"
else -> "Checking left/right balance..." else -> "Centering"
} }
val lrIcon = when { val lrIcon = when {
l4 > 0 && l5 > 0 && l4 > l5 -> "arrow.left" l4 > 0 && l5 > 0 && l4 > l5 -> "arrow.left"
@@ -1328,13 +1328,13 @@ private fun computePlacementGuide(
val hint: String; val icon: String val hint: String; val icon: String
when { when {
(ch0det || ch1det) && !ch2det && !ch3det -> { (ch0det || ch1det) && !ch2det && !ch3det -> {
hint = "Raise the sensor up"; icon = "arrow.up" hint = "Slide up"; icon = "arrow.up"
} }
(ch3det || ch2det) && !ch0det && !ch1det -> { (ch3det || ch2det) && !ch0det && !ch1det -> {
hint = "Lower the sensor down"; icon = "arrow.down" hint = "Slide down"; icon = "arrow.down"
} }
else -> { else -> {
hint = "Raise the sensor up"; icon = "arrow.up" hint = "Slide up"; icon = "arrow.up"
} }
} }
return PlacementGuideResult(hint, icon, centerDetected * 10, return PlacementGuideResult(hint, icon, centerDetected * 10,
@@ -1379,7 +1379,7 @@ private fun computePlacementGuide(
val ch5ok = detected.getOrElse(5) { false } val ch5ok = detected.getOrElse(5) { false }
// 항상 LATERAL 단계를 거침 — 전환 시 항상 Checking 표시 // 항상 LATERAL 단계를 거침 — 전환 시 항상 Checking 표시
val lrHint = "Checking left/right balance..." val lrHint = "Centering"
val lrIcon = "checkmark.circle" val lrIcon = "checkmark.circle"
val score = (40 + ((1.0 - kotlin.math.abs(gradient) / gradientThr) * 20).toInt()) val score = (40 + ((1.0 - kotlin.math.abs(gradient) / gradientThr) * 20).toInt())
.coerceIn(40, 60) .coerceIn(40, 60)
@@ -1396,16 +1396,16 @@ private fun computePlacementGuide(
val ch4ok = detected.getOrElse(4) { false } val ch4ok = detected.getOrElse(4) { false }
val ch5ok = detected.getOrElse(5) { false } val ch5ok = detected.getOrElse(5) { false }
// 항상 LATERAL 단계를 거침 // 항상 LATERAL 단계를 거침
return PlacementGuideResult("Checking left/right balance...", return PlacementGuideResult("Centering",
"checkmark.circle", 50, "checkmark.circle", 50,
phase = PlacementPhase.VERTICAL, nextPhase = PlacementPhase.LATERAL) phase = PlacementPhase.VERTICAL, nextPhase = PlacementPhase.LATERAL)
} else if (gradient > 0) { } else if (gradient > 0) {
val intensity = if (gradient > 0.8) "Lower the sensor down" else "Lower the sensor slightly" val intensity = if (gradient > 0.8) "Slide down" else "Slide down slightly"
return PlacementGuideResult(intensity, "arrow.down", return PlacementGuideResult(intensity, "arrow.down",
(20 + centerDetected * 5).coerceAtMost(35), (20 + centerDetected * 5).coerceAtMost(35),
phase = PlacementPhase.VERTICAL, nextPhase = PlacementPhase.VERTICAL) phase = PlacementPhase.VERTICAL, nextPhase = PlacementPhase.VERTICAL)
} else { } else {
val intensity = if (gradient < -0.8) "Raise the sensor up" else "Raise the sensor slightly" val intensity = if (gradient < -0.8) "Slide up" else "Slide up slightly"
return PlacementGuideResult(intensity, "arrow.up", return PlacementGuideResult(intensity, "arrow.up",
(20 + centerDetected * 5).coerceAtMost(35), (20 + centerDetected * 5).coerceAtMost(35),
phase = PlacementPhase.VERTICAL, nextPhase = PlacementPhase.VERTICAL) phase = PlacementPhase.VERTICAL, nextPhase = PlacementPhase.VERTICAL)
@@ -1443,10 +1443,10 @@ private fun computePlacementGuide(
phase = PlacementPhase.LATERAL, nextPhase = PlacementPhase.GREEN) phase = PlacementPhase.LATERAL, nextPhase = PlacementPhase.GREEN)
} }
return if (lateralOffsetMm > 0) { return if (lateralOffsetMm > 0) {
PlacementGuideResult("Move right →", "arrow.right", 45, PlacementGuideResult("Slide right →", "arrow.right", 45,
phase = PlacementPhase.LATERAL, nextPhase = PlacementPhase.LATERAL) phase = PlacementPhase.LATERAL, nextPhase = PlacementPhase.LATERAL)
} else { } else {
PlacementGuideResult("Move left ←", "arrow.left", 45, PlacementGuideResult("Slide left ←", "arrow.left", 45,
phase = PlacementPhase.LATERAL, nextPhase = PlacementPhase.LATERAL) phase = PlacementPhase.LATERAL, nextPhase = PlacementPhase.LATERAL)
} }
} }
@@ -1454,12 +1454,12 @@ private fun computePlacementGuide(
// 한쪽만 detect → 반대쪽으로 이동 (잡히는 쪽의 반대로) // 한쪽만 detect → 반대쪽으로 이동 (잡히는 쪽의 반대로)
// ch4(왼쪽)만 잡힘 = 기기가 오른쪽 치우침 = 왼쪽으로 // ch4(왼쪽)만 잡힘 = 기기가 오른쪽 치우침 = 왼쪽으로
if (ch4det && !ch5det) { if (ch4det && !ch5det) {
return PlacementGuideResult("Move left ←", "arrow.left", 40, return PlacementGuideResult("Slide left ←", "arrow.left", 40,
phase = PlacementPhase.LATERAL, nextPhase = PlacementPhase.LATERAL) phase = PlacementPhase.LATERAL, nextPhase = PlacementPhase.LATERAL)
} }
// ch5(오른쪽)만 잡힘 = 기기가 왼쪽 치우침 = 오른쪽으로 // ch5(오른쪽)만 잡힘 = 기기가 왼쪽 치우침 = 오른쪽으로
if (ch5det && !ch4det) { if (ch5det && !ch4det) {
return PlacementGuideResult("Move right →", "arrow.right", 40, return PlacementGuideResult("Slide right →", "arrow.right", 40,
phase = PlacementPhase.LATERAL, nextPhase = PlacementPhase.LATERAL) phase = PlacementPhase.LATERAL, nextPhase = PlacementPhase.LATERAL)
} }
if (!ch4det && !ch5det) { if (!ch4det && !ch5det) {
@@ -1549,7 +1549,7 @@ private fun computePlacementGuide(
if (isPass) { if (isPass) {
val score = ((1.0 - cv / cvThr).coerceIn(0.0, 1.0) * 100).toInt().coerceAtLeast(70) val score = ((1.0 - cv / cvThr).coerceIn(0.0, 1.0) * 100).toInt().coerceAtLeast(70)
return PlacementGuideResult( return PlacementGuideResult(
"Optimal position!", "In position!",
"checkmark.circle", score, cv, true, lrDev, lrPass, "checkmark.circle", score, cv, true, lrDev, lrPass,
phase = PlacementPhase.GREEN, nextPhase = PlacementPhase.GREEN) phase = PlacementPhase.GREEN, nextPhase = PlacementPhase.GREEN)
} else { } else {
@@ -1567,25 +1567,25 @@ private fun computePlacementGuide(
when { when {
// ch4/ch5 중 하나만 빠짐 → 반대 방향 안내 // ch4/ch5 중 하나만 빠짐 → 반대 방향 안내
!lrPass && !ch4det && ch5det -> { !lrPass && !ch4det && ch5det -> {
adjustHint = "Move left ←"; adjustIcon = "arrow.left" adjustHint = "Slide left ←"; adjustIcon = "arrow.left"
} }
!lrPass && ch4det && !ch5det -> { !lrPass && ch4det && !ch5det -> {
adjustHint = "Move right →"; adjustIcon = "arrow.right" adjustHint = "Slide right →"; adjustIcon = "arrow.right"
} }
!lrPass && !ch4det && !ch5det -> { !lrPass && !ch4det && !ch5det -> {
adjustHint = "Adjust left or right"; adjustIcon = "arrow.right" adjustHint = "Adjust left or right"; adjustIcon = "arrow.right"
} }
!lrPass && lrDev > DEFAULT_LR_DEV_THR -> { !lrPass && lrDev > DEFAULT_LR_DEV_THR -> {
adjustHint = "Move left ←"; adjustIcon = "arrow.left" adjustHint = "Slide left ←"; adjustIcon = "arrow.left"
} }
!lrPass && lrDev < -DEFAULT_LR_DEV_THR -> { !lrPass && lrDev < -DEFAULT_LR_DEV_THR -> {
adjustHint = "Move right →"; adjustIcon = "arrow.right" adjustHint = "Slide right →"; adjustIcon = "arrow.right"
} }
gradient > 0.3 -> { gradient > 0.3 -> {
adjustHint = "Lower the sensor down"; adjustIcon = "arrow.down" adjustHint = "Slide down"; adjustIcon = "arrow.down"
} }
gradient < -0.3 -> { gradient < -0.3 -> {
adjustHint = "Raise the sensor up"; adjustIcon = "arrow.up" adjustHint = "Slide up"; adjustIcon = "arrow.up"
} }
else -> { else -> {
// 방향 애매 + CV 미달 → ch별 len 분석으로 방향 제시 // 방향 애매 + CV 미달 → ch별 len 분석으로 방향 제시
@@ -1596,13 +1596,13 @@ private fun computePlacementGuide(
// 최소 채널이 최대의 60% 미만 → 약한 쪽으로 안내 // 최소 채널이 최대의 60% 미만 → 약한 쪽으로 안내
if (minCh <= 1) { if (minCh <= 1) {
// ch0/ch1 약함 → 위로 // ch0/ch1 약함 → 위로
adjustHint = "Raise the sensor slightly"; adjustIcon = "arrow.up" adjustHint = "Slide up slightly"; adjustIcon = "arrow.up"
} else { } else {
// ch2/ch3 약함 → 아래로 // ch2/ch3 약함 → 아래로
adjustHint = "Lower the sensor slightly"; adjustIcon = "arrow.down" adjustHint = "Slide down slightly"; adjustIcon = "arrow.down"
} }
} else { } else {
adjustHint = "Fine-tune position"; adjustIcon = "checkmark.circle" adjustHint = "Optimizing"; adjustIcon = "checkmark.circle"
} }
} }
} }