mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-27 12:56:01 -06:00
fix(talk): bind Android cancellation to action turn
Co-authored-by: Zhilong Zheng <zhengzhilong1115@gmail.com>
This commit is contained in:
@@ -1725,6 +1725,8 @@ class TalkModeManager internal constructor(
|
||||
}
|
||||
|
||||
internal suspend fun pauseRealtimeCaptureForPushToTalk(captureId: String) {
|
||||
val cancellationSessionId = realtimeSessionId
|
||||
val cancellationTurnId = realtimeOutputTurnId?.trim()?.takeIf(String::isNotEmpty)
|
||||
val captureJobs =
|
||||
synchronized(realtimeCapturePauseLock) {
|
||||
val currentSessionId = realtimeSessionId
|
||||
@@ -1741,7 +1743,13 @@ class TalkModeManager internal constructor(
|
||||
appendJob?.cancelAndJoin()
|
||||
// Stop input first so no frame can create new provider output while the
|
||||
// cancellation boundary is being established.
|
||||
if (!cancelRealtimeOutput(reason = "android-push-to-talk")) {
|
||||
if (
|
||||
!cancelRealtimeOutput(
|
||||
reason = "android-push-to-talk",
|
||||
sessionId = cancellationSessionId,
|
||||
turnId = cancellationTurnId,
|
||||
)
|
||||
) {
|
||||
Log.w(tag, "realtime output cancellation was not confirmed; closing relay")
|
||||
stopRealtimeRelay(preserveStatus = true)
|
||||
synchronized(realtimeCapturePauseLock) {
|
||||
@@ -2916,18 +2924,32 @@ class TalkModeManager internal constructor(
|
||||
}
|
||||
|
||||
fun stopTts() {
|
||||
val sessionId = realtimeSessionId
|
||||
val turnId = realtimeOutputTurnId?.trim()?.takeIf(String::isNotEmpty)
|
||||
realtimeOutputSuppressed = true
|
||||
stopRealtimePlayback()
|
||||
scope.launch { cancelRealtimeOutput(reason = "android-stop-tts") }
|
||||
if (sessionId != null && turnId != null) {
|
||||
scope.launch {
|
||||
cancelRealtimeOutput(
|
||||
reason = "android-stop-tts",
|
||||
sessionId = sessionId,
|
||||
turnId = turnId,
|
||||
)
|
||||
}
|
||||
}
|
||||
stopSpeaking(resetInterrupt = true)
|
||||
_isSpeaking.value = false
|
||||
setStatus(nativeText("Listening"))
|
||||
}
|
||||
|
||||
private suspend fun cancelRealtimeOutput(reason: String): Boolean =
|
||||
private suspend fun cancelRealtimeOutput(
|
||||
reason: String,
|
||||
sessionId: String?,
|
||||
turnId: String?,
|
||||
): Boolean =
|
||||
realtimeOutputCancellationMutex.withLock {
|
||||
val sessionId = realtimeSessionId ?: return@withLock true
|
||||
val turnId = realtimeOutputTurnId?.trim()?.takeIf(String::isNotEmpty) ?: return@withLock false
|
||||
sessionId ?: return@withLock true
|
||||
turnId ?: return@withLock false
|
||||
val clear = CompletableDeferred<String?>()
|
||||
pendingRealtimeOutputClear = clear
|
||||
try {
|
||||
|
||||
@@ -120,6 +120,23 @@ class TalkModeManagerTest {
|
||||
assertTrue(readPrivateField(manager, "pendingRealtimeOutputClear") === pendingClear)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun stopTtsCancelsTheOutputOwnedWhenTheActionStarted() =
|
||||
runTest {
|
||||
val manager = createManager(scope = this)
|
||||
val priorClear = CompletableDeferred<String?>()
|
||||
setPrivateField(manager, "realtimeSessionId", "relay-a")
|
||||
setPrivateField(manager, "realtimeOutputTurnId", "turn-a")
|
||||
setPrivateField(manager, "pendingRealtimeOutputClear", priorClear)
|
||||
|
||||
manager.stopTts()
|
||||
setPrivateField(manager, "realtimeSessionId", null)
|
||||
setPrivateField(manager, "realtimeOutputTurnId", "turn-b")
|
||||
runCurrent()
|
||||
|
||||
assertNull(readPrivateField(manager, "pendingRealtimeOutputClear"))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun disablingPlaybackCancelsTrackedJobOnce() {
|
||||
val manager = createManager()
|
||||
|
||||
Reference in New Issue
Block a user