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

Почему-то не парсит дамп и uiautomatorviewer не видит ничего.\
Снимок.PNG
Снимок.PNG
 
Спасибо за статью, очень жду вторую часть!
 
  • Спасибо
Реакции: WLDN
Сорян, разобрался, думал uiautomatorviewer подгружает всё в реальном времени
 
  • Спасибо
Реакции: WLDN
Здравствуйте! А кто знает как можно скачать картинку (например фото из инстаграмма) с помощью 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, Секунды
 
Здравствуйте! А кто знает как можно скачать картинку (например фото из инстаграмма) с помощью 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, Секунды
Привет. Нет, возможно в следующей статье покажу как парсить картинки. Пока что можете использовать скриншот экрана. Погуглите как это можно делать.
 
Друзья, прошу помощи
Задача: эмулировать новые образы телефонов (у каждого должны быть свои уникальные параметры imei, android id и тд + прокси). Устанавливать на них разные приложения, логиниться и их использовать. И сохранять эти образы каким-то образом. Т.е. поднял я образ нужного мне телефона, там уже залогинен, там уже прокси, который использовался ранее для конкретного образа телефона - поработал, сохранил обновленный образ и отложил до следующего раза
Как профили в зенно
Чтобы таких "телефонов" было несколько и с какой-то периодичностью работать в них

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

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

Подскажите, пожалуйста, какие значения изменить кроме путей, чтобы прикрутить к MEMU

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

на ноксе есть ответ, а с мему - пустота(
 
Подскажите, пожалуйста, какие значения изменить кроме путей, чтобы прикрутить к MEMU

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

на ноксе есть ответ, а с мему - пустота(
 
  • Спасибо
Реакции: Castaneda
И еще вопрос:
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

Что-то в самом ноксе получается изменили? Какое новое название ивента?
 
И еще вопрос:
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
 
Последнее редактирование:
@WLDN

Can you please explain me how to do that?
 
Последнее редактирование:
  • Спасибо
Реакции: WLDN
Кто-нибудь нашел удобный способ парсить уведомления?
Я вот через IFTTT на почту их пересылаю, может есть способ лучше?
 
Есть способ заставить работать с android.webkit.WebView ? вроде через UI Automator Viewer видит.
А как пытаюсь получить координаты не находит.
 
Спасибо за полезную статью!
 
Последнее редактирование:
  • Спасибо
Реакции: WLDN

Посмотреть вложение 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
статья огонь, реализовал то что нужно с помощью memu (memuc), но есть вопрос, в 2 потока есть понимание как работать, а вот в 3 и более происходят разногласия адб с нужной виртуальной машиной, подскажите пожалуйста решение :3
Спасибо. Как-то через списки или глобальные переменные. Нужно зацепиться за какой-то параметр эмулятора и держать его в листе ожидания, пока он не выполнит задачу.
 
  • Спасибо
Реакции: ruthless
Спасибо. Как-то через списки или глобальные переменные. Нужно зацепиться за какой-то параметр эмулятора и держать его в листе ожидания, пока он не выполнит задачу.
благодарю, дружище :) еще момент, при парсинге, viewer не всегда по неизвестной мне причине выдает какой - либо "текст", а мне нужно спарсить его для создания исключения, иногда текст появляется в нодах, но чаще всего нет, в чем причина? спасибо
 
У кого-нибудь была такая ошибка? Вылазит при попытке обновления в uiautomatorviewer, но только на некоторых страницах приложения
123444.png
 
  • Спасибо
Реакции: udder
благодарю, дружище :-) еще момент, при парсинге, viewer не всегда по неизвестной мне причине выдает какой - либо "текст", а мне нужно спарсить его для создания исключения, иногда текст появляется в нодах, но чаще всего нет, в чем причина? спасибо
Не знаю, не встречался с этим.
 
  • Спасибо
Реакции: udder
Народ кто может подсказать как такую строку adb -s 127.0.0.1:21553 exec-out screencap -p > C:/Users/Sergey/Desktop/новая1/ckr/ra2nd_name.png отправить
 
Если кому нужно решение по получению русского текста (кириллицы) в dump'е, стучитесь в ЛС. Отдам за денежное вознаграждение.
 
Последнее редактирование:
Подскажите как клонировать эмуляторы со сменой всех данных (Device, Imei, Android ID и тд)
 
Интересный глюк выскочил. При вводе текста через команду
C#:
Развернуть Свернуть Копировать
a.Text(name); //ввод текста
все дубли букв которые стоят рядом будут удалены.
Например вместо Kitty будет Kity

Так же данная команда не воспринимает текст с пробелами
не решили проблему?
 
не решили проблему?
Долго лечил в 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();
 

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