Skip to content

Commit 9a4fac9

Browse files
committed
feat: support non-ascii characters in io text
1 parent 09a027e commit 9a4fac9

File tree

1 file changed

+69
-3
lines changed

1 file changed

+69
-3
lines changed

devices/android.go

Lines changed: 69 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package devices
22

33
import (
44
"context"
5+
"encoding/base64"
56
"encoding/xml"
67
"fmt"
78
"os"
@@ -608,17 +609,82 @@ func (d *AndroidDevice) PressButton(key string) error {
608609
return nil
609610
}
610611

612+
// isDeviceKitInstalled checks if DeviceKit is installed on the device
613+
func (d *AndroidDevice) isDeviceKitInstalled() bool {
614+
appPath, err := d.GetAppPath("com.mobilenext.devicekit")
615+
return err == nil && appPath != ""
616+
}
617+
618+
// isAscii checks if text contains only ASCII characters
619+
func isAscii(text string) bool {
620+
for _, char := range text {
621+
if char > 127 {
622+
return false
623+
}
624+
}
625+
return true
626+
}
627+
628+
// escapeShellText escapes shell special characters
629+
func escapeShellText(text string) string {
630+
// escape all shell special characters that could be used for injection
631+
specialChars := `\'"`+ "`" + `
632+
|&;()<>{}[]$*?`
633+
result := ""
634+
for _, char := range text {
635+
if strings.ContainsRune(specialChars, char) {
636+
result += "\\"
637+
}
638+
result += string(char)
639+
}
640+
return result
641+
}
642+
611643
func (d *AndroidDevice) SendKeys(text string) error {
644+
if text == "" {
645+
// bailing early, so we don't run adb shell with empty string.
646+
// this happens when you prompt with a simple "submit".
647+
return nil
648+
}
649+
612650
switch text {
613651
case "\b":
614652
return d.PressButton("BACKSPACE")
615653
case "\n":
616654
return d.PressButton("ENTER")
617655
}
618656

619-
text = strings.ReplaceAll(text, " ", "\\ ")
620-
_, err := d.runAdbCommand("shell", "input", "text", text)
621-
return err
657+
if isAscii(text) {
658+
// adb shell input only supports ascii characters. and
659+
// some of the keys have to be escaped.
660+
escapedText := escapeShellText(text)
661+
_, err := d.runAdbCommand("shell", "input", "text", escapedText)
662+
return err
663+
}
664+
665+
// try sending over clipboard if DeviceKit is installed
666+
if d.isDeviceKitInstalled() {
667+
// encode text as base64
668+
base64Text := base64.StdEncoding.EncodeToString([]byte(text))
669+
670+
// send clipboard over and immediately paste it
671+
_, err := d.runAdbCommand("shell", "am", "broadcast", "-a", "devicekit.clipboard.set", "-e", "encoding", "base64", "-e", "text", base64Text, "-n", "com.mobilenext.devicekit/.ClipboardBroadcastReceiver")
672+
if err != nil {
673+
return fmt.Errorf("failed to set clipboard: %w", err)
674+
}
675+
676+
_, err = d.runAdbCommand("shell", "input", "keyevent", "KEYCODE_PASTE")
677+
if err != nil {
678+
return fmt.Errorf("failed to paste: %w", err)
679+
}
680+
681+
// clear clipboard when we're done
682+
_, _ = d.runAdbCommand("shell", "am", "broadcast", "-a", "devicekit.clipboard.clear", "-n", "com.mobilenext.devicekit/.ClipboardBroadcastReceiver")
683+
684+
return nil
685+
}
686+
687+
return fmt.Errorf("non-ASCII text is not supported on Android, please install mobilenext devicekit, see https://github.com/mobile-next/devicekit-android")
622688
}
623689

624690
func (d *AndroidDevice) OpenURL(url string) error {

0 commit comments

Comments
 (0)