UI: 피드백 반영 — Placement/Donut/뒤로가기/토스트
[Placement] - 복부 높이: 500dp→weight(1f) 유동, 스크롤 제거 - 화살표 크기: 50→70, head 20→28, stroke 7→9 - 6/6, 60% 비개발자 모드에서 제거 - Step indicator를 이미지 위로 이동 [Donut] - Bladdy 크기: 80/110dp→120/150dp (비개발자) - 도넛 크기: 260/320dp→300/360dp (비개발자) - 볼륨 텍스트 배경 제거 (투명) - 원 내부 흰색 반투명 배경 추가 - measuring 메시지 제거 (Auto 대기 중 표시 안 함) - 배터리 옆 초록 connection dot 제거 - Volume 카드 제거, Fill/Level만 유지 (maxVolumeMl 기준) - "빠른 저장"→"Quick Save" 영어 통일 - 측정 부족/미감지 토스트 제거 [기타] - HOME 뒤로가기 → "Exit App?" 확인 다이얼로그 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -11,8 +11,12 @@ import androidx.compose.foundation.layout.fillMaxSize
|
|||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.material3.Scaffold
|
import androidx.compose.material3.Scaffold
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.graphics.Color
|
||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
import app.rive.runtime.kotlin.core.Rive
|
import app.rive.runtime.kotlin.core.Rive
|
||||||
import com.example.medilightv2android.services.KeychainService
|
import com.example.medilightv2android.services.KeychainService
|
||||||
@@ -62,7 +66,30 @@ fun MedilightApp(modifier: Modifier = Modifier) {
|
|||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
val appState = remember { AppState(context) }
|
val appState = remember { AppState(context) }
|
||||||
|
|
||||||
// Android 뒤로가기 = 바로 전 화면
|
// HOME에서 뒤로가기 → 앱 종료 확인
|
||||||
|
var showExitDialog by remember { mutableStateOf(false) }
|
||||||
|
BackHandler(enabled = appState.currentScreen == AppScreen.HOME) {
|
||||||
|
showExitDialog = true
|
||||||
|
}
|
||||||
|
if (showExitDialog) {
|
||||||
|
androidx.compose.material3.AlertDialog(
|
||||||
|
onDismissRequest = { showExitDialog = false },
|
||||||
|
title = { androidx.compose.material3.Text("Exit App") },
|
||||||
|
text = { androidx.compose.material3.Text("Are you sure you want to exit?") },
|
||||||
|
confirmButton = {
|
||||||
|
androidx.compose.material3.TextButton(onClick = { (context as? android.app.Activity)?.finish() }) {
|
||||||
|
androidx.compose.material3.Text("Exit", color = Color.Red)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
dismissButton = {
|
||||||
|
androidx.compose.material3.TextButton(onClick = { showExitDialog = false }) {
|
||||||
|
androidx.compose.material3.Text("Cancel")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 다른 화면에서 뒤로가기 = 바로 전 화면
|
||||||
BackHandler(enabled = appState.currentScreen != AppScreen.HOME
|
BackHandler(enabled = appState.currentScreen != AppScreen.HOME
|
||||||
&& appState.currentScreen != AppScreen.PIN_ENTRY) {
|
&& appState.currentScreen != AppScreen.PIN_ENTRY) {
|
||||||
when (appState.currentScreen) {
|
when (appState.currentScreen) {
|
||||||
|
|||||||
+34
-60
@@ -273,22 +273,12 @@ fun PiezoMonitoringView(appState: AppState) {
|
|||||||
Log.w("PiezoMonitor", "No center channels: center=0 total=$totalWallCount/6")
|
Log.w("PiezoMonitor", "No center channels: center=0 total=$totalWallCount/6")
|
||||||
bleManager.debugLogger.warn("BV_SKIP scan=${com.example.medilightv2android.services.AdcCsvLogger.lastScanId} center=0 total=$totalWallCount/6")
|
bleManager.debugLogger.warn("BV_SKIP scan=${com.example.medilightv2android.services.AdcCsvLogger.lastScanId} center=0 total=$totalWallCount/6")
|
||||||
com.example.medilightv2android.services.AdcCsvLogger.log(rawADC, null)
|
com.example.medilightv2android.services.AdcCsvLogger.log(rawADC, null)
|
||||||
if (!isAutoMeasuring) {
|
|
||||||
handler.post { android.widget.Toast.makeText(context,
|
|
||||||
"측정 부족 (${centerWallCount}/4채널) — 센서 위치를 조절해주세요",
|
|
||||||
android.widget.Toast.LENGTH_SHORT).show() }
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Log.d("PiezoMonitor", "No valid channels — volume unchanged")
|
Log.d("PiezoMonitor", "No valid channels — volume unchanged")
|
||||||
bleManager.debugLogger.warn("BV_FAIL scan=${com.example.medilightv2android.services.AdcCsvLogger.lastScanId} no valid channels")
|
bleManager.debugLogger.warn("BV_FAIL scan=${com.example.medilightv2android.services.AdcCsvLogger.lastScanId} no valid channels")
|
||||||
val failADC = channels.sortedBy { it.channel }.map { it.buffer }
|
val failADC = channels.sortedBy { it.channel }.map { it.buffer }
|
||||||
com.example.medilightv2android.services.AdcCsvLogger.log(failADC, null)
|
com.example.medilightv2android.services.AdcCsvLogger.log(failADC, null)
|
||||||
if (!isAutoMeasuring) {
|
|
||||||
handler.post { android.widget.Toast.makeText(context,
|
|
||||||
"방광이 감지되지 않았습니다. 센서 접촉을 확인해주세요.",
|
|
||||||
android.widget.Toast.LENGTH_SHORT).show() }
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
val finalRawVol = rawVolumeMl
|
val finalRawVol = rawVolumeMl
|
||||||
@@ -575,7 +565,7 @@ fun PiezoMonitoringView(appState: AppState) {
|
|||||||
) {
|
) {
|
||||||
Icon(Icons.Default.FlashOn, contentDescription = null, tint = Color(0xFF4CAF50))
|
Icon(Icons.Default.FlashOn, contentDescription = null, tint = Color(0xFF4CAF50))
|
||||||
Spacer(modifier = Modifier.width(12.dp))
|
Spacer(modifier = Modifier.width(12.dp))
|
||||||
Text("빠른 저장", fontWeight = FontWeight.SemiBold, modifier = Modifier.weight(1f))
|
Text("Quick Save", fontWeight = FontWeight.SemiBold, modifier = Modifier.weight(1f))
|
||||||
Text("$quickVol ml", color = MlSecondaryText, fontSize = 14.sp)
|
Text("$quickVol ml", color = MlSecondaryText, fontSize = 14.sp)
|
||||||
Spacer(modifier = Modifier.width(4.dp))
|
Spacer(modifier = Modifier.width(4.dp))
|
||||||
Icon(Icons.Default.ChevronRight, contentDescription = null, tint = MlSecondaryText)
|
Icon(Icons.Default.ChevronRight, contentDescription = null, tint = MlSecondaryText)
|
||||||
@@ -707,7 +697,7 @@ fun PiezoMonitoringView(appState: AppState) {
|
|||||||
}
|
}
|
||||||
Spacer(modifier = Modifier.weight(1f))
|
Spacer(modifier = Modifier.weight(1f))
|
||||||
|
|
||||||
// Battery percentage
|
// Battery percentage (초록 dot 제거)
|
||||||
if (!isDemoMode) {
|
if (!isDemoMode) {
|
||||||
val batteryColor = when {
|
val batteryColor = when {
|
||||||
batteryLvl >= 60 -> Color(0xFF4CAF50)
|
batteryLvl >= 60 -> Color(0xFF4CAF50)
|
||||||
@@ -717,14 +707,6 @@ fun PiezoMonitoringView(appState: AppState) {
|
|||||||
Text("$batteryLvl%", fontSize = 12.sp, fontWeight = FontWeight.Bold, color = batteryColor)
|
Text("$batteryLvl%", fontSize = 12.sp, fontWeight = FontWeight.Bold, color = batteryColor)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Connection dot
|
|
||||||
Box(
|
|
||||||
modifier = Modifier
|
|
||||||
.size(8.dp)
|
|
||||||
.clip(CircleShape)
|
|
||||||
.background(if (isDemoMode || isConnected) Color(0xFF4CAF50) else Color.Red)
|
|
||||||
)
|
|
||||||
|
|
||||||
Spacer(modifier = Modifier.width(4.dp))
|
Spacer(modifier = Modifier.width(4.dp))
|
||||||
|
|
||||||
// Settings button
|
// Settings button
|
||||||
@@ -780,65 +762,57 @@ fun PiezoMonitoringView(appState: AppState) {
|
|||||||
val donutSize = if (appState.isDevMode) {
|
val donutSize = if (appState.isDevMode) {
|
||||||
if (isCompact) 220.dp else 280.dp
|
if (isCompact) 220.dp else 280.dp
|
||||||
} else {
|
} else {
|
||||||
if (isCompact) 260.dp else 320.dp // 일반 모드: 더 크게
|
if (isCompact) 300.dp else 360.dp // 일반 모드: 화면 거의 가득
|
||||||
}
|
}
|
||||||
// maxVolume 기준 비율 (0.0~1.0, 부드러운 연속값)
|
|
||||||
val maxVolumeForDonut = appState.piezoSettings.maxVolume.toDouble()
|
val maxVolumeForDonut = appState.piezoSettings.maxVolume.toDouble()
|
||||||
val donutProgress = if (maxVolumeForDonut > 0 && maxVolumeMl > 0)
|
val donutProgress = if (maxVolumeForDonut > 0 && maxVolumeMl > 0)
|
||||||
(maxVolumeMl / maxVolumeForDonut).coerceIn(0.0, 1.0) else 0.0
|
(maxVolumeMl / maxVolumeForDonut).coerceIn(0.0, 1.0) else 0.0
|
||||||
Box(contentAlignment = Alignment.Center) {
|
Box(contentAlignment = Alignment.Center) {
|
||||||
|
// 원 내부 흰색 톤 배경
|
||||||
|
if (!appState.isDevMode) {
|
||||||
|
Box(
|
||||||
|
modifier = Modifier
|
||||||
|
.size(donutSize - 50.dp)
|
||||||
|
.clip(CircleShape)
|
||||||
|
.background(Color.White.copy(alpha = 0.5f))
|
||||||
|
)
|
||||||
|
}
|
||||||
DonutChartView(
|
DonutChartView(
|
||||||
progress = donutProgress,
|
progress = donutProgress,
|
||||||
urgency = urgency,
|
urgency = urgency,
|
||||||
lineWidth = if (isCompact) 22.dp else 30.dp,
|
lineWidth = if (appState.isDevMode) {
|
||||||
|
if (isCompact) 22.dp else 30.dp
|
||||||
|
} else {
|
||||||
|
if (isCompact) 26.dp else 34.dp
|
||||||
|
},
|
||||||
size = donutSize
|
size = donutSize
|
||||||
)
|
)
|
||||||
Column(horizontalAlignment = Alignment.CenterHorizontally) {
|
Column(horizontalAlignment = Alignment.CenterHorizontally) {
|
||||||
BladdyRiveView(urgency = urgency, size = if (isCompact) 80.dp else 110.dp)
|
// Bladdy 크기: 일반 모드에서 더 크게
|
||||||
Spacer(modifier = Modifier.height(4.dp))
|
val bladdySize = if (appState.isDevMode) {
|
||||||
if (isAutoMeasuring && autoVolumeWindow.size < 5 && lastVolumeMl <= 0) {
|
if (isCompact) 80.dp else 110.dp
|
||||||
// Auto 측정 대기 중 — trimmed mean 산출 전
|
|
||||||
Row(
|
|
||||||
verticalAlignment = Alignment.CenterVertically,
|
|
||||||
modifier = Modifier
|
|
||||||
.clip(RoundedCornerShape(12.dp))
|
|
||||||
.background(MlCardBackground)
|
|
||||||
.padding(horizontal = 14.dp, vertical = 6.dp)
|
|
||||||
) {
|
|
||||||
CircularProgressIndicator(
|
|
||||||
color = Color(0xFFFF9800),
|
|
||||||
modifier = Modifier.size(16.dp),
|
|
||||||
strokeWidth = 2.dp
|
|
||||||
)
|
|
||||||
Spacer(modifier = Modifier.width(8.dp))
|
|
||||||
Text("Measuring... (${autoVolumeWindow.size}/5)",
|
|
||||||
fontSize = 14.sp, fontWeight = FontWeight.SemiBold,
|
|
||||||
color = Color(0xFFFF9800))
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
val volFontSize = if (appState.isDevMode) 18.sp else 22.sp
|
if (isCompact) 120.dp else 150.dp // 원 지름의 ~1/2
|
||||||
Text(
|
|
||||||
if (maxVolumeMl > 0) "${"%.0f".format(maxVolumeMl)} ml" else "${appState.estimatedVolume} ml",
|
|
||||||
fontSize = volFontSize,
|
|
||||||
fontWeight = FontWeight.Bold,
|
|
||||||
color = urgency.accentColor,
|
|
||||||
modifier = Modifier
|
|
||||||
.clip(RoundedCornerShape(12.dp))
|
|
||||||
.background(MlCardBackground)
|
|
||||||
.shadow(4.dp, RoundedCornerShape(12.dp), spotColor = Color.Black.copy(alpha = 0.08f))
|
|
||||||
.padding(horizontal = 16.dp, vertical = 8.dp)
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
BladdyRiveView(urgency = urgency, size = bladdySize)
|
||||||
|
Spacer(modifier = Modifier.height(4.dp))
|
||||||
|
// 볼륨 텍스트 — 배경 없이 깔끔하게
|
||||||
|
val volFontSize = if (appState.isDevMode) 18.sp else 24.sp
|
||||||
|
Text(
|
||||||
|
if (maxVolumeMl > 0) "${"%.0f".format(maxVolumeMl)} ml" else "${appState.estimatedVolume} ml",
|
||||||
|
fontSize = volFontSize,
|
||||||
|
fontWeight = FontWeight.Bold,
|
||||||
|
color = urgency.accentColor,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Info cards (Fill/Level은 maxVolume 기준)
|
// Info cards (Fill/Level은 maxVolume 기준)
|
||||||
val maxVol = appState.piezoSettings.maxVolume.toDouble()
|
val maxVol = appState.piezoSettings.maxVolume.toDouble()
|
||||||
val fillPct = if (maxVol > 0 && lastVolumeMl > 0) (lastVolumeMl / maxVol * 100).coerceAtMost(100.0) else 0.0
|
// fill/level은 maxVolumeMl(세션 최대) 기준
|
||||||
val levelVal = if (maxVol > 0 && lastVolumeMl > 0) ((lastVolumeMl / maxVol) * BladderLevel.MAX_LEVEL).toInt().coerceIn(0, BladderLevel.MAX_LEVEL) else 0
|
val fillPct = if (maxVol > 0 && maxVolumeMl > 0) (maxVolumeMl / maxVol * 100).coerceAtMost(100.0) else 0.0
|
||||||
|
val levelVal = if (maxVol > 0 && maxVolumeMl > 0) ((maxVolumeMl / maxVol) * BladderLevel.MAX_LEVEL).toInt().coerceIn(0, BladderLevel.MAX_LEVEL) else 0
|
||||||
Row(horizontalArrangement = Arrangement.spacedBy(12.dp)) {
|
Row(horizontalArrangement = Arrangement.spacedBy(12.dp)) {
|
||||||
val displayVol = if (lastVolumeMl > 0) "%.0f".format(lastVolumeMl) else "—"
|
|
||||||
InfoCard("Volume", displayVol, "ml", urgency.accentColor, Modifier.weight(1f))
|
|
||||||
InfoCard("Fill", if (fillPct > 0) "%.0f".format(fillPct) else "—", "%", urgency.accentColor, Modifier.weight(1f))
|
InfoCard("Fill", if (fillPct > 0) "%.0f".format(fillPct) else "—", "%", urgency.accentColor, Modifier.weight(1f))
|
||||||
InfoCard("Level", if (levelVal > 0) "$levelVal/${BladderLevel.MAX_LEVEL}" else "—", "", urgency.accentColor, Modifier.weight(1f))
|
InfoCard("Level", if (levelVal > 0) "$levelVal/${BladderLevel.MAX_LEVEL}" else "—", "", urgency.accentColor, Modifier.weight(1f))
|
||||||
}
|
}
|
||||||
|
|||||||
+66
-67
@@ -167,16 +167,41 @@ fun PlacementGuideView(appState: AppState) {
|
|||||||
Column(
|
Column(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxSize()
|
.fillMaxSize()
|
||||||
.verticalScroll(rememberScrollState())
|
|
||||||
.padding(horizontal = 24.dp),
|
.padding(horizontal = 24.dp),
|
||||||
horizontalAlignment = Alignment.CenterHorizontally
|
horizontalAlignment = Alignment.CenterHorizontally
|
||||||
) {
|
) {
|
||||||
|
// Step indicator + mode badge (이미지 위에 배치)
|
||||||
|
Row(
|
||||||
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
|
horizontalArrangement = Arrangement.spacedBy(8.dp),
|
||||||
|
modifier = Modifier.padding(vertical = 4.dp)
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = when (placementPhase) {
|
||||||
|
PlacementPhase.VERTICAL -> "Step 1/3"
|
||||||
|
PlacementPhase.LATERAL -> "Step 2/3"
|
||||||
|
PlacementPhase.GREEN -> "Step 3/3"
|
||||||
|
},
|
||||||
|
fontSize = 13.sp, fontWeight = FontWeight.SemiBold, color = MlSecondaryText
|
||||||
|
)
|
||||||
|
if (isSweepMode) {
|
||||||
|
Text(
|
||||||
|
"SWEEP",
|
||||||
|
fontSize = 10.sp,
|
||||||
|
fontWeight = FontWeight.Bold,
|
||||||
|
color = Color.White,
|
||||||
|
modifier = Modifier
|
||||||
|
.background(Color(0xFF7C3AED), RoundedCornerShape(4.dp))
|
||||||
|
.padding(horizontal = 6.dp, vertical = 1.dp)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// ── 하복부 Canvas 실루엣 + 센서 + 방향 가이드 ──
|
// ── 하복부 Canvas 실루엣 + 센서 + 방향 가이드 ──
|
||||||
Box(
|
Box(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.height(500.dp)
|
.weight(1f),
|
||||||
.padding(top = 8.dp),
|
|
||||||
contentAlignment = Alignment.Center
|
contentAlignment = Alignment.Center
|
||||||
) {
|
) {
|
||||||
// 하복부 실루엣 (Canvas — CKLaw body silhouette)
|
// 하복부 실루엣 (Canvas — CKLaw body silhouette)
|
||||||
@@ -278,10 +303,10 @@ fun PlacementGuideView(appState: AppState) {
|
|||||||
// ── 방향 화살표 (방광 타원 바깥) ──
|
// ── 방향 화살표 (방광 타원 바깥) ──
|
||||||
if (!greenZone) {
|
if (!greenZone) {
|
||||||
val arrowColor = Color(0xFFFF5722)
|
val arrowColor = Color(0xFFFF5722)
|
||||||
val arrowLen = 50f
|
val arrowLen = 70f
|
||||||
val arrowHead = 20f
|
val arrowHead = 28f
|
||||||
val sw = 7f
|
val sw = 9f
|
||||||
val gap = 12f
|
val gap = 14f
|
||||||
|
|
||||||
when {
|
when {
|
||||||
directionIcon.contains("up") -> {
|
directionIcon.contains("up") -> {
|
||||||
@@ -336,40 +361,41 @@ fun PlacementGuideView(appState: AppState) {
|
|||||||
alpha = if (isGreenZone) 0.7f else 0.95f
|
alpha = if (isGreenZone) 0.7f else 0.95f
|
||||||
)
|
)
|
||||||
|
|
||||||
// Score + 감지수 + R-fit (우측 상단)
|
// Score + 감지수 + R-fit (개발자 모드에서만)
|
||||||
Column(
|
if (appState.isDevMode) {
|
||||||
modifier = Modifier
|
Column(
|
||||||
.align(Alignment.TopEnd)
|
modifier = Modifier
|
||||||
.padding(4.dp),
|
.align(Alignment.TopEnd)
|
||||||
horizontalAlignment = Alignment.End
|
.padding(4.dp),
|
||||||
) {
|
horizontalAlignment = Alignment.End
|
||||||
Text("$detectedCount/6", fontSize = 16.sp, fontWeight = FontWeight.Bold,
|
) {
|
||||||
color = if (detectedCount >= 4) Color(0xFF4CAF50) else Color(0xFFFF9800))
|
Text("$detectedCount/6", fontSize = 16.sp, fontWeight = FontWeight.Bold,
|
||||||
if (placementScore > 0) {
|
color = if (detectedCount >= 4) Color(0xFF4CAF50) else Color(0xFFFF9800))
|
||||||
Text("$placementScore%", fontSize = 13.sp, fontWeight = FontWeight.Bold,
|
if (placementScore > 0) {
|
||||||
color = when {
|
Text("$placementScore%", fontSize = 13.sp, fontWeight = FontWeight.Bold,
|
||||||
placementScore >= 80 -> Color(0xFF4CAF50)
|
color = when {
|
||||||
placementScore >= 50 -> Color(0xFFFF9800)
|
placementScore >= 80 -> Color(0xFF4CAF50)
|
||||||
else -> Color.Red
|
placementScore >= 50 -> Color(0xFFFF9800)
|
||||||
})
|
else -> Color.Red
|
||||||
}
|
})
|
||||||
// SWEEP 모드: R-fit + argmax 표시
|
|
||||||
if (isSweepMode && currentRMm != null) {
|
|
||||||
val rColor = when {
|
|
||||||
bestRMm > 0 && (bestRMm - currentRMm!!) < 1.5 -> Color(0xFF4CAF50) // PEAK
|
|
||||||
currentRMm!! > (bestRMm * 0.8) -> Color(0xFF7C3AED) // RISING
|
|
||||||
else -> Color(0xFFFF9800) // FALLING/NOISE
|
|
||||||
}
|
}
|
||||||
Text("R=${"%.0f".format(currentRMm)}mm", fontSize = 12.sp,
|
if (isSweepMode && currentRMm != null) {
|
||||||
fontWeight = FontWeight.Bold, color = rColor)
|
val rColor = when {
|
||||||
if (bestRMm > 0) {
|
bestRMm > 0 && (bestRMm - currentRMm!!) < 1.5 -> Color(0xFF4CAF50)
|
||||||
Text("best=${"%.0f".format(bestRMm)}", fontSize = 10.sp,
|
currentRMm!! > (bestRMm * 0.8) -> Color(0xFF7C3AED)
|
||||||
color = Color(0xFF4CAF50).copy(alpha = 0.7f))
|
else -> Color(0xFFFF9800)
|
||||||
}
|
}
|
||||||
sagittalArgMaxCh?.let { arg ->
|
Text("R=${"%.0f".format(currentRMm)}mm", fontSize = 12.sp,
|
||||||
Text("argmax=CH$arg", fontSize = 10.sp,
|
fontWeight = FontWeight.Bold, color = rColor)
|
||||||
fontWeight = FontWeight.Bold,
|
if (bestRMm > 0) {
|
||||||
color = if (arg == 0) Color(0xFF4CAF50) else Color(0xFFFF9800))
|
Text("best=${"%.0f".format(bestRMm)}", fontSize = 10.sp,
|
||||||
|
color = Color(0xFF4CAF50).copy(alpha = 0.7f))
|
||||||
|
}
|
||||||
|
sagittalArgMaxCh?.let { arg ->
|
||||||
|
Text("argmax=CH$arg", fontSize = 10.sp,
|
||||||
|
fontWeight = FontWeight.Bold,
|
||||||
|
color = if (arg == 0) Color(0xFF4CAF50) else Color(0xFFFF9800))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -414,33 +440,6 @@ fun PlacementGuideView(appState: AppState) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Spacer(modifier = Modifier.height(4.dp))
|
|
||||||
|
|
||||||
// Step indicator + mode badge
|
|
||||||
Row(
|
|
||||||
verticalAlignment = Alignment.CenterVertically,
|
|
||||||
horizontalArrangement = Arrangement.spacedBy(8.dp)
|
|
||||||
) {
|
|
||||||
Text(
|
|
||||||
text = when (placementPhase) {
|
|
||||||
PlacementPhase.VERTICAL -> "Step 1/3"
|
|
||||||
PlacementPhase.LATERAL -> "Step 2/3"
|
|
||||||
PlacementPhase.GREEN -> "Step 3/3"
|
|
||||||
},
|
|
||||||
fontSize = 11.sp, color = MlSecondaryText
|
|
||||||
)
|
|
||||||
if (isSweepMode) {
|
|
||||||
Text(
|
|
||||||
"SWEEP",
|
|
||||||
fontSize = 10.sp,
|
|
||||||
fontWeight = FontWeight.Bold,
|
|
||||||
color = Color.White,
|
|
||||||
modifier = Modifier
|
|
||||||
.background(Color(0xFF7C3AED), RoundedCornerShape(4.dp))
|
|
||||||
.padding(horizontal = 6.dp, vertical = 1.dp)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Per-channel echo graphs (개발자 모드에서만 표시)
|
// Per-channel echo graphs (개발자 모드에서만 표시)
|
||||||
if (appState.isDevMode && lastChannelData.isNotEmpty()) {
|
if (appState.isDevMode && lastChannelData.isNotEmpty()) {
|
||||||
|
|||||||
Reference in New Issue
Block a user