610 lines
27 KiB
Kotlin
610 lines
27 KiB
Kotlin
package de.waypointaudio.ui
|
|
|
|
import android.Manifest
|
|
import android.content.pm.PackageManager
|
|
import androidx.activity.compose.rememberLauncherForActivityResult
|
|
import androidx.activity.result.contract.ActivityResultContracts
|
|
import androidx.compose.animation.animateColorAsState
|
|
import androidx.compose.animation.core.tween
|
|
import androidx.compose.foundation.background
|
|
import androidx.compose.foundation.layout.Arrangement
|
|
import androidx.compose.foundation.layout.Box
|
|
import androidx.compose.foundation.layout.Column
|
|
import androidx.compose.foundation.layout.Row
|
|
import androidx.compose.foundation.layout.Spacer
|
|
import androidx.compose.foundation.layout.fillMaxWidth
|
|
import androidx.compose.foundation.layout.height
|
|
import androidx.compose.foundation.layout.padding
|
|
import androidx.compose.foundation.layout.size
|
|
import androidx.compose.foundation.layout.width
|
|
import androidx.compose.foundation.rememberScrollState
|
|
import androidx.compose.foundation.shape.CircleShape
|
|
import androidx.compose.foundation.verticalScroll
|
|
import androidx.compose.material.icons.Icons
|
|
import androidx.compose.material.icons.filled.FiberManualRecord
|
|
import androidx.compose.material.icons.filled.LibraryMusic
|
|
import androidx.compose.material.icons.filled.Mic
|
|
import androidx.compose.material.icons.filled.MicOff
|
|
import androidx.compose.material.icons.filled.Refresh
|
|
import androidx.compose.material.icons.filled.Settings
|
|
import androidx.compose.material3.AlertDialog
|
|
import androidx.compose.material3.Button
|
|
import androidx.compose.material3.ButtonDefaults
|
|
import androidx.compose.material3.Card
|
|
import androidx.compose.material3.CardDefaults
|
|
import androidx.compose.material3.DropdownMenuItem
|
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
|
import androidx.compose.material3.ExposedDropdownMenuBox
|
|
import androidx.compose.material3.ExposedDropdownMenuDefaults
|
|
import androidx.compose.material3.HorizontalDivider
|
|
import androidx.compose.material3.Icon
|
|
import androidx.compose.material3.IconButton
|
|
import androidx.compose.material3.MaterialTheme
|
|
import androidx.compose.material3.MenuAnchorType
|
|
import androidx.compose.material3.OutlinedTextField
|
|
import androidx.compose.material3.RadioButton
|
|
import androidx.compose.material3.Switch
|
|
import androidx.compose.material3.Text
|
|
import androidx.compose.material3.TextButton
|
|
import androidx.compose.runtime.Composable
|
|
import androidx.compose.runtime.LaunchedEffect
|
|
import androidx.compose.runtime.collectAsState
|
|
import androidx.compose.runtime.getValue
|
|
import androidx.compose.runtime.mutableStateOf
|
|
import androidx.compose.runtime.remember
|
|
import androidx.compose.runtime.setValue
|
|
import androidx.compose.ui.Alignment
|
|
import androidx.compose.ui.Modifier
|
|
import androidx.compose.ui.draw.clip
|
|
import androidx.compose.ui.platform.LocalContext
|
|
import androidx.compose.ui.res.stringResource
|
|
import androidx.compose.ui.text.font.FontWeight
|
|
import androidx.compose.ui.unit.dp
|
|
import androidx.core.content.ContextCompat
|
|
import de.waypointaudio.R
|
|
import de.waypointaudio.data.AudioRoutingSettings
|
|
import de.waypointaudio.data.PttRecordingFormat
|
|
import de.waypointaudio.service.LivePttManager
|
|
|
|
/**
|
|
* Karte für Live / PTT auf dem Hauptbildschirm.
|
|
*
|
|
* v2.1.0:
|
|
* - Toggle „Live-Ausgabe" und „Aufnahme speichern" (mit Format M4A/AAC).
|
|
* - Aufnahme-Indikator (rotes Punkt) im aktiven Zustand.
|
|
* - Knopf „Aufnahmen…" öffnet die Bibliothek; Post-Aufnahme-Dialog erscheint
|
|
* automatisch nach jeder gespeicherten Aufnahme.
|
|
*
|
|
* @param pttManager Singleton-Manager
|
|
* @param onOpenRecordings Callback um die Aufnahme-Bibliothek zu öffnen
|
|
*/
|
|
@Composable
|
|
fun LivePttCard(
|
|
pttManager: LivePttManager,
|
|
onOpenRecordings: () -> Unit = {},
|
|
modifier: Modifier = Modifier
|
|
) {
|
|
val context = LocalContext.current
|
|
val pttActive by pttManager.pttActive.collectAsState()
|
|
val recordingActive by pttManager.recordingActive.collectAsState()
|
|
val routing by pttManager.routingSettings.collectAsState()
|
|
val inputDevices by pttManager.inputDevices.collectAsState()
|
|
val statusMsg by pttManager.statusMessage.collectAsState()
|
|
|
|
var hasMicPermission by remember {
|
|
mutableStateOf(
|
|
ContextCompat.checkSelfPermission(context, Manifest.permission.RECORD_AUDIO)
|
|
== PackageManager.PERMISSION_GRANTED
|
|
)
|
|
}
|
|
var showPermissionDeniedHint by remember { mutableStateOf(false) }
|
|
var showDeviceDialog by remember { mutableStateOf(false) }
|
|
|
|
val permissionLauncher = rememberLauncherForActivityResult(
|
|
ActivityResultContracts.RequestPermission()
|
|
) { granted ->
|
|
hasMicPermission = granted
|
|
if (!granted) {
|
|
showPermissionDeniedHint = true
|
|
} else {
|
|
pttManager.startPtt()
|
|
}
|
|
}
|
|
|
|
LaunchedEffect(Unit) {
|
|
pttManager.refreshDevices()
|
|
}
|
|
|
|
val pttContainerColor by animateColorAsState(
|
|
targetValue = if (pttActive)
|
|
MaterialTheme.colorScheme.error
|
|
else
|
|
MaterialTheme.colorScheme.primary,
|
|
animationSpec = tween(300),
|
|
label = "pttColor"
|
|
)
|
|
|
|
val inputDeviceName = remember(routing.selectedInputDeviceId, inputDevices) {
|
|
if (routing.selectedInputDeviceId == null) null
|
|
else inputDevices.firstOrNull { it.id == routing.selectedInputDeviceId }?.displayName
|
|
}
|
|
|
|
// v2.5.1 — Card mit sanften Cornern und größerem Innenabstand für
|
|
// einen ruhigeren Look in der GPS2Audio-Farbwelt.
|
|
Card(
|
|
modifier = modifier
|
|
.fillMaxWidth()
|
|
.padding(horizontal = 12.dp, vertical = 4.dp),
|
|
shape = androidx.compose.foundation.shape.RoundedCornerShape(14.dp),
|
|
colors = CardDefaults.cardColors(
|
|
containerColor = MaterialTheme.colorScheme.surface
|
|
),
|
|
elevation = CardDefaults.cardElevation(defaultElevation = 1.dp)
|
|
) {
|
|
Column(modifier = Modifier.padding(horizontal = 12.dp, vertical = 8.dp)) {
|
|
|
|
// ── Kopfzeile: Titel + Aufnahmen-Button + Einstellungen-Button ───
|
|
Row(
|
|
modifier = Modifier.fillMaxWidth(),
|
|
verticalAlignment = Alignment.CenterVertically
|
|
) {
|
|
Icon(
|
|
imageVector = if (pttActive) Icons.Filled.Mic else Icons.Filled.MicOff,
|
|
contentDescription = null,
|
|
tint = if (pttActive) MaterialTheme.colorScheme.error
|
|
else MaterialTheme.colorScheme.primary,
|
|
modifier = Modifier.size(20.dp)
|
|
)
|
|
Spacer(Modifier.width(8.dp))
|
|
Text(
|
|
text = stringResource(R.string.ptt_card_title),
|
|
style = MaterialTheme.typography.titleSmall,
|
|
fontWeight = FontWeight.SemiBold,
|
|
color = MaterialTheme.colorScheme.onSurface,
|
|
modifier = Modifier.weight(1f)
|
|
)
|
|
IconButton(onClick = onOpenRecordings) {
|
|
Icon(
|
|
Icons.Filled.LibraryMusic,
|
|
contentDescription = stringResource(R.string.ptt_recordings_button),
|
|
tint = MaterialTheme.colorScheme.primary
|
|
)
|
|
}
|
|
IconButton(
|
|
onClick = {
|
|
pttManager.refreshDevices()
|
|
showDeviceDialog = true
|
|
}
|
|
) {
|
|
Icon(
|
|
Icons.Filled.Settings,
|
|
contentDescription = stringResource(R.string.ptt_audio_devices_button),
|
|
tint = MaterialTheme.colorScheme.primary
|
|
)
|
|
}
|
|
}
|
|
|
|
Spacer(Modifier.height(4.dp))
|
|
|
|
// ── Status-Zeile ──────────────────────────────────────────────────
|
|
val statusText = when {
|
|
!hasMicPermission -> stringResource(R.string.ptt_status_permission_missing)
|
|
recordingActive -> stringResource(R.string.ptt_recording_status)
|
|
pttActive -> stringResource(R.string.ptt_status_active)
|
|
statusMsg != null -> statusMsg!!
|
|
else -> stringResource(R.string.ptt_status_ready)
|
|
}
|
|
|
|
Row(verticalAlignment = Alignment.CenterVertically) {
|
|
Box(
|
|
modifier = Modifier
|
|
.size(8.dp)
|
|
.clip(CircleShape)
|
|
.background(
|
|
if (pttActive) MaterialTheme.colorScheme.error
|
|
else if (!hasMicPermission) MaterialTheme.colorScheme.outline
|
|
else MaterialTheme.colorScheme.primary.copy(alpha = 0.5f)
|
|
)
|
|
)
|
|
Spacer(Modifier.width(6.dp))
|
|
Text(
|
|
text = statusText,
|
|
style = MaterialTheme.typography.bodySmall,
|
|
color = when {
|
|
pttActive -> MaterialTheme.colorScheme.error
|
|
!hasMicPermission -> MaterialTheme.colorScheme.outline
|
|
else -> MaterialTheme.colorScheme.onSurfaceVariant
|
|
}
|
|
)
|
|
if (recordingActive) {
|
|
Spacer(Modifier.width(8.dp))
|
|
Icon(
|
|
imageVector = Icons.Filled.FiberManualRecord,
|
|
contentDescription = null,
|
|
tint = MaterialTheme.colorScheme.error,
|
|
modifier = Modifier.size(12.dp)
|
|
)
|
|
}
|
|
}
|
|
|
|
if (inputDeviceName != null) {
|
|
Spacer(Modifier.height(4.dp))
|
|
Text(
|
|
text = "🎤 $inputDeviceName",
|
|
style = MaterialTheme.typography.labelSmall,
|
|
color = MaterialTheme.colorScheme.onSurfaceVariant
|
|
)
|
|
}
|
|
|
|
if (showPermissionDeniedHint) {
|
|
Spacer(Modifier.height(4.dp))
|
|
Text(
|
|
text = stringResource(R.string.ptt_permission_denied_hint),
|
|
style = MaterialTheme.typography.labelSmall,
|
|
color = MaterialTheme.colorScheme.error
|
|
)
|
|
}
|
|
|
|
// Mode summary (Live / Aufnahme / beides)
|
|
Spacer(Modifier.height(4.dp))
|
|
val modeText = when {
|
|
routing.liveOutputEnabled && routing.recordingEnabled -> "Live + Aufnahme"
|
|
routing.recordingEnabled -> "Nur Aufnahme"
|
|
else -> "Nur Live"
|
|
}
|
|
Text(
|
|
text = modeText,
|
|
style = MaterialTheme.typography.labelSmall,
|
|
color = MaterialTheme.colorScheme.outline
|
|
)
|
|
|
|
Spacer(Modifier.height(6.dp))
|
|
|
|
// ── PTT-Hauptbutton ───────────────────────────────────────────────
|
|
Button(
|
|
onClick = {
|
|
if (!hasMicPermission) {
|
|
showPermissionDeniedHint = false
|
|
permissionLauncher.launch(Manifest.permission.RECORD_AUDIO)
|
|
} else {
|
|
pttManager.togglePtt()
|
|
}
|
|
},
|
|
modifier = Modifier.fillMaxWidth(),
|
|
colors = ButtonDefaults.buttonColors(containerColor = pttContainerColor)
|
|
) {
|
|
Icon(
|
|
imageVector = if (pttActive) Icons.Filled.MicOff else Icons.Filled.Mic,
|
|
contentDescription = null,
|
|
modifier = Modifier.size(18.dp)
|
|
)
|
|
Spacer(Modifier.width(8.dp))
|
|
Text(
|
|
text = if (pttActive)
|
|
stringResource(R.string.ptt_stop)
|
|
else
|
|
stringResource(R.string.ptt_start),
|
|
style = MaterialTheme.typography.labelLarge
|
|
)
|
|
}
|
|
|
|
val noHeadset = routing.selectedOutputDeviceId == null &&
|
|
routing.selectedInputDeviceId == null
|
|
if (noHeadset && routing.liveOutputEnabled) {
|
|
Spacer(Modifier.height(6.dp))
|
|
Text(
|
|
text = stringResource(R.string.ptt_echo_warning),
|
|
style = MaterialTheme.typography.labelSmall,
|
|
color = MaterialTheme.colorScheme.outline
|
|
)
|
|
}
|
|
}
|
|
}
|
|
|
|
if (showDeviceDialog) {
|
|
PttSettingsDialog(
|
|
pttManager = pttManager,
|
|
onDismiss = { showDeviceDialog = false }
|
|
)
|
|
}
|
|
}
|
|
|
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
// PTT-Einstellungen Dialog (Live/Aufnahme + Geräte)
|
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
|
|
@OptIn(ExperimentalMaterial3Api::class)
|
|
@Composable
|
|
fun PttSettingsDialog(
|
|
pttManager: LivePttManager,
|
|
onDismiss: () -> Unit
|
|
) {
|
|
val inputDevices by pttManager.inputDevices.collectAsState()
|
|
val outputDevices by pttManager.outputDevices.collectAsState()
|
|
val routing by pttManager.routingSettings.collectAsState()
|
|
|
|
var selectedInputId by remember(routing) { mutableStateOf(routing.selectedInputDeviceId) }
|
|
var selectedOutputId by remember(routing) { mutableStateOf(routing.selectedOutputDeviceId) }
|
|
var liveEnabled by remember(routing) { mutableStateOf(routing.liveOutputEnabled) }
|
|
var recordEnabled by remember(routing) { mutableStateOf(routing.recordingEnabled) }
|
|
var format by remember(routing) { mutableStateOf(routing.recordingFormat) }
|
|
var validationHint by remember { mutableStateOf<String?>(null) }
|
|
|
|
var inputExpanded by remember { mutableStateOf(false) }
|
|
var outputExpanded by remember { mutableStateOf(false) }
|
|
|
|
AlertDialog(
|
|
onDismissRequest = onDismiss,
|
|
title = {
|
|
Row(verticalAlignment = Alignment.CenterVertically) {
|
|
Icon(Icons.Filled.Settings, contentDescription = null,
|
|
tint = MaterialTheme.colorScheme.primary)
|
|
Spacer(Modifier.width(8.dp))
|
|
Text(stringResource(R.string.audio_devices_dialog_title))
|
|
}
|
|
},
|
|
text = {
|
|
Column(
|
|
modifier = Modifier.verticalScroll(rememberScrollState()),
|
|
verticalArrangement = Arrangement.spacedBy(12.dp)
|
|
) {
|
|
// ── Live / Aufnahme-Modus ─────────────────────────────────────
|
|
Text(
|
|
text = stringResource(R.string.ptt_settings_section),
|
|
style = MaterialTheme.typography.labelMedium,
|
|
fontWeight = FontWeight.SemiBold,
|
|
color = MaterialTheme.colorScheme.primary
|
|
)
|
|
|
|
Row(
|
|
modifier = Modifier.fillMaxWidth(),
|
|
verticalAlignment = Alignment.CenterVertically
|
|
) {
|
|
Column(modifier = Modifier.weight(1f)) {
|
|
Text(
|
|
text = stringResource(R.string.ptt_live_output_toggle),
|
|
style = MaterialTheme.typography.bodyMedium,
|
|
fontWeight = FontWeight.SemiBold
|
|
)
|
|
Text(
|
|
text = stringResource(R.string.ptt_live_output_desc),
|
|
style = MaterialTheme.typography.bodySmall,
|
|
color = MaterialTheme.colorScheme.onSurfaceVariant
|
|
)
|
|
}
|
|
Switch(
|
|
checked = liveEnabled,
|
|
onCheckedChange = { v ->
|
|
liveEnabled = v
|
|
validationHint = null
|
|
if (!v && !recordEnabled) {
|
|
liveEnabled = true
|
|
validationHint = "Mindestens eine Option aktiv lassen."
|
|
}
|
|
}
|
|
)
|
|
}
|
|
|
|
Row(
|
|
modifier = Modifier.fillMaxWidth(),
|
|
verticalAlignment = Alignment.CenterVertically
|
|
) {
|
|
Column(modifier = Modifier.weight(1f)) {
|
|
Text(
|
|
text = stringResource(R.string.ptt_recording_toggle),
|
|
style = MaterialTheme.typography.bodyMedium,
|
|
fontWeight = FontWeight.SemiBold
|
|
)
|
|
Text(
|
|
text = stringResource(R.string.ptt_recording_desc),
|
|
style = MaterialTheme.typography.bodySmall,
|
|
color = MaterialTheme.colorScheme.onSurfaceVariant
|
|
)
|
|
}
|
|
Switch(
|
|
checked = recordEnabled,
|
|
onCheckedChange = { v ->
|
|
recordEnabled = v
|
|
validationHint = null
|
|
if (!v && !liveEnabled) {
|
|
recordEnabled = true
|
|
validationHint = "Mindestens eine Option aktiv lassen."
|
|
}
|
|
}
|
|
)
|
|
}
|
|
|
|
validationHint?.let { hint ->
|
|
Text(
|
|
text = hint,
|
|
style = MaterialTheme.typography.labelSmall,
|
|
color = MaterialTheme.colorScheme.error
|
|
)
|
|
}
|
|
|
|
// Format (M4A only, WAV disabled)
|
|
if (recordEnabled) {
|
|
Text(
|
|
text = stringResource(R.string.ptt_format_label),
|
|
style = MaterialTheme.typography.labelMedium,
|
|
fontWeight = FontWeight.SemiBold,
|
|
color = MaterialTheme.colorScheme.primary
|
|
)
|
|
Row(verticalAlignment = Alignment.CenterVertically) {
|
|
RadioButton(
|
|
selected = format == PttRecordingFormat.M4A_AAC,
|
|
onClick = { format = PttRecordingFormat.M4A_AAC }
|
|
)
|
|
Text(stringResource(R.string.ptt_format_m4a))
|
|
}
|
|
Row(verticalAlignment = Alignment.CenterVertically) {
|
|
RadioButton(
|
|
selected = false,
|
|
onClick = { /* WAV deaktiviert in v2.1.0 */ },
|
|
enabled = false
|
|
)
|
|
Text(
|
|
stringResource(R.string.ptt_format_wav),
|
|
color = MaterialTheme.colorScheme.outline
|
|
)
|
|
}
|
|
}
|
|
|
|
HorizontalDivider()
|
|
|
|
// ── Geräteauswahl ─────────────────────────────────────────────
|
|
TextButton(
|
|
onClick = { pttManager.refreshDevices() },
|
|
modifier = Modifier.align(Alignment.End)
|
|
) {
|
|
Icon(Icons.Filled.Refresh, contentDescription = null,
|
|
modifier = Modifier.size(16.dp))
|
|
Spacer(Modifier.width(4.dp))
|
|
Text(stringResource(R.string.audio_devices_refresh),
|
|
style = MaterialTheme.typography.labelMedium)
|
|
}
|
|
|
|
Text(
|
|
text = stringResource(R.string.audio_input_device_label),
|
|
style = MaterialTheme.typography.labelMedium,
|
|
fontWeight = FontWeight.SemiBold,
|
|
color = MaterialTheme.colorScheme.primary
|
|
)
|
|
|
|
if (inputDevices.isEmpty()) {
|
|
Text(
|
|
text = stringResource(R.string.audio_devices_no_input),
|
|
style = MaterialTheme.typography.bodySmall,
|
|
color = MaterialTheme.colorScheme.outline
|
|
)
|
|
} else {
|
|
ExposedDropdownMenuBox(
|
|
expanded = inputExpanded,
|
|
onExpandedChange = { inputExpanded = it }
|
|
) {
|
|
OutlinedTextField(
|
|
value = selectedInputId?.let { id ->
|
|
inputDevices.firstOrNull { it.id == id }?.displayName
|
|
?: stringResource(R.string.ptt_device_system_default)
|
|
} ?: stringResource(R.string.ptt_device_system_default),
|
|
onValueChange = {},
|
|
readOnly = true,
|
|
label = { Text(stringResource(R.string.ptt_input_label)) },
|
|
trailingIcon = { ExposedDropdownMenuDefaults.TrailingIcon(inputExpanded) },
|
|
modifier = Modifier
|
|
.menuAnchor(MenuAnchorType.PrimaryNotEditable)
|
|
.fillMaxWidth()
|
|
)
|
|
ExposedDropdownMenu(
|
|
expanded = inputExpanded,
|
|
onDismissRequest = { inputExpanded = false }
|
|
) {
|
|
DropdownMenuItem(
|
|
text = { Text(stringResource(R.string.ptt_device_system_default)) },
|
|
onClick = {
|
|
selectedInputId = null
|
|
inputExpanded = false
|
|
}
|
|
)
|
|
HorizontalDivider()
|
|
inputDevices.forEach { device ->
|
|
DropdownMenuItem(
|
|
text = { Text(device.displayName) },
|
|
onClick = {
|
|
selectedInputId = device.id
|
|
inputExpanded = false
|
|
}
|
|
)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
Text(
|
|
text = stringResource(R.string.audio_output_device_label),
|
|
style = MaterialTheme.typography.labelMedium,
|
|
fontWeight = FontWeight.SemiBold,
|
|
color = MaterialTheme.colorScheme.primary
|
|
)
|
|
|
|
if (outputDevices.isEmpty()) {
|
|
Text(
|
|
text = stringResource(R.string.audio_devices_no_output),
|
|
style = MaterialTheme.typography.bodySmall,
|
|
color = MaterialTheme.colorScheme.outline
|
|
)
|
|
} else {
|
|
ExposedDropdownMenuBox(
|
|
expanded = outputExpanded,
|
|
onExpandedChange = { outputExpanded = it }
|
|
) {
|
|
OutlinedTextField(
|
|
value = selectedOutputId?.let { id ->
|
|
outputDevices.firstOrNull { it.id == id }?.displayName
|
|
?: stringResource(R.string.ptt_device_system_default)
|
|
} ?: stringResource(R.string.ptt_device_system_default),
|
|
onValueChange = {},
|
|
readOnly = true,
|
|
label = { Text(stringResource(R.string.ptt_output_label)) },
|
|
trailingIcon = { ExposedDropdownMenuDefaults.TrailingIcon(outputExpanded) },
|
|
modifier = Modifier
|
|
.menuAnchor(MenuAnchorType.PrimaryNotEditable)
|
|
.fillMaxWidth()
|
|
)
|
|
ExposedDropdownMenu(
|
|
expanded = outputExpanded,
|
|
onDismissRequest = { outputExpanded = false }
|
|
) {
|
|
DropdownMenuItem(
|
|
text = { Text(stringResource(R.string.ptt_device_system_default)) },
|
|
onClick = {
|
|
selectedOutputId = null
|
|
outputExpanded = false
|
|
}
|
|
)
|
|
HorizontalDivider()
|
|
outputDevices.forEach { device ->
|
|
DropdownMenuItem(
|
|
text = { Text(device.displayName) },
|
|
onClick = {
|
|
selectedOutputId = device.id
|
|
outputExpanded = false
|
|
}
|
|
)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
HorizontalDivider()
|
|
Text(
|
|
text = stringResource(R.string.audio_devices_hint),
|
|
style = MaterialTheme.typography.labelSmall,
|
|
color = MaterialTheme.colorScheme.outline
|
|
)
|
|
}
|
|
},
|
|
confirmButton = {
|
|
Button(onClick = {
|
|
pttManager.saveRoutingSettings(
|
|
AudioRoutingSettings(
|
|
selectedInputDeviceId = selectedInputId,
|
|
selectedOutputDeviceId = selectedOutputId,
|
|
liveOutputEnabled = liveEnabled,
|
|
recordingEnabled = recordEnabled,
|
|
recordingFormat = format
|
|
)
|
|
)
|
|
onDismiss()
|
|
}) {
|
|
Text(stringResource(R.string.audio_devices_save))
|
|
}
|
|
},
|
|
dismissButton = {
|
|
TextButton(onClick = onDismiss) {
|
|
Text(stringResource(R.string.cancel))
|
|
}
|
|
}
|
|
)
|
|
}
|