From 3361ed00f26060e412f0ab6e80679c3bcf944a4f Mon Sep 17 00:00:00 2001 From: jjangddu Date: Wed, 17 Jun 2026 15:43:49 +0900 Subject: [PATCH] =?UTF-8?q?ux:=20Sensor=20Alignment=20hint=20=EB=B9=88=20?= =?UTF-8?q?=EC=B9=B8=20=EC=88=A8=EA=B9=80=20+=20V2=20=EA=B2=8C=EC=9D=B4?= =?UTF-8?q?=ED=8A=B8=20=EA=B0=95=ED=99=94=20(demo-final)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1) 힌트 메세지 박스 자체 숨김 (내용 없을 때) 기존: waitingForStart 시 Hint 2 = AnnotatedString(" ") — 빈 칸 자체는 유지 변경: 빈 텍스트면 Box 자체 렌더 X (Spacer 도 같이 제거). Hint 1: waitingForStart 일 때만 렌더 Hint 2: !waitingForStart && displayHint.text.isNotBlank() 일 때만 렌더 레이아웃: 빈 칸이 사라지므로 일러스트 위/아래 여백이 자연스럽게 통합. 2) Hint 1/2 텍스트 상하 중앙 정렬 기존: Text 에 .padding(vertical=14dp) 만 — baseline 기준이라 일부 폰트에서 미세 비대칭 가능. 변경: Box(contentAlignment=Center) 로 감싸고 Text 를 그 안에 배치 → 박스 높이 = padding 14dp top + 텍스트 + 14dp bottom 정확히 매칭. 3) PiezoMonitoring → PlacementGuide 진입 시 항상 V1 (clinical 만 V2) 기존: currentSession.mode == ALIGNMENT 만 체크 → 잔여 세션이 있으면 일반 흐름에서도 V2 강제됨. 변경: !appState.placementFromMonitoring 조건도 같이 체크. enterPlacementFromMonitoring() 가 placementFromMonitoring=true 설정 → 이 경우 항상 V1. enterAlignmentSession() (clinical 전용) 만 placementFromMonitoring=false + mode=ALIGNMENT 이라 V2 로 라우팅. 영향: - PiezoMonitoring → "재정렬" 진입 시 항상 V1 ✓ - Clinical → Sensor Alignment → V2 ✓ - 위저드 흐름 + 잔여 세션 → V1 (위저드는 placementFromMonitoring 변경 안 함) Co-Authored-By: Claude Opus 4.7 --- .../ui/views/monitoring/PlacementGuideView.kt | 100 ++++++++++-------- 1 file changed, 57 insertions(+), 43 deletions(-) diff --git a/app/src/main/java/com/example/medilightv2android/ui/views/monitoring/PlacementGuideView.kt b/app/src/main/java/com/example/medilightv2android/ui/views/monitoring/PlacementGuideView.kt index 27995f6..fcbc43b 100644 --- a/app/src/main/java/com/example/medilightv2android/ui/views/monitoring/PlacementGuideView.kt +++ b/app/src/main/java/com/example/medilightv2android/ui/views/monitoring/PlacementGuideView.kt @@ -121,10 +121,12 @@ fun PlacementGuideView(appState: AppState) { var sweepLastDetected by remember { mutableIntStateOf(0) } var sweepEverSawAll by remember { mutableStateOf(false) } - // 화면 진입 시 전체 상태 리셋. clinical alignment session 이면 V2, 그 외 V1 자동 강제. + // 화면 진입 시 전체 상태 리셋. clinical alignment session (placementFromMonitoring=false + + // 활성 ALIGNMENT 세션) 이면 V2, 그 외 (PiezoMonitoring 에서 진입 / 위저드 진입 / 잔여 세션) 는 항상 V1. LaunchedEffect(Unit) { val sess = com.example.medilightv2android.services.ClinicalSessionStore.currentSession - val isClinicalAlign = sess != null && + val isClinicalAlign = !appState.placementFromMonitoring && + sess != null && sess.mode == com.example.medilightv2android.models.SessionMode.ALIGNMENT com.example.medilightv2android.managers.GreenZoneConstants.alignmentAlgo = if (isClinicalAlign) com.example.medilightv2android.managers.AlignmentAlgo.V2 @@ -232,34 +234,40 @@ fun PlacementGuideView(appState: AppState) { .padding(horizontal = 24.dp), horizontalAlignment = Alignment.CenterHorizontally ) { - // ─── (1) Hint 1 — 시작 전 안내 (칸 상시 유지, "pubic bone" 빨강 강조) ─── - val hint1Bg = Color(0xFFFFF3E0) - val hint1Text = remember(waitingForStart) { - if (waitingForStart) buildAnnotatedString { - val full = "Place VesiScan above the pubic bone, then press Start" - val key = "pubic bone" - val idx = full.indexOf(key, ignoreCase = true) - if (idx >= 0) { - append(full.substring(0, idx)) - withStyle(SpanStyle(color = Color.Red, fontWeight = FontWeight.ExtraBold)) { - append(full.substring(idx, idx + key.length)) - } - append(full.substring(idx + key.length)) - } else append(full) - } else AnnotatedString(" ") // 공백 — 칸 높이 유지 + // ─── (1) Hint 1 — waitingForStart 일 때만 렌더 (빈 칸 자체 숨김) ─── + if (waitingForStart) { + val hint1Bg = Color(0xFFFFF3E0) + val hint1Text = remember { + buildAnnotatedString { + val full = "Place VesiScan above the pubic bone, then press Start" + val key = "pubic bone" + val idx = full.indexOf(key, ignoreCase = true) + if (idx >= 0) { + append(full.substring(0, idx)) + withStyle(SpanStyle(color = Color.Red, fontWeight = FontWeight.ExtraBold)) { + append(full.substring(idx, idx + key.length)) + } + append(full.substring(idx + key.length)) + } else append(full) + } + } + Box( + modifier = Modifier + .fillMaxWidth() + .background(hint1Bg.copy(alpha = 0.7f), RoundedCornerShape(16.dp)) + .padding(horizontal = 20.dp, vertical = 14.dp), + contentAlignment = Alignment.Center // 상하 중앙 정렬 + ) { + Text( + text = hint1Text, + fontSize = 26.sp, + fontWeight = FontWeight.Bold, + color = Color.Black, + textAlign = TextAlign.Center, + ) + } + Spacer(modifier = Modifier.height(8.dp)) } - Text( - text = hint1Text, - fontSize = 26.sp, - fontWeight = FontWeight.Bold, - color = Color.Black, - textAlign = TextAlign.Center, - modifier = Modifier - .fillMaxWidth() - .background(hint1Bg.copy(alpha = 0.7f), RoundedCornerShape(16.dp)) - .padding(horizontal = 20.dp, vertical = 14.dp) - ) - Spacer(modifier = Modifier.height(8.dp)) // 타이틀은 top bar 로 이동 (← 옆 절대 가운데) — 본문 중복 제거. // ── 가이드 텍스트 (Step 아래, 몸 위) — "pubic bone" 인라인 빨강 ExtraBold 강조 ── @@ -524,20 +532,26 @@ fun PlacementGuideView(appState: AppState) { Spacer(modifier = Modifier.height(8.dp)) - // ─── (5) Hint 2 — 측정 중 방향 안내 (waitingForStart 시 공백, pubic bone 중복 X) ─── - Text( - text = if (waitingForStart) AnnotatedString(" ") else displayHint, - fontSize = 28.sp, - fontWeight = FontWeight.Bold, - color = Color.Black, - textAlign = TextAlign.Center, - modifier = Modifier - .fillMaxWidth() - .background(hintBg.copy(alpha = 0.7f), RoundedCornerShape(16.dp)) - .padding(horizontal = 20.dp, vertical = 14.dp) - ) - - Spacer(modifier = Modifier.height(8.dp)) + // ─── (5) Hint 2 — 측정 중 방향 안내 (waitingForStart 또는 빈 텍스트면 칸 자체 숨김) ─── + val hint2Visible = !waitingForStart && displayHint.text.isNotBlank() + if (hint2Visible) { + Box( + modifier = Modifier + .fillMaxWidth() + .background(hintBg.copy(alpha = 0.7f), RoundedCornerShape(16.dp)) + .padding(horizontal = 20.dp, vertical = 14.dp), + contentAlignment = Alignment.Center + ) { + Text( + text = displayHint, + fontSize = 28.sp, + fontWeight = FontWeight.Bold, + color = Color.Black, + textAlign = TextAlign.Center, + ) + } + Spacer(modifier = Modifier.height(8.dp)) + } // 채널 dot row (개발자 모드에서만 표시) if (appState.isDevMode) {