2 место Автоматизация Android без посредников. Часть 1.

Mandyson

Client
Регистрация
03.06.2012
Сообщения
410
Благодарностей
84
Баллы
28
Почему-то не парсит дамп и uiautomatorviewer не видит ничего.\
Снимок.PNG
Снимок.PNG
 

trecho

Client
Регистрация
12.07.2018
Сообщения
171
Благодарностей
123
Баллы
43
Спасибо за статью, очень жду вторую часть!
 
  • Спасибо
Реакции: WLDN

Mandyson

Client
Регистрация
03.06.2012
Сообщения
410
Благодарностей
84
Баллы
28
Сорян, разобрался, думал uiautomatorviewer подгружает всё в реальном времени
 
  • Спасибо
Реакции: WLDN

Kinomanius

Активный пользователь
Регистрация
20.01.2020
Сообщения
184
Благодарностей
26
Баллы
28
Здравствуйте! А кто знает как можно скачать картинку (например фото из инстаграмма) с помощью adb?
Тут есть способ парсинга ... возможно ли это как то применить к картинкам?

var a = project.Context["ADB"];
project.Lists["parse"].AddRange(a.Parse("//node[@resource-id='com.google.android.youtube:id/duration' and @class='android.widget.TextView']", "text", 0, 5)); //XPath, какой аттрибут парсить, Index, Секунды
 

WLDN

Client
Регистрация
09.07.2015
Сообщения
357
Благодарностей
559
Баллы
93
Здравствуйте! А кто знает как можно скачать картинку (например фото из инстаграмма) с помощью adb?
Тут есть способ парсинга ... возможно ли это как то применить к картинкам?

var a = project.Context["ADB"];
project.Lists["parse"].AddRange(a.Parse("//node[@resource-id='com.google.android.youtube:id/duration' and @class='android.widget.TextView']", "text", 0, 5)); //XPath, какой аттрибут парсить, Index, Секунды
Привет. Нет, возможно в следующей статье покажу как парсить картинки. Пока что можете использовать скриншот экрана. Погуглите как это можно делать.
 

daniilslep

Client
Регистрация
14.11.2018
Сообщения
29
Благодарностей
3
Баллы
3
Друзья, прошу помощи
Задача: эмулировать новые образы телефонов (у каждого должны быть свои уникальные параметры imei, android id и тд + прокси). Устанавливать на них разные приложения, логиниться и их использовать. И сохранять эти образы каким-то образом. Т.е. поднял я образ нужного мне телефона, там уже залогинен, там уже прокси, который использовался ранее для конкретного образа телефона - поработал, сохранил обновленный образ и отложил до следующего раза
Как профили в зенно
Чтобы таких "телефонов" было несколько и с какой-то периодичностью работать в них

Вопросы:
1) Возможно ли вообще такое? Т.е. возможно ли сделать такие "слепки" телефонов, хранить их и поднимать, когда потребуются?
2) Чем пользоваться? Эмуляторами или реальными устройствами? Конечно, при условии, что один такой "образ" не будет равен 1 реальному телефону))
3) Куда посмотреть и где почитать? Прочитал всю "Управление любыми Android приложениями на смартфоне из ZP". Результата, который в статье описан, добился на реальном устройстве. Но как сделать требуемые "образы" - не понял.
Прочитал и эту тему и несколько других, но так и не понял, куда копать.

Прошу подсказать как это реализовать или хотя бы в какую сторону искать, т.к. совсем новичок в этой теме
 

baracuda

Client
Регистрация
19.06.2013
Сообщения
732
Благодарностей
247
Баллы
43

Castaneda

Client
Регистрация
24.05.2019
Сообщения
815
Благодарностей
274
Баллы
63
Заключение.
Надеюсь моя статья облегчит вам работу с эмуляторами и поможет создать более совершенную схему взаимодействия с ними. Это лишь небольшая часть того, как можно взаимодействовать с эмуляторами.
Каждый ваш голос за статью будет мотивировать меня делиться многими полезными фишками в следующих частях.
Спасибо за внимание, друзья! :bt:
Подскажите, пожалуйста, какие значения изменить кроме путей, чтобы прикрутить к MEMU

не разобрался что происходит при выполнении:
netstat -a -n -o | find "62388" | find "127.0.0.1" | find "620"

на ноксе есть ответ, а с мему - пустота(
 

trecho

Client
Регистрация
12.07.2018
Сообщения
171
Благодарностей
123
Баллы
43
Подскажите, пожалуйста, какие значения изменить кроме путей, чтобы прикрутить к MEMU

не разобрался что происходит при выполнении:
netstat -a -n -o | find "62388" | find "127.0.0.1" | find "620"

на ноксе есть ответ, а с мему - пустота(
 
  • Спасибо
Реакции: Castaneda

Castaneda

Client
Регистрация
24.05.2019
Сообщения
815
Благодарностей
274
Баллы
63

Castaneda

Client
Регистрация
24.05.2019
Сообщения
815
Благодарностей
274
Баллы
63
И еще вопрос:
project.Variables["event"].Value = Regex.Match(project.Variables["receiver"].Value, "(?<=mouse2 event).*").ToString().Trim(); //получение № ивэнта
пустой результат, т.к. в переменной receiver нет ничего похожего на mouse2


receiver:
I: Bus=0019 Vendor=0000 Product=0001 Version=0000
N: Name="Power Button"
P: Phys=LNXPWRBN/button/input0
S: Sysfs=/devices/LNXSYSTM:00/LNXPWRBN:00/input/input0
U: Uniq=
H: Handlers=kbd event0
B: PROP=0
B: EV=3
B: KEY=100000 0 0 0
I: Bus=0019 Vendor=0000 Product=0003 Version=0000
N: Name="Sleep Button"
P: Phys=LNXSLPBN/button/input0
S: Sysfs=/devices/LNXSYSTM:00/LNXSLPBN:00/input/input1
U: Uniq=
H: Handlers=kbd event1
B: PROP=0
B: EV=3
B: KEY=4000 0 0 0 0
I: Bus=0019 Vendor=0000 Product=0006 Version=0000
N: Name="Video Bus"
P: Phys=LNXVIDEO/video/input0
S: Sysfs=/devices/LNXSYSTM:00/LNXSYBUS:00/PNP0A03:00/LNXVIDEO:00/input/input2
U: Uniq=
H: Handlers=kbd event2
B: PROP=0
B: EV=3
B: KEY=3e000b 0 0 0 0 0 0 0
I: Bus=0003 Vendor=80ee Product=0021 Version=0110
N: Name="VirtualBox USB Tablet"
P: Phys=usb-0000:00:1f.4-1/input0
S: Sysfs=/devices/pci0000:00/0000:00:1f.4/usb1/1-1/1-1:1.0/0003:80EE:0021.0001/input/input3
U: Uniq=
H: Handlers=mouse0 event3
B: PROP=0
B: EV=1f
B: KEY=1f0000 0 0 0 0 0 0 0 0
B: REL=140
B: ABS=3
B: MSC=10
I: Bus=0000 Vendor=1234 Product=0002 Version=0001
N: Name="gpio-keys"
P: Phys=
S: Sysfs=/devices/virtual/input/input4
U: Uniq=
H: Handlers=kbd event4
B: PROP=0
B: EV=23
B: KEY=c0000 0 0 0
B: SW=1
I: Bus=0000 Vendor=1234 Product=0001 Version=0001
N: Name="Android_Input"
P: Phys=
S: Sysfs=/devices/virtual/input/input5
U: Uniq=
H: Handlers=rfkill kbd mouse1 event5
B: PROP=0
B: EV=f
B: KEY=420 0 10000 ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff fffffffe
B: REL=140
B: ABS=4600000 0
I: Bus=0000 Vendor=0000 Product=0000 Version=0000
N: Name="Android Power Button"
P: Phys=
S: Sysfs=/devices/virtual/input/input6
U: Uniq=
H: Handlers=kbd event6
B: PROP=0
B: EV=3
B: KEY=8000 100000 0 0 0
Что-то в самом ноксе получается изменили? Какое новое название ивента?
 

Castaneda

Client
Регистрация
24.05.2019
Сообщения
815
Благодарностей
274
Баллы
63
И еще вопрос:
project.Variables["event"].Value = Regex.Match(project.Variables["receiver"].Value, "(?<=mouse2 event).*").ToString().Trim(); //получение № ивэнта
пустой результат, т.к. в переменной receiver нет ничего похожего на mouse2


receiver:
I: Bus=0019 Vendor=0000 Product=0001 Version=0000
N: Name="Power Button"
P: Phys=LNXPWRBN/button/input0
S: Sysfs=/devices/LNXSYSTM:00/LNXPWRBN:00/input/input0
U: Uniq=
H: Handlers=kbd event0
B: PROP=0
B: EV=3
B: KEY=100000 0 0 0
I: Bus=0019 Vendor=0000 Product=0003 Version=0000
N: Name="Sleep Button"
P: Phys=LNXSLPBN/button/input0
S: Sysfs=/devices/LNXSYSTM:00/LNXSLPBN:00/input/input1
U: Uniq=
H: Handlers=kbd event1
B: PROP=0
B: EV=3
B: KEY=4000 0 0 0 0
I: Bus=0019 Vendor=0000 Product=0006 Version=0000
N: Name="Video Bus"
P: Phys=LNXVIDEO/video/input0
S: Sysfs=/devices/LNXSYSTM:00/LNXSYBUS:00/PNP0A03:00/LNXVIDEO:00/input/input2
U: Uniq=
H: Handlers=kbd event2
B: PROP=0
B: EV=3
B: KEY=3e000b 0 0 0 0 0 0 0
I: Bus=0003 Vendor=80ee Product=0021 Version=0110
N: Name="VirtualBox USB Tablet"
P: Phys=usb-0000:00:1f.4-1/input0
S: Sysfs=/devices/pci0000:00/0000:00:1f.4/usb1/1-1/1-1:1.0/0003:80EE:0021.0001/input/input3
U: Uniq=
H: Handlers=mouse0 event3
B: PROP=0
B: EV=1f
B: KEY=1f0000 0 0 0 0 0 0 0 0
B: REL=140
B: ABS=3
B: MSC=10
I: Bus=0000 Vendor=1234 Product=0002 Version=0001
N: Name="gpio-keys"
P: Phys=
S: Sysfs=/devices/virtual/input/input4
U: Uniq=
H: Handlers=kbd event4
B: PROP=0
B: EV=23
B: KEY=c0000 0 0 0
B: SW=1
I: Bus=0000 Vendor=1234 Product=0001 Version=0001
N: Name="Android_Input"
P: Phys=
S: Sysfs=/devices/virtual/input/input5
U: Uniq=
H: Handlers=rfkill kbd mouse1 event5
B: PROP=0
B: EV=f
B: KEY=420 0 10000 ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff fffffffe
B: REL=140
B: ABS=4600000 0
I: Bus=0000 Vendor=0000 Product=0000 Version=0000
N: Name="Android Power Button"
P: Phys=
S: Sysfs=/devices/virtual/input/input6
U: Uniq=
H: Handlers=kbd event6
B: PROP=0
B: EV=3
B: KEY=8000 100000 0 0 0
Что-то в самом ноксе получается изменили? Какое новое название ивента?
Методом тыка нашел номер ивента. Получился 5ый
И изменил mouse2 на mouse1 в RegEx
Теперь работает))


Если кто с Memu работает, подскажите почему такое не прокатывает.
Перебором все попробовал. Ни один ивент не срабатывает.

Вот receiver:

receiver:
I: Bus=0019 Vendor=0000 Product=0001 Version=0000
N: Name="Power Button"
P: Phys=LNXPWRBN/button/input0
S: Sysfs=/devices/LNXSYSTM:00/LNXPWRBN:00/input/input0
U: Uniq=
H: Handlers=kbd event0
B: PROP=0
B: EV=3
B: KEY=100000 0 0 0
I: Bus=0019 Vendor=0000 Product=0003 Version=0000
N: Name="Sleep Button"
P: Phys=LNXSLPBN/button/input0
S: Sysfs=/devices/LNXSYSTM:00/LNXSLPBN:00/input/input1
U: Uniq=
H: Handlers=kbd event1
B: PROP=0
B: EV=3
B: KEY=4000 0 0 0 0
I: Bus=0011 Vendor=0001 Product=0001 Version=ab41
N: Name="AT Translated Set 2 keyboard"
P: Phys=isa0060/serio0/input0
S: Sysfs=/devices/platform/i8042/serio0/input/input2
U: Uniq=
H: Handlers=kbd event2
B: PROP=0
B: EV=120013
B: KEY=4 2000000 3803078 f800d001 feffffdf ffefffff ffffffff fffffffe
B: MSC=10
B: LED=7
I: Bus=0011 Vendor=0002 Product=0006 Version=0000
N: Name="ImExPS/2 Generic Explorer Mouse"
P: Phys=isa0060/serio1/input0
S: Sysfs=/devices/platform/i8042/serio1/input/input5
U: Uniq=
H: Handlers=mouse0 event3
B: PROP=1
B: EV=7
B: KEY=1f0000 0 0 0 0 0 0 0 0
B: REL=143
I: Bus=0003 Vendor=80ee Product=0021 Version=0110
N: Name="MemuHyperv USB Tablet"
P: Phys=usb-0000:00:06.0-1/input0
S: Sysfs=/devices/pci0000:00/0000:00:06.0/usb1/1-1/1-1:1.0/0003:80EE:0021.0001/input/input6
U: Uniq=
H: Handlers=mouse1 event4
B: PROP=0
B: EV=1f
B: KEY=1f0000 0 0 0 0 0 0 0 0
B: REL=140
B: ABS=3
B: MSC=10
I: Bus=0001 Vendor=80ee Product=cafe Version=0501
N: Name="MemuHyperv mouse integration"
P: Phys=
S: Sysfs=/devices/pci0000:00/0000:00:04.0/input/input7
U: Uniq=
H: Handlers=event5
B: PROP=0
B: EV=b
B: KEY=10000 0 0 0 0 0 0 0 0
B: ABS=3
I: Bus=0000 Vendor=1234 Product=0001 Version=0001
N: Name="User Input"
P: Phys=
S: Sysfs=/devices/virtual/input/input8
U: Uniq=
H: Handlers=rfkill kbd mouse2 event6
B: PROP=0
B: EV=f
B: KEY=20 0 10000 ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff fffffffe
B: REL=143
B: ABS=2658000 0
 
Последнее редактирование:

dinuka624

Client
Регистрация
17.02.2021
Сообщения
11
Благодарностей
1
Баллы
3
Why i'm getting this error?

74611
 

WLDN

Client
Регистрация
09.07.2015
Сообщения
357
Благодарностей
559
Баллы
93

dinuka624

Client
Регистрация
17.02.2021
Сообщения
11
Благодарностей
1
Баллы
3
@WLDN

Can you please explain me how to do that?
 
Последнее редактирование:
  • Спасибо
Реакции: WLDN

trecho

Client
Регистрация
12.07.2018
Сообщения
171
Благодарностей
123
Баллы
43
Кто-нибудь нашел удобный способ парсить уведомления?
Я вот через IFTTT на почту их пересылаю, может есть способ лучше?
 

biohacker01

Client
Регистрация
30.10.2019
Сообщения
57
Благодарностей
37
Баллы
18
Есть способ заставить работать с android.webkit.WebView ? вроде через UI Automator Viewer видит.
А как пытаюсь получить координаты не находит.
 

cmvvo

Client
Регистрация
07.07.2019
Сообщения
38
Благодарностей
15
Баллы
8
Спасибо за полезную статью!
 
Последнее редактирование:
  • Спасибо
Реакции: WLDN

ruthless

Client
Регистрация
13.02.2017
Сообщения
215
Благодарностей
61
Баллы
28

Посмотреть вложение 55998

Привет, дорогие зенщики! :az:

Небольшое предисловие.
Обычно я стараюсь писать ботов на запросах, так как это не ресурсозатратно и в целом не сложно, если приложение слабозащищено.
Но бывают приложения, которые достаточно трудоёмко воспроизвести так, чтобы бот не умирал каждую неделю. Поэтому я решил, что некоторые вещи, например, регистраторы и фолловеры стоит делать используя автоматизацию эмуляторов.
В этой статье я поделюсь с вами своими наработками по автоматизации Android эмуляторов. И покажу как всё работает на примере эмулятора Nox и приложения Youtube. :al:

Приступим.
Ранее я автоматизировал приложения через Appium, но меня не устраивали батники, запущенные окна cmd и невысокая гибкость всей системы в целом.
Поэтому я решил сделать автоматизацию напрямую, используя только ADB и UIAutomator. Для этого я использовал библиотеку SharpAdbClient.

Разделим статью на несколько частей для удобства:
  1. Установка и настройка необходимого софта
  2. Принцип работы. Разбор методов. XPath.
  3. Определение адреса (ip:port) эмулятора, организация многопоточного режима и обработка зависаний.

1. Установка и настройка необходимого софта.

Установка UIAutomator для поиска элементов:
  • Скачиваем архив под статьёй и распаковываем.
  • Устанавливаем AndroidSDK. Eсли просит установить Java, то устанавливаем сначала её (jre-8u241-windows-x64.exe).
  • Запускаем SDKManager и устанавливаем необходимые пакеты.
    56231


    56232
  • Прописываем пути до SDK (если их нет) в переменные среды:
    • Заходим в "Систему", через поиск Windows, либо комбинация клавиш Win + Pause/Break.
    • Дополнительные параметры системы
    • Переменные среды
Создаём переменную ANDROID_HOME и прописываем путь до папки с SDK.
56235


В системной переменной Path прописываем пути до папки tools и platform-tools
56236
  • Создаём ярлык для uiautomatorviewer.bat на рабочем столе, сам файл можно найти по пути "android-sdk/tools/uiautomatorviewer.bat", там куда вы установили AndroidSDK.
  • Запускаем uiautomatorviewer.bat для теста. Если появилось окно под названием UI Automator Viewer, значит всё хорошо.

Установка и настройка эмулятора и ZennoPoster:
  • Перемещаем библиотеки (.dll) из ExternalAssemblies в директорию ZennoPoster
  • Скачиваем и устанавливаем Nox.
  • Запускаем MultiDrive и создаём 3 эмулятора для теста (для удобства в настройках можно выставить телефонную ориентацию)
  • Добавляем шаблон в ZennoPoster. Прописываем необходимые пути в настройках (nox_adb.exe и nox.exe в папке bin).
  • Запускаем последовательно 3 эмулятора, затем устанавливаем 3 потока в ZennoPoster, и запускаем для теста. Должна пойти автоматизация в Youtube.

2. Принцип работы. Разбор методов. XPath.

Как получается связать adb с эмулятором без посредника (например Appium)?

У adb есть множество команд для управления Android. Например, команда
cmd.exe:
adb shell input tap x y
позволяет нам тапнуть по указанным координатам.

Соответственно, чтобы тапнуть по нужному элементу, нам надо узнать координаты этого элемента.
На эмуляторе по умолчанию предустановлен UIAutomator, поэтому вы можете получать данные видимых элементов с помощью команды:
cmd.exe:
adb shell uiautomator dump
Но в таком случае XML сохранится в корневой папкe Android, и придется скачивать его на ПК для просмотра - это долго и неудобно, поэтому добавим к команде /dev/tty.
Теперь команда будет выглядеть так:
cmd.exe:
adb shell uiautomator dump /dev/tty
Таким образом, мы выведем содержимое XML файла в лог, в этом случае библиотека SharpAdbClient подцепит ответ и мы сможем извлечь необходимые координаты.

Приведу небольшой список часто используемых команд, которые поддерживает adb:
cmd.exe:
adb help //вывести весь список комманд
adb start-server //запустить adb сервер
adb kill-server //закрыть adb сервер
adb connect <ip:port> //подключиться к устройству
adb devices //показать список подключенных устройств
adb reboot //перезагрузить устройство
adb install <path to .apk> //установить .apk файл
adb shell // Запускать команды через терминал Shell
adb shell input x y //тапнуть по координатам
adb shell input swipe x1 y1 x2 y2 sss //свайпнуть по координатам, где sss - скорость в миллисекундах
adb shell input text <string> //отправить текст
adb shell input keyevent <event_code> //отправить event (полный список event'ов будет ниже)
adb shell pm list packages //показать установленные пакеты
adb shell pm uninstall <com.your.app> //удалить пакет
adb shell dumpsys window windows | grep -E 'mCurrentFocus|mFocusedApp' //показать название текущей активности
Все остальные команды можно найти в поисковике, достаточно написать "adb commands list".

Теперь перейдем к шаблону.
В GAC уже добавлены необходимые .dll, а в директиве using и общем коде добавлены необходимые пространства имён.
Директивы using и общий код:
using SharpAdbClient;
using System.Net;
using System.Xml;
using System.Windows.Forms;
using System.Diagnostics;
using System.Management;
Команды adb теперь можно пропустить через подключенную библиотеку SharpAdbClient.
В общем коде я написал методы для удобства, обзор которых будет на видео в конце статьи.

Пройдемся по готовым кубикам.
Начнём с проверки запущен ли adb сервер по пути из переменной settings_adb:
C#:
if (!AdbServer.Instance.GetStatus().IsRunning) {
AdbServer server = new AdbServer();
var result = server.StartServer(project.Variables["settings_adb"].Value, restartServerIfNewer: false);

    throw new Exception("Restart.");
}

Подключаемся к ip:port эмулятора. Обычно он автоматически подключается, но подстрахуемся командой:
C#:
AdbClient.Instance.Connect(new DnsEndPoint("127.0.0.1", int.Parse(Regex.Match(project.Variables["device"].Value, "(?<=:).*").ToString())));

Cоздаём объект ADB и сохраняем его в контексте, чтобы использовать далее в проекте:
C#:
ADB a = new ADB(project);
project.Context["ADB"] = a;

Создаём объект PackageManager, который позволит нам Удалять/Устанавливать приложение в этом же сниппете:
C#:
var a = project.Context["ADB"];
var device = a.Device();

SharpAdbClient.DeviceCommands.PackageManager manager = new SharpAdbClient.DeviceCommands.PackageManager(device);
try {
manager.UninstallPackage("com.google.android.youtube"); //удаление пакета
} catch (Exception e) {}
manager.InstallPackage(project.Directory + @"\youtube.apk", reinstall: false); //установка apk

Осуществляем запуск приложения:
C#:
var a = project.Context["ADB"];
a.StartApp("com.google.android.youtube/com.google.android.apps.youtube.app.WatchWhileActivity"); //запуск приложения
// в CMD получить наименование текущего окна, которое нужно вставить в этот запрос
// adb shell dumpsys window windows | grep -E 'mCurrentFocus|mFocusedApp'

Ожидаем появление элемента:
C#:
var a = project.Context["ADB"];
a.Wait("//node[@resource-id='' and @class='android.widget.ImageView']", 0, 10); //XPath, Index, Секунды

Тапаем на элемент:
C#:
var a = project.Context["ADB"];
a.Click("//node[@resource-id='com.google.android.youtube:id/menu_search' and @class='android.widget.TextView']", 0, 10); //XPath, Index, Секунды
Обратите внимание, здесь впервые мы используем XPath для поиска объекта.
Учимся составлять свой XPath (это несложно):
Для начала запустим Nox и убедимся, что он подключен к adb.
cmd.exe:
adb devices
Вы должны увидеть своё устройство в списке.
Посмотреть вложение 56001
Если подключенных устройств нет, тогда придется подключить эмулятор самостоятельно с помощью команды:
cmd.exe:
adb connect 127.0.0.1:62001
Вы должны получить сообщение о подключении. Обычно порт первого эмулятора 62001, но в моём случае 62025.
Посмотреть вложение 56002
Если не получается поймать порт, то пробуйте перебор от 62001 до 62100.
Теперь установим и запустим youtube.apk из архива, а затем запустим uiautomatorviewer.bat и сделаем dump окна.
Вы можете поводить курсором по элементам и заметите, что в правой части окна выделяются соответствующие узлы (node). Соответственно, чтобы составить XPath мы должны выделить нужный нам элемент и взять атрибуты, которые могут идентифицировать элемент в дереве.
Составим XPath для лупы (поиск), взяв атрибуты resource-id и class:
XPath:
//node[@resource-id='com.google.android.youtube:id/menu_search' and @class='android.widget.TextView']

Если случается, что элемент невозможно идентифицировать (не указаны атрибуты), то в таком случае можно составить длинный XPath, например до элемента Home слева от лупы:
XPath:
//node[@resource-id='com.google.android.youtube:id/toolbar' and @class='android.view.View']/node[@class='android.widget.TextView']
Весь синтаксис XPath можно подсмотреть здесь.


Ввод текста и нажатие event'а (Enter):
C#:
var a = project.Context["ADB"];
a.Text("ZennoLab"); //ввод текста
a.KeyEvent("66"); //Enter
0 --> "KEYCODE_UNKNOWN"
1 --> "KEYCODE_MENU"
2 --> "KEYCODE_SOFT_RIGHT"
3 --> "KEYCODE_HOME"
4 --> "KEYCODE_BACK"
5 --> "KEYCODE_CALL"
6 --> "KEYCODE_ENDCALL"
7 --> "KEYCODE_0"
8 --> "KEYCODE_1"
9 --> "KEYCODE_2"
10 --> "KEYCODE_3"
11 --> "KEYCODE_4"
12 --> "KEYCODE_5"
13 --> "KEYCODE_6"
14 --> "KEYCODE_7"
15 --> "KEYCODE_8"
16 --> "KEYCODE_9"
17 --> "KEYCODE_STAR"
18 --> "KEYCODE_POUND"
19 --> "KEYCODE_DPAD_UP"
20 --> "KEYCODE_DPAD_DOWN"
21 --> "KEYCODE_DPAD_LEFT"
22 --> "KEYCODE_DPAD_RIGHT"
23 --> "KEYCODE_DPAD_CENTER"
24 --> "KEYCODE_VOLUME_UP"
25 --> "KEYCODE_VOLUME_DOWN"
26 --> "KEYCODE_POWER"
27 --> "KEYCODE_CAMERA"
28 --> "KEYCODE_CLEAR"
29 --> "KEYCODE_A"
30 --> "KEYCODE_B"
31 --> "KEYCODE_C"
32 --> "KEYCODE_D"
33 --> "KEYCODE_E"
34 --> "KEYCODE_F"
35 --> "KEYCODE_G"
36 --> "KEYCODE_H"
37 --> "KEYCODE_I"
38 --> "KEYCODE_J"
39 --> "KEYCODE_K"
40 --> "KEYCODE_L"
41 --> "KEYCODE_M"
42 --> "KEYCODE_N"
43 --> "KEYCODE_O"
44 --> "KEYCODE_P"
45 --> "KEYCODE_Q"
46 --> "KEYCODE_R"
47 --> "KEYCODE_S"
48 --> "KEYCODE_T"
49 --> "KEYCODE_U"
50 --> "KEYCODE_V"
51 --> "KEYCODE_W"
52 --> "KEYCODE_X"
53 --> "KEYCODE_Y"
54 --> "KEYCODE_Z"
55 --> "KEYCODE_COMMA"
56 --> "KEYCODE_PERIOD"
57 --> "KEYCODE_ALT_LEFT"
58 --> "KEYCODE_ALT_RIGHT"
59 --> "KEYCODE_SHIFT_LEFT"
60 --> "KEYCODE_SHIFT_RIGHT"
61 --> "KEYCODE_TAB"
62 --> "KEYCODE_SPACE"
63 --> "KEYCODE_SYM"
64 --> "KEYCODE_EXPLORER"
65 --> "KEYCODE_ENVELOPE"
66 --> "KEYCODE_ENTER"
67 --> "KEYCODE_DEL"
68 --> "KEYCODE_GRAVE"
69 --> "KEYCODE_MINUS"
70 --> "KEYCODE_EQUALS"
71 --> "KEYCODE_LEFT_BRACKET"
72 --> "KEYCODE_RIGHT_BRACKET"
73 --> "KEYCODE_BACKSLASH"
74 --> "KEYCODE_SEMICOLON"
75 --> "KEYCODE_APOSTROPHE"
76 --> "KEYCODE_SLASH"
77 --> "KEYCODE_AT"
78 --> "KEYCODE_NUM"
79 --> "KEYCODE_HEADSETHOOK"
80 --> "KEYCODE_FOCUS"
81 --> "KEYCODE_PLUS"
82 --> "KEYCODE_MENU"
83 --> "KEYCODE_NOTIFICATION"
84 --> "KEYCODE_SEARCH"
85 --> "TAG_LAST_KEYCODE"

Так же случаются ситуации, когда вам нужно сделать двойной тап или другие действия, которых нет в списке.
На помощь приходит команда sendevent:
cmd.exe:
adb shell sendevent /dev/input/event<x>

Получаем координаты элемента, с помощью метода получения координат.
Вы можете получать "чистые" координаты, либо уже высчитанную рандомную точку. В нашем случае используем сразу второй вариант:
C#:
var a = project.Context["ADB"];
string coord = a.GetCoord("//node[@resource-id='com.google.android.youtube:id/channel_avatar' and @class='android.widget.ImageView']", 0, 10, true); //XPath, Index, Секунды, рандомная точка между x1,y1 и x2,y2
string[] coords = coord.Split(new char[] {','});
project.Variables["x"].Value = coords[0];
project.Variables["y"].Value = coords[1];

Введем команду в cmd, кликнем на эмуляторе и запишем событие:
cmd.exe:
adb shell getevent
Вы получите значения в HEX, преобразуем их в DEC с помощью любого сервиса.
Получится:
Код:
/dev/input/event<N>: 1 330 1
/dev/input/event<N>: 3 58 1
/dev/input/event<N>: 3 53 <x>
/dev/input/event<N>: 3 54 <y>
/dev/input/event<N>: 0 2 0
/dev/input/event<N>: 0 0 0
/dev/input/event<N>: 0 2 0
/dev/input/event<N>: 0 0 0
/dev/input/event<N>: 1 330 0
/dev/input/event<N>: 3 58 0
/dev/input/event<N>: 3 53 0
/dev/input/event<N>: 3 54 38
/dev/input/event<N>: 0 2 0
/dev/input/event<N>: 0 0 0
Получаем номер event'a с помощью команды:
C#:
var a = project.Context["ADB"];
a.Command("cat /proc/bus/input/devices", true);
project.Variables["event"].Value = Regex.Match(project.Variables["receiver"].Value, "(?<=mouse2 event).*").ToString().Trim(); //получение № ивэнта

Тапаем с помощью sendevent, подставив номер event'а и координаты (в нашем случае двойной тап не нужен, поэтому я закомментил вторую часть в шаблоне):
C#:
var a = project.Context["ADB"];
string evnt = project.Variables["event"].Value;
string x = project.Variables["x"].Value;
string y = project.Variables["y"].Value;
a.Command(String.Format("sendevent /dev/input/event{0} 1 330 1", evnt), false);
a.Command(String.Format("sendevent /dev/input/event{0} 3 58 1", evnt), false);
a.Command(String.Format("sendevent /dev/input/event{0} 3 53 {1}", evnt, x), false);
a.Command(String.Format("sendevent /dev/input/event{0} 3 54 {1}", evnt, y), false);
a.Command(String.Format("sendevent /dev/input/event{0} 0 2 0", evnt), false);
a.Command(String.Format("sendevent /dev/input/event{0} 0 0 0", evnt), false);
a.Command(String.Format("sendevent /dev/input/event{0} 0 2 0", evnt), false);
a.Command(String.Format("sendevent /dev/input/event{0} 0 0 0", evnt), false);
a.Command(String.Format("sendevent /dev/input/event{0} 1 330 0", evnt), false);
a.Command(String.Format("sendevent /dev/input/event{0} 3 58 0", evnt), false);
a.Command(String.Format("sendevent /dev/input/event{0} 3 53 0", evnt), false);
a.Command(String.Format("sendevent /dev/input/event{0} 3 54 38", evnt), false);
a.Command(String.Format("sendevent /dev/input/event{0} 0 2 0", evnt), false);
a.Command(String.Format("sendevent /dev/input/event{0} 0 0 0", evnt), false);

Свайпаем:
C#:
var a = project.Context["ADB"];
    a.Swipe("200", "600", "200", "200", "900"); //координаты x1, y1, x2, y2, sss - скорость в милисекундах

Также можно спарсить что-нибудь. Спарсим длительность видимых роликов (результат появится в списке шаблона):
C#:
var a = project.Context["ADB"];
project.Lists["parse"].AddRange(a.Parse("//node[@resource-id='com.google.android.youtube:id/duration' and @class='android.widget.TextView']", "text", 0, 5)); //XPath, какой аттрибут парсить, Index, Секунды

Button Back:
C#:
var a = project.Context["ADB"];
a.Back();

Button Home и Kill Process:
C#:
var a = project.Context["ADB"];
a.Home(); //Свернуть все окна
a.Kill("com.google.android.youtube"); //Убить процесс

Перезагрузить Android:
C#:
var a = project.Context["ADB"];
a.Reboot();

Загрузка файла. Я написал 2 метода для загрузки файла: обычная загрузка
C#:
var a = project.Context["ADB"];
a.UploadFile("/storage/sdcard0/ZennoLab.txt", project.Directory + @"\ZennoLab.txt");
и из переменной.
C#:
var a = project.Context["ADB"];
a.UploadFromVar("/storage/sdcard0/ZennoLab.txt", "ZennoLab TEST");

Скачивание файла:
C#:
var a = project.Context["ADB"];
a.DownloadFile("/storage/sdcard0/ZennoLab.txt", project.Directory + @"\Download_test.txt");

Удаление Файла:
C#:
var a = project.Context["ADB"];
a.Command("rm -rf /storage/sdcard0/ZennoLab.txt", false);

3. Определение IP+PORT эмулятора, организация многопоточного режима и обработка зависаний.

Для организации многопоточного режима я решил использовать залоченную глобальную переменную, в которой будут записаны id процессов (pid) окон эмуляторов через ";".
Когда шаблон запускается, то в глобальную переменную записывается pid первого по списку свободного эмулятора. Если в переменной не было этого pid, то другой поток не будет работать с этим pid. :bf:
Сам pid можно найти в Диспетчере задач (Ctrl+Shift+Esc).

Далее если ввести в cmd команду:
cmd.exe:
netstat -a -n -o
то высветится множество адресов, а в самой правой колонке мы увидем pid.

Зная, что порты для подключение adb у Nox начинаются с 620, мы можем взять адрес по pid'у с помощью команды:
cmd.exe:
netstat -a -n -o | find "PID" | find "127.0.0.1" | find "620"
вместо PID подставив id процесса (pid).

Теперь сделаем тоже самое в зенке.
Инициализируем/проверяем существование глобальной переменной:
C#:
lock(SyncObject) {
    try {
        var gbVar = project.GlobalVariables["Zappium", "process"];
        return null;
    } catch (KeyNotFoundException ex) {
        string defaultValue = String.Empty;
        project.GlobalVariables.SetVariable("Zappium", "process", defaultValue);
    }
}

Получаем свободный PID с помощью стандартного инструментария .Net:
C#:
lock(SyncObject) {
var gbVar = project.GlobalVariables["Zappium", "process"];
Process[] processes = Process.GetProcessesByName("NoxVMHandle");
var ids = processes.Select(p => p.Id);
string process = "";
foreach(int processId in ids){
    project.SendInfoToLog(processId.ToString());
    process = processId.ToString();
    if (project.Variables["process"].Value == String.Empty && !project.GlobalVariables["Zappium", "process"].Value.ToString().Contains(process)){
        project.Variables["process"].Value = process;
        gbVar.Value = gbVar.Value + process + ";";
        }
}
}

Получаем адрес устройства, подставив pid в ранее написанную команду:
C#:
Process cmd = new Process();
cmd.StartInfo.FileName = "cmd.exe";
cmd.StartInfo.RedirectStandardInput = true;
cmd.StartInfo.RedirectStandardOutput = true;
cmd.StartInfo.CreateNoWindow = true;
cmd.StartInfo.UseShellExecute = false;
cmd.Start();

cmd.StandardInput.WriteLine(String.Format("netstat -a -n -o | find \"{0}\" | find \"127.0.0.1\" | find \"620\"",project.Variables["process"].Value));

cmd.StandardInput.Flush();
cmd.StandardInput.Close();
cmd.WaitForExit();

return "127.0.0.1:" + Regex.Match(cmd.StandardOutput.ReadToEnd(), "(?<=127.0.0.1:)62.*?(?= )");

По завершению работы шаблона удаляем из глобальной переменной pid, чтобы освободить окно для следующих потоков:
C#:
lock(SyncObject) {
    var gbVar = project.GlobalVariables["Zappium", "process"];
    gbVar.Value = gbVar.Value.ToString().Replace(project.Variables["process"].Value + ";", "");
}
(В этом способе есть один минус. Если вы прерываете работу шаблона вручную, то pid не удаляется. Буду рад вашим идеям решения этой проблемы в комментариях.)

Порой случается, что эмулятор зависает, для этого я использую метод CommandLineUtilities, прописанный в общем коде, чтобы записать все процессы связанные с эмулятором, закрыть и открыть их заново.
Закрываем процессы:
C#:
Process[] processes = Process.GetProcessesByName("Nox");
var ids = processes.Select(p => p.Id);
var process = Process.GetProcessById(int.Parse(project.Variables["process"].Value));

string name = Regex.Match(CommandLineUtilities.getCommandLines(process), "(?<=--comment ).*(?= --startvm)").ToString();
project.SendInfoToLog(CommandLineUtilities.getCommandLines(process));

foreach(int processId in ids){
    var nox = Process.GetProcessById(processId);
    if (Regex.Match(CommandLineUtilities.getCommandLines(nox),"(?<=-clone:).*").ToString().Contains(name)){
        project.Variables["start_cmd"].Value = CommandLineUtilities.getCommandLines(nox);
    nox.Kill();
        break;
    }
}
process.Kill();

Открываем эмулятор заново:
C#:
var proc = System.Diagnostics.Process.Start(project.Variables["settings_nox"].Value, Regex.Match(project.Variables["start_cmd"].Value, "-clone:.*").ToString());



Заключение.
Надеюсь моя статья облегчит вам работу с эмуляторами и поможет создать более совершенную схему взаимодействия с ними. Это лишь небольшая часть того, как можно взаимодействовать с эмуляторами.
Каждый ваш голос за статью будет мотивировать меня делиться многими полезными фишками в следующих частях.
Спасибо за внимание, друзья! :bt:
статья огонь, реализовал то что нужно с помощью memu (memuc), но есть вопрос, в 2 потока есть понимание как работать, а вот в 3 и более происходят разногласия адб с нужной виртуальной машиной, подскажите пожалуйста решение :3
 
Последнее редактирование:
  • Спасибо
Реакции: WLDN и Castaneda

WLDN

Client
Регистрация
09.07.2015
Сообщения
357
Благодарностей
559
Баллы
93
статья огонь, реализовал то что нужно с помощью memu (memuc), но есть вопрос, в 2 потока есть понимание как работать, а вот в 3 и более происходят разногласия адб с нужной виртуальной машиной, подскажите пожалуйста решение :3
Спасибо. Как-то через списки или глобальные переменные. Нужно зацепиться за какой-то параметр эмулятора и держать его в листе ожидания, пока он не выполнит задачу.
 
  • Спасибо
Реакции: ruthless

ruthless

Client
Регистрация
13.02.2017
Сообщения
215
Благодарностей
61
Баллы
28
Спасибо. Как-то через списки или глобальные переменные. Нужно зацепиться за какой-то параметр эмулятора и держать его в листе ожидания, пока он не выполнит задачу.
благодарю, дружище :-) еще момент, при парсинге, viewer не всегда по неизвестной мне причине выдает какой - либо "текст", а мне нужно спарсить его для создания исключения, иногда текст появляется в нодах, но чаще всего нет, в чем причина? спасибо
 

PandaCIB

Client
Регистрация
24.10.2020
Сообщения
1
Благодарностей
1
Баллы
3
У кого-нибудь была такая ошибка? Вылазит при попытке обновления в uiautomatorviewer, но только на некоторых страницах приложения
123444.png
 
  • Спасибо
Реакции: udder

WLDN

Client
Регистрация
09.07.2015
Сообщения
357
Благодарностей
559
Баллы
93
благодарю, дружище :-) еще момент, при парсинге, viewer не всегда по неизвестной мне причине выдает какой - либо "текст", а мне нужно спарсить его для создания исключения, иногда текст появляется в нодах, но чаще всего нет, в чем причина? спасибо
Не знаю, не встречался с этим.
 

frion-seo

Client
Регистрация
27.02.2011
Сообщения
502
Благодарностей
449
Баллы
63
  • Спасибо
Реакции: udder

Cthutq899

Client
Регистрация
03.09.2019
Сообщения
168
Благодарностей
45
Баллы
28
Народ кто может подсказать как такую строку adb -s 127.0.0.1:21553 exec-out screencap -p > C:/Users/Sergey/Desktop/новая1/ckr/ra2nd_name.png отправить
 

WLDN

Client
Регистрация
09.07.2015
Сообщения
357
Благодарностей
559
Баллы
93
Если кому нужно решение по получению русского текста (кириллицы) в dump'е, стучитесь в ЛС. Отдам за денежное вознаграждение.
 
Последнее редактирование:

Castaneda

Client
Регистрация
24.05.2019
Сообщения
815
Благодарностей
274
Баллы
63
Подскажите как клонировать эмуляторы со сменой всех данных (Device, Imei, Android ID и тд)
 

IVANGOOD

Client
Регистрация
07.09.2020
Сообщения
13
Благодарностей
1
Баллы
3
Интересный глюк выскочил. При вводе текста через команду
C#:
a.Text(name); //ввод текста
все дубли букв которые стоят рядом будут удалены.
Например вместо Kitty будет Kity

Так же данная команда не воспринимает текст с пробелами
не решили проблему?
 

JuicyJose

Client
Регистрация
21.12.2014
Сообщения
20
Благодарностей
9
Баллы
3

cmvvo

Client
Регистрация
07.07.2019
Сообщения
38
Благодарностей
15
Баллы
8
не решили проблему?
Долго лечил в Memu так и не вылечил. В итоге перешел на Nox
Чтобы не было ошибки, вводите текст через adb с помощью этого сниппета:


C#:
var sv = project.Variables["sv"].Value;
//sv это серийник устройства для многопотока вида 127.0.0.1:21521

ProcessStartInfo startInfo1 = new ProcessStartInfo();
startInfo1.FileName = "adb.exe";
startInfo1.Arguments = "-s "+sv+" shell am broadcast -a ADB_INPUT_TEXT --es msg 'ТУТ ВВОДИМЫЙ ТЕКСТ'";
startInfo1.WindowStyle = ProcessWindowStyle.Hidden;
Process.Start(startInfo1).WaitForExit();
 

Кто просматривает тему: (Всего: 4, Пользователи: 0, Гости: 4)