Перейти к содержимому

Как включить api accessibility service

  • автор:

В Android 13 Google ограничит доступ к спец. возможностям

В Google решили ограничить доступ к службам специальных возможностей (API AccessibilityService) приложениям, которые загружены из неизвестных источников. Такое изменение появится на устройствах под управлением Android 13. Выясняем: что это может означать для пользователей с нарушением зрения.

✅ Ограничение обусловлено соображениями безопасности пользователей.

Дело в том, что приложения, обращающиеся к специальным возможностям, получают гораздо больше прав на устройстве, чем обычные приложения. В частности, специальные возможности дают приложению доступ к информации, отображаемой на экране устройства. Эта возможность предназначена для программ экранного доступа для пользователей с нарушением зрения, например: TalkBack и Jieshuo (Commentary) Screenreader. Но и приложения, модифицированные злоумышленниками, могут, пользуясь правами специальных возможностей, отслеживать информацию, появляющуюся на экране, включая личные и платёжные данные. только в отличии от программы экранного доступа, вовсе не с целью озвучить данные для пользователя. Причём владелец устройства может и не подозревать о такой активности приложения.

✅ Ограничение коснётся только приложений, загруженных из неизвестных источников.

На устройство с системой Android пользователь может устанавливать приложения двумя способами:

  1. Загружать из официальных магазинов приложений (Google Play, F-droid, Samsung Galaxy Store и др.). Считается, что перед публикацией в магазине проводится проверка приложений на безопасность.
  2. Скачивать файл с расширением APK с других интернет-ресурсов. Такие приложения Google называет загруженными из неизвестных или ненадёжных источников. Есть опасность, что приложение могло быть изменено злоумышленниками.

Популярную среди незрячих пользователей Android-устройств программу экранного доступа Jieshuo (Commentary) Screenreader приходится устанавливать как раз вторым способом.

✅ Кстати, рассматриваемое ограничение — не единственное. Как сообщает сайт о технологиях 4PDA, Google с 11 мая ограничивает доступ к API AccessibilityService сторонним приложениям для записи телефонных разговоров. В прочем, опция записи звонков остаётся легальной для приложений, предустановленных производителями устройств.

Отметим, что пока нет подробностей, сложно с уверенностью говорить о том, как эти изменения отразятся на незрячих пользователях будущей версии системы Android (Android 13). Тем не менее, мы обратились за комментарием к Сергею Екатеринушкину — преподавателю, обучающему начинающих пользователей с нарушением зрения (г. Тамбов).

Комментарий Сергея Екатеринушкина

«Что касается записи звонков: смартфоны фирм Huawei и Xiaomi могут записывать телефонные разговоры через стандартное приложение «Диктофон» либо непосредственно в звонилки, то есть в приложение «Телефон». Китайские производители модифицировали эти приложения таким образом, чтобы они могли записывать звонки, и встроили их в систему. Так что подобные ограничения, вводимые компанией Google, могут быть обойдены производителями устройств на уровне модификации системы.

Что касается основного вопроса о специальных возможностях, то китайский Commentary screenreader работает, например, и на смартфонах Samsung. В устройствах Samsung свой набор сервисов отвечающих за спец. возможности. Приэтом сторонние приложения использующие API AccessibilityService, не могут их задействовать. Так, в тех местах где активируется элемент обычным двойным касанием, например, при срабатывании стандартного будильника, с Commentary screenreader приходится провести двумя пальцами снизу вверх либо в всторону.

Ввод текста на фирменной клавиатуре Samsung тоже не даёт для Commentary весь свой функционал. Так при наборе текста с Commentary и клавиатурой Samsung нужно включить отображение клавиши смены раскладки (Глобус) в настройках самой клавиатуры. Другая проблема — это отсутствие функции «Ввод одним касанием», когда мы отпускаем символ, чтобы его ввести. Приходится вводить букву двойным касанием, как активируем обычный элемент управления на экране. Эти мелкие неудобства обходятся установкой клавиатуры Google.

Также, на некоторых моделях Samsung фирменный браузер Samsung internet не даёт Commentary читать web-странички. Но ведь Android — открытая ОС. Есть альтернативы в виде браузеров Chrome и FireFox.

Таким образом, можно сделать вывод, что возможны небольшие неудобства, с которыми столкнутся незрячие пользователи при обновлении до Android 13. Уверен, что часть проблем решиться заменой некоторых стандартных приложений, таких как клавиатура, либо использованием новых жестов, либо разработчики Commentary screenreader со временем исправят возникшие проблемы, а активное сообщество пользователей в этом поможет.

А если вы — начинающий пользователь, то переждите этот период адаптации и не обновляйте своё устройство до тех пор, пока не решатся критичные для вас проблемы. Но паниковать точно не стоит», — заключает Сергей Екатеринушкин.

Хотите оперативно узнавать о новых публикациях? Присоединяйтесь к нашей группе в WhatsApp.

Благодарим Вас за внимание! Пожалуйста, поделитесь с друзьями в соцсетях.

Контроль над Android. Перехватываем данные и сбрасываем настройки смартфона через самые опасные API

Кро­ме тра­дици­онных раз­решений, в Android есть три мета‑раз­решения, которые откры­вают дос­туп к весь­ма опас­ным API, поз­воля­ющим в пря­мом смыс­ле зах­ватить кон­троль над устрой­ством. В этой статье мы научим­ся их исполь­зовать, что­бы прог­рам­мно нажимать кноп­ки смар­тфо­на, перех­ватывать уве­дом­ления, извле­кать текст из полей вво­да дру­гих при­ложе­ний и сбра­сывать нас­трой­ки смар­тфо­на.

О каких API пой­дет речь?

  1. Ад­минис­три­рова­ние устрой­ства — API, пред­назна­чен­ный для кор­поратив­ных при­ложе­ний. Поз­воля­ет сбра­сывать и уста­нав­ливать пароль экра­на бло­киров­ки, сбра­сывать смар­тфон до завод­ских нас­тро­ек и уста­нав­ливать пра­вила минималь­ной слож­ности пароля. Одна из осо­бен­ностей API — зап­рещено уда­лять при­ложе­ния, получив­шие пра­ва адми­нис­тра­тора, чем с радостью поль­зуют­ся авто­ры злов­редных при­ложе­ний.
  2. Accessibility — API для реали­зации при­ложе­ний, ори­енти­рован­ных на людей с огра­ничен­ными воз­можнос­тями. Фак­тичес­ки API поз­воля­ет соз­давать аль­тер­натив­ные спо­собы управле­ния устрой­ством и поэто­му откры­вает поис­тине огромный прос­тор для зло­упот­ребле­ния. С его помощью мож­но получить дос­туп к содер­жимому экра­на прак­тичес­ки любого при­ложе­ния, нажимать кноп­ки интерфей­са и прог­рам­мно нажимать кла­виши самого смар­тфо­на. Но есть и спо­соб защиты: раз­работ­чик при­ложе­ния может пря­мо ука­зать, что опре­делен­ные эле­мен­ты интерфей­са при­ложе­ния будут недос­тупны для сер­висов Accessibility.
  3. Уве­дом­ления — API, поз­воля­ющий получить дос­туп ко всем уве­дом­лени­ям, которые отоб­ража­ются в панели уве­дом­лений. С помощью это­го API при­ложе­ние может про­читать всю информа­цию об уве­дом­лении, вклю­чая заголо­вок, текст и содер­жимое кно­пок управле­ния, нажать на эти кноп­ки и даже смах­нуть уве­дом­ление. API поль­зует­ся осо­бой популяр­ностью сре­ди раз­работ­чиков все­воз­можных бан­ков­ских тро­янов, с помощью которо­го они могут читать коды под­твержде­ния и сма­хивать пре­дуп­режда­ющие сооб­щения от бан­ков.

По­лучив дос­туп ко всем этим API, злов­редное при­ложе­ние смо­жет сде­лать со смар­тфо­ном прак­тичес­ки все что угод­но. Имен­но поэто­му для их защиты исполь­зуют­ся не тра­дици­онные зап­росы пол­номочий, на которые поль­зователь может машиналь­но отве­тить «Да», а скры­тый глу­боко в нас­трой­ках интерфейс, который при акти­вации покажет угро­жающее сооб­щение. Все, что может сде­лать при­ложе­ние, что­бы получить нуж­ное пол­номочие, — это переб­росить поль­зовате­ля в окно нас­тро­ек, пос­ле чего тот дол­жен будет най­ти нуж­ное при­ложе­ние, вклю­чить нап­ротив него перек­лючатель и сог­ласить­ся с пре­дуп­режда­ющим сооб­щени­ем.

Зас­тавить поль­зовате­ля дать раз­решение на исполь­зование этих API мож­но обма­ном. Зачас­тую злов­реды при­киды­вают­ся легитим­ными при­ложе­ниями, которым раз­решение нуж­но для работы клю­чевой фун­кци­ональ­нос­ти. К при­меру, это может быть при­ложе­ние для ведения жур­нала уве­дом­лений или при­ложе­ние для аль­тер­натив­ной жес­товой навига­ции (такому при­ложе­нию нужен сер­вис Accessibility для нажатия кно­пок навига­ции). Так­же мож­но исполь­зовать ата­ку Cloak & Dagger, что­бы перек­рыть окно нас­тро­ек дру­гим безобид­ным окном.

Нажимаем кнопки смартфона

Прос­тей­ший сер­вис Accessibility может выг­лядеть так (код на Kotlin):

Что­бы сис­тема узна­ла о нашем сер­висе, его необ­ходимо объ­явить в AndroidManifest.xml :

Это опи­сание ссы­лает­ся на кон­фигура­цион­ный файл accessibility_service_config.xml , который дол­жен быть опре­делен в катало­ге xml про­екта. Для нашего слу­чая дос­таточ­но будет такого кон­фига:

Пос­ле того как поль­зователь вклю­чит наш сер­вис Accessibility в окне «Нас­трой­ки → Спец. воз­можнос­ти», сис­тема авто­мати­чес­ки запус­тит сер­вис и мы смо­жем выпол­нить фун­кцию pressHome() , что­бы нажать кноп­ку «Домой»:

// Если service не null — значит, система успешно запустила сервисif (AccessService.service != null)

Од­ной лишь толь­ко этой фун­кци­ональ­нос­ти дос­таточ­но, что­бы реали­зовать Ransomware, который будет вызывать фун­кцию pressHome() в цик­ле и бес­конеч­но воз­вра­щать поль­зовате­ля на домаш­ний экран, не давая нор­маль­но исполь­зовать устрой­ство.

Контроль над Android. Перехватываем данные и сбрасываем настройки смартфона через самые опасные API
Ок­но вклю­чения сер­виса Accessibility в Android 11

Од­нако нас­тоящая мощь Accessibility кро­ется не в нажатии кно­пок навига­ции, а в воз­можнос­ти кон­тро­лиро­вать дру­гие при­ложе­ния.

Перехватываем содержимое полей ввода

API Accessibility был соз­дан для людей с огра­ничен­ными воз­можнос­тями. С его помощью мож­но, нап­ример, соз­дать при­ложе­ние, которое будет зачиты­вать все над­писи интерфей­са и поз­волит нажимать эле­мен­ты интерфей­са голосом. Все это дос­тижимо бла­года­ря тому, что Accessibility дает пол­ный дос­туп к интерфей­су при­ложе­ний в виде дерева эле­мен­тов: мож­но прой­ти по нему и выпол­нить над эле­мен­тами опре­делен­ные опе­рации.

Продолжение доступно только участникам

Материалы из последних выпусков становятся доступны по отдельности только через два месяца после публикации. Чтобы продолжить чтение, необходимо стать участником сообщества «Xakep.ru».

Присоединяйся к сообществу «Xakep.ru»!

Членство в сообществе в течение указанного срока откроет тебе доступ ко ВСЕМ материалам «Хакера», позволит скачивать выпуски в PDF, отключит рекламу на сайте и увеличит личную накопительную скидку! Подробнее

Адаптируем Android приложение для незрячих людей. Часть 1: основы

В один из зимних вечеров я сидел дома, читал замечательную книжку «Android-программирование для профессионалов» и наткнулся на главу про Accessibility.

Раньше я об этой теме не задумывался: клал null в поле contentDescription и жил себе спокойно. Но когда получил поверхностные знания, как слабовидящие люди пользуются смартфоном через утилиту TalkBack, у меня появилось желание погрузиться в тему доступности глубже.

Меня зовут Тимур Задворнов, я Android-разработчик в Surf. Расскажу и покажу, как работает Accessibility в Android. Посмотрим, легко ли адаптировать приложения для людей с ограниченными возможностями — или это слишком трудоёмкая задача, к которой даже не стоит подступаться.

«Разработчики и contentDescription». 2022, холст, масло

Погружаться в тему Accessibility я начал с гуглежа статистики. По данным ВОЗ, в мире более миллиарда человек обладает с той или иной формой инвалидности. «Немало», — подумал я и сразу задался другим вопросом: а почему, если в мире так много людей с инвалидностью, огромное количество популярных и не очень приложений не поддерживают приложения для слабовидящих, глухих, людей с заболеваниями опорно-двигательного аппарата? Решил, что надо исправлять это недоразумение и двигать Accessibility в массы.

Для начала разберемся, что вообще такое Accessibility

Accessibility в переводе с английского — «доступность». В контексте программного обеспечения — разработка ПО таким образом, чтобы им могли пользоваться люди с ограниченными возможностями. У термина есть общепринятое сокращение — a11y: между a и y в слове accessibility 11 букв.

Чтобы разобраться, как работает Accessibility, предлагаю задаться вопросом: а как в целом люди пользуются смартфоном? Пользователь взаимодействует с устройством, передавая ему действия: нажатия, свайпы, потряхивания. Смартфон возвращает результат действия, отображая его на экране.

Мы взаимодействуем со смартфоном как привыкли: нажимаем на экран и получаем результат действия. Люди с ограниченными возможностями пользуются смартфоном точно так же, просто они передают и получают информацию со смартфона другими путями: отправляют голосовые команды, команды с другого устройства ввода. Способов много — подход общий.

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

В Android существуют встроенные Accessibility Services: TalkBack, Switch Access, Voice Access и прочие. О них можно думать как о плагинах для Android, которые помогают людям с ограниченными возможностями пользоваться смартфоном.

Под термином «Accessibility Services» будут подразумеваться перечисленные сервисы, а не Android-сервисы для работы в бэкграунде. Пишу об этом, потому что в Android есть одноименный сервис — AccessibilityService, который работает в бэкграунде с отправляемыми ему AccessibilityEvent.

Какие Accessibility Services существуют

С каждой новой версией Android выкатывает всё больше новых сервисов для пользователей с ограниченными возможностями. Сейчас в Android есть:

  • TalkBack — зачитывает информацию о UI-компонентах;
  • Switch Access — управление устройством через специальный контроллер;
  • Voice Access — голосовое управление смартфоном;
  • Select to Speak — зачитывает текстовый контент в выделенной области;
  • Extra Dim — настройки яркости дисплея;
  • Magnification — экранная лупа;
  • Live Captions — экранные субтитры;
  • Live Transcribe — перевод речи в текст;
  • Sound Amplifier — управление звуком;
  • Lookout — распознавание объектов через камеру;
  • Action Blocks — создание сценариев использования.

И это ещё, возможно, не всё!

Остановимся подробнее на первых трёх сервисах: TalkBack, Switch Access, Voice Access. Их используют чаще всего: именно с ними придётся сталкиваться разработчику в повседневной работе. Остальные — вспомогательные. Более подробно про них можно посмотреть в официальной документации.

TalkBack

TalkBack — утилита, которая зачитывает информацию с экрана. Помогает слабовидящим людям пользоваться смартфоном.

TalkBack меняет логику управления смартфоном: пользователь выделяет нужный компонент, и система зачитывает содержимое. Компоненты экрана можно выделять либо тапом, свайпами влево и вправо или зажать палец на экране и двигать его — тогда TalkBack будет зачитывать каждый элемент, на который наведён жест. Свайп вправо — выделится следующий элемент на экране, свайп влево — предыдущий.

Все привычные обычному пользователю жесты — назад, свернуть, меню многозадачности — работают, но их нужно воспроизводить двумя пальцами. Все действия и жесты TalkBack описаны в документации.

На каждом смартфоне с Android на борту можно включить TalkBack и пройти туториал по использованию: всё это делается в настройках, пункт Accessibility.

В настройках TalkBack есть меню с настройками для разработчиков. Можно включить субтитры или добавить шорткат быстрого включения и выключения TalkBack — зажать кнопки громкости на несколько секунд. Очень помогает во время разработки.

Switch Access

Switch Access — сервис для управления смартфоном с помощью специального устройства, переключателя.

Так может выглядеть переключатель

Принцип взаимодействия со смартфоном схож с принципом работы через TalkBack. На экране выделен компонент, между компонентами можно переключаться. Обычно на переключателе есть несколько кнопок. В качестве переключателя можно использовать специальное устройство или настроить стороннюю клавиатуру, а также кнопки громкости на смартфоне для удобного дебаггинга.

Voice Access

Voice Access — сервис для голосового управления смартфоном. Пользователь говорит, какое действие нужно сделать на экране: нажать кнопку «Лайк», пролистать вниз, ввести текст в поле «Сообщение». Действие выполняется. На видео — Voice Access в действии.

На самом деле Voice Access далеко не идеальная вещь: иногда тупит и некорректно распознаёт слова — особенно названия приложений.

Что происходит со стороны разработчиков

Давайте посмотрим, какие инструменты есть для разработчиков, чтобы UX приложений не страдал. Хорошие новости: все компоненты из стандартной библиотеки Android по умолчанию поддерживают Accessibility. Не придётся с нуля разбираться и делать доступными базовые компоненты: TextView, EditText, Switch и другие.

Приложение Accessibility Scanner для помощи разработчикам

Google выпустил для разработчиков приложение Accessibility Scanner: оно помогает найти некомфортные для людей с ограниченными возможностями места в приложении.

Accessibility Scanner работает просто:

Результаты работы Accessibility Scanner на экране приложения Twitter. Оранжевым подсвечиваются проблемные места. Есть подсказки, как исправить ошибки, с пояснениями, почему нужно сделать так

  • Открываем, даём все разрешения, оно отображается над всеми приложениями как плавающая кнопка.
  • Заходим в приложение, которое нужно проверить, переходим на нужный экран, нажимаем на кнопку Snapshot.
  • Ждём, пока Accessibility Scanner проанализирует экран, и смотрим на результат.

Accessibility Scanner — отличная штука, но у него есть существенный недостаток. Сканер находит только очевидные проблемы: размер текста, размер кликабельной области компонента, отсутствие лейбла у изображения и так далее. В более сложных случаях он бесполезен. Например, не поможет с распознаванием кастомных вью и правильной интерпретацией для слабовидящих: что зачитывать с этого компонента, какие действия с ним можно совершить.

Давайте разберемся, как сделать приложение более доступным с помощью нескольких простых шагов: они закроют процентов 90 всевозможных кейсов.

Шрифты

Правило простое: всегда указывайте размер шрифтов в sp! Вы меня спросите: «Зачем для шрифтов придумали какую-то отдельную единицу? Если я укажу размер в dp, никто не умрёт».

Если бы всё было так просто, мои вы хорошие

Если пользователь увеличит шрифты в системе, нужно, чтобы они увеличились и в приложении. Для этого и пригодится sp-юнит: если указать размер текста 16sp, а пользователь в системе поставил коэффициент увеличения текста = 1,25, то размер шрифта станет 20dp. На иллюстрациях видно, как изменяется размер шрифта, указанный в sp.

Заголовки

TalkBack предоставляет удобную навигацию между заголовками. Если на экране много текста с заголовками или, например, на экране есть список с подсписками, которые также помечены заголовками, в эти TextView дополнительно следует положить true в поле accessibilityHeading . Тогда текст будет считываться как заголовок.

Этот параметр появился в Android 9. Если нужно использовать его на более старых версиях, эту проблему решает compat-версия этого параметра — ViewCompat.setAccessibilityHeading(view, boolean) .

Контраст

Люди с нарушениями зрения могут воспринимать цвета по-другому: стоит следить, все ли элементы на экране чётко различимы между собой, есть ли цветовой контраст. По гайдлайнам Android, хорошая практика — устанавливать цветовой контраст на наложенных друг на друга компонентах с соотношением более чем 3:1 для больших текстов и 4,5:1 для остального контента.

Что это за цифры? Это соотношение между цветами, которое показывает, насколько сильно контрастируют два цвета между собой. Чтобы было понятно, разберем на примере: соотношение белого цвета (#FFFFFF) к чёрному (#000000) равняется 21:1 — это максимальное возможное соотношение. Два одинаковых цвета имеют отношение 1:1. Проверить контраст между цветами можно на сайте WebAIM.

Пример плохого и хорошего контраста

КАПС — НЕ НАШ БРО

На некоторых девайсах TalkBack читает текст, написанный заглавными буквами, как аббревиатуру – по букве. Сами понимаете, воспринимать это на слух очень трудно.

Заглавными буквами следует писать только аббревиатуры. Основной текст — строчными буквами или sentence case. Если всё-таки нужно оставить текст заглавными буквами, используйте параметр android:textAllCaps=”true” — и тогда всё будет ок.

Размеры элементов

Любые кликабельные элементы на экране должны иметь размер как минимум 48x48dp. Такой размер рекомендован Google: область нажатия пальцем на экран равняется приблизительно этому значению. В вебе, например, рекомендованный размер 44x44px.

Content Description

Не будем лукавить: все мы когда-то устанавливали поле contentDescription в null , потому что «да зачем оно мне надо». Но если вы хотите, чтобы приложение было доступным, придётся разбираться, что это за поле за такое.

Поле contentDescription используется в основном для визуальных компонентов без текста — ImageButton , ImageView и так далее. Это нужно, чтобы TalkBack при наведении на этот элемент не зачитал «Unlabeled, Button, double-tap to activate», а дал конкретную информацию: что за элемент, зачем он нужен на экране и что произойдет, если нажать на него. Эта строка обязательно должна быть локализована: если пользователь из Франции запустит TalkBack, а ему на чистом японском зачитают «дескрипшен», вряд ли кому-то от этого станет легче. И вообще: любые строки, связанные с a11y, будь то contentDescription или дополнительные действия TalkBack, о которых мы поговорим далее, должны быть локализованы.

В этой статье я буду использовать английскую версию TalkBack и локализованные строки так же на английском языке. Не беспокойтесь: на русском TalkBack тоже работает.

Если ImageView имеет на экране чисто декоративную роль, contentDescription можно не указывать. Тогда важно указать другой параметр — importantForAccessibility . Он отвечает за необходимость прочтения TalkBack содержимого этого компонента. У этого параметра есть значения:

  • yes — компонент обязателен для прочтения TalkBack.
  • no — компонент пропускается.
  • noHideDescendants — компонент и его дочерние компоненты пропускаются.
  • auto — система сама определяет, обязателен ли компонент для TalkBack. Это значение по умолчанию.
Live Regions

Иногда нужно при действии с одним компонентом на экране обновить значение компонента, не находящегося в фокусе. Для этого у View в Android есть поле accessibilityLiveRegion , которое позволяет зачитать значение из вью, если оно было изменено. У accessibilityLiveRegion есть три возможных значения:

  • none — ничего не обновлять, значение по умолчанию.
  • polite — если значение обновилось, но TalkBack ещё воспроизводит старое значение, новое значение зачитается после того, как TalkBack закончит говорить.
  • assertive — противоположное значению polite . При обновлении значения TalkBack перестанет зачитывать старое значение и сразу начнёт зачитывать новое.

Проще понять на примере: у нас есть кнопка Increment, которая увеличивает значение на 1 и отображает новое значение в TextView. Изменяем значение accessibilityLiveRegion у TextView на polite и вот какой будет результат.

Порядок навигации между View

В идеале порядок должен идти слева направо, сверху вниз, но иногда этот порядок приходится менять для более удобной навигации между компонентами. На картинке ниже — как раз такой пример. Он надуманный, это можно сделать гораздо лучше: объединить заголовок и значение или использовать параметр labelFor .

На этом примере хотелось бы, чтобы после чтения заголовка читалось значение, для которого предназначен этот заголовок. Чтобы изменять порядок навигации, у всех View есть поле accessibilityTraversalBefore . В него нужно передать идентификатор компонента, на который мы хотим переключаться после фокуса текущей выделенной вью.

Также у View есть противоположный параметр — accessibilityTraversalAfter . В нём лежит идентификатор компонента, после которого текущий компонент будет выделен TalkBack. Иногда случается, что нужно задать параметр динамически через код: для этого у объекта класса AccessibilityNodeInfo вызвать метод setTraversalBefore или setTraversalAfter . Подробнее про AccessibilityNodeInfo и AccessibilityDelegate расписано далее.

Давайте укажем к каждому TextView с заголовком параметр android:accessibilityTraversalBefore=»@id/value_textview» , где @id/value_textview — идентификатор соответствующего заголовка TextView со значением. Получим следующий порядок навигации:

Кастомные View

Обычно кастомные вью состоят из нескольких более простых компонентов: текстов, кнопок, свитчеров. Хорошая практика — делать так, чтобы компонент выделялся TalkBack как единый, а не выделял отдельно компоненты-составные, и предлагал какие-то действия что можно сделать с этим сложным компонентом.

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

Кастомная вью, которую мы будем делать доступной

По умолчанию, если ничего не делать, TalkBack просто зачитает текстовое содержимое компонента: название кошелька, баланс и адрес.

Что зачитывает TalkBack

Как можно улучшить UX для незрячих? Сделаем компонент более доступным за несколько простых шагов.

1. Все дочерние элементы пометить importantForAccessibility = no , чтобы TalkBack не читал ничего, кроме кнопок «пополнить» и «перевести». С ними разберёмся позже.

2. Даём корневому элементу понятный contentDescription . Нужно определить, какая информация полезна для прочтения TalkBack. Пользователю будет полезно знать название кошелька и баланс. Обновлять contentDescription будем в функции setData , которая вызывается при инициализации компонента.

fun setData( title: String, balance: String, address: String ) = with(binding) < titleTv.text = title balanceTv.text = balance addressTv.text = address // Строка со значением "Wallet card: %s, balance is %s" root.contentDescription = context.getString( R.string.wallet_card_accessibility_description, title, balance ) >

3. Обрабатываем взаимодействие с компонентом. Какой смысл этого компонента? Что он делает? Выделим список возможных действий:

  • По нажатию на карточку переходим на экран деталей по кошельку.
  • По нажатию на кнопку «Перевести» или «Пополнить» переходим на экран переводов. С кнопками делать ничего не нужно, мы их не помечали как игнорируемые и они по умолчанию поддерживают accessibility.
  • Возможность скопировать адрес кошелька.
  • Возможность отобразить адрес в формате QR-кода.

Для перехода к деталям кошелька почти ничего не требуется, если уже назначен слушатель на событие нажатия на карточку. Единственное, что стоит переопределить, — название действия. По умолчанию при нажатии на кликабельный элемент TalkBack будет зачитывать «Double tap to activate» Это понятно, если элемент — кнопка или свитчер. Но если компонент сложный, «activate» не передаёт нужного контекста. Вот бы было можно заменить описание действия, дав больше контекста. И — сюрприз — так можно!

Каждая вью содержит сущность AccessibilityDelegate . Согласно документации, делегат позволяет делать компоненты доступными не с помощью наследования, а с помощью композиции. Он может перехватывать события с UI, создавать кастомные экшны, редактировать существующие — и ещё много вещей, которые прокачают доступность компонентов.

Давайте создадим делегата для нашей карточки. Будем использовать класс ViewCompat для обратной совместимости. Всё это опишем в методе setupAccessibility , который также вызываем при инициализации компонента.

ViewCompat.setAccessibilityDelegate( binding.root, object : AccessibilityDelegateCompat() < override fun onInitializeAccessibilityNodeInfo( host: View, info: AccessibilityNodeInfoCompat ) < super.onInitializeAccessibilityNodeInfo(host, info) val customAction = AccessibilityNodeInfoCompat.AccessibilityActionCompat( AccessibilityNodeInfoCompat.ACTION_CLICK, // Строка со значением "View wallet details" context.getString(R.string.wallet_card_accessibility_card_click_action) ) info.addAction(customAction) >> )

Вы великолепны. Теперь TalkBack при наведении на карточку зачитает действие как «Double tap to view wallet details».

Разберёмся с функциональностью, связанной с адресом. Для компонента помимо основных действий — нажатия, долгого нажатия и так далее — можно добавлять кастомные действия и вызывать их через меню TalkBack. Чтобы открыть меню, нужно при включенном TalkBack сделать L-образный свайп сверху вниз или свайп тремя пальцами вниз — если версия TalkBack выше 9.1. Для добавления кастомного действия используются:

  • Функция ViewCompat.addAccessibilityAction , куда передаём корневую вью и название действия.
  • Класс AccessibilityViewCommand , в которое кладём код с действием, — просто лямбда.
private fun setupAccessibility() < . . . ViewCompat.addAccessibilityAction( binding.root, // Строка "Show address QR code" context.getString(R.string.wallet_card_accessibility_show_qr_code_action) ) < _, _ ->// Открываем диалог с QR здесь Toast.makeText(context, "QR code has shown", Toast.LENGTH_SHORT).show() true > >

По такой же логике добавляем второе действие — копирование адреса кошелька.

private fun setupAccessibility() < . . . ViewCompat.addAccessibilityAction( binding.root, // Строка "Copy the wallet address" context.getString(R.string.wallet_card_accessibility_copy_the_address_action) ) < _, _ ->// Кладём строку с адресом в буфер обмена Toast.makeText(context, "Address has copied", Toast.LENGTH_SHORT).show() true > >

Кастомная вью теперь гораздо доступнее, чем была до этого. Вот так это будет выглядеть со стороны пользователя.

Можно и дальше придумывать, как сделать этот компонент более доступным. Я разобрал базовые случаи и инструменты для работы с Accessibility. Например, ещё придумал, что можно добавить кастомный экшн на чтение адреса полностью или первых и последних пяти символов.

Если получается много однотипного кода, можно вынести повторяющиеся вещи в функции-расширения. Кастомные действия также подходят для более сложных случаев взаимодействия с UI: перетаскивание элементов в списке, swipe-to-delete и прочие.

Возьмём другой пример. Пойдём дальше по тематике приложения с криптовалютами. У нас есть такой вот глупенький кастомный свитч, у которого есть 2 состояния: пополнить или перевести. Внутри это просто LinearLayout с двумя кнопками.

Кастомный свитч

По смыслу это один компонент, но если ничего не делать, TalkBack будет читать эти две кнопки как отдельные компоненты. Чтобы сделать свитчер более доступным, нужно следующее:

1. Дать осмысленное название свитчу с помощью параметра contentDescription . В этот раз мы можем сделать это прямо в XML, потому что контент вью неизменный, в отличие от прошлой вью — там у нас на входе были название кошелька и баланс.

2. Все кнопки пометить importantForAccessibility = no , чтобы TalkBack не выделял их.

3. У этого компонента два состояния: пополнить или перевести. По смыслу эта кастомная вью — свитчер, но TalkBack про это ничего не знает. Чтобы добавить состояние для компонента, есть поле stateDescription . А зачем оно нужно? Мы же просто можем класть состояние в contentDescription и эффект будет тот же.

Всё опять не так просто, как хотелось бы

Каждый раз, когда мы обновляем состояние компонента и, следовательно, обновляем contentDescription , TalkBack будет его зачитывать.

В contentDescription , помимо состояния, есть ещё описание самого компонента. Чтобы его не повторять, а воспроизводить только изменённое состояние, это состояние выделили в отдельное поле. Хорошая практика: в contentDescription класть только описание компонента — «Operation type». Состояние класть в stateDescription — «Deposit» или «Transfer». Давайте обновлять у нашего свитчера состояние при изменении:

depositButton.setOnClickListener < depositButton.select() transferButton.deselect() updateStateDescription() >transferButton.setOnClickListener
private fun updateStateDescription() < ViewCompat.setStateDescription( binding.root, context.getString( if (binding.transferButton.isSelected) < R.string.operation_switch_transfer_label >else < R.string.operation_switch_deposit_label >) ) >

4. Помимо состояния, вью обозначает, что за компонент и какую роль он играет на экране: кнопки, переключателя, чекбокса, меню-бара и так далее. Можно просто закинуть эту роль в contentDescription — но, конечно, это плохая практика.

Есть два способа обозначить роли: указать className или использовать поле roleDescription . В чём их отличие? Поле className следует перегружать, если кастомный компонент имеет роль стандартного компонента: Button, Checkbox, RadioButton — просто его вёрстка и реализация отличаются.

Если компонент уникальный и у него нет аналогов в библиотеке стандартных компонентов, можно явно указать его название его название с помощью параметра roleDescription . Такими компонентами могут быть рекламные баннеры или кастомные меню-панели. В нашем случае мы создаём аналог Switch, поэтому будем использовать установку роли через className .

ViewCompat.setAccessibilityDelegate( root, object : AccessibilityDelegateCompat() < override fun onInitializeAccessibilityNodeInfo( host: View, info: AccessibilityNodeInfoCompat ) < super.onInitializeAccessibilityNodeInfo(host, info) info.className = Switch::class.java.name // Если вам нужно указать отличную от стандартной роль info.roleDescription = context.getString(R.string.role_description_switch) >> )

5. Мы хотим, чтобы при двойном нажатии на вью при включенном TalkBack свитчер менял своё значение. Этого мы можем добиться с помощью функции ViewCompat.replaceAccessibilityAction . Она переопределяет поведение переданного в функцию экшна, в нашем случае ACTION_CLICK на поведение, переданное в лямбде.

ViewCompat.replaceAccessibilityAction( root, AccessibilityNodeInfoCompat.AccessibilityActionCompat.ACTION_CLICK, // Строка "Toggle" context.getString(R.string.operation_switch_accessibility_toggle_action) ) < _, _ ->if (transferButton.isSelected) < depositButton.performClick() >else < transferButton.performClick() >true >

Всё! Что у нас получилось в итоге, можно посмотреть на видео ниже. Все примеры кода лежат у меня на GitHub.

Accessibility проще, чем кажется

  • Всё не так сложно, как казалось на первый взгляд. Не нужно создавать отдельную версию приложения для людей с ограниченными возможностями.
  • Стоит чаще запускать Accessibility Scanner и исправлять ошибки. Не забываем про указание заголовков для удобной навигации, не кладём null в contentDescription , следим за порядком навигации между компонентами и делаем кастомные вью доступными!
  • Все строки, связанные с a11y, следует выносить в strings.xml и делать для них локализацию для языков, которые поддерживаются приложением.
  • Все стандартные компоненты Android SDK по умолчанию поддерживают a11y.
  • Во время тестирования приложения попробуйте попользоваться им, не подглядывая на экран, — только через TalkBack. Может быть, вы найдете моменты, которые можно было бы сделать лучше. ��

В поддержке Accessibility одни плюсы: люди с ограниченными возможностями смогут пользоваться приложениями без неудобств, в то же время в большинстве случаев у разработчиков поддержка не занимает много времени и ресурсов. Правда, порой встречаются очень необычные кейсы, где поддержать a11y — это вызов для всей команды разработки. Примерами могут послужить сложные кастомные вью, нарисованные на Canvas, большие таблицы с огромным количеством данных или графики.

Больше полезного про Android — в нашем телеграм-канале Surf Android Team. Здесь мы публикуем кейсы, лучшие практики, новости и вакансии Surf, а также проводим прямые эфиры. Присоединяйтесь!

Больше кейсов команды Surf ищите на сайте >>

  • surf
  • accessibility
  • доступность
  • мобильная разработка
  • разработка под андроид
  • разработка под android
  • talkback
  • разработка мобильных приложений

Как программно включить/отключить службу специальных возможностей в Android

Я хотел бы программно включить/отключить службы Accessibility, перечисленные в разделе Настройки->Доступность.

Я мог бы запустить Intent Accessibility следующим образом:

Intent intent = new Intent(android.provider.Settings.ACTION_ACCESSIBILITY_SETTINGS); startActivityForResult(intent, 0); 

Но я не имею никакого представления о том, как включить службы, перечисленные в представлении, через мой код.

Пожалуйста, предоставьте мне свои представления.

Поделиться Источник 08 апреля 2012 в 07:28

11 ответов

Я нашел решение, которое сработало для меня, вызвав

Settings.Secure.putString(getContentResolver(), Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES, "pkgname/classname"); Settings.Secure.putString(getContentResolver(), Settings.Secure.ACCESSIBILITY_ENABLED, "1"); 

Где pkgname — это имя вашего пакета, а classname — это имя класса вашей службы доступности.

Если вам нужно включить несколько служб или вы не хотите уничтожать предыдущие настройки, вы можете использовать : для разделения других служб.

Также вам может понадобиться запустить как системное приложение, и вам могут понадобиться следующие разрешения

Однако, согласно @Rupert Rawnsley, это может не работать на некоторых версиях Android, я работаю на Android 4.0.4 и надеюсь, что это сработает для вас.

PS. Если это не сработает, возможно, вы найдете удачу в /data/data/com.android.providers.settings/databases/settings.db/secure ,, где Android хранит защищенные настройки.

Поделиться 29 января 2015 в 08:24

Начиная с Android 6.0, вы можете использовать:

adb shell settings put secure enabled_accessibility_services packagname/servicename 

Настройки settings.db из старых релизов больше не присутствуют на Android 6.0.

Поделиться 14 августа 2016 в 05:01

В Android 7 (API 24), AccessibilityService может программно отключиться, вызвав метод disableSelf().

Поделиться 07 ноября 2016 в 09:10

AccessibilityService является специальным и не может быть запущен программно.

Поделиться 24 мая 2012 в 15:33

Лучшее, что вы можете сделать, это вручную открыть настройки доступности с помощью:

Интент intent = new Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS)

и запустить интент — вы также можете сделать это из предпочтительного XML-файла:

Поделиться 03 декабря 2014 в 12:24

В инструментальных тестах я запускаю свой сервис доступности вот так:

private fun enableAccessibilityService() < val packageName = "com.example" val className = "$packageName.service.MyService" val string = "enabled_accessibility_services" val cmd = "settings put secure $string $packageName/$className" InstrumentationRegistry.getInstrumentation() .getUiAutomation(UiAutomation.FLAG_DONT_SUPPRESS_ACCESSIBILITY_SERVICES) .executeShellCommand(cmd) .close() TimeUnit.SECONDS.sleep(3) >

Я тестировал на Android 6 и 8. Это также работает для несистемных приложений.

Поделиться 03 августа 2018 в 12:34

Вот рабочее решение (если ваша активность не запущена, оно отключится)

@Override protected void onServiceConnected() < super.onServiceConnected(); Toast.makeText(this, "Connected!",Toast.LENGTH_SHORT).show(); ScheduledExecutorService scheduleTaskExecutor = Executors.newScheduledThreadPool(1); scheduleTaskExecutor.scheduleAtFixedRate(new Runnable() < public void run() < serviceChecker(); >>, 0, 5, TimeUnit.SECONDS); > private void serviceChecker()< if(!isActivityRunning(MainActivity.class)) < if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) < disableSelf(); >> > protected Boolean isActivityRunning(Class activityClass) < ActivityManager activityManager = (ActivityManager) getBaseContext().getSystemService(Context.ACTIVITY_SERVICE); Listtasks = activityManager.getRunningTasks(Integer.MAX_VALUE); for (ActivityManager.RunningTaskInfo task : tasks) < if (activityClass.getCanonicalName().equalsIgnoreCase(task.baseActivity.getClassName())) return true; >return false; > 

Поделиться 24 марта 2019 в 03:23

На всякий случай, если кто-то все еще хочет отключить обратный разговор из adb, когда вы застряли в экране блокировки, вводя пароль или закрепление. Одна вещь, которую вы можете попробовать, это adb shell am force-stop com.google.android.marvin.talkback

Поделиться 20 марта 2017 в 23:18

Вы можете включить службы доступности для нескольких приложений, разделив их двоеточием, например, так:

adb shell settings put secure enabled_accessibility_services com.app1/com.app1.MyAccessibilityService:com.app2/com.app2.MyAccessibilityService 

Поделиться 09 апреля 2021 в 19:34

enabled_accessibility_services содержит все включенные службы, разделенные на ‘:’. Это что-то вроде ‘serviceA:serviceB:serviceC’

Если вы замените строку, она включит ваш сервис, но также отключит все остальные.

Правильным кодом будет:

 fun enableAccessibility(activity: Activity) < var accessibilityServices: String? = Settings.Secure.getString( activity.contentResolver, Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES ) if (accessibilityServices == null) < accessibilityServices = "" >else if (accessibilityServices.isNotEmpty()) < accessibilityServices = ":$accessibilityServices" >Settings.Secure.putString( activity.contentResolver, Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES, "$/$$accessibilityServices" ) Settings.Secure.putString( activity.contentResolver, Settings.Secure.ACCESSIBILITY_ENABLED, "1" ) > 

Вам нужно только объявить это разрешение:

Вы можете включить это разрешение только если вы корневые или через adb.

Поделиться 21 ноября 2021 в 05:46

Я нашел этот пост: Как программно проверить, объявлен ли сервис в AndroidManifest.xml? . Верхний ответ говорит о PackageManager, который сообщает вам, что запущено.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *