diff --git a/app/src/main/java/com/example/medilightv2android/ui/views/connection/DeviceScanView.kt b/app/src/main/java/com/example/medilightv2android/ui/views/connection/DeviceScanView.kt index 548dfe0..ec68846 100644 --- a/app/src/main/java/com/example/medilightv2android/ui/views/connection/DeviceScanView.kt +++ b/app/src/main/java/com/example/medilightv2android/ui/views/connection/DeviceScanView.kt @@ -95,21 +95,28 @@ fun DeviceScanView(appState: AppState) { } // 서비스 준비 + 페어링(bonding) 완료 확인 후 다음 화면 + var showPairingHint by remember { mutableStateOf(false) } LaunchedEffect(isServiceReady) { if (isServiceReady) { connectingDeviceAddress = null - // 페어링 완료 대기 (최대 30초, 500ms 간격 폴링) - var waited = 0 - while (!bleManager.isBonded && waited < 30000) { - kotlinx.coroutines.delay(500) - waited += 500 - } if (bleManager.isBonded) { + // 이미 페어링됨 → 바로 진행 appState.deviceConnected() } else { - // 페어링 거부/타임아웃 → 연결 해제 - bleManager.disconnect() - bleManager.connectionError.value = "Pairing required. Please pair the device." + // 페어링 필요 → 안내 표시 + 대기 + showPairingHint = true + var waited = 0 + while (!bleManager.isBonded && waited < 30000) { + kotlinx.coroutines.delay(500) + waited += 500 + } + showPairingHint = false + if (bleManager.isBonded) { + appState.deviceConnected() + } else { + bleManager.disconnect() + bleManager.connectionError.value = "Pairing required. Please pair the device." + } } } } @@ -120,6 +127,16 @@ fun DeviceScanView(appState: AppState) { DisposableEffect(Unit) { onDispose { bleManager.stopScan() } } + // 페어링 안내 다이얼로그 + if (showPairingHint) { + AlertDialog( + onDismissRequest = {}, + title = { Text("Pairing Required", fontWeight = FontWeight.Bold) }, + text = { Text("A Bluetooth pairing request will appear.\nTap 'Pair', then enter PIN if prompted.") }, + confirmButton = {} + ) + } + Column( modifier = Modifier .fillMaxSize() 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 2e5808d..5911e4f 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 @@ -92,6 +92,8 @@ fun PiezoMonitoringView(appState: AppState) { val screenWidth = LocalConfiguration.current.screenWidthDp val isCompact = screenWidth < 400 + // 태블릿(600dp+) 글자 스케일: 폰=1.0, 태블릿=~1.6 + val fontScale = (screenWidth / 360f).coerceIn(1.0f, 1.8f) val isDemoMode = appState.isDemoMode val urgency = appState.bladderLevel.urgency @@ -769,7 +771,7 @@ fun PiezoMonitoringView(appState: AppState) { cornerRadius = androidx.compose.ui.geometry.CornerRadius(2f)) } Spacer(modifier = Modifier.width(4.dp)) - Text("$batteryLvl%", fontSize = 12.sp, fontWeight = FontWeight.Bold, color = batteryColor) + Text("$batteryLvl%", fontSize = (12 * fontScale).sp, fontWeight = FontWeight.Bold, color = batteryColor) } } @@ -789,7 +791,7 @@ fun PiezoMonitoringView(appState: AppState) { ) { Icon(Icons.Default.LocalHospital, "Catheter", tint = MlPrimary, modifier = Modifier.size(16.dp)) Spacer(modifier = Modifier.width(4.dp)) - Text("$cathCount", fontSize = 13.sp, fontWeight = FontWeight.Bold, color = MlPrimary) + Text("$cathCount", fontSize = (13 * fontScale).sp, fontWeight = FontWeight.Bold, color = MlPrimary) } } if (showAddCatheter) { @@ -862,7 +864,7 @@ fun PiezoMonitoringView(appState: AppState) { val statusColor = urgency.accentColor Text( text = statusText, - fontSize = 14.sp, + fontSize = (14 * fontScale).sp, fontWeight = FontWeight.Bold, color = statusColor, modifier = Modifier @@ -873,7 +875,7 @@ fun PiezoMonitoringView(appState: AppState) { // Donut chart with Bladdy — 화면 비율 기반 val screenWidthDp = screenWidth.dp - val donutRatio = if (appState.isDevMode) 0.65f else 0.85f + val donutRatio = if (appState.isDevMode) 0.65f else 0.75f val donutSize = screenWidthDp * donutRatio val lineWidthRatio = if (appState.isDevMode) 0.07f else 0.08f val donutLineWidth = donutSize * lineWidthRatio @@ -917,7 +919,7 @@ fun PiezoMonitoringView(appState: AppState) { } } - // Info cards (Fill/Level은 maxVolume 기준) + // Info cards (Fill/Level은 maxVolume 기준)ㅋ val maxVol = appState.piezoSettings.maxVolume.toDouble() // fill/level은 maxVolumeMl(세션 최대) 기준 val fillPct = if (maxVol > 0 && maxVolumeMl > 0) (maxVolumeMl / maxVol * 100).coerceAtMost(100.0) else 0.0 @@ -934,7 +936,7 @@ fun PiezoMonitoringView(appState: AppState) { } // Control buttons: Void/Catheterize / Auto / Spot - val btnHeight = (screenWidth * 0.15f).dp.coerceIn(50.dp, 70.dp) + val btnHeight = (screenWidth * 0.18f).dp.coerceIn(56.dp, 80.dp) Row(horizontalArrangement = Arrangement.spacedBy(if (appState.isDevMode) 8.dp else 10.dp)) { // Void / Catheterize — 즉시 카테터 1개 감소 + 기록 Button( @@ -952,7 +954,7 @@ fun PiezoMonitoringView(appState: AppState) { android.widget.Toast.makeText(context, "Recorded", android.widget.Toast.LENGTH_SHORT).show() }, enabled = !isSpotInProgress && !isAutoMeasuring, - modifier = Modifier.weight(1.3f).height(btnHeight), + modifier = Modifier.weight(1f).height(btnHeight), shape = RoundedCornerShape(14.dp), colors = ButtonDefaults.buttonColors( containerColor = MlPrimary.copy(alpha = 0.1f), @@ -960,8 +962,8 @@ fun PiezoMonitoringView(appState: AppState) { ) ) { Column(horizontalAlignment = Alignment.CenterHorizontally) { - Text("Voided", fontSize = 13.sp, fontWeight = FontWeight.SemiBold, color = MlPrimary) - Text("Catheterized", fontSize = 13.sp, fontWeight = FontWeight.SemiBold, color = MlPrimary) + Text("Void/", fontSize = (13 * fontScale).sp, fontWeight = FontWeight.SemiBold, color = MlPrimary) + Text("Catheterization", fontSize = (13 * fontScale).sp, fontWeight = FontWeight.SemiBold, color = MlPrimary) } } @@ -979,9 +981,9 @@ fun PiezoMonitoringView(appState: AppState) { ) ) { if (isAutoMeasuring) { - Text("Stop Scan", fontSize = 14.sp, fontWeight = FontWeight.SemiBold, color = Color.White) + Text("Stop Scan", fontSize = (14 * fontScale).sp, fontWeight = FontWeight.SemiBold, color = Color.White) } else { - Text("Auto Scan", fontSize = 14.sp, fontWeight = FontWeight.SemiBold, color = Color.White) + Text("Auto Scan", fontSize = (14 * fontScale).sp, fontWeight = FontWeight.SemiBold, color = Color.White) } } @@ -1032,7 +1034,7 @@ fun PiezoMonitoringView(appState: AppState) { if (isSpotInProgress) { CircularProgressIndicator(color = Color.White, modifier = Modifier.size(18.dp), strokeWidth = 2.dp) } else { - Text("Single Scan", fontSize = 14.sp, fontWeight = FontWeight.SemiBold, color = Color.White) + Text("Single Scan", fontSize = (14 * fontScale).sp, fontWeight = FontWeight.SemiBold, color = Color.White) } } } @@ -1788,19 +1790,20 @@ private fun InfoCard(title: String, value: String, unit: String, color: Color, m shape = RoundedCornerShape(14.dp), colors = CardDefaults.cardColors(containerColor = MlCardBackground) ) { + val fs = (LocalConfiguration.current.screenWidthDp / 360f).coerceIn(1.0f, 1.8f) Column( modifier = Modifier.padding(vertical = 14.dp).fillMaxWidth(), horizontalAlignment = Alignment.CenterHorizontally, verticalArrangement = Arrangement.spacedBy(4.dp) ) { Row(verticalAlignment = Alignment.Bottom) { - Text(value, fontSize = 20.sp, fontWeight = FontWeight.Bold, color = Color(0xFF1C1B1F)) + Text(value, fontSize = (20 * fs).sp, fontWeight = FontWeight.Bold, color = Color(0xFF1C1B1F)) if (unit.isNotEmpty()) { Spacer(modifier = Modifier.width(3.dp)) - Text(unit, fontSize = 12.sp, color = MlSecondaryText, modifier = Modifier.padding(bottom = 2.dp)) + Text(unit, fontSize = (12 * fs).sp, color = MlSecondaryText, modifier = Modifier.padding(bottom = 2.dp)) } } - Text(title, fontSize = 11.sp, fontWeight = FontWeight.SemiBold, color = MlSecondaryText) + Text(title, fontSize = (11 * fs).sp, fontWeight = FontWeight.SemiBold, color = MlSecondaryText) } } } 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 1dce11e..9537a46 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 @@ -235,14 +235,14 @@ fun PlacementGuideView(appState: AppState) { } Text( text = displayHint, - fontSize = 15.sp, + fontSize = 28.sp, fontWeight = FontWeight.SemiBold, color = hintFg, textAlign = TextAlign.Center, modifier = Modifier .fillMaxWidth() - .background(hintBg, RoundedCornerShape(12.dp)) - .padding(12.dp) + .background(hintBg, RoundedCornerShape(16.dp)) + .padding(horizontal = 20.dp, vertical = 18.dp) ) // ── 하복부 Canvas 실루엣 + 센서 + 방향 가이드 ── @@ -401,7 +401,7 @@ fun PlacementGuideView(appState: AppState) { contentDescription = "Sensor", modifier = Modifier .size(width = sensorW, height = sensorH) - .align(androidx.compose.ui.BiasAlignment(0f, 0.44f)), + .align(androidx.compose.ui.BiasAlignment(0f, 0.55f)), contentScale = androidx.compose.ui.layout.ContentScale.Fit, alpha = 0.95f ) @@ -802,11 +802,11 @@ fun PlacementGuideView(appState: AppState) { }, modifier = Modifier .fillMaxWidth() - .height(52.dp), - shape = RoundedCornerShape(14.dp), + .height(96.dp), + shape = RoundedCornerShape(18.dp), colors = ButtonDefaults.buttonColors(containerColor = Color(0xFFFF9800)) ) { - Text("Start Alignment", fontSize = 16.sp, fontWeight = FontWeight.Bold, color = Color.White) + Text("Start Alignment", fontSize = 28.sp, fontWeight = FontWeight.Bold, color = Color.White) } } @@ -819,16 +819,16 @@ fun PlacementGuideView(appState: AppState) { enabled = isLocked, // GREEN(Optimal)일 때만 활성 modifier = Modifier .fillMaxWidth() - .height(52.dp), - shape = RoundedCornerShape(14.dp), + .height(96.dp), + shape = RoundedCornerShape(18.dp), colors = ButtonDefaults.buttonColors( containerColor = if (isLocked) Color(0xFF4CAF50) else MlPrimary, disabledContainerColor = Color.Gray.copy(alpha = 0.3f) ) ) { - Icon(Icons.Filled.CheckCircle, contentDescription = null, tint = Color.White) - Spacer(modifier = Modifier.width(8.dp)) - Text("Start Scanning", fontSize = 16.sp, fontWeight = FontWeight.Bold, color = Color.White) + Icon(Icons.Filled.CheckCircle, contentDescription = null, tint = Color.White, modifier = Modifier.size(32.dp)) + Spacer(modifier = Modifier.width(10.dp)) + Text("Start Scanning", fontSize = 28.sp, fontWeight = FontWeight.Bold, color = Color.White) } // Skip button