Accessibility
The SDK has basic accessibility support through static contentDescription in XML layouts. Dynamic messages, custom ChatComponents, and color contrast are the integrator's responsibility.
A list of what the SDK supports out of the box (TalkBack for built-in elements, sp units for Dynamic Type), and what must be ensured on the app side (contrast of color pairs in ChatColors, contentDescription for custom icons). A pre-release checklist is at the end of the page.
A11y support matrix
The table below shows what the SDK covers out of the box, what is partially covered, and what is the integrator's responsibility.
| Feature | Support | Proof / comment |
|---|---|---|
| TalkBack (screen reader) | Basic | contentDescription is set only on some decorative elements (play/info, operator avatar, loader, error icons). Toolbar icons, input field, and bottom chat panel are not covered — TalkBack reads them as "Unnamed button". New incoming messages are not announced via LiveRegion |
| Dynamic Type / Accessibility Scaling | System | SDK text sizes are defined in sp (R.dimen.ecc_*) — they scale with system font settings |
| High Contrast Text | Partial | System High Contrast Text (Android 13+, API 33) applies only to Material semantic colors. ChatColors values defined as hex literals are not automatically adjusted by the system |
| Reduce Animations | Not implemented | The SDK does not check Settings.Global.ANIMATOR_DURATION_SCALE (API 21+) or AccessibilityManager.isAnimationsEnabled() (API 31+). Animations (typing, scrolling, loaders) play regardless of system flags |
| RTL (Right-To-Left) | Minimal | The SDK layouts do not use an explicit layoutDirection, and some paddings are set via physical paddingLeft/Right / marginLeft/Right instead of logical Start/End — mirroring of chips, ratings, and date plates in RTL locales is not guaranteed. For Arabic/Hebrew, set <application android:supportsRtl="true"> on the app side and visually verify the chat screen |
| Color contrast | Integrator's responsibility | The SDK does not validate contrast between ChatColors colors. Target level — WCAG 2.1 AA: ≥ 4.5:1 for regular text (< 18 sp regular / < 14 sp bold), ≥ 3:1 for large text (≥ 18 sp regular / ≥ 14 sp bold) and UI elements / graphical objects (criterion 1.4.11) |
Known limitations
The SDK does not call View.announceForAccessibility(...) and does not use accessibilityLiveRegion for the RecyclerView with the message history. A TalkBack user sees a new message only when manually scrolling to it. Until this is fixed in the SDK, implement announcements yourself — subscribe to ChatUpdateProcessor.newMessageFlow (Delegates) and call announceForAccessibility on your screen's root view.
Do not set accessibilityLiveRegion="polite" or "assertive" on the message-history RecyclerView itself. For long lists, this is a known antipattern: TalkBack will re-read the content on every notifyItemInserted, notifyDataSetChanged, and scroll.
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
// Get the event bus via the SDK's public DI (see ../methods/delegates.md):
val processor: ChatUpdateProcessor by inject()
// The snippet is written for a Fragment (uses viewLifecycleOwner). In an Activity,
// replace `viewLifecycleOwner.lifecycleScope` with `lifecycleScope`.
// newMessageFlow emits ChatItem; incoming operator messages are ConsultPhrase
viewLifecycleOwner.lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.STARTED) {
processor.newMessageFlow
.filterIsInstance<ConsultPhrase>()
.collect { phrase ->
// newMessageFlow emits from Dispatchers.Unconfined —
// switch to the main thread explicitly for the UI call.
withContext(Dispatchers.Main) {
rootView.announceForAccessibility(
getString(R.string.a11y_new_incoming_message, phrase.phraseText.orEmpty())
)
}
}
}
}
flowOn(Dispatchers.Main) is not suitable for this purpose — it changes the upstream emitter context, not the collector's. The subscription in lifecycleScope already runs on Main; for reliability, wrap the UI call in withContext(Dispatchers.Main). Details about the ChatUpdateProcessor unconfined context are in Known limitations.
ChatComponents without contentDescriptionIf you override components via ChatComponents or ChatFlows and substitute icons/images, make sure every ImageView/ImageButton has a contentDescription. Otherwise TalkBack will skip the element.
contentDescriptionAn outgoing file message in the SDK is marked contentDescription="chatText" (a raw identifier, not a localized string) — TalkBack literally reads "chatText". Until this is fixed in the SDK, override the component via ChatComponents.imageComponent / ChatComponents.bubbleComponent, or set contentDescription programmatically in the view holder.
Critical ChatColors pairs to verify contrast
ChatColors has 116 fields; the following "text / background" pairs are critical for contrast. Target levels — WCAG 2.1 AA (≥ 4.5:1 for regular text, ≥ 3:1 for large text and UI elements). Check via WebAIM Contrast Checker.
| Pair (foreground / background) | Purpose | Default values |
|---|---|---|
incomingText / incomingBubble | Incoming message text on the operator bubble | ecc_black / ecc_white |
outgoingText / outgoingBubble | Outgoing message text on the user bubble | ecc_white / main (ecc_green_83b144) |
inputMessage / inputFieldBackground | Message input field | ecc_black / ecc_white |
mainTextWhenErrorBubble / errorBackground | Text inside an error bubble | ecc_white / ecc_error_red_df0000 |
unreadMessagesCountText / unreadMsgSticker | Unread message counter | ecc_chat_unread_msg_count_text / ecc_chat_unread_msg_sticker_background |
searchText / toolbar | Toolbar search field | ecc_white / ecc_chat_toolbar |
errorText default equals errorBackgroundThe errorText and errorBackground fields in ChatColors both point to R.color.ecc_error_red_df0000 — contrast "1:1". If your custom scenario renders errorText over errorBackground, override one of the two fields manually. For text inside the error bubble, a separate field mainTextWhenErrorBubble = ecc_white is used, which provides correct contrast.
Accessibility automated tests
For CI, add Espresso-Accessibility checks to all UI tests that touch the chat screen.
import androidx.test.espresso.accessibility.AccessibilityChecks
import org.junit.Before
@Before
fun enableA11yChecks() {
AccessibilityChecks.enable()
.setRunChecksFromRootView(true)
}
This runs the accessibility-test-framework on every UI interaction and fails the test if at least one element in the hierarchy violates basic rules (missing contentDescription, insufficient contrast, touch zone < 48dp).
If the SDK contains elements that fail the a11y check (touch zone/contrast), use setSuppressingResultMatcher(...) with a whitelist of those specific views; do not disable the check globally.
Pre-release checklist
- Walked through the chat with TalkBack: toolbar, input field, attach / voice / send buttons, reply, file download are announced meaningfully (not "Unnamed button", not
chatText). Override buttons withoutcontentDescriptionviaChatComponents. - Checked the contrast of
ChatColorspairs (≥ 4.5:1regular text,≥ 3:1large text and UI) via WebAIM Contrast Checker. The list of pairs is above. - Enabled the maximum font (
adb shell settings put system font_scale 1.5) — text is not clipped. - If the app supports RTL — checked the chat with
Force RTL layout direction. Not all SDK paddings are set logically (Start/End); some UI may not mirror. - Enabled "Remove animations" — the SDK does not account for this flag itself; a workaround may be required on the app side.
- Ran automated tests with Espresso-Accessibility — see Accessibility automated tests.
- If you added your own
ecc_content_descr_*strings — they are translated into all supported locales.
adb shell settings put system font_scale 1.5 # maximum font
adb shell settings put global force_rtl_layout_direction 1 # RTL
adb shell settings put global animator_duration_scale 0 # disable animations
Related sections
- Design system: overview — customization levels.
- Themes — three
ChatThemeconstructors, ready-made theme examples. - Colors —
ChatColorstable, including fields from the "Critical pairs" section above. - Typography — sp units and Dynamic Type.
- Images — drawables, including message status icons.
- Components — overriding elements while preserving a11y.
- Flows — screen-specific customization.
- Known limitations — general SDK 5.x bug list (including the
ChatUpdateProcessorunconfined context).