Как устроен input и что он умеет:
Бинарь живёт на устройстве, все вызовы идут через adb shell. Запуск без аргументов печатает справку, и это первое, что надо сделать на новой прошивке, потому что набор подкоманд зависит от версии Android.
Код: Выделить всё
$ adb shell input
Usage: input [<source>] [-d DISPLAY_ID] <command> [<arg>...]
...
The commands and default sources are:
text <string> (Default: touchscreen)
keyevent [--longpress|--doubletap] <key code number or name> ... (Default: keyboard)
tap <x> <y> (Default: touchscreen)
swipe <x1> <y1> <x2> <y2> [duration(ms)] (Default: touchscreen)
draganddrop <x1> <y1> <x2> <y2> [duration(ms)] (Default: touchscreen)
motionevent <DOWN|UP|MOVE|CANCEL> <x> <y> (Default: touchscreen)
keycombination [-t duration(ms)] <key code 1> <key code 2> ... (Default: keyboard)
input keyevent, коды клавиш:
keyevent шлёт нажатие одной клавиши. Принимает числовой код или символьное имя из класса KeyEvent, причём имя работает и с префиксом KEYCODE_, и без него.
Код: Выделить всё
adb shell input keyevent 4
adb shell input keyevent KEYCODE_BACK
adb shell input keyevent BACK
Код: Выделить всё
3 KEYCODE_HOME домой
4 KEYCODE_BACK назад
24 KEYCODE_VOLUME_UP громкость плюс
25 KEYCODE_VOLUME_DOWN громкость минус
26 KEYCODE_POWER кнопка питания (переключает экран!)
61 KEYCODE_TAB следующее поле формы
66 KEYCODE_ENTER ввод
67 KEYCODE_DEL backspace
82 KEYCODE_MENU меню
85 KEYCODE_MEDIA_PLAY_PAUSE
187 KEYCODE_APP_SWITCH недавние приложения
223 KEYCODE_SLEEP только гасит экран
224 KEYCODE_WAKEUP только будит экран
В один вызов можно передать несколько кодов, они отработают последовательно:
Код: Выделить всё
adb shell input keyevent 25 25 25
Код: Выделить всё
adb shell input keyevent --longpress KEYCODE_POWER
Модификаторы и комбинации:
Послать Ctrl+A двумя отдельными keyevent не выйдет: каждый вызов это полный цикл нажал-отпустил, модификатор успевает отжаться до следующей клавиши. До Android 13 комбинации делали только через sendevent или scrcpy. С Android 13 есть keycombination:
Код: Выделить всё
adb shell input keycombination KEYCODE_CTRL_LEFT KEYCODE_A
adb shell input keycombination KEYCODE_ALT_LEFT KEYCODE_TAB
adb shell input keycombination KEYCODE_POWER KEYCODE_VOLUME_DOWN
input tap и input swipe:
tap жмёт в точку экрана. Координаты в пикселях от левого верхнего угла в текущей ориентации, размер экрана узнаём так (подробно про wm в главе 13):
Код: Выделить всё
$ adb shell wm size
Physical size: 1080x2400
Код: Выделить всё
adb shell input tap 540 1200
swipe ведёт палец из точки в точку, последний аргумент это длительность в миллисекундах:
Код: Выделить всё
adb shell input swipe 540 1800 540 600 300
adb shell input swipe 100 960 980 960 80
Долгое нажатие делается свайпом в ту же самую точку:
Код: Выделить всё
adb shell input swipe 540 1200 540 1200 800
Код: Выделить всё
adb shell input draganddrop 540 1200 200 600 1000
input text:
Печатает строку в сфокусированное поле. Сначала tap по полю, потом text:
Код: Выделить всё
adb shell input tap 540 586
adb shell input text qa_user_77
Код: Выделить всё
adb shell input text 'S3cret%sPass'
Код: Выделить всё
adb shell "input text 'hello world & co'"
Код: Выделить всё
$ adb shell input text hello world
Error: Invalid arguments for command: text
Главное ограничение: input text молча игнорирует кириллицу. Символы идут через KeyCharacterMap виртуальной клавиатуры, а там только ASCII. Стандартный обход, клавиатура ADBKeyboard (проект senzhk/ADBKeyBoard, ставится обычным adb install):
Код: Выделить всё
adb install ADBKeyboard.apk
adb shell ime enable com.android.adbkeyboard/.AdbIME
adb shell ime set com.android.adbkeyboard/.AdbIME
adb shell am broadcast -a ADB_INPUT_TEXT --es msg 'Привет, Cyberlake'
sendevent, сырые события и мультитач:
input работает на уровне системы, а sendevent пишет события прямо в файлы драйверов /dev/input/eventN. Root обычно не нужен: пользователь shell состоит в группе input. Это инструмент для того, чего input не умеет: мультитач, жесты с точным таймингом, эмуляция конкретного железа.
Сначала находим тачскрин:
Код: Выделить всё
$ adb shell getevent -lp
add device 5: /dev/input/event2
name: "goodix_ts"
events:
ABS (0003): ABS_MT_SLOT : value 0, min 0, max 9
ABS_MT_POSITION_X : value 0, min 0, max 1079
ABS_MT_POSITION_Y : value 0, min 0, max 2399
ABS_MT_TRACKING_ID : value 0, min 0, max 65535
Живые события смотрим через getevent, так же "записывают" реальный жест, чтобы изучить его структуру:
Код: Выделить всё
$ adb shell getevent -l /dev/input/event2
EV_ABS ABS_MT_TRACKING_ID 0000039c
EV_ABS ABS_MT_POSITION_X 0000021c
EV_ABS ABS_MT_POSITION_Y 000004b0
EV_SYN SYN_REPORT 00000000
Одиночный тап по протоколу multi-touch B (все современные тачскрины):
Код: Выделить всё
adb shell '
D=/dev/input/event2
sendevent $D 3 57 1001 # ABS_MT_TRACKING_ID, палец появился
sendevent $D 3 53 540 # ABS_MT_POSITION_X
sendevent $D 3 54 1200 # ABS_MT_POSITION_Y
sendevent $D 1 330 1 # BTN_TOUCH down
sendevent $D 0 0 0 # SYN_REPORT, фиксируем кадр
sendevent $D 3 57 -1 # палец исчез
sendevent $D 1 330 0
sendevent $D 0 0 0
'
Код: Выделить всё
D=/dev/input/event2
sendevent $D 3 47 0 # слот 0
sendevent $D 3 57 2001
sendevent $D 3 53 400
sendevent $D 3 54 1000
sendevent $D 3 47 1 # слот 1
sendevent $D 3 57 2002
sendevent $D 3 53 700
sendevent $D 3 54 1400
sendevent $D 1 330 1
sendevent $D 0 0 0
Сценарий 1, разблокировка экрана:
Устройство с PIN 4821 и свайпом вверх для показа пин-пада:
Код: Выделить всё
adb shell input keyevent KEYCODE_WAKEUP
adb shell input swipe 540 1900 540 700 200
adb shell input text 4821
adb shell input keyevent 66
Код: Выделить всё
$ adb shell dumpsys power | grep mWakefulness=
mWakefulness=Awake
Сценарий 2, запуск приложения:
Тыкать по иконкам рабочего стола, плохая идея, их позиции нестабильны. Запускают через am (этому посвящена глава 12) или monkey, а input берёт на себя то, что внутри приложения:
Код: Выделить всё
adb shell am start -n org.telegram.messenger/org.telegram.ui.LaunchActivity
adb shell monkey -p ru.yandex.searchplugin -c android.intent.category.LAUNCHER 1
Сценарий 3, заполнение формы логина:
Координаты полей берём из дампа интерфейса:
Код: Выделить всё
$ adb shell uiautomator dump
UI hierchary dumped to: /sdcard/window_dump.xml
$ adb pull /sdcard/window_dump.xml .
Код: Выделить всё
adb shell "
input tap 540 586 && sleep 0.4
input text 'qa2026@cyberlake.ru'
input keyevent 61 && sleep 0.2
input text 'S3cret%sPass9'
input keyevent 66
"
Частые ошибки:
События уходят в погасший экран и пропадают без всякой ошибки. Начинайте сценарий с WAKEUP и проверки mWakefulness.
На Xiaomi/Redmi (MIUI и HyperOS) input из adb по умолчанию кидает исключение:
Код: Выделить всё
java.lang.SecurityException: Injecting input events requires the caller (or the source of the instrumentation, if any) to have the INJECT_EVENTS permission.
Координаты tap привязаны к текущей ориентации: повернулся экран, и (540, 1200) указывает в другое место. Фиксируйте ориентацию перед прогоном (глава 16 про settings) или снимайте координаты заново.
Свайп без длительности превращается во флинг, скролл уезжает дальше, чем ожидалось, и тесты мигают. Явно указывайте 200-400 мс.
input text на строке с пробелом без кавычек падает с Invalid arguments, а на кириллице молча не делает ничего. Оба случая разобрали выше, держите под рукой %s и ADBKeyboard.
В следующей главе займёмся am вплотную: запуск Activity, сервисов и broadcast с параметрами, без которых сценарии автоматизации не собрать.