feat(android): wire voice recording and transcript into ConnectionService
This commit is contained in:
@@ -62,8 +62,19 @@ class CommandRouter(
|
|||||||
currentSteps.value = currentSteps.value + step
|
currentSteps.value = currentSteps.value + step
|
||||||
Log.d(TAG, "Step ${step.step}: ${step.reasoning}")
|
Log.d(TAG, "Step ${step.step}: ${step.reasoning}")
|
||||||
}
|
}
|
||||||
|
"transcript_partial" -> {
|
||||||
|
ConnectionService.overlayTranscript.value = msg.text ?: ""
|
||||||
|
ConnectionService.instance?.overlay?.updateTranscript(msg.text ?: "")
|
||||||
|
Log.d(TAG, "Transcript partial: ${msg.text}")
|
||||||
|
}
|
||||||
|
"transcript_final" -> {
|
||||||
|
ConnectionService.overlayTranscript.value = msg.text ?: ""
|
||||||
|
ConnectionService.instance?.overlay?.updateTranscript(msg.text ?: "")
|
||||||
|
Log.d(TAG, "Transcript final: ${msg.text}")
|
||||||
|
}
|
||||||
"goal_completed" -> {
|
"goal_completed" -> {
|
||||||
currentGoalStatus.value = if (msg.success == true) GoalStatus.Completed else GoalStatus.Failed
|
currentGoalStatus.value = if (msg.success == true) GoalStatus.Completed else GoalStatus.Failed
|
||||||
|
ConnectionService.instance?.overlay?.returnToIdle()
|
||||||
Log.i(TAG, "Goal completed: success=${msg.success}, steps=${msg.stepsUsed}")
|
Log.i(TAG, "Goal completed: success=${msg.success}, steps=${msg.stepsUsed}")
|
||||||
}
|
}
|
||||||
"goal_failed" -> {
|
"goal_failed" -> {
|
||||||
|
|||||||
@@ -30,6 +30,11 @@ import android.net.Uri
|
|||||||
import android.provider.Settings
|
import android.provider.Settings
|
||||||
import com.thisux.droidclaw.model.StopGoalMessage
|
import com.thisux.droidclaw.model.StopGoalMessage
|
||||||
import com.thisux.droidclaw.overlay.AgentOverlay
|
import com.thisux.droidclaw.overlay.AgentOverlay
|
||||||
|
import com.thisux.droidclaw.model.VoiceStartMessage
|
||||||
|
import com.thisux.droidclaw.model.VoiceChunkMessage
|
||||||
|
import com.thisux.droidclaw.model.VoiceStopMessage
|
||||||
|
import com.thisux.droidclaw.model.OverlayMode
|
||||||
|
import androidx.compose.runtime.snapshotFlow
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
import kotlinx.coroutines.flow.first
|
import kotlinx.coroutines.flow.first
|
||||||
@@ -47,6 +52,7 @@ class ConnectionService : LifecycleService() {
|
|||||||
val currentGoalStatus = MutableStateFlow(GoalStatus.Idle)
|
val currentGoalStatus = MutableStateFlow(GoalStatus.Idle)
|
||||||
val currentGoal = MutableStateFlow("")
|
val currentGoal = MutableStateFlow("")
|
||||||
val errorMessage = MutableStateFlow<String?>(null)
|
val errorMessage = MutableStateFlow<String?>(null)
|
||||||
|
val overlayTranscript = MutableStateFlow("")
|
||||||
var instance: ConnectionService? = null
|
var instance: ConnectionService? = null
|
||||||
|
|
||||||
const val ACTION_CONNECT = "com.thisux.droidclaw.CONNECT"
|
const val ACTION_CONNECT = "com.thisux.droidclaw.CONNECT"
|
||||||
@@ -59,13 +65,31 @@ class ConnectionService : LifecycleService() {
|
|||||||
private var commandRouter: CommandRouter? = null
|
private var commandRouter: CommandRouter? = null
|
||||||
private var captureManager: ScreenCaptureManager? = null
|
private var captureManager: ScreenCaptureManager? = null
|
||||||
private var wakeLock: PowerManager.WakeLock? = null
|
private var wakeLock: PowerManager.WakeLock? = null
|
||||||
private var overlay: AgentOverlay? = null
|
internal var overlay: AgentOverlay? = null
|
||||||
|
|
||||||
override fun onCreate() {
|
override fun onCreate() {
|
||||||
super.onCreate()
|
super.onCreate()
|
||||||
instance = this
|
instance = this
|
||||||
createNotificationChannel()
|
createNotificationChannel()
|
||||||
overlay = AgentOverlay(this)
|
overlay = AgentOverlay(this)
|
||||||
|
overlay?.onAudioChunk = { base64 ->
|
||||||
|
webSocket?.sendTyped(VoiceChunkMessage(data = base64))
|
||||||
|
}
|
||||||
|
overlay?.onVoiceSend = { _ ->
|
||||||
|
webSocket?.sendTyped(VoiceStopMessage(action = "send"))
|
||||||
|
}
|
||||||
|
overlay?.onVoiceCancel = {
|
||||||
|
webSocket?.sendTyped(VoiceStopMessage(action = "cancel"))
|
||||||
|
}
|
||||||
|
overlay?.let { ov ->
|
||||||
|
lifecycleScope.launch {
|
||||||
|
snapshotFlow { ov.mode.value }.collect { mode ->
|
||||||
|
if (mode == OverlayMode.Listening) {
|
||||||
|
webSocket?.sendTyped(VoiceStartMessage())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
|
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
|
||||||
|
|||||||
Reference in New Issue
Block a user