Skip to content

Commit 97817c2

Browse files
authored
Improve poll creation TalkBack accessibility: action verb, toggle rows, error live region, reorder actions (#6445)
* Announce a verb on the Create Poll button StreamButton's clickable was set with no onClickLabel, so TalkBack defaulted to the generic "double-tap to activate" hint. Add an optional `onClickLabel` parameter to StreamButton and pass it through to clickable. PollCreationHeader's create button now provides "create poll" as the verb, so TalkBack reads "Create Poll, button. Double-tap to create poll." instead of the generic hint. * Make poll switch rows toggleable and merged Each switch row in `PollSwitchList` (top-level options and the inner limit-votes row) wraps a title, description and a `StreamSwitch` — TalkBack focused each as its own node, so a user had to swipe three times to learn what one switch controlled. Wrap each row in `Modifier.toggleable(role = Role.Switch) + semantics(mergeDescendants = true) {}` so the row becomes a single focus stop announced as "{title}, {description}, switch, on/off", with the row's onValueChange firing on tap or double-tap. The inner `StreamSwitch` now takes `onCheckedChange = null` (the pattern its KDoc already documents), which makes the visual switch decorative while the parent row owns the toggle action. Sighted users also gain a larger hit target. * Announce poll option errors via a polite live region `PollOptionErrorRow` mounts conditionally when an option has an error (currently only the duplicate-title case). Sighted users see the icon and message appear inline; TalkBack users had no signal until they swiped to the row. Add `Modifier.semantics(mergeDescendants = true) { liveRegion = LiveRegionMode.Polite }` so TalkBack announces the existing error message the moment the row enters the tree. * Expose poll option reordering via TalkBack custom actions The reorder UX relies on dragging a handle, which isn't a gesture TalkBack supports natively. Expose two `CustomAccessibilityAction`s ("Move up" / "Move down") on each option's drag handle so TalkBack users can reorder via the local context menu. Each action is wired to a shared `move` lambda used by both the gesture's `onSettle` callback and the new accessibility actions, and the action list is built per row so the top option doesn't offer Move up and the bottom option doesn't offer Move down. * Simplify poll option move callbacks * Pin poll switch row min height to interactive size * Include option name in reorder and remove a11y labels * Anchor poll drag handle a11y on its full Box bounds * Let integrators supply the poll create click label * Disambiguate Create Poll button label from screen heading * Hide poll input placeholder text from accessibility * Hide redundant switch state on poll switch rows
1 parent 06299b8 commit 97817c2

15 files changed

Lines changed: 148 additions & 51 deletions

File tree

stream-chat-android-compose/api/stream-chat-android-compose.api

Lines changed: 15 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -578,13 +578,13 @@ public final class io/getstream/chat/android/compose/ui/attachments/preview/Comp
578578
public static final field INSTANCE Lio/getstream/chat/android/compose/ui/attachments/preview/ComposableSingletons$MediaGalleryPreviewScreenKt;
579579
public fun <init> ()V
580580
public final fun getLambda$-1186868066$stream_chat_android_compose_release ()Lkotlin/jvm/functions/Function7;
581+
public final fun getLambda$-1317756983$stream_chat_android_compose_release ()Lkotlin/jvm/functions/Function2;
581582
public final fun getLambda$-1613254346$stream_chat_android_compose_release ()Lkotlin/jvm/functions/Function2;
582583
public final fun getLambda$-177774698$stream_chat_android_compose_release ()Lkotlin/jvm/functions/Function2;
583-
public final fun getLambda$-1899518912$stream_chat_android_compose_release ()Lkotlin/jvm/functions/Function2;
584584
public final fun getLambda$-2102393707$stream_chat_android_compose_release ()Lkotlin/jvm/functions/Function2;
585585
public final fun getLambda$-210669840$stream_chat_android_compose_release ()Lkotlin/jvm/functions/Function2;
586586
public final fun getLambda$-244397814$stream_chat_android_compose_release ()Lkotlin/jvm/functions/Function2;
587-
public final fun getLambda$-49086904$stream_chat_android_compose_release ()Lkotlin/jvm/functions/Function2;
587+
public final fun getLambda$-357715190$stream_chat_android_compose_release ()Lkotlin/jvm/functions/Function2;
588588
public final fun getLambda$-52298206$stream_chat_android_compose_release ()Lkotlin/jvm/functions/Function2;
589589
public final fun getLambda$-563857596$stream_chat_android_compose_release ()Lkotlin/jvm/functions/Function2;
590590
public final fun getLambda$-822354111$stream_chat_android_compose_release ()Lkotlin/jvm/functions/Function2;
@@ -593,10 +593,10 @@ public final class io/getstream/chat/android/compose/ui/attachments/preview/Comp
593593
public final fun getLambda$1327866271$stream_chat_android_compose_release ()Lkotlin/jvm/functions/Function2;
594594
public final fun getLambda$1331114327$stream_chat_android_compose_release ()Lkotlin/jvm/functions/Function2;
595595
public final fun getLambda$1375691933$stream_chat_android_compose_release ()Lkotlin/jvm/functions/Function2;
596-
public final fun getLambda$1465472073$stream_chat_android_compose_release ()Lkotlin/jvm/functions/Function2;
597596
public final fun getLambda$1549246429$stream_chat_android_compose_release ()Lkotlin/jvm/functions/Function2;
598597
public final fun getLambda$1672511429$stream_chat_android_compose_release ()Lkotlin/jvm/functions/Function2;
599598
public final fun getLambda$1672603643$stream_chat_android_compose_release ()Lkotlin/jvm/functions/Function2;
599+
public final fun getLambda$1711218079$stream_chat_android_compose_release ()Lkotlin/jvm/functions/Function2;
600600
public final fun getLambda$1880503530$stream_chat_android_compose_release ()Lkotlin/jvm/functions/Function2;
601601
public final fun getLambda$1902056283$stream_chat_android_compose_release ()Lkotlin/jvm/functions/Function2;
602602
public final fun getLambda$19148184$stream_chat_android_compose_release ()Lkotlin/jvm/functions/Function2;
@@ -765,14 +765,14 @@ public final class io/getstream/chat/android/compose/ui/channel/info/ComposableS
765765
public fun <init> ()V
766766
public final fun getLambda$-1114359326$stream_chat_android_compose_release ()Lkotlin/jvm/functions/Function2;
767767
public final fun getLambda$-1207226509$stream_chat_android_compose_release ()Lkotlin/jvm/functions/Function2;
768+
public final fun getLambda$-1284495299$stream_chat_android_compose_release ()Lkotlin/jvm/functions/Function2;
768769
public final fun getLambda$-1330176946$stream_chat_android_compose_release ()Lkotlin/jvm/functions/Function2;
769-
public final fun getLambda$-1754400971$stream_chat_android_compose_release ()Lkotlin/jvm/functions/Function2;
770770
public final fun getLambda$-2125016004$stream_chat_android_compose_release ()Lkotlin/jvm/functions/Function2;
771+
public final fun getLambda$-572168236$stream_chat_android_compose_release ()Lkotlin/jvm/functions/Function2;
771772
public final fun getLambda$1139087319$stream_chat_android_compose_release ()Lkotlin/jvm/functions/Function2;
772773
public final fun getLambda$1302953779$stream_chat_android_compose_release ()Lkotlin/jvm/functions/Function3;
773774
public final fun getLambda$2046556554$stream_chat_android_compose_release ()Lkotlin/jvm/functions/Function2;
774775
public final fun getLambda$217368988$stream_chat_android_compose_release ()Lkotlin/jvm/functions/Function2;
775-
public final fun getLambda$756179806$stream_chat_android_compose_release ()Lkotlin/jvm/functions/Function2;
776776
}
777777

778778
public final class io/getstream/chat/android/compose/ui/channel/info/ComposableSingletons$ChannelInfoMemberInfoModalSheetKt {
@@ -822,10 +822,10 @@ public final class io/getstream/chat/android/compose/ui/channel/info/ComposableS
822822
public static final field INSTANCE Lio/getstream/chat/android/compose/ui/channel/info/ComposableSingletons$GroupChannelEditScreenKt;
823823
public fun <init> ()V
824824
public final fun getLambda$-1290011797$stream_chat_android_compose_release ()Lkotlin/jvm/functions/Function2;
825-
public final fun getLambda$-1600862874$stream_chat_android_compose_release ()Lkotlin/jvm/functions/Function2;
826825
public final fun getLambda$-1839460112$stream_chat_android_compose_release ()Lkotlin/jvm/functions/Function2;
827826
public final fun getLambda$-1996460951$stream_chat_android_compose_release ()Lkotlin/jvm/functions/Function2;
828827
public final fun getLambda$-949819074$stream_chat_android_compose_release ()Lkotlin/jvm/functions/Function2;
828+
public final fun getLambda$-952635417$stream_chat_android_compose_release ()Lkotlin/jvm/functions/Function2;
829829
public final fun getLambda$1273794324$stream_chat_android_compose_release ()Lkotlin/jvm/functions/Function2;
830830
public final fun getLambda$1411993017$stream_chat_android_compose_release ()Lkotlin/jvm/functions/Function2;
831831
public final fun getLambda$1947836857$stream_chat_android_compose_release ()Lkotlin/jvm/functions/Function2;
@@ -872,9 +872,9 @@ public final class io/getstream/chat/android/compose/ui/channels/header/Composab
872872
public final fun getLambda$-1782552518$stream_chat_android_compose_release ()Lkotlin/jvm/functions/Function2;
873873
public final fun getLambda$-39363265$stream_chat_android_compose_release ()Lkotlin/jvm/functions/Function2;
874874
public final fun getLambda$-903354658$stream_chat_android_compose_release ()Lkotlin/jvm/functions/Function2;
875+
public final fun getLambda$1125356903$stream_chat_android_compose_release ()Lkotlin/jvm/functions/Function2;
875876
public final fun getLambda$1371852185$stream_chat_android_compose_release ()Lkotlin/jvm/functions/Function2;
876877
public final fun getLambda$318691263$stream_chat_android_compose_release ()Lkotlin/jvm/functions/Function2;
877-
public final fun getLambda$423422310$stream_chat_android_compose_release ()Lkotlin/jvm/functions/Function2;
878878
public final fun getLambda$621281156$stream_chat_android_compose_release ()Lkotlin/jvm/functions/Function2;
879879
}
880880

@@ -1402,7 +1402,7 @@ public final class io/getstream/chat/android/compose/ui/components/messageaction
14021402
public static final field INSTANCE Lio/getstream/chat/android/compose/ui/components/messageactions/ComposableSingletons$MessageActionsHeaderKt;
14031403
public fun <init> ()V
14041404
public final fun getLambda$-1164493217$stream_chat_android_compose_release ()Lkotlin/jvm/functions/Function2;
1405-
public final fun getLambda$1304549080$stream_chat_android_compose_release ()Lkotlin/jvm/functions/Function2;
1405+
public final fun getLambda$746565017$stream_chat_android_compose_release ()Lkotlin/jvm/functions/Function2;
14061406
}
14071407

14081408
public final class io/getstream/chat/android/compose/ui/components/messageactions/ComposableSingletons$MessageActionsKt {
@@ -1616,8 +1616,8 @@ public final class io/getstream/chat/android/compose/ui/components/moderatedmess
16161616
public final class io/getstream/chat/android/compose/ui/components/poll/ComposableSingletons$PollAnswersKt {
16171617
public static final field INSTANCE Lio/getstream/chat/android/compose/ui/components/poll/ComposableSingletons$PollAnswersKt;
16181618
public fun <init> ()V
1619-
public final fun getLambda$-1411166830$stream_chat_android_compose_release ()Lkotlin/jvm/functions/Function2;
16201619
public final fun getLambda$-1780669891$stream_chat_android_compose_release ()Lkotlin/jvm/functions/Function2;
1620+
public final fun getLambda$-1805570669$stream_chat_android_compose_release ()Lkotlin/jvm/functions/Function2;
16211621
public final fun getLambda$-1980307438$stream_chat_android_compose_release ()Lkotlin/jvm/functions/Function3;
16221622
public final fun getLambda$-232122758$stream_chat_android_compose_release ()Lkotlin/jvm/functions/Function2;
16231623
public final fun getLambda$-49181804$stream_chat_android_compose_release ()Lkotlin/jvm/functions/Function3;
@@ -1879,7 +1879,6 @@ public final class io/getstream/chat/android/compose/ui/messages/attachments/pol
18791879
public static final field INSTANCE Lio/getstream/chat/android/compose/ui/messages/attachments/poll/ComposableSingletons$PollCreationHeaderKt;
18801880
public fun <init> ()V
18811881
public final fun getLambda$1291237378$stream_chat_android_compose_release ()Lkotlin/jvm/functions/Function2;
1882-
public final fun getLambda$1775632701$stream_chat_android_compose_release ()Lkotlin/jvm/functions/Function2;
18831882
public final fun getLambda$226546083$stream_chat_android_compose_release ()Lkotlin/jvm/functions/Function2;
18841883
}
18851884

@@ -1904,15 +1903,15 @@ public final class io/getstream/chat/android/compose/ui/messages/attachments/pol
19041903
}
19051904

19061905
public final class io/getstream/chat/android/compose/ui/messages/attachments/poll/CreatePollScreenKt {
1907-
public static final fun CreatePollScreen (Lkotlin/jvm/functions/Function0;Lkotlin/jvm/functions/Function1;Landroidx/compose/runtime/Composer;I)V
1906+
public static final fun CreatePollScreen (Lkotlin/jvm/functions/Function0;Lkotlin/jvm/functions/Function1;Ljava/lang/String;Landroidx/compose/runtime/Composer;II)V
19081907
}
19091908

19101909
public final class io/getstream/chat/android/compose/ui/messages/attachments/poll/PollCreationDiscardDialogKt {
19111910
public static final fun PollCreationDiscardDialog (ZLkotlin/jvm/functions/Function0;Lkotlin/jvm/functions/Function0;Landroidx/compose/runtime/Composer;II)V
19121911
}
19131912

19141913
public final class io/getstream/chat/android/compose/ui/messages/attachments/poll/PollCreationHeaderKt {
1915-
public static final fun PollCreationHeader (Landroidx/compose/ui/Modifier;Lkotlin/jvm/functions/Function0;ZLkotlin/jvm/functions/Function0;Lkotlin/jvm/functions/Function3;Lkotlin/jvm/functions/Function3;Lkotlin/jvm/functions/Function3;Landroidx/compose/runtime/Composer;II)V
1914+
public static final fun PollCreationHeader (Landroidx/compose/ui/Modifier;Lkotlin/jvm/functions/Function0;ZLkotlin/jvm/functions/Function0;Ljava/lang/String;Lkotlin/jvm/functions/Function3;Lkotlin/jvm/functions/Function3;Lkotlin/jvm/functions/Function3;Landroidx/compose/runtime/Composer;II)V
19161915
}
19171916

19181917
public final class io/getstream/chat/android/compose/ui/messages/attachments/poll/PollOptionDuplicated : io/getstream/chat/android/compose/ui/messages/attachments/poll/PollOptionError {
@@ -2030,11 +2029,11 @@ public final class io/getstream/chat/android/compose/ui/messages/composer/intern
20302029
public static final field INSTANCE Lio/getstream/chat/android/compose/ui/messages/composer/internal/ComposableSingletons$AudioRecordingContentKt;
20312030
public fun <init> ()V
20322031
public final fun getLambda$-129303699$stream_chat_android_compose_release ()Lkotlin/jvm/functions/Function2;
2033-
public final fun getLambda$-1855012696$stream_chat_android_compose_release ()Lkotlin/jvm/functions/Function2;
20342032
public final fun getLambda$-2139230466$stream_chat_android_compose_release ()Lkotlin/jvm/functions/Function2;
2035-
public final fun getLambda$-630641788$stream_chat_android_compose_release ()Lkotlin/jvm/functions/Function2;
2036-
public final fun getLambda$491551885$stream_chat_android_compose_release ()Lkotlin/jvm/functions/Function2;
2033+
public final fun getLambda$-744252283$stream_chat_android_compose_release ()Lkotlin/jvm/functions/Function2;
2034+
public final fun getLambda$2045550761$stream_chat_android_compose_release ()Lkotlin/jvm/functions/Function2;
20372035
public final fun getLambda$545156019$stream_chat_android_compose_release ()Lkotlin/jvm/functions/Function2;
2036+
public final fun getLambda$549534158$stream_chat_android_compose_release ()Lkotlin/jvm/functions/Function2;
20382037
}
20392038

20402039
public final class io/getstream/chat/android/compose/ui/messages/composer/internal/ComposableSingletons$MessageComposerEditIndicatorKt {
@@ -3783,7 +3782,7 @@ public final class io/getstream/chat/android/compose/ui/theme/ComposableSingleto
37833782
public static final field INSTANCE Lio/getstream/chat/android/compose/ui/theme/ComposableSingletons$ChatComponentFactoryKt;
37843783
public fun <init> ()V
37853784
public final fun getLambda$-845861723$stream_chat_android_compose_release ()Lkotlin/jvm/functions/Function2;
3786-
public final fun getLambda$1812629511$stream_chat_android_compose_release ()Lkotlin/jvm/functions/Function2;
3785+
public final fun getLambda$1301617352$stream_chat_android_compose_release ()Lkotlin/jvm/functions/Function2;
37873786
public final fun getLambda$69070923$stream_chat_android_compose_release ()Lkotlin/jvm/functions/Function2;
37883787
}
37893788

stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/components/button/StreamButton.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@ internal fun StreamButton(
8383
onClick: () -> Unit,
8484
modifier: Modifier = Modifier,
8585
enabled: Boolean = true,
86+
onClickLabel: String? = null,
8687
style: StreamButtonStyle = StreamButtonStyleDefaults.primarySolid,
8788
size: StreamButtonSize = StreamButtonSize.Medium,
8889
content: @Composable () -> Unit,
@@ -95,6 +96,7 @@ internal fun StreamButton(
9596
.ifNotNull(style.borderColor(enabled)) { border(1.dp, it, CircleShape) }
9697
.clickable(
9798
onClick = onClick,
99+
onClickLabel = onClickLabel,
98100
enabled = enabled,
99101
role = Role.Button,
100102
interactionSource = remember(::MutableInteractionSource),

stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/components/poll/PollOptionInput.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ import androidx.compose.ui.focus.FocusRequester
3939
import androidx.compose.ui.focus.focusRequester
4040
import androidx.compose.ui.graphics.SolidColor
4141
import androidx.compose.ui.semantics.contentDescription
42+
import androidx.compose.ui.semantics.hideFromAccessibility
4243
import androidx.compose.ui.semantics.semantics
4344
import androidx.compose.ui.text.TextRange
4445
import androidx.compose.ui.text.TextStyle
@@ -127,6 +128,7 @@ public fun PollOptionInput(
127128
if (value.isBlank()) {
128129
Text(
129130
text = description,
131+
modifier = Modifier.semantics { hideFromAccessibility() },
130132
style = typography.bodyDefault,
131133
color = colors.inputTextPlaceholder,
132134
)

stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/messages/attachments/poll/CreatePollScreen.kt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,12 +52,14 @@ import io.getstream.chat.android.models.CreatePollParams
5252
* @param onBack Called when the user navigates back from the poll creation screen
5353
* (via back press or discard). Resets the ViewModel state.
5454
* @param onCreatePoll Called when the user submits a new poll configuration.
55+
* @param onCreatePollLabel Semantic / accessibility label for the poll creation button that triggers [onCreatePoll].
5556
*/
5657
@Suppress("LongMethod")
5758
@Composable
5859
public fun CreatePollScreen(
5960
onBack: () -> Unit,
6061
onCreatePoll: (CreatePollParams) -> Unit,
62+
onCreatePollLabel: String? = null,
6163
) {
6264
val viewModel: CreatePollViewModel = viewModel(
6365
factory = CreatePollViewModelFactory(ChatTheme.config.polls),
@@ -88,6 +90,7 @@ public fun CreatePollScreen(
8890
PollCreationHeader(
8991
modifier = Modifier.fillMaxWidth(),
9092
enabledCreation = state.isCreationEnabled,
93+
onPollCreateClickedLabel = onCreatePollLabel,
9194
onPollCreateClicked = {
9295
onCreatePoll(
9396
createPollParamsFrom(

stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/messages/attachments/poll/PollCreationHeader.kt

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ import io.getstream.chat.android.compose.ui.theme.ChatTheme
5050
* @param onBackPressed A lambda that will be executed if users click the back button on the default [leadingContent].
5151
* @param enabledCreation Represents if user can click the creation button or not.
5252
* @param onPollCreateClicked A lambda that will be executed if users click the poll creation button.
53+
* @param onPollCreateClickedLabel Semantic / accessibility label for [onPollCreateClicked].
5354
* @param leadingContent Customizable composable function that represents the leading content of a poll creation item, usually
5455
* holding a back action button.
5556
* @param centerContent Customizable composable function that represents the center content of a poll creation item, usually
@@ -63,6 +64,7 @@ public fun PollCreationHeader(
6364
onBackPressed: () -> Unit = {},
6465
enabledCreation: Boolean,
6566
onPollCreateClicked: () -> Unit,
67+
onPollCreateClickedLabel: String? = null,
6668
leadingContent: @Composable (RowScope.() -> Unit)? = null,
6769
centerContent: @Composable (RowScope.() -> Unit)? = null,
6870
trailingContent: @Composable (RowScope.() -> Unit)? = null,
@@ -86,7 +88,8 @@ public fun PollCreationHeader(
8688

8789
trailingContent?.invoke(this) ?: DefaultPollOptionsHeaderTrailingContent(
8890
enabled = enabledCreation,
89-
onPollCreateClicked = onPollCreateClicked,
91+
onPollCreateClick = onPollCreateClicked,
92+
onPollCreateClickLabel = onPollCreateClickedLabel,
9093
)
9194
}
9295
}
@@ -118,18 +121,21 @@ internal fun DefaultPollOptionsHeaderCenterContent(modifier: Modifier, title: St
118121
}
119122

120123
@Composable
121-
internal fun DefaultPollOptionsHeaderTrailingContent(
124+
private fun DefaultPollOptionsHeaderTrailingContent(
122125
enabled: Boolean,
123-
onPollCreateClicked: () -> Unit,
126+
onPollCreateClick: () -> Unit,
127+
onPollCreateClickLabel: String? = null,
124128
) {
129+
val onClickLabel = onPollCreateClickLabel ?: stringResource(R.string.stream_compose_poll_create_action)
125130
StreamButton(
126-
onClick = onPollCreateClicked,
131+
onClick = onPollCreateClick,
127132
enabled = enabled,
133+
onClickLabel = onClickLabel,
128134
style = StreamButtonStyleDefaults.primarySolid,
129135
) {
130136
Icon(
131137
painter = painterResource(R.drawable.stream_design_ic_checkmark),
132-
contentDescription = stringResource(R.string.stream_compose_poll_title),
138+
contentDescription = onClickLabel,
133139
)
134140
}
135141
}

0 commit comments

Comments
 (0)