diff --git a/app/src/main/java/com/example/medilightv2android/ui/components/BatteryWarningBanner.kt b/app/src/main/java/com/example/medilightv2android/ui/components/BatteryWarningBanner.kt new file mode 100644 index 0000000..50d9678 --- /dev/null +++ b/app/src/main/java/com/example/medilightv2android/ui/components/BatteryWarningBanner.kt @@ -0,0 +1,69 @@ +package com.example.medilightv2android.ui.components + +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.width +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.BatteryAlert +import androidx.compose.material3.Icon +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import com.example.medilightv2android.ble.BleManager + +/** + * 배터리 잔량 경고 배너 — PlacementGuide / PiezoMonitoring 상단. + * + * 임계: + * - 15% 이하 → 주황 경고 ("기기 배터리 부족 — 곧 충전하세요") + * - 5% 이하 → 빨강 위급 ("기기 배터리 위급 — 측정이 갑자기 중단될 수 있음") + * - 그 외 / 미연결 / 0% (아직 미수신) → 숨김 + */ +@Composable +fun BatteryWarningBanner(bleManager: BleManager, modifier: Modifier = Modifier) { + val isConnected by bleManager.isConnected + val batt by bleManager.batteryLevel + if (!isConnected) return + if (batt <= 0) return // 응답 받기 전엔 0 → false positive 방지 + if (batt > LOW_BATTERY_THRESHOLD) return + + val critical = batt <= CRITICAL_BATTERY_THRESHOLD + val bgColor = if (critical) Color(0xFFD32F2F) else Color(0xFFFF9500) + val title = if (critical) "기기 배터리 위급" else "기기 배터리 부족" + val subtitle = if (critical) + "현재 ${batt}% · 측정이 갑자기 중단될 수 있습니다. 즉시 충전하세요." + else + "현재 ${batt}% · 곧 충전이 필요합니다." + + Row( + modifier = modifier + .fillMaxWidth() + .padding(horizontal = 16.dp, vertical = 6.dp) + .clip(RoundedCornerShape(10.dp)) + .background(bgColor) + .padding(horizontal = 12.dp, vertical = 8.dp), + verticalAlignment = Alignment.CenterVertically + ) { + Icon(Icons.Default.BatteryAlert, "Battery", tint = Color.White) + Spacer(modifier = Modifier.width(10.dp)) + Column(modifier = Modifier.weight(1f)) { + Text(title, fontSize = 13.sp, fontWeight = FontWeight.Bold, color = Color.White) + Text(subtitle, fontSize = 11.sp, color = Color.White.copy(alpha = 0.9f)) + } + } +} + +const val LOW_BATTERY_THRESHOLD = 15 +const val CRITICAL_BATTERY_THRESHOLD = 5 diff --git a/app/src/main/java/com/example/medilightv2android/ui/views/monitoring/PiezoMonitoringView.kt b/app/src/main/java/com/example/medilightv2android/ui/views/monitoring/PiezoMonitoringView.kt index 484fed1..192412e 100644 --- a/app/src/main/java/com/example/medilightv2android/ui/views/monitoring/PiezoMonitoringView.kt +++ b/app/src/main/java/com/example/medilightv2android/ui/views/monitoring/PiezoMonitoringView.kt @@ -951,6 +951,7 @@ fun PiezoMonitoringView(appState: AppState) { // 펌웨어 구버전 경고 com.example.medilightv2android.ui.components.FirmwareWarningBanner(bleManager) + com.example.medilightv2android.ui.components.BatteryWarningBanner(bleManager) val scrollState = rememberScrollState() LaunchedEffect(showSettings) { @@ -1307,10 +1308,15 @@ fun PiezoMonitoringView(appState: AppState) { // ── Floating Settings Overlay ── if (showSettings) { + // 뒤 dim — 탭하면 닫힘 + 뒤 컨텐츠로 터치 새는 거 차단 (iOS modal scrim 동작). Box( modifier = Modifier .fillMaxSize() .background(Color.Black.copy(alpha = 0.3f)) + .clickable( + interactionSource = remember { androidx.compose.foundation.interaction.MutableInteractionSource() }, + indication = null + ) { showSettings = false } ) Card( shape = RoundedCornerShape(16.dp), @@ -1321,6 +1327,11 @@ fun PiezoMonitoringView(appState: AppState) { .padding(horizontal = 16.dp, vertical = 12.dp) .align(Alignment.TopCenter) .shadow(12.dp, RoundedCornerShape(16.dp)) + // 카드 자체 공백 영역 탭 — 뒤 dim 까지 전파 안 되게 흡수 (의도치 않은 closing 방지). + .clickable( + interactionSource = remember { androidx.compose.foundation.interaction.MutableInteractionSource() }, + indication = null + ) { /* no-op — 소비만 */ } ) { // 폰/태블릿 자동 분기 — fontScale은 360dp 기준 1.0~1.8 (line 118 정의) val isTablet = screenWidth >= 600 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 18f3394..d3eb3db 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 @@ -193,6 +193,8 @@ fun PlacementGuideView(appState: AppState) { // 펌웨어 구버전 경고 com.example.medilightv2android.ui.components.FirmwareWarningBanner(bleManager) + // 배터리 잔량 경고 (<15%) + com.example.medilightv2android.ui.components.BatteryWarningBanner(bleManager) Box(modifier = Modifier.fillMaxSize()) { Column( diff --git a/app/src/main/java/com/example/medilightv2android/ui/views/personalization/PiezoPersonalizationView.kt b/app/src/main/java/com/example/medilightv2android/ui/views/personalization/PiezoPersonalizationView.kt index a157680..2153b78 100644 --- a/app/src/main/java/com/example/medilightv2android/ui/views/personalization/PiezoPersonalizationView.kt +++ b/app/src/main/java/com/example/medilightv2android/ui/views/personalization/PiezoPersonalizationView.kt @@ -158,6 +158,9 @@ private fun MaxVolumeCard(appState: AppState) { @Composable private fun CatheterThresholdCard(appState: AppState) { val threshold = appState.bladderLevel.catheterizationThreshold + val maxVol = appState.piezoSettings.maxVolume + val pct = threshold * 10 // level 1~10 → 10~100% + val ml = (maxVol * pct / 100) // 자동 환산 Card( modifier = Modifier.fillMaxWidth(), shape = RoundedCornerShape(20.dp), @@ -167,19 +170,21 @@ private fun CatheterThresholdCard(appState: AppState) { modifier = Modifier.padding(horizontal = 20.dp, vertical = 18.dp), horizontalAlignment = Alignment.CenterHorizontally ) { - Text("Catheter Threshold", fontSize = 28.sp, fontWeight = FontWeight.ExtraBold) + Text("Voiding Reminder", fontSize = 28.sp, fontWeight = FontWeight.ExtraBold) + Text("방광이 이 정도 차오르면 알림", + fontSize = 12.sp, color = MlSecondaryText, + modifier = Modifier.padding(top = 2.dp)) Spacer(modifier = Modifier.height(8.dp)) Row(verticalAlignment = Alignment.Bottom) { - Text("Level", fontSize = 20.sp, fontWeight = FontWeight.SemiBold, color = MlSecondaryText, - modifier = Modifier.padding(bottom = 8.dp)) - Spacer(modifier = Modifier.width(6.dp)) - Text("$threshold", fontSize = 48.sp, + Text("$pct", fontSize = 48.sp, fontWeight = FontWeight.ExtraBold, color = MlPrimary) - Spacer(modifier = Modifier.width(6.dp)) - Text("/10", fontSize = 20.sp, fontWeight = FontWeight.SemiBold, color = MlSecondaryText, - modifier = Modifier.padding(bottom = 8.dp)) + Spacer(modifier = Modifier.width(4.dp)) + Text("%", fontSize = 24.sp, fontWeight = FontWeight.Bold, color = MlPrimary, + modifier = Modifier.padding(bottom = 6.dp)) } - Spacer(modifier = Modifier.height(8.dp)) + Text("≈ $ml mL (of $maxVol mL)", + fontSize = 14.sp, fontWeight = FontWeight.SemiBold, color = MlSecondaryText) + Spacer(modifier = Modifier.height(10.dp)) Row(horizontalArrangement = Arrangement.spacedBy(32.dp)) { StepperButton(icon = Icons.Default.Remove) { appState.updateCatheterizationThreshold((threshold - 1).coerceIn(1, 10))