UI #2: AutoScan Interval → Scan Interval + SEC/MIN 단위 토글
회의 피드백: - 제목 'Auto Scan Interval' → 'Scan Interval' - 한글 설명 → '측정 간격 조절' - ms 단위 표시 → sec 기본 + min 토글 - 최소 600ms 고정 (펌웨어 응답 시간 + 안전 마진) 구현: - SEC 모드: 0.6 ~ 5.0 s (0.2s step) — 짧은 간격, 실시간 - MIN 모드: 1 ~ 10 min (30s step) — 장시간 측정, 배터리 절약 - 토글 버튼(SEC/MIN) 활성 시 진입 모드의 최소값으로 reset - 슬라이더/스테퍼/표시값 모두 모드에 맞춰 동적 변환 - 기본 모드 추론: 저장된 ms < 60000이면 SEC, 아니면 MIN PiezoMonitoring in-monitoring settings 슬라이더는 sec-only로 그대로 유지. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
+69
-15
@@ -27,14 +27,19 @@ import com.example.medilightv2android.ui.theme.*
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
/**
|
||||
* Auto Scan Interval 설정 화면.
|
||||
* Scan Interval 설정 화면.
|
||||
*
|
||||
* 흐름: PiezoPersonalization(MaxCapacity + CatheterThreshold)
|
||||
* → 본 화면 (Auto Scan Interval)
|
||||
* → 본 화면 (Scan Interval)
|
||||
* → PlacementGuide → PiezoMonitoring
|
||||
*
|
||||
* 600~5000ms 범위, 200ms 단위 snap. PiezoMonitoring의 in-monitoring settings에서
|
||||
* 같은 슬라이더로 측정 중에도 조정 가능.
|
||||
* 단위 토글 (SEC/MIN):
|
||||
* - SEC 모드: 0.6 ~ 5.0 s (0.2s step) — 짧은 간격, 실시간 모니터링
|
||||
* - MIN 모드: 1 ~ 10 min (30s step) — 장시간 측정, 배터리 절약
|
||||
* - 모드 전환 시 최소값(SEC=0.6s / MIN=1min)으로 reset
|
||||
* 최소 600ms 고정 (펌웨어 응답 시간 + 안전 마진).
|
||||
*
|
||||
* PiezoMonitoring의 in-monitoring settings는 SEC 슬라이더만 유지 (회의 결정).
|
||||
*/
|
||||
@Composable
|
||||
fun AutoScanIntervalSetupView(appState: AppState) {
|
||||
@@ -105,6 +110,11 @@ fun AutoScanIntervalSetupView(appState: AppState) {
|
||||
|
||||
@Composable
|
||||
private fun AutoScanIntervalCard(appState: AppState) {
|
||||
// 단위 모드 — 현재 저장된 ms 값으로 기본 모드 추론 (60000ms 미만 = SEC)
|
||||
var unitMode by remember {
|
||||
mutableStateOf(if (appState.piezoSettings.autoScanIntervalMs < 60000) "sec" else "min")
|
||||
}
|
||||
|
||||
Card(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
shape = RoundedCornerShape(20.dp),
|
||||
@@ -114,45 +124,74 @@ private fun AutoScanIntervalCard(appState: AppState) {
|
||||
modifier = Modifier.padding(horizontal = 20.dp, vertical = 24.dp),
|
||||
horizontalAlignment = Alignment.CenterHorizontally
|
||||
) {
|
||||
Text("Auto Scan Interval", fontSize = 34.sp, fontWeight = FontWeight.ExtraBold)
|
||||
Text("Scan Interval", fontSize = 34.sp, fontWeight = FontWeight.ExtraBold)
|
||||
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
Text("Auto scan 사이클 간격 — 짧을수록 빠른 갱신, 길수록 안정",
|
||||
Text("측정 간격 조절",
|
||||
fontSize = 13.sp, color = MlSecondaryText)
|
||||
|
||||
Spacer(modifier = Modifier.height(20.dp))
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
|
||||
// SEC / MIN 토글
|
||||
Row(horizontalArrangement = Arrangement.spacedBy(8.dp)) {
|
||||
UnitToggleButton(label = "SEC", selected = unitMode == "sec") {
|
||||
if (unitMode != "sec") {
|
||||
unitMode = "sec"
|
||||
appState.piezoSettings = appState.piezoSettings.copy(autoScanIntervalMs = 600)
|
||||
}
|
||||
}
|
||||
UnitToggleButton(label = "MIN", selected = unitMode == "min") {
|
||||
if (unitMode != "min") {
|
||||
unitMode = "min"
|
||||
appState.piezoSettings = appState.piezoSettings.copy(autoScanIntervalMs = 60000)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
|
||||
// 현재 값 표시 (모드에 따라 sec/min 변환)
|
||||
val currentMs = appState.piezoSettings.autoScanIntervalMs
|
||||
Row(verticalAlignment = Alignment.Bottom) {
|
||||
Text("${appState.piezoSettings.autoScanIntervalMs}", fontSize = 64.sp,
|
||||
val displayValue = if (unitMode == "sec") "%.1f".format(currentMs / 1000.0)
|
||||
else "%.1f".format(currentMs / 60000.0)
|
||||
Text(displayValue, fontSize = 64.sp,
|
||||
fontWeight = FontWeight.ExtraBold, color = MlPrimary)
|
||||
Spacer(modifier = Modifier.width(8.dp))
|
||||
Text("ms", fontSize = 24.sp, fontWeight = FontWeight.SemiBold, color = MlSecondaryText,
|
||||
Text(if (unitMode == "sec") "s" else "min",
|
||||
fontSize = 24.sp, fontWeight = FontWeight.SemiBold, color = MlSecondaryText,
|
||||
modifier = Modifier.padding(bottom = 12.dp))
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.height(12.dp))
|
||||
|
||||
// 스테퍼 — 모드별 step 크기
|
||||
val stepMs = if (unitMode == "sec") 200 else 30_000
|
||||
val minMs = if (unitMode == "sec") 600 else 60_000
|
||||
val maxMs = if (unitMode == "sec") 5_000 else 600_000
|
||||
Row(horizontalArrangement = Arrangement.spacedBy(32.dp)) {
|
||||
StepperButton(icon = Icons.Default.Remove) {
|
||||
val next = (appState.piezoSettings.autoScanIntervalMs - 200).coerceIn(600, 5000)
|
||||
val next = (currentMs - stepMs).coerceIn(minMs, maxMs)
|
||||
appState.piezoSettings = appState.piezoSettings.copy(autoScanIntervalMs = next)
|
||||
}
|
||||
StepperButton(icon = Icons.Default.Add) {
|
||||
val next = (appState.piezoSettings.autoScanIntervalMs + 200).coerceIn(600, 5000)
|
||||
val next = (currentMs + stepMs).coerceIn(minMs, maxMs)
|
||||
appState.piezoSettings = appState.piezoSettings.copy(autoScanIntervalMs = next)
|
||||
}
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.height(12.dp))
|
||||
|
||||
// 슬라이더 — 모드별 범위/snap
|
||||
val stepCount = ((maxMs - minMs) / stepMs) - 1 // Slider steps는 양 끝점 사이 점 개수
|
||||
Slider(
|
||||
value = appState.piezoSettings.autoScanIntervalMs.toFloat(),
|
||||
value = currentMs.toFloat(),
|
||||
onValueChange = {
|
||||
val snapped = ((it / 200f).roundToInt() * 200).coerceIn(600, 5000)
|
||||
val snapped = ((it / stepMs).roundToInt() * stepMs).coerceIn(minMs, maxMs)
|
||||
appState.piezoSettings = appState.piezoSettings.copy(autoScanIntervalMs = snapped)
|
||||
},
|
||||
valueRange = 600f..5000f,
|
||||
steps = 21,
|
||||
valueRange = minMs.toFloat()..maxMs.toFloat(),
|
||||
steps = stepCount.coerceAtLeast(0),
|
||||
modifier = Modifier.padding(horizontal = 24.dp),
|
||||
colors = SliderDefaults.colors(thumbColor = MlPrimary, activeTrackColor = MlPrimary)
|
||||
)
|
||||
@@ -160,6 +199,21 @@ private fun AutoScanIntervalCard(appState: AppState) {
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun UnitToggleButton(label: String, selected: Boolean, onClick: () -> Unit) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.clip(RoundedCornerShape(8.dp))
|
||||
.background(if (selected) MlPrimary else Color.Gray.copy(alpha = 0.15f))
|
||||
.clickable(onClick = onClick)
|
||||
.padding(horizontal = 24.dp, vertical = 8.dp),
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
Text(label, fontSize = 14.sp, fontWeight = FontWeight.Bold,
|
||||
color = if (selected) Color.White else Color.Black)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun StepperButton(icon: androidx.compose.ui.graphics.vector.ImageVector, onClick: () -> Unit) {
|
||||
Box(
|
||||
|
||||
Reference in New Issue
Block a user