Перейти к основному содержимому
Версия: 5.21.0

Доступность

SDK имеет базовую поддержку доступности через статические contentDescription в XML-layouts. Динамические сообщения, кастомные ChatComponents и контрастность цветов остаются на стороне интегратора.

Что описано в этом разделе

Перечень того, что SDK поддерживает «из коробки» (TalkBack для встроенных элементов, sp-юниты для Dynamic Type), и того, что необходимо обеспечить на стороне приложения (контраст пар цветов в ChatColors, contentDescription для кастомных иконок). Чек-лист перед релизом — в конце страницы.

Матрица поддержки a11y

В таблице ниже — что покрывает SDK «из коробки», что покрыто частично и за что отвечает интегратор.

ФункцияПоддержкаПруф / комментарий
TalkBack (screen reader)БазоваяcontentDescription задан только на части декоративных элементов (play/info, аватар оператора, лоадер, иконки ошибок). Иконки тулбара, поля ввода и нижней панели чата не покрыты — TalkBack озвучит их как «Кнопка без названия». Новые входящие сообщения не объявляются через LiveRegion
Dynamic Type / Accessibility ScalingСистемноРазмеры текста SDK заданы в sp (R.dimen.ecc_*) — увеличиваются вместе с системными настройками шрифта
High Contrast TextЧастичноСистемный High Contrast Text (Android 13+, API 33) применяется только к семантическим цветам Material. У значений ChatColors, заданных как hex-литералы, контраст системой автоматически не подкручивается
Reduce AnimationsНе реализованоSDK не проверяет ни Settings.Global.ANIMATOR_DURATION_SCALE (API 21+), ни AccessibilityManager.isAnimationsEnabled() (API 31+). Анимации (типинг, прокрутка, лоадеры) воспроизводятся независимо от системных флагов
RTL (Right-To-Left)МинимальнаяВ layouts SDK не используется явный layoutDirection, но часть отступов задана через физические paddingLeft/Right / marginLeft/Right вместо логических Start/End — зеркалирование чипов, рейтингов и плашек дат при RTL-локали не гарантировано. Для Arabic/Hebrew установите <application android:supportsRtl="true"> на стороне приложения и проверьте экран чата визуально
Контрастность цветовОтветственность интегратораSDK не валидирует контраст между цветами ChatColors. Целевой уровень — WCAG 2.1 AA: ≥ 4.5:1 для обычного текста (< 18sp regular / < 14sp bold), ≥ 3:1 для крупного текста (≥ 18sp regular / ≥ 14sp bold) и UI-элементов / графических объектов (критерий 1.4.11)

Известные ограничения

Динамические сообщения не объявляются TalkBack автоматически

SDK не вызывает View.announceForAccessibility(...) и не использует accessibilityLiveRegion для RecyclerView с историей сообщений. TalkBack-пользователь увидит новое сообщение только при прокрутке к нему вручную. До исправления в SDK реализуйте объявления самостоятельно — подпишитесь на ChatUpdateProcessor.newMessageFlow (Делегаты) и вызовите announceForAccessibility на root-view вашего экрана.

Не устанавливайте accessibilityLiveRegion="polite" или "assertive" на сам RecyclerView истории сообщений. Для длинных списков это известный антипаттерн: TalkBack будет повторно зачитывать содержимое при каждом notifyItemInserted, notifyDataSetChanged и прокрутке.

import androidx.lifecycle.Lifecycle
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
import edna.chatcenter.core.chatUpdates.ChatUpdateProcessor
import edna.chatcenter.core.models.ConsultPhrase
import edna.chatcenter.core.serviceLocator.core.inject
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.filterIsInstance
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext

// Получение шины событий — через публичный DI SDK (см. ../methods/delegates.md):
val processor: ChatUpdateProcessor by inject()

// Снипет написан для Fragment (использует viewLifecycleOwner). В Activity
// замените `viewLifecycleOwner.lifecycleScope` на `lifecycleScope`.
// newMessageFlow эмитит ChatItem; входящие сообщения от оператора — это ConsultPhrase
viewLifecycleOwner.lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.STARTED) {
processor.newMessageFlow
.filterIsInstance<ConsultPhrase>()
.collect { phrase ->
// newMessageFlow эмитится из Dispatchers.Unconfined —
// явно переключаемся на главный поток для UI-вызова.
withContext(Dispatchers.Main) {
rootView.announceForAccessibility(
getString(R.string.a11y_new_incoming_message, phrase.phraseText.orEmpty())
)
}
}
}
}

flowOn(Dispatchers.Main) для этой цели не подойдёт — он меняет контекст upstream-эмиттера, а не коллектора. Подписка в lifecycleScope уже идёт на Main; для надёжности оборачивайте UI-вызов в withContext(Dispatchers.Main). Подробности про unconfined-контекст ChatUpdateProcessor — в Известных ограничениях.

Кастомные ChatComponents без contentDescription

Если вы переопределяете компоненты через ChatComponents или ChatFlows и подменяете иконки/изображения, обязательно проверьте, что у каждого ImageView/ImageButton задан contentDescription. Иначе TalkBack пропустит элемент.

У исходящего файлового сообщения contentDescription — debug-строка

Исходящее файловое сообщение в SDK помечено contentDescription="chatText" (сырой идентификатор, не локализованная строка) — TalkBack буквально зачитает «chatText». До исправления в SDK переопределите компонент через ChatComponents.imageComponent / ChatComponents.bubbleComponent либо задайте contentDescription программно во вью-холдере.

Критичные пары ChatColors для проверки контраста

У ChatColors 116 полей; для контраста ключевы следующие пары «текст / фон». Целевые уровни — WCAG 2.1 AA (≥ 4.5:1 для обычного текста, ≥ 3:1 для крупного текста и UI-элементов). Проверяйте через WebAIM Contrast Checker.

Пара (foreground / background)НазначениеДефолтные значения
incomingText / incomingBubbleТекст входящего сообщения на бабле оператораecc_black / ecc_white
outgoingText / outgoingBubbleТекст исходящего сообщения на бабле пользователяecc_white / main (ecc_green_83b144)
inputMessage / inputFieldBackgroundПоле ввода сообщенияecc_black / ecc_white
mainTextWhenErrorBubble / errorBackgroundТекст внутри бабла-ошибкиecc_white / ecc_error_red_df0000
unreadMessagesCountText / unreadMsgStickerСчётчик непрочитанных сообщенийecc_chat_unread_msg_count_text / ecc_chat_unread_msg_sticker_background
searchText / toolbarПоле поиска в тулбареecc_white / ecc_chat_toolbar
Дефолт errorText совпадает с errorBackground

Поля errorText и errorBackground в ChatColors оба указывают на R.color.ecc_error_red_df0000 — контраст «1:1». Если ваш кастом сценарий рисует errorText поверх errorBackground, переопределите одно из двух полей вручную. Для текста внутри самого бабла-ошибки используется отдельное поле mainTextWhenErrorBubble = ecc_white, оно даёт корректный контраст.

Автотесты доступности

Для CI добавьте Espresso-Accessibility-проверки во все UI-тесты, затрагивающие экран чата.

import androidx.test.espresso.accessibility.AccessibilityChecks
import org.junit.Before

@Before
fun enableA11yChecks() {
AccessibilityChecks.enable()
.setRunChecksFromRootView(true)
}

Это запускает accessibility-test-framework на каждом UI-взаимодействии и проваливает тест, если хотя бы один элемент в иерархии нарушает базовые правила (нет contentDescription, недостаточный контраст, тач-зона < 48dp).

примечание

Если внутри SDK есть элементы, проваливающие a11y-проверку (тач-зона/контраст), используйте setSuppressingResultMatcher(...) с whitelist именно этих view; глобально проверку не отключайте.

Чек-лист перед релизом

  • Прошлись по чату с TalkBack: тулбар, поле ввода, кнопки прикрепления / голосового / отправки, ответ, скачивание файла озвучиваются осмысленно (не «Кнопка без названия», не chatText). Кнопки без contentDescription переопределите через ChatComponents.
  • Проверили контраст пар ChatColors (≥ 4.5:1 обычный текст, ≥ 3:1 крупный текст и UI) через WebAIM Contrast Checker. Список пар — выше.
  • Включили максимальный шрифт (adb shell settings put system font_scale 1.5) — текст не обрезается.
  • Если приложение поддерживает RTL — проверили чат с Force RTL layout direction. В SDK не все отступы заданы логически (Start/End), часть UI может не зеркалироваться.
  • Включили «Убрать анимации» — SDK сам этот флаг не учитывает, может потребоваться workaround на стороне приложения.
  • Запустили автотесты с Espresso-Accessibility — см. Автотесты доступности.
  • Если добавляли свои строки ecc_content_descr_* — они переведены на все поддерживаемые локали.
adb shell settings put system font_scale 1.5                  # максимальный шрифт
adb shell settings put global force_rtl_layout_direction 1 # RTL
adb shell settings put global animator_duration_scale 0 # выключить анимации

Связанные разделы