Skip to main content
Version: 5.21.0

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.

What this section covers

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.

FeatureSupportProof / comment
TalkBack (screen reader)BasiccontentDescription 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 ScalingSystemSDK text sizes are defined in sp (R.dimen.ecc_*) — they scale with system font settings
High Contrast TextPartialSystem 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 AnimationsNot implementedThe 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)MinimalThe 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 contrastIntegrator's responsibilityThe 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

Dynamic messages are not announced by TalkBack automatically

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.

Custom ChatComponents without contentDescription

If 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.

Outgoing file message has a debug string for contentDescription

An 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)PurposeDefault values
incomingText / incomingBubbleIncoming message text on the operator bubbleecc_black / ecc_white
outgoingText / outgoingBubbleOutgoing message text on the user bubbleecc_white / main (ecc_green_83b144)
inputMessage / inputFieldBackgroundMessage input fieldecc_black / ecc_white
mainTextWhenErrorBubble / errorBackgroundText inside an error bubbleecc_white / ecc_error_red_df0000
unreadMessagesCountText / unreadMsgStickerUnread message counterecc_chat_unread_msg_count_text / ecc_chat_unread_msg_sticker_background
searchText / toolbarToolbar search fieldecc_white / ecc_chat_toolbar
errorText default equals errorBackground

The 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).

note

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 without contentDescription via ChatComponents.
  • Checked the contrast of ChatColors pairs (≥ 4.5:1 regular text, ≥ 3:1 large 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
  • Design system: overview — customization levels.
  • Themes — three ChatTheme constructors, ready-made theme examples.
  • ColorsChatColors table, 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 ChatUpdateProcessor unconfined context).