From b84561dba56b0fc0883b1e35afbf806810efeb57 Mon Sep 17 00:00:00 2001 From: evgeny Date: Wed, 28 Oct 2026 09:02:37 +0100 Subject: [PATCH] Use shared capture icons and menus for camera and resolution selection --- .../chat/ui/screens/CameraRecordScreen.kt | 55 ++++++++++++++++--- 1 file changed, 46 insertions(+), 9 deletions(-) diff --git a/tools/chatgui-android/app/src/main/java/com/utun/chat/ui/screens/CameraRecordScreen.kt b/tools/chatgui-android/app/src/main/java/com/utun/chat/ui/screens/CameraRecordScreen.kt index 61f0f36a..663f1a4c 100644 --- a/tools/chatgui-android/app/src/main/java/com/utun/chat/ui/screens/CameraRecordScreen.kt +++ b/tools/chatgui-android/app/src/main/java/com/utun/chat/ui/screens/CameraRecordScreen.kt @@ -41,9 +41,11 @@ import com.utun.chat.ui.components.ZoomableImage import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext import java.io.File +import kotlin.math.roundToInt enum class CaptureMode { PHOTO, VIDEO } private enum class CapturePhase { IDLE, CAPTURING, RECORDING, PAUSED, FINALIZING, CAPTURED, SENDING } +private enum class CameraOption { CAMERA, RESOLUTION } private enum class CaptureQuality(val label: String, val quality: Quality, val bitrate: Int) { SD_LOW("480p 512k", Quality.SD, 512_000), SD("480p 1M", Quality.SD, 1_000_000), HD("720p 2M", Quality.HD, 2_000_000), FHD("1080p 4.5M", Quality.FHD, 4_500_000) @@ -254,6 +256,9 @@ fun CameraRecordScreen(initialMode: CaptureMode, onSend: (String, CaptureMode) - } val idle = phase == CapturePhase.IDLE + var menu by remember(mode, lens?.cameraId) { mutableStateOf(null) } + LaunchedEffect(idle) { if (!idle) menu = null } + LaunchedEffect(menu) { LogManager.addLog("DEBUG", "Capture", "Toolbar menu=$menu mode=$mode camera=${lens?.cameraId}") } Scaffold(topBar = { TopAppBar(title = { Row(Modifier.horizontalScroll(rememberScrollState()), horizontalArrangement = Arrangement.spacedBy(8.dp)) { @@ -282,15 +287,47 @@ fun CameraRecordScreen(initialMode: CaptureMode, onSend: (String, CaptureMode) - horizontalAlignment = Alignment.CenterHorizontally) { if (idle) { Row(Modifier.fillMaxWidth().horizontalScroll(rememberScrollState()).padding(horizontal = 12.dp), - horizontalArrangement = Arrangement.spacedBy(8.dp)) { - lenses.forEach { item -> FilterChip(selected = lens == item, onClick = { lens = item }, label = { Text(item.label) }) } - } - Row(Modifier.fillMaxWidth().horizontalScroll(rememberScrollState()).padding(horizontal = 12.dp), - horizontalArrangement = Arrangement.spacedBy(8.dp)) { - if (mode == CaptureMode.PHOTO) resolutions.forEach { item -> - FilterChip(selected = resolution == item, onClick = { resolution = item }, label = { Text(item.label) }) - } else CaptureQuality.values().forEach { item -> - FilterChip(selected = quality == item, onClick = { quality = item }, label = { Text(item.label) }) + horizontalArrangement = Arrangement.spacedBy(4.dp), verticalAlignment = Alignment.CenterVertically) { + Box { + val name = lens?.let { "${it.label} ${it.mm}".trim() } ?: "—" + CaptureControlButton(stringResource(R.string.stream_camera, name), enabled = lenses.isNotEmpty(), + onClick = { menu = CameraOption.CAMERA }) { + val focal = lens?.eqFocal?.takeIf { it > 0 }?.let { + stringResource(R.string.stream_camera_focal_short, it.roundToInt()) + } ?: "—" + CaptureValueIcon(CaptureIconShape.CAMERA, focal, front = lens?.type == LensType.SELFIE) + } + DropdownMenu(expanded = menu == CameraOption.CAMERA, onDismissRequest = { menu = null }) { + lenses.forEach { item -> + CaptureOptionItem("${item.label} ${item.mm}".trim(), lens == item, onClick = { + menu = null; lens = item + LogManager.addLog("INFO", "Capture", "Camera selected id=${item.cameraId} mode=$mode") + }) + } + } + } + Box { + val label = if (mode == CaptureMode.PHOTO) resolution?.label ?: "—" else quality.label + CaptureControlButton(stringResource(R.string.stream_resolution, label), + enabled = mode == CaptureMode.VIDEO || resolutions.isNotEmpty(), onClick = { menu = CameraOption.RESOLUTION }) { + val value = if (mode == CaptureMode.PHOTO) resolution?.let { "${minOf(it.width, it.height)}p" } ?: "—" + else quality.label.substringBefore(' ') + CaptureValueIcon(CaptureIconShape.SCREEN, value, + detail = if (mode == CaptureMode.VIDEO) quality.label.substringAfter(' ') else null) + } + DropdownMenu(expanded = menu == CameraOption.RESOLUTION, onDismissRequest = { menu = null }) { + if (mode == CaptureMode.PHOTO) resolutions.forEach { item -> + CaptureOptionItem(item.label, resolution == item, onClick = { + menu = null; resolution = item + LogManager.addLog("INFO", "Capture", "Photo resolution selected ${item.label}") + }) + } else CaptureQuality.values().forEach { item -> + CaptureOptionItem(item.label, quality == item, onClick = { + menu = null; quality = item + LogManager.addLog("INFO", "Capture", "Video quality selected ${item.label}") + }) + } + } } } }