Wire VoiceLoop into ClippySession lifecycle and ClippyScreen talk button
This commit is contained in:
@@ -3,6 +3,7 @@ package farm.hobbs.clippyauto.auto
|
|||||||
import androidx.car.app.CarContext
|
import androidx.car.app.CarContext
|
||||||
import androidx.car.app.Screen
|
import androidx.car.app.Screen
|
||||||
import androidx.car.app.model.*
|
import androidx.car.app.model.*
|
||||||
|
import farm.hobbs.clippyauto.VoiceLoop
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The Android Auto screen for ClippyAuto.
|
* The Android Auto screen for ClippyAuto.
|
||||||
@@ -15,6 +16,11 @@ import androidx.car.app.model.*
|
|||||||
class ClippyScreen(carContext: CarContext) : Screen(carContext) {
|
class ClippyScreen(carContext: CarContext) : Screen(carContext) {
|
||||||
|
|
||||||
private var statusText = "Tap to talk to Clippy"
|
private var statusText = "Tap to talk to Clippy"
|
||||||
|
private var voiceLoop: VoiceLoop? = null
|
||||||
|
|
||||||
|
fun attachVoiceLoop(loop: VoiceLoop) {
|
||||||
|
voiceLoop = loop
|
||||||
|
}
|
||||||
|
|
||||||
override fun onGetTemplate(): Template {
|
override fun onGetTemplate(): Template {
|
||||||
val action = Action.Builder()
|
val action = Action.Builder()
|
||||||
@@ -51,11 +57,7 @@ class ClippyScreen(carContext: CarContext) : Screen(carContext) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun onTalkPressed() {
|
private fun onTalkPressed() {
|
||||||
// TODO: Wire to VoiceStateMachine
|
voiceLoop?.onTalkPressed()
|
||||||
// IDLE → start listening
|
|
||||||
// SPEAKING → interrupt (fade TTS, activate STT)
|
|
||||||
statusText = "Listening..."
|
|
||||||
invalidate() // Refresh the screen
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun updateStatus(status: String) {
|
fun updateStatus(status: String) {
|
||||||
|
|||||||
@@ -5,26 +5,55 @@ import androidx.car.app.Screen
|
|||||||
import androidx.car.app.Session
|
import androidx.car.app.Session
|
||||||
import androidx.lifecycle.DefaultLifecycleObserver
|
import androidx.lifecycle.DefaultLifecycleObserver
|
||||||
import androidx.lifecycle.LifecycleOwner
|
import androidx.lifecycle.LifecycleOwner
|
||||||
|
import farm.hobbs.clippyauto.VoiceLoop
|
||||||
|
import farm.hobbs.clippyauto.matrix.MatrixRestClient
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Android Auto session — manages the lifecycle of a Clippy voice interaction.
|
* Android Auto session — manages the lifecycle of a Clippy voice interaction.
|
||||||
* Sends session_start on create, session_end on destroy.
|
* Owns the VoiceLoop and passes it to the screen.
|
||||||
*/
|
*/
|
||||||
class ClippySession : Session(), DefaultLifecycleObserver {
|
class ClippySession : Session(), DefaultLifecycleObserver {
|
||||||
|
|
||||||
|
private var voiceLoop: VoiceLoop? = null
|
||||||
|
private var screen: ClippyScreen? = null
|
||||||
|
|
||||||
|
// TODO: Move to config / SharedPreferences / SetupActivity
|
||||||
|
companion object {
|
||||||
|
const val HOMESERVER = "https://mess.hobbs.farm"
|
||||||
|
const val ROOM_ID = "" // Set after #clippy-voice room is created
|
||||||
|
const val CLIPPY_USER = "@clippy:hobbs.farm"
|
||||||
|
}
|
||||||
|
|
||||||
override fun onCreateScreen(intent: Intent): Screen {
|
override fun onCreateScreen(intent: Intent): Screen {
|
||||||
lifecycle.addObserver(this)
|
lifecycle.addObserver(this)
|
||||||
return ClippyScreen(carContext)
|
val clippyScreen = ClippyScreen(carContext)
|
||||||
|
screen = clippyScreen
|
||||||
|
return clippyScreen
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onStart(owner: LifecycleOwner) {
|
override fun onStart(owner: LifecycleOwner) {
|
||||||
// TODO: Send session_start signal to Matrix #clippy-voice room
|
val matrixClient = MatrixRestClient(HOMESERVER)
|
||||||
// TODO: Start Matrix sync (fallback path)
|
|
||||||
// TODO: Subscribe to FCM response bus
|
voiceLoop = VoiceLoop(
|
||||||
|
context = carContext,
|
||||||
|
matrixClient = matrixClient,
|
||||||
|
roomId = ROOM_ID,
|
||||||
|
clippyUserId = CLIPPY_USER,
|
||||||
|
onStateChange = { state, status ->
|
||||||
|
screen?.updateStatus(status)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
voiceLoop?.initialize()
|
||||||
|
voiceLoop?.startSession()
|
||||||
|
|
||||||
|
// Give the screen a reference so the talk button works
|
||||||
|
screen?.attachVoiceLoop(voiceLoop!!)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onStop(owner: LifecycleOwner) {
|
override fun onStop(owner: LifecycleOwner) {
|
||||||
// TODO: Send session_end signal to Matrix
|
voiceLoop?.endSession()
|
||||||
// TODO: Clean up voice pipeline
|
voiceLoop?.destroy()
|
||||||
|
voiceLoop = null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user