@@ -42,6 +42,7 @@ import androidx.compose.material.icons.automirrored.filled.Send
4242import androidx.compose.material.icons.automirrored.filled.VolumeOff
4343import androidx.compose.material.icons.automirrored.filled.VolumeUp
4444import androidx.compose.material.icons.filled.Close
45+ import androidx.compose.material.icons.filled.Cloud
4546import androidx.compose.material.icons.filled.GraphicEq
4647import androidx.compose.material.icons.filled.Info
4748import androidx.compose.material.icons.filled.Mic
@@ -78,6 +79,7 @@ import androidx.core.content.ContextCompat
7879fun VoiceScreen (
7980 viewModel : MainViewModel ,
8081 onOpenCommand : () -> Unit ,
82+ onOpenGatewaySettings : () -> Unit ,
8183 onOpenVoiceSettings : () -> Unit ,
8284) {
8385 val context = LocalContext .current
@@ -113,6 +115,7 @@ fun VoiceScreen(
113115
114116 val activeConversation = if (voiceCaptureMode == VoiceCaptureMode .TalkMode ) talkModeConversation else micConversation
115117 val voiceActive = micEnabled || micIsSending || talkModeEnabled
118+ val gatewayReady = gatewayStatus.isVoiceGatewayReady()
116119 val activeStatus =
117120 voiceStatusLabel(
118121 gatewayStatus = gatewayStatus,
@@ -161,7 +164,7 @@ fun VoiceScreen(
161164 verticalArrangement = Arrangement .spacedBy(8 .dp),
162165 ) {
163166 VoiceHeader (
164- statusText = if (voiceActive) activeStatus else " Your voice command center." ,
167+ statusText = if (voiceActive || ! gatewayReady ) activeStatus else " Your voice command center." ,
165168 speakerEnabled = speakerEnabled,
166169 onToggleSpeaker = { viewModel.setSpeakerEnabled(! speakerEnabled) },
167170 onOpenCommand = onOpenCommand,
@@ -175,6 +178,7 @@ fun VoiceScreen(
175178 talkModeListening = talkModeListening,
176179 talkModeSpeaking = talkModeSpeaking,
177180 micLiveTranscript = micLiveTranscript,
181+ gatewayReady = gatewayReady,
178182 onStartTalk = {
179183 runVoiceAction(
180184 action = VoiceAction .Talk ,
@@ -198,6 +202,7 @@ fun VoiceScreen(
198202 run = { viewModel.setMicEnabled(! micEnabled) },
199203 )
200204 },
205+ onConnectGateway = onOpenGatewaySettings,
201206 )
202207
203208 if (! hasMicPermission) {
@@ -582,8 +587,10 @@ private fun VoiceHero(
582587 talkModeListening : Boolean ,
583588 talkModeSpeaking : Boolean ,
584589 micLiveTranscript : String? ,
590+ gatewayReady : Boolean ,
585591 onStartTalk : () -> Unit ,
586592 onStartDictation : () -> Unit ,
593+ onConnectGateway : () -> Unit ,
587594) {
588595 Column (horizontalAlignment = Alignment .CenterHorizontally , verticalArrangement = Arrangement .spacedBy(10 .dp)) {
589596 VoiceOrb (
@@ -607,6 +614,7 @@ private fun VoiceHero(
607614 talkModeListening -> " Listening"
608615 talkModeEnabled -> " Talk is live"
609616 micEnabled -> " Dictation is listening"
617+ ! gatewayReady -> " Gateway offline"
610618 else -> " Ready to talk"
611619 },
612620 style = ClawTheme .type.body,
@@ -634,24 +642,46 @@ private fun VoiceHero(
634642 ClawPanel (contentPadding = PaddingValues (horizontal = 0 .dp, vertical = 0 .dp)) {
635643 VoiceModeRow (
636644 title = if (talkModeEnabled) " End Talk" else " Realtime Talk" ,
637- subtitle = if (talkModeEnabled) " Conversation is live" else " Natural conversation in real time" ,
645+ subtitle =
646+ when {
647+ talkModeEnabled -> " Conversation is live"
648+ gatewayReady -> " Natural conversation in real time"
649+ else -> " Connect gateway to start"
650+ },
638651 icon = if (talkModeEnabled) Icons .Default .PhoneDisabled else Icons .Default .RecordVoiceOver ,
639652 onClick = onStartTalk,
653+ enabled = gatewayReady || talkModeEnabled,
640654 )
641655 VoiceModeRow (
642656 title = if (micEnabled) " Stop Dictation" else " Dictation" ,
643- subtitle = if (micEnabled) " Listening for one turn" else " Convert speech to text" ,
657+ subtitle =
658+ when {
659+ micEnabled -> " Listening for one turn"
660+ gatewayReady -> " Convert speech to text"
661+ else -> " Connect gateway to start"
662+ },
644663 icon = if (micEnabled) Icons .Default .MicOff else Icons .Default .TextFields ,
645664 onClick = onStartDictation,
665+ enabled = gatewayReady || micEnabled,
646666 )
647667 }
648668
649669 VoiceProviderCard (gatewayStatus = gatewayStatus)
650670
651671 VoicePrimaryAction (
652- text = if (talkModeEnabled) " End Talk" else " Start Talk" ,
653- icon = if (talkModeEnabled) Icons .Default .PhoneDisabled else Icons .Default .Phone ,
654- onClick = onStartTalk,
672+ text =
673+ when {
674+ talkModeEnabled -> " End Talk"
675+ gatewayReady -> " Start Talk"
676+ else -> " Connect Gateway"
677+ },
678+ icon =
679+ when {
680+ talkModeEnabled -> Icons .Default .PhoneDisabled
681+ gatewayReady -> Icons .Default .Phone
682+ else -> Icons .Default .Cloud
683+ },
684+ onClick = if (gatewayReady || talkModeEnabled) onStartTalk else onConnectGateway,
655685 )
656686 }
657687}
@@ -662,8 +692,9 @@ private fun VoiceModeRow(
662692 subtitle : String ,
663693 icon : androidx.compose.ui.graphics.vector.ImageVector ,
664694 onClick : () -> Unit ,
695+ enabled : Boolean = true,
665696) {
666- Surface (onClick = onClick, color = Color .Transparent , contentColor = ClawTheme .colors.text) {
697+ Surface (onClick = onClick, enabled = enabled, color = Color .Transparent , contentColor = ClawTheme .colors.text) {
667698 Row (
668699 modifier = Modifier .fillMaxWidth().heightIn(min = 60 .dp).padding(horizontal = 10 .dp, vertical = 6 .dp),
669700 verticalAlignment = Alignment .CenterVertically ,
@@ -672,19 +703,26 @@ private fun VoiceModeRow(
672703 Surface (
673704 modifier = Modifier .size(34 .dp),
674705 shape = CircleShape ,
675- color = ClawTheme .colors.surface,
676- contentColor = ClawTheme .colors.text,
706+ color = if (enabled) ClawTheme .colors.surface else ClawTheme .colors.canvas ,
707+ contentColor = if (enabled) ClawTheme .colors.text else ClawTheme .colors.textSubtle ,
677708 border = BorderStroke (1 .dp, ClawTheme .colors.border),
678709 ) {
679710 Box (contentAlignment = Alignment .Center ) {
680711 Icon (imageVector = icon, contentDescription = null , modifier = Modifier .size(16 .dp))
681712 }
682713 }
683714 Column (modifier = Modifier .weight(1f ), verticalArrangement = Arrangement .spacedBy(2 .dp)) {
684- Text (text = title, style = ClawTheme .type.body, color = ClawTheme .colors.text, maxLines = 1 )
715+ Text (text = title, style = ClawTheme .type.body, color = if (enabled) ClawTheme .colors.text else ClawTheme .colors.textMuted , maxLines = 1 )
685716 Text (text = subtitle, style = ClawTheme .type.caption.copy(fontSize = 12.5 .sp, lineHeight = 16 .sp), color = ClawTheme .colors.textMuted, maxLines = 1 )
686717 }
687- Icon (imageVector = Icons .AutoMirrored .Filled .KeyboardArrowRight , contentDescription = null , modifier = Modifier .size(21 .dp), tint = ClawTheme .colors.textMuted)
718+ if (enabled) {
719+ Icon (
720+ imageVector = Icons .AutoMirrored .Filled .KeyboardArrowRight ,
721+ contentDescription = null ,
722+ modifier = Modifier .size(21 .dp),
723+ tint = ClawTheme .colors.textMuted,
724+ )
725+ }
688726 }
689727 }
690728}
0 commit comments