fix(android): preserve emoji in voice error status (#108101)

* fix(android): preserve emoji in voice error status

* chore(android): sync native i18n inventory

* fix(android): align voice status i18n allowlist
This commit is contained in:
Leon-SK668
2026-07-15 16:16:27 +08:00
committed by GitHub
parent 42d42703e6
commit 9b6361a50c
4 changed files with 83 additions and 67 deletions
+65 -65
View File
@@ -10619,7 +10619,7 @@
},
{
"kind": "ui-call",
"line": 219,
"line": 220,
"path": "apps/android/app/src/main/java/ai/openclaw/app/ui/VoiceScreen.kt",
"source": "Your voice command center.",
"surface": "android",
@@ -10627,7 +10627,7 @@
},
{
"kind": "ui-call",
"line": 323,
"line": 324,
"path": "apps/android/app/src/main/java/ai/openclaw/app/ui/VoiceScreen.kt",
"source": "Transcribe then send",
"surface": "android",
@@ -10635,7 +10635,7 @@
},
{
"kind": "ui-call",
"line": 325,
"line": 326,
"path": "apps/android/app/src/main/java/ai/openclaw/app/ui/VoiceScreen.kt",
"source": "Dictation settings",
"surface": "android",
@@ -10643,7 +10643,7 @@
},
{
"kind": "ui-call",
"line": 336,
"line": 337,
"path": "apps/android/app/src/main/java/ai/openclaw/app/ui/VoiceScreen.kt",
"source": "Sending to chat...",
"surface": "android",
@@ -10651,7 +10651,7 @@
},
{
"kind": "ui-call",
"line": 336,
"line": 337,
"path": "apps/android/app/src/main/java/ai/openclaw/app/ui/VoiceScreen.kt",
"source": "Start speaking...",
"surface": "android",
@@ -10659,7 +10659,7 @@
},
{
"kind": "ui-call",
"line": 368,
"line": 369,
"path": "apps/android/app/src/main/java/ai/openclaw/app/ui/VoiceScreen.kt",
"source": "Speech provider",
"surface": "android",
@@ -10667,7 +10667,7 @@
},
{
"kind": "ui-call",
"line": 420,
"line": 421,
"path": "apps/android/app/src/main/java/ai/openclaw/app/ui/VoiceScreen.kt",
"source": "Tip: stop listening to send the captured turn.",
"surface": "android",
@@ -10675,7 +10675,7 @@
},
{
"kind": "ui-call",
"line": 430,
"line": 431,
"path": "apps/android/app/src/main/java/ai/openclaw/app/ui/VoiceScreen.kt",
"source": "Cancel",
"surface": "android",
@@ -10683,7 +10683,7 @@
},
{
"kind": "ui-call",
"line": 431,
"line": 432,
"path": "apps/android/app/src/main/java/ai/openclaw/app/ui/VoiceScreen.kt",
"source": "Send to Chat",
"surface": "android",
@@ -10691,7 +10691,7 @@
},
{
"kind": "ui-call",
"line": 460,
"line": 461,
"path": "apps/android/app/src/main/java/ai/openclaw/app/ui/VoiceScreen.kt",
"source": "Back to voice",
"surface": "android",
@@ -10699,7 +10699,7 @@
},
{
"kind": "ui-call",
"line": 468,
"line": 469,
"path": "apps/android/app/src/main/java/ai/openclaw/app/ui/VoiceScreen.kt",
"source": "OpenClaw speaking",
"surface": "android",
@@ -10707,7 +10707,7 @@
},
{
"kind": "ui-call",
"line": 470,
"line": 471,
"path": "apps/android/app/src/main/java/ai/openclaw/app/ui/VoiceScreen.kt",
"source": "Realtime voice",
"surface": "android",
@@ -10715,7 +10715,7 @@
},
{
"kind": "ui-call",
"line": 472,
"line": 473,
"path": "apps/android/app/src/main/java/ai/openclaw/app/ui/VoiceScreen.kt",
"source": "Connected",
"surface": "android",
@@ -10723,7 +10723,7 @@
},
{
"kind": "ui-call",
"line": 479,
"line": 480,
"path": "apps/android/app/src/main/java/ai/openclaw/app/ui/VoiceScreen.kt",
"source": "Talk settings",
"surface": "android",
@@ -10731,7 +10731,7 @@
},
{
"kind": "ui-call",
"line": 503,
"line": 504,
"path": "apps/android/app/src/main/java/ai/openclaw/app/ui/VoiceScreen.kt",
"source": "Live transcript",
"surface": "android",
@@ -10739,7 +10739,7 @@
},
{
"kind": "ui-call",
"line": 514,
"line": 515,
"path": "apps/android/app/src/main/java/ai/openclaw/app/ui/VoiceScreen.kt",
"source": "Mute",
"surface": "android",
@@ -10747,7 +10747,7 @@
},
{
"kind": "ui-call",
"line": 514,
"line": 515,
"path": "apps/android/app/src/main/java/ai/openclaw/app/ui/VoiceScreen.kt",
"source": "Unmute",
"surface": "android",
@@ -10755,7 +10755,7 @@
},
{
"kind": "ui-call",
"line": 520,
"line": 521,
"path": "apps/android/app/src/main/java/ai/openclaw/app/ui/VoiceScreen.kt",
"source": "End",
"surface": "android",
@@ -10763,7 +10763,7 @@
},
{
"kind": "ui-call",
"line": 543,
"line": 544,
"path": "apps/android/app/src/main/java/ai/openclaw/app/ui/VoiceScreen.kt",
"source": "Listening for your next turn.",
"surface": "android",
@@ -10771,7 +10771,7 @@
},
{
"kind": "ui-call",
"line": 551,
"line": 552,
"path": "apps/android/app/src/main/java/ai/openclaw/app/ui/VoiceScreen.kt",
"source": "Listening response...",
"surface": "android",
@@ -10779,7 +10779,7 @@
},
{
"kind": "ui-call",
"line": 638,
"line": 639,
"path": "apps/android/app/src/main/java/ai/openclaw/app/ui/VoiceScreen.kt",
"source": "Search voice",
"surface": "android",
@@ -10787,7 +10787,7 @@
},
{
"kind": "ui-call",
"line": 646,
"line": 647,
"path": "apps/android/app/src/main/java/ai/openclaw/app/ui/VoiceScreen.kt",
"source": "Voice",
"surface": "android",
@@ -10795,7 +10795,7 @@
},
{
"kind": "ui-call",
"line": 657,
"line": 658,
"path": "apps/android/app/src/main/java/ai/openclaw/app/ui/VoiceScreen.kt",
"source": "Mute speaker",
"surface": "android",
@@ -10803,7 +10803,7 @@
},
{
"kind": "ui-call",
"line": 657,
"line": 658,
"path": "apps/android/app/src/main/java/ai/openclaw/app/ui/VoiceScreen.kt",
"source": "Unmute speaker",
"surface": "android",
@@ -10811,7 +10811,7 @@
},
{
"kind": "ui-call",
"line": 702,
"line": 703,
"path": "apps/android/app/src/main/java/ai/openclaw/app/ui/VoiceScreen.kt",
"source": "OpenClaw is replying",
"surface": "android",
@@ -10819,7 +10819,7 @@
},
{
"kind": "ui-call",
"line": 705,
"line": 706,
"path": "apps/android/app/src/main/java/ai/openclaw/app/ui/VoiceScreen.kt",
"source": "Dictation is listening",
"surface": "android",
@@ -10827,7 +10827,7 @@
},
{
"kind": "ui-call",
"line": 736,
"line": 737,
"path": "apps/android/app/src/main/java/ai/openclaw/app/ui/VoiceScreen.kt",
"source": "Realtime Talk",
"surface": "android",
@@ -10835,7 +10835,7 @@
},
{
"kind": "ui-call",
"line": 739,
"line": 740,
"path": "apps/android/app/src/main/java/ai/openclaw/app/ui/VoiceScreen.kt",
"source": "Conversation is live",
"surface": "android",
@@ -10843,7 +10843,7 @@
},
{
"kind": "ui-call",
"line": 748,
"line": 749,
"path": "apps/android/app/src/main/java/ai/openclaw/app/ui/VoiceScreen.kt",
"source": "Dictation",
"surface": "android",
@@ -10851,7 +10851,7 @@
},
{
"kind": "ui-call",
"line": 748,
"line": 749,
"path": "apps/android/app/src/main/java/ai/openclaw/app/ui/VoiceScreen.kt",
"source": "Stop Dictation",
"surface": "android",
@@ -10859,7 +10859,7 @@
},
{
"kind": "ui-call",
"line": 751,
"line": 752,
"path": "apps/android/app/src/main/java/ai/openclaw/app/ui/VoiceScreen.kt",
"source": "Listening for one turn",
"surface": "android",
@@ -10867,7 +10867,7 @@
},
{
"kind": "ui-call",
"line": 753,
"line": 754,
"path": "apps/android/app/src/main/java/ai/openclaw/app/ui/VoiceScreen.kt",
"source": "Connect gateway to start",
"surface": "android",
@@ -10875,7 +10875,7 @@
},
{
"kind": "ui-call",
"line": 770,
"line": 771,
"path": "apps/android/app/src/main/java/ai/openclaw/app/ui/VoiceScreen.kt",
"source": "End Talk",
"surface": "android",
@@ -10883,7 +10883,7 @@
},
{
"kind": "ui-call",
"line": 771,
"line": 772,
"path": "apps/android/app/src/main/java/ai/openclaw/app/ui/VoiceScreen.kt",
"source": "Set Up Talk",
"surface": "android",
@@ -10891,7 +10891,7 @@
},
{
"kind": "ui-call",
"line": 772,
"line": 773,
"path": "apps/android/app/src/main/java/ai/openclaw/app/ui/VoiceScreen.kt",
"source": "Start Talk",
"surface": "android",
@@ -10899,7 +10899,7 @@
},
{
"kind": "ui-call",
"line": 773,
"line": 774,
"path": "apps/android/app/src/main/java/ai/openclaw/app/ui/VoiceScreen.kt",
"source": "Connect Gateway",
"surface": "android",
@@ -10907,7 +10907,7 @@
},
{
"kind": "ui-call",
"line": 885,
"line": 886,
"path": "apps/android/app/src/main/java/ai/openclaw/app/ui/VoiceScreen.kt",
"source": "Voice setup",
"surface": "android",
@@ -10915,7 +10915,7 @@
},
{
"kind": "ui-call",
"line": 917,
"line": 918,
"path": "apps/android/app/src/main/java/ai/openclaw/app/ui/VoiceScreen.kt",
"source": "Ready",
"surface": "android",
@@ -10923,7 +10923,7 @@
},
{
"kind": "ui-call",
"line": 918,
"line": 919,
"path": "apps/android/app/src/main/java/ai/openclaw/app/ui/VoiceScreen.kt",
"source": "Setup",
"surface": "android",
@@ -10931,7 +10931,7 @@
},
{
"kind": "ui-call",
"line": 919,
"line": 920,
"path": "apps/android/app/src/main/java/ai/openclaw/app/ui/VoiceScreen.kt",
"source": "Attention",
"surface": "android",
@@ -10939,7 +10939,7 @@
},
{
"kind": "ui-call",
"line": 920,
"line": 921,
"path": "apps/android/app/src/main/java/ai/openclaw/app/ui/VoiceScreen.kt",
"source": "Unverified",
"surface": "android",
@@ -10947,7 +10947,7 @@
},
{
"kind": "ui-call",
"line": 921,
"line": 922,
"path": "apps/android/app/src/main/java/ai/openclaw/app/ui/VoiceScreen.kt",
"source": "Offline",
"surface": "android",
@@ -10955,7 +10955,7 @@
},
{
"kind": "ui-call",
"line": 939,
"line": 940,
"path": "apps/android/app/src/main/java/ai/openclaw/app/ui/VoiceScreen.kt",
"source": "Talk: ${gatewayTalkSetupDescription(readiness.realtimeTalk)}",
"surface": "android",
@@ -10963,7 +10963,7 @@
},
{
"kind": "ui-call",
"line": 940,
"line": 941,
"path": "apps/android/app/src/main/java/ai/openclaw/app/ui/VoiceScreen.kt",
"source": "Dictation: ${gatewayTalkSetupDescription(readiness.dictation)}",
"surface": "android",
@@ -10971,7 +10971,7 @@
},
{
"kind": "ui-call",
"line": 1046,
"line": 1047,
"path": "apps/android/app/src/main/java/ai/openclaw/app/ui/VoiceScreen.kt",
"source": "OpenClaw",
"surface": "android",
@@ -10979,7 +10979,7 @@
},
{
"kind": "ui-call",
"line": 1046,
"line": 1047,
"path": "apps/android/app/src/main/java/ai/openclaw/app/ui/VoiceScreen.kt",
"source": "You",
"surface": "android",
@@ -10987,7 +10987,7 @@
},
{
"kind": "ui-call",
"line": 1053,
"line": 1054,
"path": "apps/android/app/src/main/java/ai/openclaw/app/ui/VoiceScreen.kt",
"source": "Listening...",
"surface": "android",
@@ -10995,7 +10995,7 @@
},
{
"kind": "ui-call",
"line": 1069,
"line": 1070,
"path": "apps/android/app/src/main/java/ai/openclaw/app/ui/VoiceScreen.kt",
"source": "Sending",
"surface": "android",
@@ -11003,7 +11003,7 @@
},
{
"kind": "ui-call",
"line": 1071,
"line": 1072,
"path": "apps/android/app/src/main/java/ai/openclaw/app/ui/VoiceScreen.kt",
"source": "OpenClaw is preparing a response.",
"surface": "android",
@@ -11011,7 +11011,7 @@
},
{
"kind": "ui-call",
"line": 1086,
"line": 1087,
"path": "apps/android/app/src/main/java/ai/openclaw/app/ui/VoiceScreen.kt",
"source": "Permission needed",
"surface": "android",
@@ -11019,7 +11019,7 @@
},
{
"kind": "ui-call",
"line": 1087,
"line": 1088,
"path": "apps/android/app/src/main/java/ai/openclaw/app/ui/VoiceScreen.kt",
"source": "Microphone access is needed.",
"surface": "android",
@@ -11027,7 +11027,7 @@
},
{
"kind": "ui-call",
"line": 1089,
"line": 1090,
"path": "apps/android/app/src/main/java/ai/openclaw/app/ui/VoiceScreen.kt",
"source": "OpenClaw only listens when you start Talk or Dictation.",
"surface": "android",
@@ -11035,7 +11035,7 @@
},
{
"kind": "ui-call",
"line": 1093,
"line": 1094,
"path": "apps/android/app/src/main/java/ai/openclaw/app/ui/VoiceScreen.kt",
"source": "Enable Microphone",
"surface": "android",
@@ -11043,7 +11043,7 @@
},
{
"kind": "ui-call",
"line": 1161,
"line": 1162,
"path": "apps/android/app/src/main/java/ai/openclaw/app/ui/VoiceScreen.kt",
"source": "OpenClaw is speaking",
"surface": "android",
@@ -11051,7 +11051,7 @@
},
{
"kind": "ui-call",
"line": 1163,
"line": 1164,
"path": "apps/android/app/src/main/java/ai/openclaw/app/ui/VoiceScreen.kt",
"source": "Talk is live",
"surface": "android",
@@ -11059,7 +11059,7 @@
},
{
"kind": "ui-call",
"line": 1164,
"line": 1165,
"path": "apps/android/app/src/main/java/ai/openclaw/app/ui/VoiceScreen.kt",
"source": "Sending dictation",
"surface": "android",
@@ -11067,7 +11067,7 @@
},
{
"kind": "ui-call",
"line": 1165,
"line": 1166,
"path": "apps/android/app/src/main/java/ai/openclaw/app/ui/VoiceScreen.kt",
"source": "Listening",
"surface": "android",
@@ -11075,7 +11075,7 @@
},
{
"kind": "ui-call",
"line": 1166,
"line": 1167,
"path": "apps/android/app/src/main/java/ai/openclaw/app/ui/VoiceScreen.kt",
"source": "$micQueuedMessages queued",
"surface": "android",
@@ -11083,7 +11083,7 @@
},
{
"kind": "ui-call",
"line": 1167,
"line": 1168,
"path": "apps/android/app/src/main/java/ai/openclaw/app/ui/VoiceScreen.kt",
"source": "Gateway offline",
"surface": "android",
@@ -11091,7 +11091,7 @@
},
{
"kind": "ui-call",
"line": 1168,
"line": 1169,
"path": "apps/android/app/src/main/java/ai/openclaw/app/ui/VoiceScreen.kt",
"source": "Ready to talk",
"surface": "android",
@@ -11099,7 +11099,7 @@
},
{
"kind": "ui-call",
"line": 1223,
"line": 1224,
"path": "apps/android/app/src/main/java/ai/openclaw/app/ui/VoiceScreen.kt",
"source": "Realtime voice provider is not configured.",
"surface": "android",
@@ -11107,7 +11107,7 @@
},
{
"kind": "ui-call",
"line": 1226,
"line": 1227,
"path": "apps/android/app/src/main/java/ai/openclaw/app/ui/VoiceScreen.kt",
"source": "Realtime transcription provider is not configured.",
"surface": "android",
@@ -11115,7 +11115,7 @@
},
{
"kind": "ui-call",
"line": 1229,
"line": 1230,
"path": "apps/android/app/src/main/java/ai/openclaw/app/ui/VoiceScreen.kt",
"source": "Microphone permission is required.",
"surface": "android",
@@ -11123,7 +11123,7 @@
},
{
"kind": "ui-call",
"line": 1239,
"line": 1240,
"path": "apps/android/app/src/main/java/ai/openclaw/app/ui/VoiceScreen.kt",
"source": "Connected and ready",
"surface": "android",
@@ -11131,7 +11131,7 @@
},
{
"kind": "ui-call",
"line": 1239,
"line": 1240,
"path": "apps/android/app/src/main/java/ai/openclaw/app/ui/VoiceScreen.kt",
"source": "Gateway not connected",
"surface": "android",
@@ -7,6 +7,7 @@ import ai.openclaw.app.gatewayTalkSetupDescription
import ai.openclaw.app.i18n.nativeString
import ai.openclaw.app.isReady
import ai.openclaw.app.requiresSetup
import ai.openclaw.app.takeUtf16Safe
import ai.openclaw.app.ui.design.ClawPanel
import ai.openclaw.app.ui.design.ClawPlainIconButton
import ai.openclaw.app.ui.design.ClawPrimaryButton
@@ -1228,7 +1229,7 @@ private fun userFacingVoiceAttentionStatus(status: String): String {
if (lower.contains("microphone permission required")) {
return nativeString("Microphone permission is required.")
}
return if (normalized.length <= 90) normalized else "${normalized.take(87)}..."
return if (normalized.length <= 90) normalized else "${normalized.takeUtf16Safe(87)}..."
}
private fun String.isVoiceGatewayReady(): Boolean {
@@ -74,6 +74,21 @@ class VoiceScreenLogicTest {
)
}
@Test
fun voiceRuntimeAttentionStatusPreservesUtf16BoundariesAtLimit() {
val splitPairPrefix = "failed: ${"x".repeat(78)}"
assertEquals(
"$splitPairPrefix...",
voiceRuntimeAttentionStatus("$splitPairPrefix😀${"y".repeat(10)}"),
)
val completePairPrefix = "failed: ${"x".repeat(77)}"
assertEquals(
"$completePairPrefix😀...",
voiceRuntimeAttentionStatus("$completePairPrefix😀${"y".repeat(10)}"),
)
}
@Test
fun talkSessionWaveformPhaseFollowsTalkState() {
assertEquals(
+1 -1
View File
@@ -467,7 +467,7 @@ const ALLOWED_UI_LITERALS = new Map<string, ReadonlySet<string>>([
],
[
"apps/android/app/src/main/java/ai/openclaw/app/ui/VoiceScreen.kt",
new Set(["${normalized.take(87)}..."]),
new Set(["${normalized.takeUtf16Safe(87)}..."]),
],
[
"apps/android/app/src/main/java/ai/openclaw/app/ui/chat/ChatCommandControls.kt",