refactor(android): remove orphaned legacy screens (#102809)

This commit is contained in:
Vincent Koc
2026-07-09 06:02:30 -07:00
committed by GitHub
parent 124119a898
commit cb7468fc58
12 changed files with 76 additions and 2225 deletions
+47 -591
View File
@@ -195,7 +195,7 @@
},
{
"kind": "ui-state-text",
"line": 640,
"line": 639,
"path": "apps/android/app/src/main/java/ai/openclaw/app/NodeRuntime.kt",
"source": "Offline",
"surface": "android",
@@ -203,7 +203,7 @@
},
{
"kind": "conditional-branch",
"line": 2914,
"line": 2913,
"path": "apps/android/app/src/main/java/ai/openclaw/app/NodeRuntime.kt",
"source": "Failed: no secure gateway endpoint was detected. Enable gateway TLS or Tailscale Serve, or use a trusted private LAN address with Unencrypted selected.",
"surface": "android",
@@ -211,7 +211,7 @@
},
{
"kind": "conditional-branch",
"line": 2916,
"line": 2915,
"path": "apps/android/app/src/main/java/ai/openclaw/app/NodeRuntime.kt",
"source": "Failed: secure endpoint reached, but TLS fingerprint verification timed out. Check Tailscale Serve or gateway TLS and retry.",
"surface": "android",
@@ -219,7 +219,7 @@
},
{
"kind": "conditional-branch",
"line": 2918,
"line": 2917,
"path": "apps/android/app/src/main/java/ai/openclaw/app/NodeRuntime.kt",
"source": "Failed: couldn't reach the secure gateway endpoint for this host.",
"surface": "android",
@@ -425,22 +425,6 @@
"surface": "android",
"id": "native.android.5b17d331b254e403"
},
{
"kind": "conditional-branch",
"line": 54,
"path": "apps/android/app/src/main/java/ai/openclaw/app/tools/ToolDisplay.kt",
"source": "$emoji $label",
"surface": "android",
"id": "native.android.1623b8cd55455bfc"
},
{
"kind": "conditional-branch",
"line": 54,
"path": "apps/android/app/src/main/java/ai/openclaw/app/tools/ToolDisplay.kt",
"source": "$emoji $label: $detailLine",
"surface": "android",
"id": "native.android.a393de564f65660f"
},
{
"kind": "conditional-branch",
"line": 47,
@@ -979,7 +963,7 @@
},
{
"kind": "conditional-branch",
"line": 86,
"line": 80,
"path": "apps/android/app/src/main/java/ai/openclaw/app/ui/GatewayDiagnostics.kt",
"source": "Setup code expired",
"surface": "android",
@@ -987,7 +971,7 @@
},
{
"kind": "conditional-branch",
"line": 87,
"line": 81,
"path": "apps/android/app/src/main/java/ai/openclaw/app/ui/GatewayDiagnostics.kt",
"source": "Gateway token needed",
"surface": "android",
@@ -995,7 +979,7 @@
},
{
"kind": "conditional-branch",
"line": 88,
"line": 82,
"path": "apps/android/app/src/main/java/ai/openclaw/app/ui/GatewayDiagnostics.kt",
"source": "Gateway token not configured",
"surface": "android",
@@ -1003,7 +987,7 @@
},
{
"kind": "conditional-branch",
"line": 89,
"line": 83,
"path": "apps/android/app/src/main/java/ai/openclaw/app/ui/GatewayDiagnostics.kt",
"source": "Gateway password needed",
"surface": "android",
@@ -1011,7 +995,7 @@
},
{
"kind": "conditional-branch",
"line": 90,
"line": 84,
"path": "apps/android/app/src/main/java/ai/openclaw/app/ui/GatewayDiagnostics.kt",
"source": "Gateway password invalid",
"surface": "android",
@@ -1019,7 +1003,7 @@
},
{
"kind": "conditional-branch",
"line": 91,
"line": 85,
"path": "apps/android/app/src/main/java/ai/openclaw/app/ui/GatewayDiagnostics.kt",
"source": "Gateway password not configured",
"surface": "android",
@@ -1027,7 +1011,7 @@
},
{
"kind": "conditional-branch",
"line": 92,
"line": 86,
"path": "apps/android/app/src/main/java/ai/openclaw/app/ui/GatewayDiagnostics.kt",
"source": "Gateway access needs review",
"surface": "android",
@@ -1035,7 +1019,7 @@
},
{
"kind": "conditional-branch",
"line": 93,
"line": 87,
"path": "apps/android/app/src/main/java/ai/openclaw/app/ui/GatewayDiagnostics.kt",
"source": "Saved auth invalid",
"surface": "android",
@@ -1043,7 +1027,7 @@
},
{
"kind": "conditional-branch",
"line": 94,
"line": 88,
"path": "apps/android/app/src/main/java/ai/openclaw/app/ui/GatewayDiagnostics.kt",
"source": "Device identity required",
"surface": "android",
@@ -1051,7 +1035,7 @@
},
{
"kind": "ui-toast",
"line": 177,
"line": 171,
"path": "apps/android/app/src/main/java/ai/openclaw/app/ui/GatewayDiagnostics.kt",
"source": "Copied gateway diagnostics",
"surface": "android",
@@ -3825,526 +3809,6 @@
"surface": "android",
"id": "native.android.f36439e78187c554"
},
{
"kind": "ui-call",
"line": 475,
"path": "apps/android/app/src/main/java/ai/openclaw/app/ui/SettingsSheet.kt",
"source": "DEVICE",
"surface": "android",
"id": "native.android.46ba30c035d8f9b3"
},
{
"kind": "ui-call",
"line": 486,
"path": "apps/android/app/src/main/java/ai/openclaw/app/ui/SettingsSheet.kt",
"source": "Name",
"surface": "android",
"id": "native.android.00d9f5fe4abd05a0"
},
{
"kind": "ui-call",
"line": 496,
"path": "apps/android/app/src/main/java/ai/openclaw/app/ui/SettingsSheet.kt",
"source": "$deviceModel · $appVersion",
"surface": "android",
"id": "native.android.d35345db50231440"
},
{
"kind": "ui-call",
"line": 508,
"path": "apps/android/app/src/main/java/ai/openclaw/app/ui/SettingsSheet.kt",
"source": "Default Assistant",
"surface": "android",
"id": "native.android.a3959f171202f031"
},
{
"kind": "ui-call",
"line": 544,
"path": "apps/android/app/src/main/java/ai/openclaw/app/ui/SettingsSheet.kt",
"source": "MEDIA",
"surface": "android",
"id": "native.android.2949f2c21abc7e61"
},
{
"kind": "ui-call",
"line": 555,
"path": "apps/android/app/src/main/java/ai/openclaw/app/ui/SettingsSheet.kt",
"source": "Microphone",
"surface": "android",
"id": "native.android.d8116d29802a3122"
},
{
"kind": "conditional-branch",
"line": 558,
"path": "apps/android/app/src/main/java/ai/openclaw/app/ui/SettingsSheet.kt",
"source": "Granted",
"surface": "android",
"id": "native.android.4485058344287d1f"
},
{
"kind": "conditional-branch",
"line": 558,
"path": "apps/android/app/src/main/java/ai/openclaw/app/ui/SettingsSheet.kt",
"source": "Required for voice transcription.",
"surface": "android",
"id": "native.android.e2a32f3002f5dccf"
},
{
"kind": "ui-call",
"line": 585,
"path": "apps/android/app/src/main/java/ai/openclaw/app/ui/SettingsSheet.kt",
"source": "Camera",
"surface": "android",
"id": "native.android.72cb670153cdb9a2"
},
{
"kind": "ui-call",
"line": 586,
"path": "apps/android/app/src/main/java/ai/openclaw/app/ui/SettingsSheet.kt",
"source": "Photos and video clips (foreground only).",
"surface": "android",
"id": "native.android.29a57979453ac97e"
},
{
"kind": "ui-call",
"line": 594,
"path": "apps/android/app/src/main/java/ai/openclaw/app/ui/SettingsSheet.kt",
"source": "NOTIFICATIONS",
"surface": "android",
"id": "native.android.893b84d511b75a97"
},
{
"kind": "ui-call",
"line": 605,
"path": "apps/android/app/src/main/java/ai/openclaw/app/ui/SettingsSheet.kt",
"source": "System Notifications",
"surface": "android",
"id": "native.android.d316b20dd0309255"
},
{
"kind": "ui-call",
"line": 607,
"path": "apps/android/app/src/main/java/ai/openclaw/app/ui/SettingsSheet.kt",
"source": "Alerts and foreground service.",
"surface": "android",
"id": "native.android.ef0ca43d6ab9e92c"
},
{
"kind": "ui-call",
"line": 632,
"path": "apps/android/app/src/main/java/ai/openclaw/app/ui/SettingsSheet.kt",
"source": "Notification Listener Access",
"surface": "android",
"id": "native.android.30bcd3f0ebf33d13"
},
{
"kind": "ui-call",
"line": 634,
"path": "apps/android/app/src/main/java/ai/openclaw/app/ui/SettingsSheet.kt",
"source": "Required for `notifications.list`, `notifications.actions`, and forwarded notification events.",
"surface": "android",
"id": "native.android.f5c666d3707d9a31"
},
{
"kind": "conditional-branch",
"line": 646,
"path": "apps/android/app/src/main/java/ai/openclaw/app/ui/SettingsSheet.kt",
"source": "Enable",
"surface": "android",
"id": "native.android.5ad85205f857a25b"
},
{
"kind": "ui-call",
"line": 657,
"path": "apps/android/app/src/main/java/ai/openclaw/app/ui/SettingsSheet.kt",
"source": "SMS",
"surface": "android",
"id": "native.android.581a2f434f14c5a2"
},
{
"kind": "ui-call",
"line": 659,
"path": "apps/android/app/src/main/java/ai/openclaw/app/ui/SettingsSheet.kt",
"source": "Grants Android SMS access. The Gateway must separately allow `sms.search` and, only if needed, `sms.send`.",
"surface": "android",
"id": "native.android.cba20518e9758043"
},
{
"kind": "ui-call",
"line": 696,
"path": "apps/android/app/src/main/java/ai/openclaw/app/ui/SettingsSheet.kt",
"source": "Forward Notification Events",
"surface": "android",
"id": "native.android.8c32ca1f11a93a25"
},
{
"kind": "ui-call",
"line": 735,
"path": "apps/android/app/src/main/java/ai/openclaw/app/ui/SettingsSheet.kt",
"source": "Package Filter: Allowlist",
"surface": "android",
"id": "native.android.0ca0ef510d5c07d3"
},
{
"kind": "ui-call",
"line": 737,
"path": "apps/android/app/src/main/java/ai/openclaw/app/ui/SettingsSheet.kt",
"source": "Only listed package IDs are forwarded.",
"surface": "android",
"id": "native.android.57bc87918c65bb9f"
},
{
"kind": "ui-call",
"line": 753,
"path": "apps/android/app/src/main/java/ai/openclaw/app/ui/SettingsSheet.kt",
"source": "Package Filter: Blocklist",
"surface": "android",
"id": "native.android.c20fb2c785da76b8"
},
{
"kind": "ui-call",
"line": 755,
"path": "apps/android/app/src/main/java/ai/openclaw/app/ui/SettingsSheet.kt",
"source": "All packages except listed IDs are forwarded.",
"surface": "android",
"id": "native.android.7395a775f7d34568"
},
{
"kind": "conditional-branch",
"line": 778,
"path": "apps/android/app/src/main/java/ai/openclaw/app/ui/SettingsSheet.kt",
"source": "Close App Picker",
"surface": "android",
"id": "native.android.1ffc2ba152a64d9b"
},
{
"kind": "conditional-branch",
"line": 778,
"path": "apps/android/app/src/main/java/ai/openclaw/app/ui/SettingsSheet.kt",
"source": "Open App Picker",
"surface": "android",
"id": "native.android.f4ae672967a25eb4"
},
{
"kind": "ui-call",
"line": 797,
"path": "apps/android/app/src/main/java/ai/openclaw/app/ui/SettingsSheet.kt",
"source": "Search apps",
"surface": "android",
"id": "native.android.a06d9e470d1d453f"
},
{
"kind": "ui-call",
"line": 809,
"path": "apps/android/app/src/main/java/ai/openclaw/app/ui/SettingsSheet.kt",
"source": "Show System Apps",
"surface": "android",
"id": "native.android.b035f039d963c268"
},
{
"kind": "ui-call",
"line": 811,
"path": "apps/android/app/src/main/java/ai/openclaw/app/ui/SettingsSheet.kt",
"source": "Include Android/system packages in results.",
"surface": "android",
"id": "native.android.1600a4884e2a41da"
},
{
"kind": "ui-call",
"line": 850,
"path": "apps/android/app/src/main/java/ai/openclaw/app/ui/SettingsSheet.kt",
"source": "Quiet Hours",
"surface": "android",
"id": "native.android.e2ef6b37990a691c"
},
{
"kind": "ui-call",
"line": 852,
"path": "apps/android/app/src/main/java/ai/openclaw/app/ui/SettingsSheet.kt",
"source": "Suppress forwarding during a local time window.",
"surface": "android",
"id": "native.android.a4f770f835357f6c"
},
{
"kind": "ui-call",
"line": 874,
"path": "apps/android/app/src/main/java/ai/openclaw/app/ui/SettingsSheet.kt",
"source": "Quiet Start (HH:mm)",
"surface": "android",
"id": "native.android.e1869f00aa2189c7"
},
{
"kind": "ui-call",
"line": 882,
"path": "apps/android/app/src/main/java/ai/openclaw/app/ui/SettingsSheet.kt",
"source": "Use 24-hour HH:mm format, for example 22:00.",
"surface": "android",
"id": "native.android.d608320273495697"
},
{
"kind": "ui-call",
"line": 891,
"path": "apps/android/app/src/main/java/ai/openclaw/app/ui/SettingsSheet.kt",
"source": "Quiet End (HH:mm)",
"surface": "android",
"id": "native.android.ddcfd9ba56cc1aeb"
},
{
"kind": "ui-call",
"line": 899,
"path": "apps/android/app/src/main/java/ai/openclaw/app/ui/SettingsSheet.kt",
"source": "Use 24-hour HH:mm format, for example 07:00.",
"surface": "android",
"id": "native.android.4921ed94f99d6e63"
},
{
"kind": "ui-call",
"line": 918,
"path": "apps/android/app/src/main/java/ai/openclaw/app/ui/SettingsSheet.kt",
"source": "Save Quiet Hours",
"surface": "android",
"id": "native.android.556d0a3c15366f05"
},
{
"kind": "ui-call",
"line": 926,
"path": "apps/android/app/src/main/java/ai/openclaw/app/ui/SettingsSheet.kt",
"source": "Max Events / Minute",
"surface": "android",
"id": "native.android.63501cb40aae19ea"
},
{
"kind": "ui-call",
"line": 944,
"path": "apps/android/app/src/main/java/ai/openclaw/app/ui/SettingsSheet.kt",
"source": "Save Rate",
"surface": "android",
"id": "native.android.cad16090254466f2"
},
{
"kind": "ui-call",
"line": 953,
"path": "apps/android/app/src/main/java/ai/openclaw/app/ui/SettingsSheet.kt",
"source": "Route Session Key (optional)",
"surface": "android",
"id": "native.android.a08855b4ebbbd10a"
},
{
"kind": "ui-call",
"line": 960,
"path": "apps/android/app/src/main/java/ai/openclaw/app/ui/SettingsSheet.kt",
"source": "Blank keeps notification events on this device's default notification route. Set a key only to pin forwarding into a different session.",
"surface": "android",
"id": "native.android.db352fa71ce1e349"
},
{
"kind": "ui-call",
"line": 982,
"path": "apps/android/app/src/main/java/ai/openclaw/app/ui/SettingsSheet.kt",
"source": "Save Session Route",
"surface": "android",
"id": "native.android.6051f516f0825a19"
},
{
"kind": "ui-call",
"line": 990,
"path": "apps/android/app/src/main/java/ai/openclaw/app/ui/SettingsSheet.kt",
"source": "DATA ACCESS",
"surface": "android",
"id": "native.android.908f014114bee723"
},
{
"kind": "ui-call",
"line": 1002,
"path": "apps/android/app/src/main/java/ai/openclaw/app/ui/SettingsSheet.kt",
"source": "Photos",
"surface": "android",
"id": "native.android.da5835468d89c5c7"
},
{
"kind": "ui-call",
"line": 1003,
"path": "apps/android/app/src/main/java/ai/openclaw/app/ui/SettingsSheet.kt",
"source": "Access recent photos.",
"surface": "android",
"id": "native.android.37cc59babec722da"
},
{
"kind": "ui-call",
"line": 1028,
"path": "apps/android/app/src/main/java/ai/openclaw/app/ui/SettingsSheet.kt",
"source": "Contacts",
"surface": "android",
"id": "native.android.7a5393132c797c82"
},
{
"kind": "ui-call",
"line": 1029,
"path": "apps/android/app/src/main/java/ai/openclaw/app/ui/SettingsSheet.kt",
"source": "Search and add contacts.",
"surface": "android",
"id": "native.android.2d3e44bab8c02b4e"
},
{
"kind": "ui-call",
"line": 1055,
"path": "apps/android/app/src/main/java/ai/openclaw/app/ui/SettingsSheet.kt",
"source": "Calendar",
"surface": "android",
"id": "native.android.be1a5598d23e8b99"
},
{
"kind": "ui-call",
"line": 1056,
"path": "apps/android/app/src/main/java/ai/openclaw/app/ui/SettingsSheet.kt",
"source": "Read and create events.",
"surface": "android",
"id": "native.android.dbd6257d7ed809a9"
},
{
"kind": "ui-call",
"line": 1083,
"path": "apps/android/app/src/main/java/ai/openclaw/app/ui/SettingsSheet.kt",
"source": "Call Log",
"surface": "android",
"id": "native.android.735879662e992dcd"
},
{
"kind": "ui-call",
"line": 1084,
"path": "apps/android/app/src/main/java/ai/openclaw/app/ui/SettingsSheet.kt",
"source": "Search recent call history.",
"surface": "android",
"id": "native.android.3c842261a945e43c"
},
{
"kind": "conditional-branch",
"line": 1098,
"path": "apps/android/app/src/main/java/ai/openclaw/app/ui/SettingsSheet.kt",
"source": "Grant",
"surface": "android",
"id": "native.android.16306ca8baec0e6e"
},
{
"kind": "conditional-branch",
"line": 1098,
"path": "apps/android/app/src/main/java/ai/openclaw/app/ui/SettingsSheet.kt",
"source": "Manage",
"surface": "android",
"id": "native.android.c5dd189c29c4983f"
},
{
"kind": "ui-call",
"line": 1110,
"path": "apps/android/app/src/main/java/ai/openclaw/app/ui/SettingsSheet.kt",
"source": "Motion",
"surface": "android",
"id": "native.android.475e97f2df757f8b"
},
{
"kind": "ui-call",
"line": 1111,
"path": "apps/android/app/src/main/java/ai/openclaw/app/ui/SettingsSheet.kt",
"source": "Track steps and activity.",
"surface": "android",
"id": "native.android.fa5092a818a770ab"
},
{
"kind": "ui-call",
"line": 1140,
"path": "apps/android/app/src/main/java/ai/openclaw/app/ui/SettingsSheet.kt",
"source": "LOCATION",
"surface": "android",
"id": "native.android.304217db5e6b3192"
},
{
"kind": "ui-call",
"line": 1151,
"path": "apps/android/app/src/main/java/ai/openclaw/app/ui/SettingsSheet.kt",
"source": "Off",
"surface": "android",
"id": "native.android.474bf9e9dd0bb1ab"
},
{
"kind": "ui-call",
"line": 1152,
"path": "apps/android/app/src/main/java/ai/openclaw/app/ui/SettingsSheet.kt",
"source": "Disable location sharing.",
"surface": "android",
"id": "native.android.7d3e67457d28228a"
},
{
"kind": "ui-call",
"line": 1164,
"path": "apps/android/app/src/main/java/ai/openclaw/app/ui/SettingsSheet.kt",
"source": "While Using",
"surface": "android",
"id": "native.android.5d60864f0652a0ab"
},
{
"kind": "ui-call",
"line": 1165,
"path": "apps/android/app/src/main/java/ai/openclaw/app/ui/SettingsSheet.kt",
"source": "Only while OpenClaw is open.",
"surface": "android",
"id": "native.android.3b2109ae1192e191"
},
{
"kind": "ui-call",
"line": 1177,
"path": "apps/android/app/src/main/java/ai/openclaw/app/ui/SettingsSheet.kt",
"source": "Precise Location",
"surface": "android",
"id": "native.android.b92944ae7d52f9e3"
},
{
"kind": "ui-call",
"line": 1178,
"path": "apps/android/app/src/main/java/ai/openclaw/app/ui/SettingsSheet.kt",
"source": "Use precise GPS when available.",
"surface": "android",
"id": "native.android.a8bc0f574f3563b0"
},
{
"kind": "ui-call",
"line": 1192,
"path": "apps/android/app/src/main/java/ai/openclaw/app/ui/SettingsSheet.kt",
"source": "PREFERENCES",
"surface": "android",
"id": "native.android.8c3caf6577f67577"
},
{
"kind": "ui-call",
"line": 1203,
"path": "apps/android/app/src/main/java/ai/openclaw/app/ui/SettingsSheet.kt",
"source": "Prevent Sleep",
"surface": "android",
"id": "native.android.a88b03a8b3e7a353"
},
{
"kind": "ui-call",
"line": 1204,
"path": "apps/android/app/src/main/java/ai/openclaw/app/ui/SettingsSheet.kt",
"source": "Keep screen awake while open.",
"surface": "android",
"id": "native.android.a347983646f16109"
},
{
"kind": "ui-call",
"line": 1211,
"path": "apps/android/app/src/main/java/ai/openclaw/app/ui/SettingsSheet.kt",
"source": "Debug Canvas",
"surface": "android",
"id": "native.android.95b2da6c6a76bb18"
},
{
"kind": "ui-call",
"line": 1212,
"path": "apps/android/app/src/main/java/ai/openclaw/app/ui/SettingsSheet.kt",
"source": "Show status overlay on canvas.",
"surface": "android",
"id": "native.android.9f346a677f3367e7"
},
{
"kind": "ui-named-argument",
"line": 437,
@@ -5907,7 +5371,7 @@
},
{
"kind": "ui-named-argument",
"line": 300,
"line": 322,
"path": "apps/android/app/src/main/java/ai/openclaw/app/ui/chat/ChatScreen.kt",
"source": "Chat needs attention",
"surface": "android",
@@ -5915,7 +5379,7 @@
},
{
"kind": "ui-named-argument",
"line": 482,
"line": 504,
"path": "apps/android/app/src/main/java/ai/openclaw/app/ui/chat/ChatScreen.kt",
"source": "All",
"surface": "android",
@@ -5923,7 +5387,7 @@
},
{
"kind": "ui-named-argument",
"line": 540,
"line": 562,
"path": "apps/android/app/src/main/java/ai/openclaw/app/ui/chat/ChatScreen.kt",
"source": "OpenClaw",
"surface": "android",
@@ -5931,7 +5395,7 @@
},
{
"kind": "ui-named-argument",
"line": 561,
"line": 583,
"path": "apps/android/app/src/main/java/ai/openclaw/app/ui/chat/ChatScreen.kt",
"source": "New chat",
"surface": "android",
@@ -5939,7 +5403,7 @@
},
{
"kind": "ui-named-argument",
"line": 566,
"line": 588,
"path": "apps/android/app/src/main/java/ai/openclaw/app/ui/chat/ChatScreen.kt",
"source": "More new chat options",
"surface": "android",
@@ -5947,7 +5411,7 @@
},
{
"kind": "ui-named-argument",
"line": 581,
"line": 603,
"path": "apps/android/app/src/main/java/ai/openclaw/app/ui/chat/ChatScreen.kt",
"source": "Refresh chat",
"surface": "android",
@@ -5955,7 +5419,7 @@
},
{
"kind": "ui-named-argument",
"line": 584,
"line": 606,
"path": "apps/android/app/src/main/java/ai/openclaw/app/ui/chat/ChatScreen.kt",
"source": "Chat",
"surface": "android",
@@ -5963,7 +5427,7 @@
},
{
"kind": "ui-named-argument",
"line": 732,
"line": 754,
"path": "apps/android/app/src/main/java/ai/openclaw/app/ui/chat/ChatScreen.kt",
"source": "Loading session",
"surface": "android",
@@ -5971,7 +5435,7 @@
},
{
"kind": "ui-named-argument",
"line": 759,
"line": 781,
"path": "apps/android/app/src/main/java/ai/openclaw/app/ui/chat/ChatScreen.kt",
"source": "Jump to latest",
"surface": "android",
@@ -5979,7 +5443,7 @@
},
{
"kind": "conditional-branch",
"line": 785,
"line": 807,
"path": "apps/android/app/src/main/java/ai/openclaw/app/ui/chat/ChatScreen.kt",
"source": "Ready when you are",
"surface": "android",
@@ -5987,7 +5451,7 @@
},
{
"kind": "ui-named-argument",
"line": 816,
"line": 838,
"path": "apps/android/app/src/main/java/ai/openclaw/app/ui/chat/ChatScreen.kt",
"source": "Fix connection",
"surface": "android",
@@ -5995,7 +5459,7 @@
},
{
"kind": "ui-named-argument",
"line": 817,
"line": 839,
"path": "apps/android/app/src/main/java/ai/openclaw/app/ui/chat/ChatScreen.kt",
"source": "Copy diagnostics",
"surface": "android",
@@ -6003,7 +5467,7 @@
},
{
"kind": "conditional-branch",
"line": 1001,
"line": 1023,
"path": "apps/android/app/src/main/java/ai/openclaw/app/ui/chat/ChatScreen.kt",
"source": "Preparing audio…",
"surface": "android",
@@ -6011,7 +5475,7 @@
},
{
"kind": "conditional-branch",
"line": 1001,
"line": 1023,
"path": "apps/android/app/src/main/java/ai/openclaw/app/ui/chat/ChatScreen.kt",
"source": "Speaking…",
"surface": "android",
@@ -6019,7 +5483,7 @@
},
{
"kind": "ui-named-argument",
"line": 1022,
"line": 1044,
"path": "apps/android/app/src/main/java/ai/openclaw/app/ui/chat/ChatScreen.kt",
"source": "Tools running",
"surface": "android",
@@ -6027,7 +5491,7 @@
},
{
"kind": "ui-named-argument",
"line": 1024,
"line": 1046,
"path": "apps/android/app/src/main/java/ai/openclaw/app/ui/chat/ChatScreen.kt",
"source": "OpenClaw is working",
"surface": "android",
@@ -6035,7 +5499,7 @@
},
{
"kind": "ui-named-argument",
"line": 1027,
"line": 1049,
"path": "apps/android/app/src/main/java/ai/openclaw/app/ui/chat/ChatScreen.kt",
"source": "+${toolCalls.size - 4} more",
"surface": "android",
@@ -6043,7 +5507,7 @@
},
{
"kind": "ui-named-argument",
"line": 1037,
"line": 1059,
"path": "apps/android/app/src/main/java/ai/openclaw/app/ui/chat/ChatScreen.kt",
"source": "Thinking",
"surface": "android",
@@ -6051,7 +5515,7 @@
},
{
"kind": "ui-named-argument",
"line": 1038,
"line": 1060,
"path": "apps/android/app/src/main/java/ai/openclaw/app/ui/chat/ChatScreen.kt",
"source": "OpenClaw is preparing a response.",
"surface": "android",
@@ -6059,7 +5523,7 @@
},
{
"kind": "ui-named-argument",
"line": 1200,
"line": 1222,
"path": "apps/android/app/src/main/java/ai/openclaw/app/ui/chat/ChatScreen.kt",
"source": "Stop",
"surface": "android",
@@ -6067,7 +5531,7 @@
},
{
"kind": "ui-named-argument",
"line": 1266,
"line": 1288,
"path": "apps/android/app/src/main/java/ai/openclaw/app/ui/chat/ChatScreen.kt",
"source": "Default",
"surface": "android",
@@ -6075,7 +5539,7 @@
},
{
"kind": "conditional-branch",
"line": 1332,
"line": 1354,
"path": "apps/android/app/src/main/java/ai/openclaw/app/ui/chat/ChatScreen.kt",
"source": "Pin model",
"surface": "android",
@@ -6083,7 +5547,7 @@
},
{
"kind": "conditional-branch",
"line": 1332,
"line": 1354,
"path": "apps/android/app/src/main/java/ai/openclaw/app/ui/chat/ChatScreen.kt",
"source": "Unpin model",
"surface": "android",
@@ -6091,7 +5555,7 @@
},
{
"kind": "ui-named-argument",
"line": 1349,
"line": 1371,
"path": "apps/android/app/src/main/java/ai/openclaw/app/ui/chat/ChatScreen.kt",
"source": "No commands found",
"surface": "android",
@@ -6099,7 +5563,7 @@
},
{
"kind": "ui-named-argument",
"line": 1411,
"line": 1433,
"path": "apps/android/app/src/main/java/ai/openclaw/app/ui/chat/ChatScreen.kt",
"source": "Gateway offline",
"surface": "android",
@@ -6107,7 +5571,7 @@
},
{
"kind": "ui-named-argument",
"line": 1511,
"line": 1533,
"path": "apps/android/app/src/main/java/ai/openclaw/app/ui/chat/ChatScreen.kt",
"source": "Attach image",
"surface": "android",
@@ -6115,7 +5579,7 @@
},
{
"kind": "ui-named-argument",
"line": 1546,
"line": 1568,
"path": "apps/android/app/src/main/java/ai/openclaw/app/ui/chat/ChatScreen.kt",
"source": "Message OpenClaw",
"surface": "android",
@@ -6123,7 +5587,7 @@
},
{
"kind": "ui-named-argument",
"line": 1562,
"line": 1584,
"path": "apps/android/app/src/main/java/ai/openclaw/app/ui/chat/ChatScreen.kt",
"source": "Open voice",
"surface": "android",
@@ -6131,7 +5595,7 @@
},
{
"kind": "ui-named-argument",
"line": 1611,
"line": 1633,
"path": "apps/android/app/src/main/java/ai/openclaw/app/ui/chat/ChatScreen.kt",
"source": "Remove attachment",
"surface": "android",
@@ -6139,7 +5603,7 @@
},
{
"kind": "conditional-branch",
"line": 1632,
"line": 1654,
"path": "apps/android/app/src/main/java/ai/openclaw/app/ui/chat/ChatScreen.kt",
"source": "Main",
"surface": "android",
@@ -6147,7 +5611,7 @@
},
{
"kind": "conditional-branch",
"line": 1640,
"line": 1662,
"path": "apps/android/app/src/main/java/ai/openclaw/app/ui/chat/ChatScreen.kt",
"source": "$emoji $name",
"surface": "android",
@@ -6155,7 +5619,7 @@
},
{
"kind": "ui-named-argument",
"line": 1699,
"line": 1721,
"path": "apps/android/app/src/main/java/ai/openclaw/app/ui/chat/ChatScreen.kt",
"source": "Send",
"surface": "android",
@@ -6163,20 +5627,12 @@
},
{
"kind": "conditional-branch",
"line": 1739,
"line": 1761,
"path": "apps/android/app/src/main/java/ai/openclaw/app/ui/chat/ChatScreen.kt",
"source": "$contextLabel · ${contextMeterThinkingLabel(thinkingLevel)}",
"surface": "android",
"id": "native.android.4e6c67df44d588d3"
},
{
"kind": "ui-named-argument",
"line": 297,
"path": "apps/android/app/src/main/java/ai/openclaw/app/ui/chat/ChatSheetContent.kt",
"source": "CHAT ERROR",
"surface": "android",
"id": "native.android.826804c0189e03d0"
},
{
"kind": "ui-named-argument",
"line": 109,
@@ -8,7 +8,6 @@ import kotlinx.serialization.json.buildJsonObject
internal object AndroidScreenshotFixture {
const val mainSessionKey = "agent:main:node-screenshot"
const val primarySessionTitle = "Android release planning"
const val chatReadyText = "Ready when you are"
val agents =
listOf(
@@ -279,7 +279,6 @@ data class GatewayConnectionProblem(
val minimumProbeProtocol: Int? = null,
) {
val isPairingRequired: Boolean = code == "PAIRING_REQUIRED"
val isProtocolMismatch: Boolean = code == "PROTOCOL_MISMATCH"
val canAutoRetry: Boolean =
isPairingRequired &&
(
@@ -48,10 +48,6 @@ data class ToolDisplaySummary(
if (!detail.isNullOrBlank()) parts.add(detail)
return if (parts.isEmpty()) null else parts.joinToString(" · ")
}
/** Single-line fallback for compact tool rows that do not render detail separately. */
val summaryLine: String
get() = if (detailLine != null) "$emoji $label: $detailLine" else "$emoji $label"
}
/** Resolves tool-call names and args into user-facing Android display text. */
@@ -24,12 +24,6 @@ internal fun openClawAndroidVersionLabel(): String {
/** Normalizes blank gateway status text for display and diagnostics copy. */
internal fun gatewayStatusForDisplay(statusText: String): String = statusText.trim().ifEmpty { "Offline" }
/** Returns true when the status has enough signal to show diagnostics affordances. */
internal fun gatewayStatusHasDiagnostics(statusText: String): Boolean {
val lower = gatewayStatusForDisplay(statusText).lowercase()
return lower != "offline" && !lower.contains("connecting")
}
/** Resolves the best non-secret endpoint label available to diagnostics surfaces. */
internal fun gatewayDiagnosticsEndpoint(
remoteAddress: String?,
@@ -122,7 +122,6 @@ internal object MobileColorsAccessor {
// Keep these accessors while screens migrate to `MobileColorsAccessor.current`.
// Each getter must stay composable so callers always read the active theme.
internal val mobileSurface: Color @Composable get() = LocalMobileColors.current.surface
internal val mobileSurfaceStrong: Color @Composable get() = LocalMobileColors.current.surfaceStrong
internal val mobileCardSurface: Color @Composable get() = LocalMobileColors.current.cardSurface
internal val mobileBorder: Color @Composable get() = LocalMobileColors.current.border
internal val mobileBorderStrong: Color @Composable get() = LocalMobileColors.current.borderStrong
@@ -132,8 +131,6 @@ internal val mobileTextTertiary: Color @Composable get() = LocalMobileColors.cur
internal val mobileAccent: Color @Composable get() = LocalMobileColors.current.accent
internal val mobileAccentSoft: Color @Composable get() = LocalMobileColors.current.accentSoft
internal val mobileAccentBorderStrong: Color @Composable get() = LocalMobileColors.current.accentBorderStrong
internal val mobileSuccess: Color @Composable get() = LocalMobileColors.current.success
internal val mobileSuccessSoft: Color @Composable get() = LocalMobileColors.current.successSoft
internal val mobileWarning: Color @Composable get() = LocalMobileColors.current.warning
internal val mobileWarningSoft: Color @Composable get() = LocalMobileColors.current.warningSoft
internal val mobileDanger: Color @Composable get() = LocalMobileColors.current.danger
@@ -141,7 +138,6 @@ internal val mobileDangerSoft: Color @Composable get() = LocalMobileColors.curre
internal val mobileCodeBg: Color @Composable get() = LocalMobileColors.current.codeBg
internal val mobileCodeText: Color @Composable get() = LocalMobileColors.current.codeText
internal val mobileCodeBorder: Color @Composable get() = LocalMobileColors.current.codeBorder
internal val mobileCodeAccent: Color @Composable get() = LocalMobileColors.current.codeAccent
// Build the page backdrop from semantic surfaces so light/dark palettes keep
// their contrast relationship without duplicating raw color stops.
@@ -165,15 +161,6 @@ internal val mobileFontFamily =
Font(resId = R.font.manrope_700_bold, weight = FontWeight.Bold),
)
internal val mobileTitle1 =
TextStyle(
fontFamily = mobileFontFamily,
fontWeight = FontWeight.SemiBold,
fontSize = 24.sp,
lineHeight = 30.sp,
letterSpacing = (-0.5).sp,
)
internal val mobileHeadline =
TextStyle(
fontFamily = mobileFontFamily,
File diff suppressed because it is too large Load Diff
@@ -106,6 +106,28 @@ import java.util.Date
import java.util.Locale
import kotlin.math.roundToInt
/** Returns a pending assistant prompt only when chat can accept it immediately. */
internal fun resolvePendingAssistantAutoSend(
pendingPrompt: String?,
healthOk: Boolean,
pendingRunCount: Int,
): String? {
val prompt = pendingPrompt?.trim()?.ifEmpty { null } ?: return null
if (!healthOk || pendingRunCount > 0) return null
return prompt
}
/** Chooses the session key to load for initial chat hydration, if any. */
internal fun resolveInitialChatLoadSessionKey(
sessionKey: String,
mainSessionKey: String,
): String? {
val current = sessionKey.trim()
val main = mainSessionKey.trim().ifEmpty { "main" }
if (current.isNotEmpty() && current != "main" && current != main) return null
return main
}
/** Full chat surface that wires MainViewModel state to messages, attachments, voice, and composer actions. */
@Composable
fun ChatScreen(
@@ -1,304 +0,0 @@
package ai.openclaw.app.ui.chat
import ai.openclaw.app.MainViewModel
import ai.openclaw.app.chat.ChatSessionEntry
import ai.openclaw.app.ui.mobileAccent
import ai.openclaw.app.ui.mobileAccentBorderStrong
import ai.openclaw.app.ui.mobileBorderStrong
import ai.openclaw.app.ui.mobileCallout
import ai.openclaw.app.ui.mobileCaption1
import ai.openclaw.app.ui.mobileCaption2
import ai.openclaw.app.ui.mobileCardSurface
import ai.openclaw.app.ui.mobileDanger
import ai.openclaw.app.ui.mobileDangerSoft
import ai.openclaw.app.ui.mobileText
import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.result.contract.ActivityResultContracts
import androidx.compose.foundation.BorderStroke
import androidx.compose.foundation.horizontalScroll
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.imePadding
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateListOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
/** Returns a pending assistant prompt only when chat can accept it immediately. */
internal fun resolvePendingAssistantAutoSend(
pendingPrompt: String?,
healthOk: Boolean,
pendingRunCount: Int,
): String? {
val prompt = pendingPrompt?.trim()?.ifEmpty { null } ?: return null
if (!healthOk || pendingRunCount > 0) return null
return prompt
}
/** Chooses the session key to load for initial chat hydration, if any. */
internal fun resolveInitialChatLoadSessionKey(
sessionKey: String,
mainSessionKey: String,
): String? {
val current = sessionKey.trim()
val main = mainSessionKey.trim().ifEmpty { "main" }
if (current.isNotEmpty() && current != "main" && current != main) return null
return main
}
/** Main Android chat sheet content: session picker, message list, and composer. */
@Composable
fun ChatSheetContent(viewModel: MainViewModel) {
val messages by viewModel.chatMessages.collectAsState()
val historyLoading by viewModel.chatHistoryLoading.collectAsState()
val errorText by viewModel.chatError.collectAsState()
val pendingRunCount by viewModel.pendingRunCount.collectAsState()
val healthOk by viewModel.chatHealthOk.collectAsState()
val sessionKey by viewModel.chatSessionKey.collectAsState()
val mainSessionKey by viewModel.mainSessionKey.collectAsState()
val thinkingLevel by viewModel.chatThinkingLevel.collectAsState()
val selectedModelRef by viewModel.chatSelectedModelRef.collectAsState()
// Gate from the controller's agent-scoped catalog — the same source the send gate reads —
// so the visible control can never disagree with what sends actually carry.
val modelCatalog by viewModel.chatModelCatalog.collectAsState()
val thinkingSupported = thinkingSupportedForSelection(selectedModelRef, modelCatalog)
val streamingAssistantText by viewModel.chatStreamingAssistantText.collectAsState()
val pendingToolCalls by viewModel.chatPendingToolCalls.collectAsState()
val sessions by viewModel.chatSessions.collectAsState()
val chatCommands by viewModel.chatCommands.collectAsState()
val chatDraft by viewModel.chatDraft.collectAsState()
val pendingAssistantAutoSend by viewModel.pendingAssistantAutoSend.collectAsState()
val assistantAutoSendInFlight by viewModel.assistantAutoSendInFlight.collectAsState()
val outboxItems by viewModel.chatOutboxItems.collectAsState()
val messageSpeechState by viewModel.chatMessageSpeech.collectAsState()
val gatewayConnectionDisplay by viewModel.gatewayConnectionDisplay.collectAsState()
val gatewayOffline = !gatewayConnectionDisplay.isConnected
val micEnabled by viewModel.micEnabled.collectAsState()
val micIsListening by viewModel.micIsListening.collectAsState()
val micCooldown by viewModel.micCooldown.collectAsState()
val talkModeEnabled by viewModel.talkModeEnabled.collectAsState()
val talkModeListening by viewModel.talkModeListening.collectAsState()
DisposableEffect(viewModel) {
onDispose(viewModel::stopChatMessageSpeech)
}
LaunchedEffect(Unit) {
val loadSessionKey = resolveInitialChatLoadSessionKey(sessionKey, mainSessionKey)
if (loadSessionKey != null) {
viewModel.loadChat(loadSessionKey)
}
viewModel.refreshChatCommands()
}
LaunchedEffect(pendingAssistantAutoSend, assistantAutoSendInFlight, healthOk, pendingRunCount, thinkingLevel) {
// Assistant-launch prompts should wait for a healthy idle chat so they do
// not race an already-running turn.
if (!healthOk) return@LaunchedEffect
val prompt =
resolvePendingAssistantAutoSend(
pendingPrompt = pendingAssistantAutoSend,
healthOk = healthOk,
pendingRunCount = pendingRunCount,
) ?: return@LaunchedEffect
viewModel.dispatchPendingAssistantAutoSend(
pendingPrompt = prompt,
thinking = thinkingLevel,
)
}
val context = LocalContext.current
val resolver = context.contentResolver
val scope = rememberCoroutineScope()
val attachments = remember { mutableStateListOf<PendingAttachment>() }
val micCaptureActive = micEnabled || micIsListening || micCooldown || talkModeEnabled || talkModeListening
val voiceNoteRecorder =
rememberVoiceNoteRecorderController(
viewModel = viewModel,
onFinished = attachments::add,
)
val voiceNoteState by voiceNoteRecorder.state.collectAsState()
val voiceNoteElapsedMs by voiceNoteRecorder.elapsedMs.collectAsState()
val pickImages =
rememberLauncherForActivityResult(ActivityResultContracts.GetMultipleContents()) { uris ->
if (uris.isNullOrEmpty()) return@rememberLauncherForActivityResult
scope.launch(Dispatchers.IO) {
// Bound both count and encoded size before attachments enter Compose
// state; sending uses these already-compressed payloads directly.
val next =
uris.take(8).mapNotNull { uri ->
try {
loadSizedImageAttachment(resolver, uri)
} catch (_: Throwable) {
null
}
}
withContext(Dispatchers.Main) {
attachments.addAll(next)
}
}
}
Column(
modifier =
Modifier
.fillMaxSize()
.padding(horizontal = 20.dp, vertical = 12.dp),
verticalArrangement = Arrangement.spacedBy(8.dp),
) {
ChatThreadSelector(
sessionKey = sessionKey,
sessions = sessions,
mainSessionKey = mainSessionKey,
onSelectSession = { key -> viewModel.switchChatSession(key) },
)
if (!errorText.isNullOrBlank()) {
ChatErrorRail(errorText = errorText!!)
}
ChatMessageListCard(
sessionKey = sessionKey,
messages = messages,
historyLoading = historyLoading,
pendingRunCount = pendingRunCount,
pendingToolCalls = pendingToolCalls,
streamingAssistantText = streamingAssistantText,
healthOk = healthOk,
gatewayOffline = gatewayOffline,
modifier = Modifier.weight(1f, fill = true),
outboxItems =
outboxItemsForSession(
items = outboxItems,
sessionKey = sessionKey,
mainSessionKey = mainSessionKey,
),
onRetryOutbox = viewModel::retryChatOutboxCommand,
onDeleteOutbox = viewModel::deleteChatOutboxCommand,
onReplyMessage = viewModel::setChatReplyDraft,
speechState = messageSpeechState,
onToggleListen = viewModel::toggleChatMessageSpeech,
)
Row(modifier = Modifier.fillMaxWidth().imePadding()) {
ChatComposer(
draftText = chatDraft,
healthOk = healthOk,
thinkingLevel = thinkingLevel,
thinkingSupported = thinkingSupported,
pendingRunCount = pendingRunCount,
commands = chatCommands,
attachments = attachments,
onDraftApplied = viewModel::clearChatDraft,
onPickImages = { pickImages.launch("image/*") },
onRemoveAttachment = { id -> attachments.removeAll { it.id == id } },
voiceNoteState = voiceNoteState,
voiceNoteElapsedMs = voiceNoteElapsedMs,
recordVoiceNoteEnabled = pendingRunCount == 0 && !micCaptureActive,
onStartVoiceNote = { scope.launch { voiceNoteRecorder.start() } },
onCancelVoiceNote = voiceNoteRecorder::cancel,
onFinishVoiceNote = voiceNoteRecorder::finish,
onSetThinkingLevel = { level -> viewModel.setChatThinkingLevel(level) },
onRefresh = {
viewModel.refreshChat()
viewModel.refreshChatSessions(limit = 200)
viewModel.refreshChatCommands()
},
onAbort = { viewModel.abortChat() },
onSend = { text ->
val outgoing = attachments.map(PendingAttachment::toOutgoingAttachment)
val pendingAttachments = attachments.toList()
attachments.clear()
val accepted = viewModel.sendChatAwaitAcceptance(message = text, thinking = thinkingLevel, attachments = outgoing)
if (!accepted && attachments.isEmpty()) {
// Refused sends must not silently drop selected attachments either.
attachments.addAll(pendingAttachments)
}
accepted
},
)
}
}
}
@Composable
private fun ChatThreadSelector(
sessionKey: String,
sessions: List<ChatSessionEntry>,
mainSessionKey: String,
onSelectSession: (String) -> Unit,
) {
val sessionOptions =
remember(sessionKey, sessions, mainSessionKey) {
resolveSessionChoices(sessionKey, sessions, mainSessionKey = mainSessionKey)
}
Row(
modifier = Modifier.fillMaxWidth().horizontalScroll(rememberScrollState()),
horizontalArrangement = Arrangement.spacedBy(8.dp),
) {
for (entry in sessionOptions) {
val active = entry.key == sessionKey
Surface(
onClick = { onSelectSession(entry.key) },
shape = RoundedCornerShape(14.dp),
color = if (active) mobileAccent else mobileCardSurface,
border = BorderStroke(1.dp, if (active) mobileAccentBorderStrong else mobileBorderStrong),
tonalElevation = 0.dp,
shadowElevation = 0.dp,
) {
Text(
text = friendlySessionName(entry.displayName ?: entry.key),
style = mobileCaption1.copy(fontWeight = if (active) FontWeight.Bold else FontWeight.SemiBold),
color = if (active) Color.White else mobileText,
maxLines = 1,
overflow = TextOverflow.Ellipsis,
modifier = Modifier.padding(horizontal = 12.dp, vertical = 8.dp),
)
}
}
}
}
@Composable
private fun ChatErrorRail(errorText: String) {
Surface(
modifier = Modifier.fillMaxWidth(),
color = mobileDangerSoft,
shape = RoundedCornerShape(12.dp),
border = androidx.compose.foundation.BorderStroke(1.dp, mobileDanger),
) {
Column(modifier = Modifier.padding(horizontal = 10.dp, vertical = 8.dp), verticalArrangement = Arrangement.spacedBy(2.dp)) {
Text(
text = "CHAT ERROR",
style = mobileCaption2.copy(letterSpacing = 0.6.sp),
color = mobileDanger,
)
Text(text = errorText, style = mobileCallout, color = mobileText)
}
}
}
@@ -3,7 +3,7 @@ package ai.openclaw.app.ui
import org.junit.Assert.assertEquals
import org.junit.Test
class SettingsSheetNotificationAppsTest {
class SettingsScreensNotificationAppsTest {
@Test
fun resolveNotificationCandidatePackages_keepsConfiguredPackagesVisible() {
val packages =
@@ -7,7 +7,7 @@ import org.junit.Assert.assertNull
import org.junit.Assert.assertTrue
import org.junit.Test
class ChatSheetContentTest {
class ChatScreenTest {
@Test
fun agentChipUsesEmojiAndFallsBackToId() {
assertEquals(
+5 -5
View File
@@ -104,10 +104,10 @@ Hard rule: avoid ultra-thin weights on light backgrounds.
## 11. Source Of Truth
- `app/src/main/java/ai/openclaw/android/ui/OpenClawTheme.kt`
- `app/src/main/java/ai/openclaw/android/ui/OnboardingFlow.kt`
- `app/src/main/java/ai/openclaw/android/ui/RootScreen.kt`
- `app/src/main/java/ai/openclaw/android/ui/SettingsSheet.kt`
- `app/src/main/java/ai/openclaw/android/MainViewModel.kt`
- `app/src/main/java/ai/openclaw/app/ui/OpenClawTheme.kt`
- `app/src/main/java/ai/openclaw/app/ui/OnboardingFlow.kt`
- `app/src/main/java/ai/openclaw/app/ui/RootScreen.kt`
- `app/src/main/java/ai/openclaw/app/ui/SettingsScreens.kt`
- `app/src/main/java/ai/openclaw/app/MainViewModel.kt`
If style and implementation diverge, update both in the same change.