Автоматизуємо роботу з програмами на Android за допомогою Accessibility Service

автоматизуємо

Зміст статті

Як ми говорили в попередній статті, Accessibility Service може отримувати події, що відбуваються на екрані, але він може і викликати їх. Наприклад,знаходити потрібні елементи в додатку і клацнути по них.

Для дослідницьких цілей я створив додаток, який сам в себе кликатиме з власного сервісу :).

Підготовка до роботи сервісу

Щоб наш сервіс почав роботу, йому потрібно надати права у спеціальному розділі налаштувань. Як перенаправити туди користувача, ти вже знаєш із першої статті. Додатково ми самі можемо перевірити ще раз, чи є ці права у додатка.

Тут accessibilityservice_id - це рядок виду "ім'я пакета/.сервіс", у нас це ru.androidtools.selfclicker/.ClickService.

Ось опис сервісу з маніфесту:

Параметр label відповідає за назву програми у налаштуваннях сервісу спецможливостей. У розділі meta-data вказується опис потрібних функцій для роботи сервісу. Ось файлserviceconfig :

У ньому ми описуємо повноваження сервісу, типи подій, які може обробляти, і активити, яке запуститься налаштування роботи сервісу.

Повний опис цих параметрів, як завжди, є у документації.

роботу
checkAccess() повернув false!

Xakep #240. Ghidra

Життєвим циклом сервісу управляє система. Самі зупинити сервіс ми не можемо. ОС самостійно вивантажить непотрібні послуги - наприклад, навіщо крутити сервіс для програми, яка не запущена?

Ми можемо прив'язати сервіс до потрібного додатку. Як тільки ми дали дозвіл на роботу, сервіс викличе методonServiceConnected. Він має викликати методsetServiceInfo() з параметромAccessibilityServiceInfo. Зафільтрацію додатків, із якими працює сервіс, відповідає рядковий масивpackageNames.

роботу
Так, він точно буде

Працюємо з подіями AccessibilityEvent

Після роздачі всіх дозволів нам треба запустити потрібну програму. Якщо ми знаємо його ім'я пакета, зробити це просто:

Для запуску програми з чистого аркуша використовуємо прапорIntent.FLAG_ACTIVITY_CLEAR_TOP. В іншому випадку програма може повернутися на екран зі старим станом, дуже далеким від стартового екрана.

Тепер потрібно обробляти події у методіonAccessibilityEvent. Подія має тип, він допоможе визначити, що сталося (наприклад, змінилося вікно, клікнули по елементу, елемент отримав фокус). Щоб отримати джерело подіїAccessibilityNodeInfo, потрібно об'єкт події викликати методgetSource().

Джерело має багато корисних властивостей, які у роботі: текст, ID, ім'я класу. У нього можуть бути батьківські та дочірні елементи.

Він може бути клікабельнимisClickable(), і, щоб клацнути по ньому як нормальний користувач, потрібно викликати методperformAction(AccessibilityNodeInfo.ACTION_CLICK).

Якщо ми хочемо більш глобальних дій, наприкладнатиснути клавішу «Назад» на пристрої, слід викликати методperformGlobalAction() з потрібним параметром.

Щоб знайти на екрані потрібнуAccessibilityNodeInfo, ми можемо викликати один із методів: пошук за ID (findAccessibilityNodeInfosByViewId) та пошук за текстом (findAccessibilityNodeInfosByText). Будь готовий до того, що він поверне нам масив елементів чи взагалі жодного.

Потренуємося на кішках, точніше – на віконцях

Ось розмітка нашого піддослідного екрану:

Деякі елементи мають ID та текст, іншілише текст, деякі неклікабельні.

Іноді обробники кліків встановлюють на області, що перевищують своїми розмірами елемент із текстом або картинкою.

Вивчаємо це завдання за допомогою методуdebugClick.

Ось що вийшло в балку:

Щоб відтворити послідовність кліків, потрібно спочатку вивчити елементи, які будуть натискатися. Але іноді також важлива і послідовність їх натискань.

Для натискання на перші дві кнопки можна використовуватиfindAccessibilityNodeInfosByText таfindAccessibilityNodeInfosByViewId. Якщо текст у елементах повторюється, додатково можна перевіряти наClassName або на батька.

Щоб клікнути в нашLinearLayout, потрібно отримати йогоAccessibilityNodeInfo, ID у нього немає, але є дочірні елементиTextView таButton, у яких є текст.

Для початку нам потрібно отримати один із них, а потім клікнути в його батька.

Бувають і обернені ситуації, коли є батько, а кликаємо ми до дочірніх. Для цього використовуйnodeInfo.getChildCount() і звертайся до елемента в циклі IDnodeInfo.getChild(id) (якщо не помиляюся, нумерація ID йде з нуля).

Починати роботу сервісу краще з події зміни вікна:

Якщо весь алгоритм дій вже готовий, можна запускати сервіс автоматично черезAlarmManager, наприклад раз на добу.

Скасувати запуск можна так:

Висновок

КласAccessibilityService дозволить позбавитися рутинних операцій на твоєму Android-пристрої. Його можливостей достатньо, щоб реалізувати майже будь-яке завдання, головне – дати дозволи та знайти клікабельний елемент на екрані.