1. Введение
Службы специальных возможностей — это функция платформы Android, предназначенная для предоставления пользователю альтернативной навигационной обратной связи от имени приложений, установленных на устройствах Android. Служба специальных возможностей может общаться с пользователем от имени приложения, например, преобразуя текст в речь или обеспечивая тактильную обратную связь, когда пользователь наводит курсор на важную область экрана. В этой лаборатории кода показано, как создать очень простую службу специальных возможностей.
Что такое служба доступности?
Служба специальных возможностей помогает пользователям с ограниченными возможностями использовать устройства и приложения Android. Это давно действующая привилегированная служба, которая помогает пользователям обрабатывать информацию на экране и позволяет им осмысленно взаимодействовать с устройством.
Примеры общих служб доступности
- Switch Access : позволяет пользователям Android с ограничениями мобильности взаимодействовать с устройствами с помощью одного или нескольких переключателей.
- Голосовой доступ (бета): позволяет пользователям Android с ограничениями в передвижении управлять устройством с помощью голосовых команд.
- Talkback : программа чтения с экрана, обычно используемая слабовидящими или слепыми пользователями.
Создание службы доступности
Хотя Google предоставляет такие услуги, как Switch Access, Voice Access и Talkback, для пользователей Android, эти сервисы не могут обслуживать всех пользователей с ограниченными возможностями. Поскольку многие пользователи с ограниченными возможностями имеют уникальные потребности, API-интерфейсы Android для создания служб специальных возможностей открыты, и разработчики могут свободно создавать службы специальных возможностей и распространять их через Play Store.
Что вы будете строить
В этой лаборатории кода вы разработаете простой сервис, который выполняет несколько полезных функций с помощью API специальных возможностей. Если вы можете написать базовое приложение для Android, вы можете разработать аналогичный сервис.
API доступности является мощным: код службы, которую вы будете создавать, содержится всего в четырех файлах и использует около 200 строк кода!
Конечный пользователь
Вы создадите сервис для гипотетического пользователя со следующими характеристиками:
- Пользователю трудно добраться до боковых кнопок устройства.
- Пользователь испытывает трудности с прокруткой или перелистыванием.
Подробности услуги
Ваш сервис наложит на экран глобальную панель действий. Пользователь может нажимать кнопки на этой панели для выполнения следующих действий:
- Выключите устройство, не нажимая кнопку питания на боковой панели телефона.
- Отрегулируйте громкость, не касаясь кнопок громкости на боковой панели телефона.
- Выполняйте действия прокрутки без фактической прокрутки.
- Выполните смахивание без использования жеста смахивания.
Что вам понадобится
В этой лаборатории кода предполагается, что вы будете использовать следующее:
- Компьютер под управлением Android Studio.
- Терминал для выполнения простых команд оболочки.
- Устройство под управлением Android 7.0 (Nougat), подключенное к компьютеру, который вы будете использовать для разработки.
Давайте начнем!
2. Приступаем к настройке
Используя терминал, создайте каталог, в котором вы будете работать. Перейдите в этот каталог.
Загрузите код
Вы можете клонировать репозиторий, содержащий код для этой лаборатории кода:
git clone https://github.com/android/codelab-android-accessibility.git
Репозиторий содержит несколько проектов Android Studio. Используя Android Studio, откройте GlobalActionBarService .
Запустите Android Studio, щелкнув значок Studio:
Выберите параметр «Импортировать проект (Eclipse ADT, Gradle и т. д.) :
Перейдите к месту, где вы клонировали источник, и выберите GlobalActionBarService.
Затем с помощью терминала перейдите в корневой каталог.
3. Понимание стартового кода
Изучите проект, который вы открыли.
Базовый скелет службы доступности уже создан для вас. Весь код, который вы напишете в этой лаборатории, ограничен следующими четырьмя файлами:
- приложение/источник/основной/ AndroidManifest.xml
- приложение/src/main/res/layout/ action_bar.xml
- приложение/src/main/res/xml/ global_action_bar_service.xml
- app/src/main/java/com/example/android/globalactionbarservice/ GlobalActionBarService.java
Вот пошаговое описание содержимого каждого файла.
AndroidManifest.xml
Информация о службе доступности объявляется в манифесте:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.android.globalactionbarservice">
<application>
<service
android:name=".GlobalActionBarService"
android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE"
android:exported="true">
<intent-filter>
<action android:name="android.accessibilityservice.AccessibilityService" />
</intent-filter>
<meta-data
android:name="android.accessibilityservice"
android:resource="@xml/global_action_bar_service" />
</service>
</application>
</manifest>
Следующие три обязательных элемента объявлены в AndroidManifest.xml :
- Разрешение на привязку к службе доступности:
<service
...
android:permission = "android.permission.BIND_ACCESSIBILITY_SERVICE">
...
</service>
- Цель AccessibilityService :
<intent-filter>
<action android:name="android.accessibilityservice.AccessibilityService" />
</intent-filter>
- Расположение файла, содержащего метаданные для создаваемой вами службы:
<meta-data
...
android:resource="@xml/global_action_bar_service" />
</service>
global_action_bar_service.xml
Этот файл содержит метаданные службы.
<accessibility-service xmlns:android="http://schemas.android.com/apk/res/android"
android:accessibilityFeedbackType="feedbackGeneric"
android:accessibilityFlags="flagDefault"
android:canPerformGestures="true"
android:canRetrieveWindowContent="true" />
С помощью элемента <accessibility-service> были определены следующие метаданные:
- Тип обратной связи для этой службы (в этой кодовой лаборатории используется FeedbackGeneric , который является хорошим вариантом по умолчанию).
- Флаги доступности для службы (в этой лаборатории кода используются флаги по умолчанию).
- Возможности, необходимые для сервиса:
- Чтобы выполнить пролистывание, для android:canPerformGestures установлено значение true .
- Чтобы получить содержимое окна, для android:canRetrieveWindowContent установлено значение true .
GlobalActionBarService.java
Большая часть кода службы специальных возможностей находится в GlobalActionBarService.java . Первоначально файл содержит абсолютный минимум кода для службы специальных возможностей:
- Класс, расширяющий AccessibilityService .
- Несколько обязательных переопределенных методов (в этой кодовой лаборатории оставлены пустыми).
public class GlobalActionBarService extends AccessibilityService {
@Override
public void onAccessibilityEvent(AccessibilityEvent event) {
}
@Override
public void onInterrupt() {
}
}
Вы добавите код в этот файл во время работы над кодом.
action_bar.xml
Служба предоставляет пользовательский интерфейс с четырьмя кнопками, а файл макета action_bar.xml содержит разметку для отображения этих кнопок:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content">
</LinearLayout>
На данный момент этот файл содержит пустой LinearLayout . Вы добавите разметку для кнопок во время работы над кодом.
Запуск приложения
Убедитесь, что устройство подключено к вашему компьютеру. Нажмите зеленый значок воспроизведения . из строки меню в верхнюю часть экрана. Это должно запустить приложение, над которым вы работаете.
Откройте «Настройки» > «Специальные возможности». На вашем устройстве установлена служба глобальной панели действий .
Нажмите «Служба глобальной панели действий» и включите ее. Вы должны увидеть следующее диалоговое окно разрешения:
Служба специальных возможностей запрашивает разрешение наблюдать за действиями пользователя, получать содержимое окон и выполнять жесты от имени пользователя! При использовании сторонней службы специальных возможностей убедитесь, что вы действительно доверяете источнику !
Запуск службы мало что дает, поскольку мы еще не добавили никаких функций. Давайте начнем это делать.
4. Создание кнопок
Откройте action_bar.xml в res/layout . Добавьте разметку внутри пустого LinearLayout :
<LinearLayout ...>
<Button
android:id="@+id/power"
android:text="@string/power"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<Button
android:id="@+id/volume_up"
android:text="@string/volume"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<Button
android:id="@+id/scroll"
android:text="@string/scroll"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<Button
android:id="@+id/swipe"
android:text="@string/swipe"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</LinearLayout>
Это создает кнопки, которые пользователь будет нажимать для запуска действий на устройстве.
Откройте GlobalActionBarService.java и добавьте переменную для хранения макета панели действий:
public class GlobalActionBarService extends AccessibilityService {
FrameLayout mLayout;
...
}
Теперь добавьте метод onServiceStarted() :
public class GlobalActionBarService extends AccessibilityService {
FrameLayout mLayout;
@Override
protected void onServiceConnected() {
// Create an overlay and display the action bar
WindowManager wm = (WindowManager) getSystemService(WINDOW_SERVICE);
mLayout = new FrameLayout(this);
WindowManager.LayoutParams lp = new WindowManager.LayoutParams();
lp.type = WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY;
lp.format = PixelFormat.TRANSLUCENT;
lp.flags |= WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
lp.width = WindowManager.LayoutParams.WRAP_CONTENT;
lp.height = WindowManager.LayoutParams.WRAP_CONTENT;
lp.gravity = Gravity.TOP;
LayoutInflater inflater = LayoutInflater.from(this);
inflater.inflate(R.layout.action_bar, mLayout);
wm.addView(mLayout, lp);
}
}
Код расширяет макет и добавляет панель действий в верхнюю часть экрана.
Метод onServiceConnected() запускается при подключении службы. В настоящее время служба доступности имеет все разрешения, необходимые для ее функционирования. Ключевое разрешение, которое вы здесь будете использовать, — это разрешение WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY . Это разрешение позволяет вам рисовать прямо на экране над существующим содержимым без необходимости проходить сложный процесс получения разрешений.
Жизненный цикл службы доступности
Жизненный цикл услуги доступности управляется исключительно системой и соответствует установленному жизненному циклу услуги.
- Служба доступности запускается, когда пользователь явно включает эту службу в настройках устройства.
- После того как система привязывается к службе, она вызывает onServiceConnected() . Этот метод может быть переопределен службами, которые хотят выполнить настройку пост-привязки.
- Служба доступности останавливается либо тогда, когда пользователь отключает ее в настройках устройства, либо когда она вызывает метод DisableSelf() .
Запуск службы
Прежде чем вы сможете запустить службу с помощью Android Studio, вам необходимо убедиться, что параметры запуска настроены правильно.
Отредактируйте конфигурацию запуска (используйте «Выполнить» в верхнем меню и перейдите в «Редактировать конфигурации». Затем в раскрывающемся списке измените параметр запуска с «Действия по умолчанию» на «Ничего»).
Теперь вы сможете запустить службу с помощью Android Studio.
Нажмите зеленый значок воспроизведения . из строки меню в верхнюю часть экрана. Затем перейдите в «Настройки» > «Доступность» и включите службу глобальной панели действий.
Вы должны увидеть четыре кнопки, образующие пользовательский интерфейс службы, наложенные поверх содержимого, отображаемого на экране.
Теперь вы добавите функциональность четырем кнопкам, чтобы пользователь мог прикасаться к ним для выполнения полезных действий.
5. Настройка кнопки питания
Добавьте метод configurePowerButton() в GlobalActionBarService.java :
private void configurePowerButton() {
Button powerButton = (Button) mLayout.findViewById(R.id.power);
powerButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
performGlobalAction(GLOBAL_ACTION_POWER_DIALOG);
}
});
}
Для доступа к меню кнопки питания configurePowerButton() использует метод PerformGlobalAction() , предоставляемый AccessibilityService . Код, который вы только что добавили, прост: нажатие кнопки запускает onClickListener() . При этом вызывается PerformGlobalAction(GLOBAL_ACTION_POWER_DIALOG) и отображается диалоговое окно включения питания для пользователя.
Обратите внимание, что глобальные действия не привязаны ни к каким представлениям. Нажатие кнопки «Назад», кнопки «Домой», кнопки «Недавние» — это другие примеры глобальных действий.
Теперь добавьте configurePowerButton() в конец метода onServiceConnected() :
@Override
protected void onServiceConnected() {
...
configurePowerButton();
}
Нажмите зеленый значок воспроизведения . из строки меню в верхнюю часть экрана. Затем перейдите в «Настройки» > «Доступность» и запустите службу глобальной панели действий.
Нажмите кнопку питания, чтобы отобразить диалоговое окно питания.
6. Настройка кнопки громкости
Добавьте метод configureVolumeButton() в GlobalActionBarService.java :
private void configureVolumeButton() {
Button volumeUpButton = (Button) mLayout.findViewById(R.id.volume_up);
volumeUpButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
AudioManager audioManager = (AudioManager) getSystemService(AUDIO_SERVICE);
audioManager.adjustStreamVolume(AudioManager.STREAM_MUSIC,
AudioManager.ADJUST_RAISE, AudioManager.FLAG_SHOW_UI);
}
});
}
Метод configureVolumeButton() добавляет метод onClickListener() , который срабатывает, когда пользователь нажимает кнопку громкости. Внутри этого прослушивателя configureVolumeButton() использует AudioManager для регулировки громкости потока.
Обратите внимание, что любой может контролировать громкость (для этого вам не обязательно быть службой доступности).
Теперь добавьте configureVolumeButton() в конец метода onServiceConnected() :
@Override
protected void onServiceConnected() {
...
configureVolumeButton();
}
Нажмите зеленый значок воспроизведения . из строки меню в верхнюю часть экрана. Затем перейдите в «Настройки» > «Доступность» и запустите службу глобальной панели действий.
Нажмите кнопку громкости, чтобы изменить громкость.
Гипотетический пользователь, который не может добраться до регуляторов громкости на боковой стороне устройства, теперь может использовать службу глобальной панели действий для изменения (увеличения) громкости.
7. Настройка кнопки прокрутки
Этот раздел включает в себя кодирование двух методов. Первый метод находит прокручиваемый узел, а второй метод выполняет действие прокрутки от имени пользователя.
Добавьте метод findScrollableNode в GlobalActionBarService.java :
private AccessibilityNodeInfo findScrollableNode(AccessibilityNodeInfo root) {
Deque<AccessibilityNodeInfo> deque = new ArrayDeque<>();
deque.add(root);
while (!deque.isEmpty()) {
AccessibilityNodeInfo node = deque.removeFirst();
if (node.getActionList().contains(AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_FORWARD)) {
return node;
}
for (int i = 0; i < node.getChildCount(); i++) {
deque.addLast(node.getChild(i));
}
}
return null;
}
Служба доступности не имеет доступа к фактическим изображениям на экране. Вместо этого он видит отражение того, что находится на экране, в виде дерева, состоящего из объектов AccessibilityNodeInfo . Эти объекты содержат информацию о представлении, которое они представляют (расположение представления, любой текст, связанный с представлением, метаданные, добавленные для обеспечения доступности, действия, поддерживаемые представлением и т. д.). Метод findScrollableNode() выполняет обход этого дерева в ширину, начиная с корневого узла. Если он находит прокручиваемый узел (т. е. узел, который поддерживает действие AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_FORWARD )
, он возвращает его, в противном случае он возвращает ноль.
Теперь добавьте метод configureScrollButton() в GlobalActionBarService.java :
private void configureScrollButton() {
Button scrollButton = (Button) mLayout.findViewById(R.id.scroll);
scrollButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
AccessibilityNodeInfo scrollable = findScrollableNode(getRootInActiveWindow());
if (scrollable != null) {
scrollable.performAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_FORWARD.getId());
}
}
});
}
Этот метод создает метод onClickListener() , который срабатывает при нажатии кнопки прокрутки. Он пытается найти прокручиваемый узел и в случае успеха выполняет действие прокрутки.
Теперь добавьте configureScrollButton() в onServiceConnected() :
@Override
protected void onServiceConnected() {
...
configureScrollButton();
}
Нажмите зеленый значок воспроизведения . из строки меню в верхнюю часть экрана. Затем перейдите в «Настройки» > «Доступность» и запустите службу глобальной панели действий.
Нажмите кнопку «Назад», чтобы перейти в «Настройки» > «Специальные возможности». Элементы в настройках специальных возможностей можно прокручивать, а нажатие кнопки «Прокрутка» выполняет действие прокрутки. Наш гипотетический пользователь, который не может легко выполнять действия прокрутки, теперь может использовать кнопку «Прокрутка» для прокрутки списка элементов.
8. Настройка кнопки прокрутки
Добавьте метод configureSwipeButton() в GlobalActionBarService.java :
private void configureSwipeButton() {
Button swipeButton = (Button) mLayout.findViewById(R.id.swipe);
swipeButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Path swipePath = new Path();
swipePath.moveTo(1000, 1000);
swipePath.lineTo(100, 1000);
GestureDescription.Builder gestureBuilder = new GestureDescription.Builder();
gestureBuilder.addStroke(new GestureDescription.StrokeDescription(swipePath, 0, 500));
dispatchGesture(gestureBuilder.build(), null, null);
}
});
}
Метод configureSwipeButton() использует новый API, добавленный в N, который выполняет жесты от имени пользователя. Код использует объект GestureDescription , чтобы указать путь для выполнения жеста (в этой лаборатории кода используются жестко закодированные значения), а затем отправляет жест пролистывания от имени пользователя с помощью метода AccessibilityService sendGesture() .
Теперь добавьте configureSwipeButton() в onServiceConnected() :
@Override
protected void onServiceConnected() {
...
configureSwipeButton();
}
Нажмите зеленый значок воспроизведения . из строки меню в верхнюю часть экрана. Затем перейдите в «Настройки» > «Доступность» и запустите службу глобальной панели действий.
Самый простой способ проверить работу свайпа — открыть приложение «Карты» , установленное на вашем телефоне. После загрузки карты нажатие кнопки «Провести» проведет по экрану вправо.
9. Резюме
Поздравляем! Вы создали простой и функциональный сервис специальных возможностей.
Вы можете расширить эту услугу различными способами. Например:
- Сделайте панель действий подвижной (пока она находится поверх экрана).
- Разрешить пользователю увеличивать и уменьшать громкость.
- Разрешите пользователю проводить пальцем влево и вправо.
- Добавьте поддержку дополнительных жестов, на которые может реагировать панель действий.
Эта лаборатория кода охватывает лишь небольшое подмножество функций, предоставляемых API специальных возможностей. API также охватывает следующее (неполный список):
- Поддержка нескольких окон.
- Поддержка AccessibilityEvent s. При изменении пользовательского интерфейса службы специальных возможностей уведомляются об этих изменениях с помощью объектов AccessibilityEvent. Затем служба может реагировать соответствующим образом на изменения пользовательского интерфейса.
- Возможность управления увеличением.
Эта лаборатория кода поможет вам начать писать службу специальных возможностей. Если вы знаете пользователя с конкретными проблемами доступности, которые вы хотели бы решить, теперь вы можете создать сервис, который поможет этому пользователю.