@@ -2,6 +2,7 @@ package devices
22
33import (
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+
611643func (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
624690func (d * AndroidDevice ) OpenURL (url string ) error {
0 commit comments