From 104d70d465dcff58e99ca1f989df4272e7c7566f Mon Sep 17 00:00:00 2001 From: evgeny Date: Thu, 29 Oct 2026 02:34:14 +0100 Subject: [PATCH] Handle audio device menu launch errors and document UI ownership --- .../ui/components/MediaAudioDeviceButton.kt | 17 +++++++++++++---- .../main/java/com/utun/chat/ui/theme/Theme.kt | 1 + 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/tools/chatgui-android/app/src/main/java/com/utun/chat/ui/components/MediaAudioDeviceButton.kt b/tools/chatgui-android/app/src/main/java/com/utun/chat/ui/components/MediaAudioDeviceButton.kt index 6d1a1d3e..ae1ab11c 100644 --- a/tools/chatgui-android/app/src/main/java/com/utun/chat/ui/components/MediaAudioDeviceButton.kt +++ b/tools/chatgui-android/app/src/main/java/com/utun/chat/ui/components/MediaAudioDeviceButton.kt @@ -3,6 +3,7 @@ package com.utun.chat.ui.components import android.Manifest import android.content.pm.PackageManager import android.os.Build +import android.widget.Toast import androidx.activity.compose.rememberLauncherForActivityResult import androidx.activity.result.contract.ActivityResultContracts import androidx.compose.foundation.layout.* @@ -25,7 +26,10 @@ import com.utun.chat.data.MediaAudioController import com.utun.chat.data.MediaAudioProfile import com.utun.chat.data.LogManager -/** Один выбор для рации и трансляций; при заданном expanded состоянием и закрытием управляет владелец. */ +/** Общий профиль рации/трансляций: preferred и actual раздельны, недоступные устройства выключены. + * expanded=null хранит transient menu state локально; иначе caller управляет им через onExpandedChange. + * Для Bluetooth на API 31+ выбор применяется после permission ACK; отказ сохраняет прежний профиль. + */ @Composable fun MediaAudioDeviceButton(tint: Color = LocalContentColor.current, expanded: Boolean? = null, properties: PopupProperties = PopupProperties(focusable = true), onExpandedChange: (Boolean) -> Unit = {}) { @@ -66,9 +70,14 @@ fun MediaAudioDeviceButton(tint: Color = LocalContentColor.current, expanded: Bo DropdownMenuItem(text = { Text(stringResource(label)) }, enabled = available, trailingIcon = { if (state.preferred == profile) Text("✓") }, onClick = { expand(false) - if (profile == MediaAudioProfile.BLUETOOTH && !bluetoothPermission) - permission.launch(Manifest.permission.BLUETOOTH_CONNECT) - else MediaAudioController.select(profile) + try { + if (profile == MediaAudioProfile.BLUETOOTH && !bluetoothPermission) + permission.launch(Manifest.permission.BLUETOOTH_CONNECT) + else MediaAudioController.select(profile) + } catch (e: Exception) { + LogManager.addLog("ERROR", "media_audio", "Device selection request failed profile=$profile: $e") + Toast.makeText(context, R.string.media_audio_failed, Toast.LENGTH_LONG).show() + } }) } HorizontalDivider() diff --git a/tools/chatgui-android/app/src/main/java/com/utun/chat/ui/theme/Theme.kt b/tools/chatgui-android/app/src/main/java/com/utun/chat/ui/theme/Theme.kt index 555c6405..61c4422d 100644 --- a/tools/chatgui-android/app/src/main/java/com/utun/chat/ui/theme/Theme.kt +++ b/tools/chatgui-android/app/src/main/java/com/utun/chat/ui/theme/Theme.kt @@ -44,6 +44,7 @@ private val DarkColors = darkColorScheme( scrim = Color.Black ) +/** Постоянная Material3 dark palette и фоновый Surface на весь контейнер; системная/dynamic тема не выбирается. */ @Composable fun VibeChatTheme(content: @Composable () -> Unit) { LaunchedEffect(Unit) {