Handle audio device menu launch errors and document UI ownership

This commit is contained in:
evgeny committed 2026-10-29 13:34:25 +01:00
1 parent 257df6c520
commit 104d70d465
2 files changed
+14 -4

No files matched your search

@@ -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()
@@ -44,6 +44,7 @@ private val DarkColors = darkColorScheme(
scrim = Color.Black
)
/** Постоянная Material3 dark palette и фоновый Surface на весь контейнер; системная/dynamic тема не выбирается. */
@Composable
fun VibeChatTheme(content: @Composable () -> Unit) {
LaunchedEffect(Unit) {