From 51c6bbb96fcadd96373bdd8bb8a3d0a08348bed6 Mon Sep 17 00:00:00 2001 From: evgeny Date: Mon, 19 Oct 2026 21:30:02 +0200 Subject: [PATCH] Queue Android call camera startup until video probing completes --- .../com/utun/chat/data/CallVideoEngine.kt | 25 ++++++++++++++----- .../com/utun/chat/ui/screens/CallScreen.kt | 11 ++++---- 2 files changed, 24 insertions(+), 12 deletions(-) diff --git a/tools/chatgui-android/app/src/main/java/com/utun/chat/data/CallVideoEngine.kt b/tools/chatgui-android/app/src/main/java/com/utun/chat/data/CallVideoEngine.kt index 8de8e58f..c53477c1 100644 --- a/tools/chatgui-android/app/src/main/java/com/utun/chat/data/CallVideoEngine.kt +++ b/tools/chatgui-android/app/src/main/java/com/utun/chat/data/CallVideoEngine.kt @@ -172,12 +172,25 @@ class CallVideoEngine(private val context: Context, private val id: Long) { fun surface(surface: Surface?, release: () -> Unit = {}) = player.surface(surface, release) fun detachSurface(surface: Surface) = player.detach(surface) fun presented(surface: Surface, pts: Long) = player.presented(surface, pts) - fun select(settings: CallVideoSettings) { handler.post { - if (!stopped) { - wanted = settings; excluded = emptySet(); observeOrientation(settings.enabled) - mutable.value = mutable.value.copy(settings = settings, transmitting = settings.enabled && mutable.value.transmitting, notice = null) - } - } } + fun select(settings: CallVideoSettings) { + // Команда ждёт завершения probe в том же worker; пустая камера означает найденный при подготовке режим. + if (!handler.post { + if (stopped) { LogManager.addLog("WARN", "call", "video selection after stop id=$id settings=$settings"); return@post } + val selected = if (settings.camera.isEmpty()) wanted.copy(enabled = settings.enabled) else settings + val current = mutable.value + if (selected.enabled && (!current.ready || current.sizes(selected.camera, selected.codec).none { + it.width == selected.width && it.height == selected.height + })) { + LogManager.addLog("WARN", "call", "video selection unavailable id=$id ready=${current.ready} " + + "requested=$settings resolved=$selected status=${current.status}") + mutable.value = current.copy(notice = R.string.call_video_unavailable) + return@post + } + wanted = selected; excluded = emptySet(); observeOrientation(selected.enabled) + mutable.value = current.copy(settings = selected, transmitting = selected.enabled && current.transmitting, notice = null) + LogManager.addLog("INFO", "call", "video selection accepted id=$id requested=$settings resolved=$selected ready=${current.ready}") + }) LogManager.addLog("WARN", "call", "video selection rejected by stopped worker id=$id settings=$settings") + } fun range(value: CallVideoBitrateRange) { handler.post { if (CallVideoNative.range(id, value.minimum * 1000, value.maximum * 1000)) mutable.value = mutable.value.copy(range = value) else report("Bitrate range rejected") diff --git a/tools/chatgui-android/app/src/main/java/com/utun/chat/ui/screens/CallScreen.kt b/tools/chatgui-android/app/src/main/java/com/utun/chat/ui/screens/CallScreen.kt index bb43437d..af5f62d0 100644 --- a/tools/chatgui-android/app/src/main/java/com/utun/chat/ui/screens/CallScreen.kt +++ b/tools/chatgui-android/app/src/main/java/com/utun/chat/ui/screens/CallScreen.kt @@ -137,12 +137,11 @@ private fun CallVideoButton(video: CallVideoEngine, showLabel: Boolean, onOption onClick = { onOptions() val enabled = !state.settings.enabled - val available = state.ready && state.sizes().isNotEmpty() - LogManager.addLog(if (enabled && !available) "WARN" else "INFO", "call", - "video button enabled=$enabled ready=${state.ready} available=$available; options shown") - if (enabled && !available) { - Toast.makeText(context, state.notice ?: if (state.ready) R.string.call_video_unavailable - else R.string.call_video_initializing, Toast.LENGTH_LONG).show() + val unavailable = state.ready && state.sizes().isEmpty() + LogManager.addLog(if (enabled && unavailable) "WARN" else "INFO", "call", + "video button enabled=$enabled ready=${state.ready} unavailable=$unavailable; options shown, start queued if preparing") + if (enabled && unavailable) { + Toast.makeText(context, state.notice ?: R.string.call_video_unavailable, Toast.LENGTH_LONG).show() } else if (!enabled || ContextCompat.checkSelfPermission(context, Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED) CallController.setVideo(state.settings.copy(enabled = enabled)) else requestPermission()