package libs import ( "encoding/base64" "fmt" "regexp" "strings" "time" ) // ---------- 输入法功能 ---------- // 输入法相关常量 const ( // imeID AdbKeyboard 输入法的标识符 imeID = "com.github.uiautomator/.AdbKeyboard" // broadcastResultOK 广播成功返回码 broadcastResultOK = -1 ) // BroadcastResult 广播命令的返回结果 type BroadcastResult struct { Code int // 结果码,-1 表示成功 Data string // 返回数据 } // InputMethod 提供输入法相关操作 // 通过 AdbKeyboard 输入法实现快速文本输入 type InputMethod struct { device *Device } // NewInputMethod 创建输入法操作实例 func NewInputMethod(device *Device) *InputMethod { return &InputMethod{device: device} } // CurrentIME 获取当前活动的输入法 func (im *InputMethod) CurrentIME() (string, error) { resp, err := im.device.Shell("settings", "get", "secure", "default_input_method") if err != nil { return "", err } return strings.TrimSpace(resp.Output), nil } // SetInputIME 启用或禁用 AdbKeyboard 输入法 func (im *InputMethod) SetInputIME(enable bool) error { if !enable { _, err := im.device.Shell("ime", "disable", imeID) return err } // 检查是否已经设置为当前输入法 current, err := im.CurrentIME() if err == nil && current == imeID { return nil } // 检查是否已安装 if !im.IsInstalled() { return &InputIMEError{Message: "AdbKeyboard 输入法未安装,请先安装 app-uiautomator.apk"} } // 启用并设置为默认输入法 im.device.Shell("ime", "enable", imeID) im.device.Shell("ime", "set", imeID) im.device.Shell("settings", "put", "secure", "default_input_method", imeID) // 等待输入法就绪 return im.waitReady() } // IsInstalled 检查 AdbKeyboard 输入法是否已安装 func (im *InputMethod) IsInstalled() bool { list, _ := im.getIMEList() for _, id := range list { if id == imeID { return true } } return false } // getIMEList 获取设备上所有输入法列表 func (im *InputMethod) getIMEList() ([]string, error) { resp, err := im.device.Shell("ime", "list", "-s") if err != nil { return nil, err } lines := strings.Split(strings.TrimSpace(resp.Output), "\n") var result []string for _, line := range lines { line = strings.TrimSpace(line) if line != "" { result = append(result, line) } } return result, nil } // waitReady 等待输入法就绪 func (im *InputMethod) waitReady() error { for i := 0; i < 10; i++ { current, err := im.CurrentIME() if err == nil && current == imeID { return nil } time.Sleep(300 * time.Millisecond) } return &InputIMEError{Message: "等待输入法就绪超时"} } // broadcast 发送广播命令 func (im *InputMethod) broadcast(action string, extras map[string]string) (*BroadcastResult, error) { args := []string{"am", "broadcast", "-a", action} for k, v := range extras { args = append(args, "--es", k, v) } resp, err := im.device.Shell(args...) if err != nil { return nil, err } // 解析返回结果 // 格式: result=-1 data="success" result := &BroadcastResult{Code: 0} reResult := regexp.MustCompile(`result=(-?\d+)`) reData := regexp.MustCompile(`data="([^"]+)"`) if m := reResult.FindStringSubmatch(resp.Output); len(m) > 1 { fmt.Sscanf(m[1], "%d", &result.Code) } if m := reData.FindStringSubmatch(resp.Output); len(m) > 1 { result.Data = m[1] } return result, nil } // mustBroadcast 发送广播并确保成功 func (im *InputMethod) mustBroadcast(action string, extras map[string]string) error { var lastErr error for i := 0; i < 3; i++ { result, err := im.broadcast(action, extras) if err != nil { lastErr = err time.Sleep(time.Duration(1000+i*500) * time.Millisecond) continue } if result.Code == broadcastResultOK { return nil } lastErr = fmt.Errorf("广播 %s 失败: code=%d data=%s", action, result.Code, result.Data) time.Sleep(time.Duration(1000+i*500) * time.Millisecond) } return lastErr } // SendKeys 通过 AdbKeyboard 输入法输入文本 // 自动启用输入法并在输入完成后隐藏键盘 func (im *InputMethod) SendKeys(text string) error { if err := im.SetInputIME(true); err != nil { return err } // Base64 编码文本 encoded := base64.StdEncoding.EncodeToString([]byte(text)) // 发送文本输入广播 if err := im.mustBroadcast("ADB_KEYBOARD_INPUT_TEXT", map[string]string{ "text": encoded, }); err != nil { return err } // 隐藏键盘 im.mustBroadcast("ADB_KEYBOARD_HIDE", nil) return nil } // SendAction 模拟输入法编辑器动作 // code 为动作代码: // // "go"/"search"/"send"/"next"/"done"/"previous" 或数字 func (im *InputMethod) SendAction(code string) error { if err := im.SetInputIME(true); err != nil { return err } // 将名称转换为代码 actionCodes := map[string]string{ "go": "2", "search": "3", "send": "4", "next": "5", "done": "6", "previous": "7", } codeStr := code if mapped, ok := actionCodes[strings.ToLower(code)]; ok { codeStr = mapped } return im.mustBroadcast("ADB_KEYBOARD_EDITOR_CODE", map[string]string{ "code": codeStr, }) } // ClearText 通过输入法清除文本 func (im *InputMethod) ClearText() error { if err := im.SetInputIME(true); err != nil { return err } return im.mustBroadcast("ADB_KEYBOARD_CLEAR_TEXT", nil) }