Browse Source

android: fix PTT — one Box across all states, release now works; 1s min duration

- PTT Box: same composable identity in idle/recording states (just changes bg color)
  Gesture coroutine survives recomposition → onPttReleased() fires on finger lift.
  When locked, Box is replaced by cancel/send IconButtons.
- Min voice duration: 500→1000ms (C and ViewModel)
topo_upd
evgeny 2 months ago
parent
commit
00afc5c80a
  1. 20
      tools/chatgui-android/app/src/main/java/com/utun/chat/ui/components/MessageBubble.kt
  2. 2
      tools/chatgui-android/app/src/main/java/com/utun/chat/viewmodel/ChatViewModel.kt
  3. 2
      tools/chatgui-android/libutun_lite/voice_recorder.c

20
tools/chatgui-android/app/src/main/java/com/utun/chat/ui/components/MessageBubble.kt

@ -167,27 +167,20 @@ fun InputBar(
Spacer(Modifier.width(4.dp)) Spacer(Modifier.width(4.dp))
if (isRecording && isRecordingLocked) { if (isRecording && isRecordingLocked) {
/* Locked: cancel + send voice */ /* Locked: cancel + send voice — PTT box replaced by action buttons */
IconButton(onClick = onVoiceCancel) { IconButton(onClick = onVoiceCancel) {
Text("\u2715", fontSize = 20.sp, color = Color(0xFFE53935)) Text("\u2715", fontSize = 20.sp, color = Color(0xFFE53935))
} }
IconButton(onClick = onVoiceSend) { IconButton(onClick = onVoiceSend) {
Text("\u25B6", fontSize = 20.sp, color = Color(0xFF4CAF50)) Text("\u25B6", fontSize = 20.sp, color = Color(0xFF4CAF50))
} }
} else if (isRecording) {
/* Holding: only PTT active */
IconButton(
onClick = {},
enabled = true,
modifier = Modifier.background(Color(0xFFE53935), RoundedCornerShape(4.dp))
) {
Text("\uD83C\uDFA4", fontSize = 20.sp)
}
} else { } else {
/* Idle: PTT + send */ /* PTT Box — always present (same identity), just changes appearance */
val pttBg = if (isRecording) Color(0xFFE53935) else Color.Transparent
Box( Box(
modifier = Modifier modifier = Modifier
.size(48.dp) .size(48.dp)
.background(pttBg, RoundedCornerShape(4.dp))
.pointerInput(hasMicPerm) { .pointerInput(hasMicPerm) {
awaitPointerEventScope { awaitPointerEventScope {
val down = awaitPointerEvent() val down = awaitPointerEvent()
@ -223,16 +216,17 @@ fun InputBar(
}, },
contentAlignment = Alignment.Center contentAlignment = Alignment.Center
) { ) {
Text("\uD83C\uDFA4", fontSize = 20.sp) Text("\uD83C\uDFA4", fontSize = 20.sp, color = if (isRecording) Color.White else Color.Unspecified)
} }
if (!isRecording) {
Spacer(Modifier.width(4.dp)) Spacer(Modifier.width(4.dp))
Button(onClick = onSend, enabled = enabled && text.isNotBlank()) { Button(onClick = onSend, enabled = enabled && text.isNotBlank()) {
Text("Send") Text("Send")
} }
} }
} }
}
} }
private fun formatDuration(ms: Int): String { private fun formatDuration(ms: Int): String {

2
tools/chatgui-android/app/src/main/java/com/utun/chat/viewmodel/ChatViewModel.kt

@ -239,7 +239,7 @@ class ChatViewModel : ViewModel() {
fun sendVoiceMessage() { fun sendVoiceMessage() {
val duration = stopVoiceRecording() val duration = stopVoiceRecording()
if (duration < 500) { if (duration < 1000) {
LogManager.addLog("INFO", "VM", "voice too short (${duration}ms), discarded") LogManager.addLog("INFO", "VM", "voice too short (${duration}ms), discarded")
} }
} }

2
tools/chatgui-android/libutun_lite/voice_recorder.c

@ -221,7 +221,7 @@ int voice_recorder_stop(int* out_duration_ms) {
return -1; return -1;
} }
if (elapsed_ms < 500) { if (elapsed_ms < 1000) {
DEBUG_INFO(DEBUG_CATEGORY_GENERAL, "voice_recorder_stop: too short %lldms, discarding", (long long)elapsed_ms); DEBUG_INFO(DEBUG_CATEGORY_GENERAL, "voice_recorder_stop: too short %lldms, discarding", (long long)elapsed_ms);
voice_cleanup_locked(g_rec); voice_cleanup_locked(g_rec);
pthread_mutex_unlock(&g_rec->mtx); pthread_mutex_unlock(&g_rec->mtx);

Loading…
Cancel
Save