feat: implement robust multi-tier text input architecture and ADB diagnostics
- Android Text Input & IME Normalization:
- Implement progressive 3-tier input architecture in AndroidAdbDriver and AndroidDeviceController:
- Tier 1 (Primary): Zero-dependency clipboard injection + KEYCODE_PASTE (immune to IME Pinyin interception, full Unicode/Emoji support, preserves multiline formatting).
- Tier 2 (Broadcast): Base64 broadcast injection when ADBKeyboard is active.
- Tier 3 (Fallback): Native ADB physical keyevent fallback.
- Add literal newline normalization (\r\n, \n, \r) to prevent JSON escape string corruption (e.g. '10086\n' -> '10086、n').
- Add set_clipboard method to UIAutomatorClient without auxiliary APK requirements.
- Diagnostics & System Probes:
- Add adb_keys diagnostics probe for ADB key auth checks.
- Integrate diagnostics into CLI doctor command.
- Admin Console & Showcase UI:
- Improve model service and system router in admin console.
- Enhance workspace stream view and session state in showcase UI.
This commit is contained in:
1 parent
42b8988661
commit
ea3708a98f
19 files changed
+785
-89
No files matched your search
@@ -156,22 +156,46 @@ class AndroidDeviceController(MobileDeviceController):
|
||||
|
||||
async def input_text(self, text: str) -> bool:
|
||||
try:
|
||||
# Fast path for ASCII text: Use ADB directly.
|
||||
# This avoids toggling FastInputIME, which hides the keyboard and can cause focus loss.
|
||||
is_ascii = all(ord(c) < 128 for c in text)
|
||||
if is_ascii:
|
||||
return self._input_text_adb_fallback(text)
|
||||
norm_text = (
|
||||
text.replace(r"\r\n", "\n")
|
||||
.replace(r"\n", "\n")
|
||||
.replace(r"\r", "\n")
|
||||
)
|
||||
|
||||
self.ui_adb_client.send_text(text)
|
||||
return True
|
||||
# 1. Tier 1: Try clipboard injection + KEYCODE_PASTE (Zero IME interference, preserves multiline, works for all charsets)
|
||||
if self.ui_adb_client:
|
||||
try:
|
||||
set_clip_ok = False
|
||||
if hasattr(self.ui_adb_client, "set_clipboard"):
|
||||
set_clip_ok = self.ui_adb_client.set_clipboard(norm_text)
|
||||
elif hasattr(self.ui_adb_client, "_device") and self.ui_adb_client._device:
|
||||
self.ui_adb_client._device.set_clipboard(norm_text)
|
||||
set_clip_ok = True
|
||||
elif hasattr(self.ui_adb_client, "_ensure_connected"):
|
||||
dev = self.ui_adb_client._ensure_connected()
|
||||
dev.set_clipboard(norm_text)
|
||||
set_clip_ok = True
|
||||
|
||||
if set_clip_ok:
|
||||
self.device.shell("input keyevent 279")
|
||||
return True
|
||||
except Exception as e:
|
||||
logger.debug(f"Clipboard paste fallback: {e}")
|
||||
|
||||
return self._input_text_adb_fallback(norm_text)
|
||||
except Exception as e:
|
||||
logger.warning(f"UIAutomator2 send_text failed: {e}, falling back to ADB shell")
|
||||
return self._input_text_adb_fallback(text)
|
||||
logger.error(f"Failed to input text: {e}")
|
||||
return False
|
||||
|
||||
def _input_text_adb_fallback(self, text: str) -> bool:
|
||||
"""Fallback method using ADB shell input text command."""
|
||||
try:
|
||||
lines = text.split("\n")
|
||||
norm_text = (
|
||||
text.replace(r"\r\n", "\n")
|
||||
.replace(r"\n", "\n")
|
||||
.replace(r"\r", "\n")
|
||||
)
|
||||
lines = norm_text.split("\n")
|
||||
for line_idx, line in enumerate(lines):
|
||||
if line_idx > 0:
|
||||
self.device.shell("input keyevent 66")
|
||||
|
||||
Reference in new issue
Block a user