diff --git a/tools/chatgui-android/app/src/main/java/com/utun/chat/ui/components/MessageBubble.kt b/tools/chatgui-android/app/src/main/java/com/utun/chat/ui/components/MessageBubble.kt index 44b9873e..027019fc 100644 --- a/tools/chatgui-android/app/src/main/java/com/utun/chat/ui/components/MessageBubble.kt +++ b/tools/chatgui-android/app/src/main/java/com/utun/chat/ui/components/MessageBubble.kt @@ -183,35 +183,37 @@ fun InputBar( .background(pttBg, RoundedCornerShape(4.dp)) .pointerInput(hasMicPerm) { awaitPointerEventScope { - val down = awaitPointerEvent() - val ch = down.changes.firstOrNull() ?: return@awaitPointerEventScope - if (!ch.pressed) return@awaitPointerEventScope - ch.consume() - - if (!hasMicPerm) { - permLauncher.launch(Manifest.permission.RECORD_AUDIO) - return@awaitPointerEventScope - } - - onPttPressed() - var locked = false - var totalDragY = 0f - var lastY = ch.position.y - while (true) { - val event = awaitPointerEvent() - val change = event.changes.firstOrNull() ?: break - if (change.pressed) { - val dy = change.position.y - lastY - lastY = change.position.y - totalDragY += dy - if (totalDragY < -lockThresholdPx && !locked) { locked = true; onPttLock() } - change.consume() - } else { - change.consume(); break + val down = awaitPointerEvent() + val ch = down.changes.firstOrNull() ?: continue + if (!ch.pressed) continue + ch.consume() + + if (!hasMicPerm) { + permLauncher.launch(Manifest.permission.RECORD_AUDIO) + continue + } + + onPttPressed() + var locked = false + var totalDragY = 0f + var lastY = ch.position.y + + while (true) { + val event = awaitPointerEvent() + val change = event.changes.firstOrNull() ?: break + if (change.pressed) { + val dy = change.position.y - lastY + lastY = change.position.y + totalDragY += dy + if (totalDragY < -lockThresholdPx && !locked) { locked = true; onPttLock() } + change.consume() + } else { + change.consume(); break + } } + onPttReleased() } - onPttReleased() } }, contentAlignment = Alignment.Center diff --git a/tools/chatgui-android/libutun_lite/voice_recorder.c b/tools/chatgui-android/libutun_lite/voice_recorder.c index 1073c04f..e9e0805a 100644 --- a/tools/chatgui-android/libutun_lite/voice_recorder.c +++ b/tools/chatgui-android/libutun_lite/voice_recorder.c @@ -15,6 +15,7 @@ #include #include #include +#include #define MEDIA_BLOCK_MIN (10 * 1024 * 1024) #define MEDIA_BLOCK_MAX (25 * 1024 * 1024) @@ -263,13 +264,21 @@ int voice_recorder_stop(int* out_duration_ms) { /* ensure media dir exists */ char media_dir[1024]; snprintf(media_dir, sizeof(media_dir), "%s/media/%s", g_rec->db_path, g_rec->channel_id); + DEBUG_INFO(DEBUG_CATEGORY_GENERAL, "voice_recorder_stop: db_path=%s ch=%s media_dir=%s", + g_rec->db_path, g_rec->channel_id, media_dir); { char tmp[1024]; size_t off = 0; for (size_t i = 0; media_dir[i] && off < sizeof(tmp) - 1; i++) { tmp[off++] = media_dir[i]; - if (media_dir[i] == '/' && off > 1) { tmp[off] = '\0'; mkdir(tmp, 0755); } + if (media_dir[i] == '/' && off > 1) { + tmp[off - 1] = '\0'; + if (mkdir(tmp, 0755) != 0 && errno != EEXIST) + DEBUG_WARN(DEBUG_CATEGORY_GENERAL, "voice_recorder_stop: mkdir(%s) errno=%d", tmp, errno); + tmp[off - 1] = '/'; + } } - mkdir(tmp, 0755); + if (mkdir(tmp, 0755) != 0 && errno != EEXIST) + DEBUG_WARN(DEBUG_CATEGORY_GENERAL, "voice_recorder_stop: mkdir(%s) errno=%d", tmp, errno); } /* encode to Opus and write blocks */ @@ -288,7 +297,7 @@ int voice_recorder_stop(int* out_duration_ms) { snprintf(temp_path, sizeof(temp_path), "%s/%s_%s_%s.opus", media_dir, dt, basename, suffix); FILE* of = fopen(temp_path, "wb"); if (!of) { - DEBUG_ERROR(DEBUG_CATEGORY_GENERAL, "voice_recorder_stop: cannot create %s", temp_path); + DEBUG_ERROR(DEBUG_CATEGORY_GENERAL, "voice_recorder_stop: cannot create %s (errno=%d)", temp_path, errno); opus_codec_encoder_destroy(enc); voice_cleanup_locked(g_rec); pthread_mutex_unlock(&g_rec->mtx);