





















@@ -22,6 +22,7 @@ import ai.openclaw.app.ui.design.ClawTextBadge
2222import ai.openclaw.app.ui.design.ClawTextField
2323import ai.openclaw.app.ui.design.ClawTheme
2424import androidx.compose.foundation.BorderStroke
25+import androidx.compose.foundation.background
2526import androidx.compose.foundation.layout.Arrangement
2627import androidx.compose.foundation.layout.Box
2728import androidx.compose.foundation.layout.Column
@@ -35,20 +36,24 @@ import androidx.compose.foundation.layout.padding
3536import androidx.compose.foundation.layout.size
3637import androidx.compose.foundation.lazy.LazyColumn
3738import androidx.compose.foundation.shape.CircleShape
39+import androidx.compose.foundation.shape.RoundedCornerShape
3840import androidx.compose.material.icons.Icons
3941import androidx.compose.material.icons.automirrored.filled.ArrowBack
42+import androidx.compose.material.icons.automirrored.filled.KeyboardArrowRight
4043import androidx.compose.material.icons.automirrored.filled.ScreenShare
4144import androidx.compose.material.icons.automirrored.filled.VolumeUp
4245import androidx.compose.material.icons.filled.Bolt
4346import androidx.compose.material.icons.filled.CameraAlt
4447import androidx.compose.material.icons.filled.Cloud
48+import androidx.compose.material.icons.filled.GraphicEq
4549import androidx.compose.material.icons.filled.Info
4650import androidx.compose.material.icons.filled.LocationOn
4751import androidx.compose.material.icons.filled.Lock
4852import androidx.compose.material.icons.filled.Mic
4953import androidx.compose.material.icons.filled.Notifications
5054import androidx.compose.material.icons.filled.Palette
5155import androidx.compose.material.icons.filled.Person
56+import androidx.compose.material.icons.filled.PlayArrow
5257import androidx.compose.material.icons.filled.Storage
5358import androidx.compose.material3.HorizontalDivider
5459import androidx.compose.material3.Icon
@@ -389,21 +394,139 @@ private fun V2VoiceSettingsScreen(
389394val speakerEnabled by viewModel.speakerEnabled.collectAsState()
390395val micEnabled by viewModel.micEnabled.collectAsState()
391396val talkModeEnabled by viewModel.talkModeEnabled.collectAsState()
392-val micStatusText by viewModel.micStatusText.collectAsState()
393-val talkModeStatusText by viewModel.talkModeStatusText.collectAsState()
394397395-V2SettingsDetailFrame(title = "Voice", subtitle = "Control talk, dictation, and playback.", icon = Icons.Default.Mic, onBack = onBack) {
396-V2SettingsTogglePanel(
397- rows =
398-listOf(
399-V2SettingsToggleRow("Speaker", if (speakerEnabled) "Assistant replies play aloud." else "Assistant speech is muted.", Icons.AutoMirrored.Filled.VolumeUp, speakerEnabled, viewModel::setSpeakerEnabled),
400-V2SettingsToggleRow("Dictation", micStatusText, Icons.Default.Mic, micEnabled, viewModel::setMicEnabled),
401-V2SettingsToggleRow("Realtime Talk", talkModeStatusText, Icons.Default.Bolt, talkModeEnabled, viewModel::setTalkModeEnabled),
402- ),
398+V2SettingsDetailFrame(title = "Talk Provider Setup", subtitle = "Configure voice, transport, and playback.", icon = Icons.Default.Mic, onBack = onBack) {
399+Column(verticalArrangement = Arrangement.spacedBy(10.dp)) {
400+V2VoiceSetupPanel(
401+ voiceActive = micEnabled || talkModeEnabled,
402+ )
403+Text(text = "Audio Test", style = ClawTheme.type.section, color = ClawTheme.colors.text)
404+Text(text = "Check that OpenClaw can speak clearly on this phone.", style = ClawTheme.type.body, color = ClawTheme.colors.textMuted)
405+V2SettingsWaveformPanel(active = speakerEnabled)
406+V2VoiceSetupActionRow(
407+ title = if (speakerEnabled) "Mute speaker" else "Enable speaker",
408+ subtitle = if (speakerEnabled) "Replies play aloud" else "Assistant speech muted",
409+ icon = Icons.AutoMirrored.Filled.VolumeUp,
410+ statusText = if (speakerEnabled) "On" else "Muted",
411+ ready = speakerEnabled,
412+ onClick = { viewModel.setSpeakerEnabled(!speakerEnabled) },
413+ )
414+ClawPrimaryButton(text = "Save Voice Setup", onClick = onBack, modifier = Modifier.fillMaxWidth(), icon = Icons.Default.GraphicEq)
415+ }
416+ }
417+}
418+419+@Composable
420+private fun V2VoiceSetupPanel(
421+voiceActive: Boolean,
422+) {
423+Column(verticalArrangement = Arrangement.spacedBy(9.dp)) {
424+V2VoiceSetupActionRow(
425+ title = "Realtime Provider",
426+ subtitle = "Gateway voice relay",
427+ icon = Icons.Default.GraphicEq,
428+ statusText = if (voiceActive) "Live" else "Ready",
429+ ready = true,
430+ )
431+V2VoiceSetupActionRow(
432+ title = "Voice",
433+ subtitle = "Voice input",
434+ icon = Icons.Default.Mic,
435+ statusText = "Configured",
436+ ready = true,
437+ )
438+V2VoiceSetupActionRow(
439+ title = "Transport",
440+ subtitle = "Socket relay",
441+ icon = Icons.Default.Bolt,
442+ statusText = "Configured",
443+ ready = true,
403444 )
404445 }
405446}
406447448+@Composable
449+private fun V2VoiceSetupActionRow(
450+title: String,
451+subtitle: String,
452+icon: ImageVector,
453+statusText: String,
454+ready: Boolean,
455+onClick: (() -> Unit)? = null,
456+) {
457+val rowModifier = Modifier.fillMaxWidth().heightIn(min = 68.dp)
458+Surface(
459+ onClick = onClick ?: {},
460+ enabled = onClick != null,
461+ modifier = rowModifier,
462+ shape = RoundedCornerShape(ClawTheme.radii.panel),
463+ color = ClawTheme.colors.surface,
464+ contentColor = ClawTheme.colors.text,
465+ border = BorderStroke(1.dp, ClawTheme.colors.border),
466+ ) {
467+Row(
468+ modifier = Modifier.fillMaxWidth().padding(horizontal = 12.dp, vertical = 10.dp),
469+ verticalAlignment = Alignment.CenterVertically,
470+ horizontalArrangement = Arrangement.spacedBy(11.dp),
471+ ) {
472+Surface(
473+ modifier = Modifier.size(38.dp),
474+ shape = CircleShape,
475+ color = ClawTheme.colors.canvas,
476+ contentColor = ClawTheme.colors.text,
477+ border = BorderStroke(1.dp, ClawTheme.colors.borderStrong),
478+ ) {
479+Box(contentAlignment = Alignment.Center) {
480+Icon(imageVector = icon, contentDescription = null, modifier = Modifier.size(19.dp))
481+ }
482+ }
483+Column(modifier = Modifier.weight(1f), verticalArrangement = Arrangement.spacedBy(2.dp)) {
484+Text(text = title, style = ClawTheme.type.section, color = ClawTheme.colors.text, maxLines = 1, overflow = TextOverflow.Ellipsis)
485+Text(text = subtitle, style = ClawTheme.type.body, color = ClawTheme.colors.textMuted, maxLines = 1, overflow = TextOverflow.Ellipsis)
486+ }
487+Row(verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.spacedBy(7.dp)) {
488+Box(
489+ modifier =
490+Modifier
491+ .size(7.dp)
492+ .background(if (ready) ClawTheme.colors.success else ClawTheme.colors.textSubtle, CircleShape),
493+ )
494+Text(text = statusText, style = ClawTheme.type.body, color = ClawTheme.colors.textMuted, maxLines = 1)
495+Icon(imageVector = Icons.AutoMirrored.Filled.KeyboardArrowRight, contentDescription = null, modifier = Modifier.size(20.dp), tint = ClawTheme.colors.textMuted)
496+ }
497+ }
498+ }
499+}
500+501+@Composable
502+private fun V2SettingsWaveformPanel(active: Boolean) {
503+Surface(
504+ modifier = Modifier.fillMaxWidth().height(76.dp),
505+ shape = RoundedCornerShape(ClawTheme.radii.panel),
506+ color = ClawTheme.colors.surface,
507+ contentColor = ClawTheme.colors.text,
508+ border = BorderStroke(1.dp, ClawTheme.colors.border),
509+ ) {
510+Row(
511+ modifier = Modifier.fillMaxWidth().padding(horizontal = 18.dp),
512+ verticalAlignment = Alignment.CenterVertically,
513+ horizontalArrangement = Arrangement.spacedBy(5.dp),
514+ ) {
515+Icon(imageVector = Icons.Default.PlayArrow, contentDescription = null, modifier = Modifier.size(24.dp), tint = ClawTheme.colors.text)
516+Row(modifier = Modifier.weight(1f), horizontalArrangement = Arrangement.SpaceEvenly, verticalAlignment = Alignment.CenterVertically) {
517+listOf(6, 12, 18, 11, 28, 34, 18, 10, 8, 24, 38, 31, 12, 8, 18, 30, 40, 22, 12, 8, 20, 29, 16, 8).forEachIndexed { index, height ->
518+Box(
519+ modifier =
520+Modifier
521+ .size(width = 2.dp, height = (if (active) height else 7 + index % 4 * 4).dp)
522+ .background(if (active) ClawTheme.colors.text else ClawTheme.colors.textSubtle, RoundedCornerShape(999.dp)),
523+ )
524+ }
525+ }
526+ }
527+ }
528+}
529+407530@Composable
408531private fun V2NotificationSettingsScreen(
409532viewModel: MainViewModel,
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。