fix: 도넛차트 recomposition 버그 + Spot 롱프레스 자동측정 + BV 4채널(CH0~CH3)
- bladderLevel.copy() 할당으로 Compose recomposition 트리거 (도넛차트 실시간 갱신) - Spot 버튼: Button→Box+detectTapGestures (탭=단일, 롱프레스=30초 자동측정) - startAutoMeasure() + LaunchedEffect 30초 반복 루프 - BV 추정 CH0~CH4(5ch)→CH0~CH3(4ch) 세로 채널만 사용 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
+56
-37
@@ -8,6 +8,8 @@ import androidx.compose.material3.OutlinedTextField
|
|||||||
import androidx.compose.foundation.Canvas
|
import androidx.compose.foundation.Canvas
|
||||||
import androidx.compose.foundation.background
|
import androidx.compose.foundation.background
|
||||||
import androidx.compose.foundation.clickable
|
import androidx.compose.foundation.clickable
|
||||||
|
import androidx.compose.foundation.gestures.detectTapGestures
|
||||||
|
import androidx.compose.ui.input.pointer.pointerInput
|
||||||
import androidx.compose.foundation.layout.*
|
import androidx.compose.foundation.layout.*
|
||||||
import androidx.compose.foundation.rememberScrollState
|
import androidx.compose.foundation.rememberScrollState
|
||||||
import androidx.compose.foundation.shape.CircleShape
|
import androidx.compose.foundation.shape.CircleShape
|
||||||
@@ -109,10 +111,10 @@ fun PiezoMonitoringView(appState: AppState) {
|
|||||||
if (validChannels.isNotEmpty()) {
|
if (validChannels.isNotEmpty()) {
|
||||||
val analysisResult = analyzer.analyzeMultiChannel(validChannels)
|
val analysisResult = analyzer.analyzeMultiChannel(validChannels)
|
||||||
|
|
||||||
// Build walls list for BV estimation (5 channels)
|
// Build walls list for BV estimation (CH0~CH3, 세로 4채널만)
|
||||||
val walls: MutableList<Pair<Int, Int>?> = MutableList(5) { null }
|
val walls: MutableList<Pair<Int, Int>?> = MutableList(4) { null }
|
||||||
for (ch in analysisResult.channels) {
|
for (ch in analysisResult.channels) {
|
||||||
if (ch.channel < 5 && ch.result != null) {
|
if (ch.channel < 4 && ch.result != null) {
|
||||||
walls[ch.channel] = Pair(ch.result.ant, ch.result.post)
|
walls[ch.channel] = Pair(ch.result.ant, ch.result.post)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -142,7 +144,7 @@ fun PiezoMonitoringView(appState: AppState) {
|
|||||||
val maxVol = appState.piezoSettings.maxVolume.toDouble()
|
val maxVol = appState.piezoSettings.maxVolume.toDouble()
|
||||||
val pct = if (maxVol > 0) (lastVolumeMl / maxVol).coerceAtMost(1.0) else 0.0
|
val pct = if (maxVol > 0) (lastVolumeMl / maxVol).coerceAtMost(1.0) else 0.0
|
||||||
val level = (pct * BladderLevel.MAX_LEVEL).toInt().coerceIn(1, BladderLevel.MAX_LEVEL)
|
val level = (pct * BladderLevel.MAX_LEVEL).toInt().coerceIn(1, BladderLevel.MAX_LEVEL)
|
||||||
appState.bladderLevel.currentLevel = level
|
appState.bladderLevel = appState.bladderLevel.copy(currentLevel = level)
|
||||||
}
|
}
|
||||||
isMeasuring = false
|
isMeasuring = false
|
||||||
measureRetryCount = 0
|
measureRetryCount = 0
|
||||||
@@ -183,10 +185,25 @@ fun PiezoMonitoringView(appState: AppState) {
|
|||||||
isMeasuring = false
|
isMeasuring = false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun startAutoMeasure() {
|
||||||
|
isAutoMeasuring = true
|
||||||
|
measure()
|
||||||
|
}
|
||||||
|
|
||||||
fun stopAutoMeasure() {
|
fun stopAutoMeasure() {
|
||||||
isAutoMeasuring = false
|
isAutoMeasuring = false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Auto-measure: repeat every 30s while active
|
||||||
|
LaunchedEffect(isAutoMeasuring) {
|
||||||
|
if (isAutoMeasuring) {
|
||||||
|
while (isAutoMeasuring) {
|
||||||
|
kotlinx.coroutines.delay(30_000)
|
||||||
|
if (isAutoMeasuring && !isMeasuring) measure()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Setup disconnect handling
|
// Setup disconnect handling
|
||||||
LaunchedEffect(Unit) {
|
LaunchedEffect(Unit) {
|
||||||
bleManager.onUnexpectedDisconnect = {
|
bleManager.onUnexpectedDisconnect = {
|
||||||
@@ -634,44 +651,46 @@ fun PiezoMonitoringView(appState: AppState) {
|
|||||||
Text("Catheterize", fontSize = 16.sp, fontWeight = FontWeight.SemiBold, color = MlPrimary)
|
Text("Catheterize", fontSize = 16.sp, fontWeight = FontWeight.SemiBold, color = MlPrimary)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Spot button
|
// Spot button (tap = single, long press = auto 30s)
|
||||||
Button(
|
Box(
|
||||||
onClick = {
|
modifier = Modifier
|
||||||
if (isAutoMeasuring) stopAutoMeasure()
|
.weight(1f)
|
||||||
else if (!isMeasuring) measure()
|
.height(56.dp)
|
||||||
},
|
.clip(RoundedCornerShape(16.dp))
|
||||||
modifier = Modifier.weight(1f).height(56.dp),
|
.background(
|
||||||
shape = RoundedCornerShape(16.dp),
|
|
||||||
colors = ButtonDefaults.buttonColors(containerColor = Color.Transparent),
|
|
||||||
contentPadding = PaddingValues(),
|
|
||||||
enabled = !isMeasuring || isAutoMeasuring
|
|
||||||
) {
|
|
||||||
Box(
|
|
||||||
modifier = Modifier.fillMaxSize().background(
|
|
||||||
Brush.horizontalGradient(
|
Brush.horizontalGradient(
|
||||||
if (isAutoMeasuring) listOf(Color.Red, Color(0xFFFF9500))
|
if (isAutoMeasuring) listOf(Color.Red, Color(0xFFFF9500))
|
||||||
else urgency.gradientColors
|
else urgency.gradientColors
|
||||||
),
|
|
||||||
RoundedCornerShape(16.dp)
|
|
||||||
),
|
|
||||||
contentAlignment = Alignment.Center
|
|
||||||
) {
|
|
||||||
Row(verticalAlignment = Alignment.CenterVertically) {
|
|
||||||
if (isMeasuring && !isAutoMeasuring) {
|
|
||||||
CircularProgressIndicator(color = Color.White, modifier = Modifier.size(20.dp), strokeWidth = 2.dp)
|
|
||||||
}
|
|
||||||
Spacer(modifier = Modifier.width(8.dp))
|
|
||||||
Text(
|
|
||||||
when {
|
|
||||||
isAutoMeasuring -> "Stop"
|
|
||||||
isMeasuring -> "Measuring..."
|
|
||||||
else -> "Spot"
|
|
||||||
},
|
|
||||||
fontSize = 16.sp,
|
|
||||||
fontWeight = FontWeight.SemiBold,
|
|
||||||
color = Color.White
|
|
||||||
)
|
)
|
||||||
|
)
|
||||||
|
.pointerInput(isAutoMeasuring, isMeasuring) {
|
||||||
|
detectTapGestures(
|
||||||
|
onTap = {
|
||||||
|
if (isAutoMeasuring) stopAutoMeasure()
|
||||||
|
else if (!isMeasuring) measure()
|
||||||
|
},
|
||||||
|
onLongPress = {
|
||||||
|
if (!isAutoMeasuring && !isMeasuring) startAutoMeasure()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
},
|
||||||
|
contentAlignment = Alignment.Center
|
||||||
|
) {
|
||||||
|
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||||
|
if (isMeasuring && !isAutoMeasuring) {
|
||||||
|
CircularProgressIndicator(color = Color.White, modifier = Modifier.size(20.dp), strokeWidth = 2.dp)
|
||||||
}
|
}
|
||||||
|
Spacer(modifier = Modifier.width(8.dp))
|
||||||
|
Text(
|
||||||
|
when {
|
||||||
|
isAutoMeasuring -> "Stop"
|
||||||
|
isMeasuring -> "Measuring..."
|
||||||
|
else -> "Spot"
|
||||||
|
},
|
||||||
|
fontSize = 16.sp,
|
||||||
|
fontWeight = FontWeight.SemiBold,
|
||||||
|
color = Color.White
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user