Skip to content

Commit 2e0da99

Browse files
authored
Merge pull request #5017 from element-hq/feature/bma/a11y/sessionVerification
[a11y] Improve session verification screens
2 parents fe8b685 + b547b6a commit 2e0da99

File tree

31 files changed

+123
-209
lines changed

31 files changed

+123
-209
lines changed

features/analytics/impl/src/main/kotlin/io/element/android/features/analytics/impl/AnalyticsOptInView.kt

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,13 +29,13 @@ import io.element.android.compound.theme.ElementTheme
2929
import io.element.android.compound.tokens.generated.CompoundIcons
3030
import io.element.android.features.analytics.api.AnalyticsOptInEvents
3131
import io.element.android.libraries.designsystem.atomic.molecules.ButtonColumnMolecule
32+
import io.element.android.libraries.designsystem.atomic.molecules.IconTitleSubtitleMolecule
3233
import io.element.android.libraries.designsystem.atomic.organisms.InfoListItem
3334
import io.element.android.libraries.designsystem.atomic.organisms.InfoListOrganism
3435
import io.element.android.libraries.designsystem.atomic.pages.HeaderFooterPage
3536
import io.element.android.libraries.designsystem.background.OnboardingBackground
3637
import io.element.android.libraries.designsystem.components.BigIcon
3738
import io.element.android.libraries.designsystem.components.ClickableLinkText
38-
import io.element.android.libraries.designsystem.components.PageTitle
3939
import io.element.android.libraries.designsystem.preview.ElementPreview
4040
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
4141
import io.element.android.libraries.designsystem.text.buildAnnotatedStringWithStyledPart
@@ -89,10 +89,10 @@ private fun AnalyticsOptInHeader(
8989
Column(
9090
horizontalAlignment = Alignment.CenterHorizontally,
9191
) {
92-
PageTitle(
93-
modifier = Modifier.padding(top = 60.dp, bottom = 12.dp),
92+
IconTitleSubtitleMolecule(
93+
modifier = Modifier.padding(top = 60.dp, bottom = 28.dp),
9494
title = stringResource(id = R.string.screen_analytics_prompt_title, state.applicationName),
95-
subtitle = stringResource(id = R.string.screen_analytics_prompt_help_us_improve),
95+
subTitle = stringResource(id = R.string.screen_analytics_prompt_help_us_improve),
9696
iconStyle = BigIcon.Style.Default(CompoundIcons.Chart())
9797
)
9898
if (state.hasPolicyLink) {

features/ftue/impl/src/main/kotlin/io/element/android/features/ftue/impl/notifications/NotificationsOptInView.kt

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,10 +31,10 @@ import io.element.android.compound.theme.ElementTheme
3131
import io.element.android.compound.tokens.generated.CompoundIcons
3232
import io.element.android.features.ftue.impl.R
3333
import io.element.android.libraries.designsystem.atomic.molecules.ButtonColumnMolecule
34+
import io.element.android.libraries.designsystem.atomic.molecules.IconTitleSubtitleMolecule
3435
import io.element.android.libraries.designsystem.atomic.pages.HeaderFooterPage
3536
import io.element.android.libraries.designsystem.background.OnboardingBackground
3637
import io.element.android.libraries.designsystem.components.BigIcon
37-
import io.element.android.libraries.designsystem.components.PageTitle
3838
import io.element.android.libraries.designsystem.components.avatar.Avatar
3939
import io.element.android.libraries.designsystem.components.avatar.AvatarData
4040
import io.element.android.libraries.designsystem.components.avatar.AvatarSize
@@ -59,7 +59,7 @@ fun NotificationsOptInView(
5959
.statusBarsPadding()
6060
.fillMaxSize(),
6161
background = { OnboardingBackground() },
62-
header = { NotificationsOptInHeader(modifier = Modifier.padding(top = 60.dp, bottom = 12.dp)) },
62+
header = { NotificationsOptInHeader(modifier = Modifier.padding(top = 60.dp, bottom = 28.dp)) },
6363
footer = { NotificationsOptInFooter(state) },
6464
) {
6565
NotificationsOptInContent()
@@ -70,10 +70,10 @@ fun NotificationsOptInView(
7070
private fun NotificationsOptInHeader(
7171
modifier: Modifier = Modifier,
7272
) {
73-
PageTitle(
73+
IconTitleSubtitleMolecule(
7474
modifier = modifier,
7575
title = stringResource(R.string.screen_notification_optin_title),
76-
subtitle = stringResource(R.string.screen_notification_optin_subtitle),
76+
subTitle = stringResource(R.string.screen_notification_optin_subtitle),
7777
iconStyle = BigIcon.Style.Default(CompoundIcons.NotificationsSolid()),
7878
)
7979
}

features/ftue/impl/src/main/kotlin/io/element/android/features/ftue/impl/sessionverification/choosemode/ChooseSelfVerificationModeView.kt

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,9 @@ import io.element.android.compound.theme.ElementTheme
2424
import io.element.android.compound.tokens.generated.CompoundIcons
2525
import io.element.android.features.ftue.impl.R
2626
import io.element.android.libraries.designsystem.atomic.molecules.ButtonColumnMolecule
27+
import io.element.android.libraries.designsystem.atomic.molecules.IconTitleSubtitleMolecule
2728
import io.element.android.libraries.designsystem.atomic.pages.HeaderFooterPage
2829
import io.element.android.libraries.designsystem.components.BigIcon
29-
import io.element.android.libraries.designsystem.components.PageTitle
3030
import io.element.android.libraries.designsystem.preview.ElementPreview
3131
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
3232
import io.element.android.libraries.designsystem.theme.components.Button
@@ -65,10 +65,11 @@ fun ChooseSelfVerificationModeView(
6565
)
6666
},
6767
header = {
68-
PageTitle(
68+
IconTitleSubtitleMolecule(
69+
modifier = Modifier.padding(bottom = 16.dp),
6970
iconStyle = BigIcon.Style.Default(CompoundIcons.LockSolid()),
7071
title = stringResource(id = R.string.screen_identity_confirmation_title),
71-
subtitle = stringResource(id = R.string.screen_identity_confirmation_subtitle)
72+
subTitle = stringResource(id = R.string.screen_identity_confirmation_subtitle)
7273
)
7374
},
7475
footer = {

features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/incoming/IncomingVerificationState.kt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,5 +36,9 @@ data class IncomingVerificationState(
3636
data object Canceled : Step
3737
data object Completed : Step
3838
data object Failure : Step
39+
40+
val isTimeLimited: Boolean
41+
get() = this is Initial ||
42+
this is Verifying
3943
}
4044
}

features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/incoming/IncomingVerificationView.kt

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
package io.element.android.features.verifysession.impl.incoming
99

1010
import androidx.activity.compose.BackHandler
11+
import androidx.compose.foundation.focusable
1112
import androidx.compose.foundation.layout.Arrangement
1213
import androidx.compose.foundation.layout.Column
1314
import androidx.compose.foundation.layout.fillMaxWidth
@@ -19,6 +20,11 @@ import androidx.compose.ui.Alignment
1920
import androidx.compose.ui.Modifier
2021
import androidx.compose.ui.graphics.Color
2122
import androidx.compose.ui.res.stringResource
23+
import androidx.compose.ui.semantics.ProgressBarRangeInfo
24+
import androidx.compose.ui.semantics.contentDescription
25+
import androidx.compose.ui.semantics.focused
26+
import androidx.compose.ui.semantics.progressBarRangeInfo
27+
import androidx.compose.ui.semantics.semantics
2228
import androidx.compose.ui.text.style.TextAlign
2329
import androidx.compose.ui.tooling.preview.PreviewParameter
2430
import androidx.compose.ui.unit.dp
@@ -30,9 +36,9 @@ import io.element.android.features.verifysession.impl.incoming.ui.SessionDetails
3036
import io.element.android.features.verifysession.impl.ui.VerificationBottomMenu
3137
import io.element.android.features.verifysession.impl.ui.VerificationContentVerifying
3238
import io.element.android.features.verifysession.impl.ui.VerificationUserProfileContent
39+
import io.element.android.libraries.designsystem.atomic.molecules.IconTitleSubtitleMolecule
3340
import io.element.android.libraries.designsystem.atomic.pages.HeaderFooterPage
3441
import io.element.android.libraries.designsystem.components.BigIcon
35-
import io.element.android.libraries.designsystem.components.PageTitle
3642
import io.element.android.libraries.designsystem.components.button.BackButton
3743
import io.element.android.libraries.designsystem.preview.ElementPreview
3844
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
@@ -140,10 +146,26 @@ private fun IncomingVerificationHeader(step: Step, request: VerificationRequest.
140146
}
141147
Step.Failure -> R.string.screen_session_verification_request_failure_subtitle
142148
}
143-
PageTitle(
149+
val timeLimitMessage = if (step.isTimeLimited) {
150+
stringResource(CommonStrings.a11y_time_limited_action_required)
151+
} else {
152+
""
153+
}
154+
IconTitleSubtitleMolecule(
155+
modifier = Modifier
156+
.padding(bottom = 16.dp)
157+
.semantics(mergeDescendants = true) {
158+
contentDescription = timeLimitMessage
159+
focused = true
160+
if (iconStyle == BigIcon.Style.Loading) {
161+
// Same code than Modifier.progressSemantics()
162+
progressBarRangeInfo = ProgressBarRangeInfo.Indeterminate
163+
}
164+
}
165+
.focusable(),
144166
iconStyle = iconStyle,
145167
title = stringResource(id = titleTextId),
146-
subtitle = stringResource(id = subtitleTextId)
168+
subTitle = stringResource(id = subtitleTextId),
147169
)
148170
}
149171

@@ -187,7 +209,9 @@ private fun ContentInitial(
187209
}
188210
is VerificationRequest.Incoming.User -> {
189211
Column(
190-
modifier = Modifier.fillMaxWidth().padding(top = 24.dp),
212+
modifier = Modifier
213+
.fillMaxWidth()
214+
.padding(top = 24.dp),
191215
) {
192216
VerificationUserProfileContent(
193217
userId = request.details.senderProfile.userId,

features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/incoming/ui/SessionDetailsView.kt

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import androidx.compose.runtime.Composable
1818
import androidx.compose.ui.Alignment
1919
import androidx.compose.ui.Modifier
2020
import androidx.compose.ui.res.stringResource
21+
import androidx.compose.ui.semantics.semantics
2122
import androidx.compose.ui.unit.dp
2223
import io.element.android.compound.theme.ElementTheme
2324
import io.element.android.features.verifysession.impl.R
@@ -46,7 +47,8 @@ fun SessionDetailsView(
4647
color = ElementTheme.colors.borderDisabled,
4748
shape = RoundedCornerShape(8.dp)
4849
)
49-
.padding(24.dp),
50+
.padding(24.dp)
51+
.semantics(mergeDescendants = true) {},
5052
verticalArrangement = Arrangement.spacedBy(12.dp),
5153
) {
5254
Row(
@@ -76,6 +78,7 @@ fun SessionDetailsView(
7678
label = stringResource(CommonStrings.common_device_id),
7779
text = deviceId.value,
7880
modifier = Modifier.weight(5f),
81+
spellText = true,
7982
)
8083
}
8184
}

features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/outgoing/OutgoingVerificationState.kt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,5 +29,11 @@ data class OutgoingVerificationState(
2929
data class Verifying(val data: SessionVerificationData, val state: AsyncData<Unit>) : Step
3030
data object Completed : Step
3131
data object Exit : Step
32+
33+
val isTimeLimited: Boolean
34+
get() = this is Initial ||
35+
this is AwaitingOtherDeviceResponse ||
36+
this is Ready ||
37+
this is Verifying
3238
}
3339
}

features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/outgoing/OutgoingVerificationView.kt

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ package io.element.android.features.verifysession.impl.outgoing
99

1010
import androidx.activity.compose.BackHandler
1111
import androidx.compose.foundation.clickable
12+
import androidx.compose.foundation.focusable
1213
import androidx.compose.foundation.layout.Arrangement
1314
import androidx.compose.foundation.layout.Box
1415
import androidx.compose.foundation.layout.Row
@@ -22,6 +23,11 @@ import androidx.compose.ui.Alignment
2223
import androidx.compose.ui.Modifier
2324
import androidx.compose.ui.graphics.Color
2425
import androidx.compose.ui.res.stringResource
26+
import androidx.compose.ui.semantics.ProgressBarRangeInfo
27+
import androidx.compose.ui.semantics.contentDescription
28+
import androidx.compose.ui.semantics.focused
29+
import androidx.compose.ui.semantics.progressBarRangeInfo
30+
import androidx.compose.ui.semantics.semantics
2531
import androidx.compose.ui.tooling.preview.PreviewParameter
2632
import androidx.compose.ui.unit.dp
2733
import io.element.android.compound.theme.ElementTheme
@@ -31,9 +37,9 @@ import io.element.android.features.verifysession.impl.outgoing.OutgoingVerificat
3137
import io.element.android.features.verifysession.impl.ui.VerificationBottomMenu
3238
import io.element.android.features.verifysession.impl.ui.VerificationContentVerifying
3339
import io.element.android.libraries.architecture.AsyncData
40+
import io.element.android.libraries.designsystem.atomic.molecules.IconTitleSubtitleMolecule
3441
import io.element.android.libraries.designsystem.atomic.pages.HeaderFooterPage
3542
import io.element.android.libraries.designsystem.components.BigIcon
36-
import io.element.android.libraries.designsystem.components.PageTitle
3743
import io.element.android.libraries.designsystem.components.button.BackButton
3844
import io.element.android.libraries.designsystem.preview.ElementPreview
3945
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
@@ -180,11 +186,26 @@ private fun OutgoingVerificationHeader(step: Step, request: VerificationRequest.
180186
}
181187
is Step.Exit -> return
182188
}
183-
184-
PageTitle(
189+
val timeLimitMessage = if (step.isTimeLimited) {
190+
stringResource(CommonStrings.a11y_time_limited_action_required)
191+
} else {
192+
""
193+
}
194+
IconTitleSubtitleMolecule(
195+
modifier = Modifier
196+
.padding(bottom = 16.dp)
197+
.semantics(mergeDescendants = true) {
198+
contentDescription = timeLimitMessage
199+
focused = true
200+
if (iconStyle == BigIcon.Style.Loading) {
201+
// Same code than Modifier.progressSemantics()
202+
progressBarRangeInfo = ProgressBarRangeInfo.Indeterminate
203+
}
204+
}
205+
.focusable(),
185206
iconStyle = iconStyle,
186207
title = stringResource(id = titleTextId),
187-
subtitle = stringResource(id = subtitleTextId)
208+
subTitle = stringResource(id = subtitleTextId),
188209
)
189210
}
190211

features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/ui/VerificationContentVerifying.kt

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ import androidx.compose.ui.Alignment
2424
import androidx.compose.ui.Modifier
2525
import androidx.compose.ui.res.painterResource
2626
import androidx.compose.ui.res.stringResource
27+
import androidx.compose.ui.semantics.contentDescription
28+
import androidx.compose.ui.semantics.semantics
2729
import androidx.compose.ui.text.style.TextAlign
2830
import androidx.compose.ui.text.style.TextOverflow
2931
import androidx.compose.ui.unit.dp
@@ -39,14 +41,20 @@ internal fun VerificationContentVerifying(
3941
modifier: Modifier = Modifier,
4042
) {
4143
Box(
42-
modifier = modifier.fillMaxSize().padding(bottom = 20.dp),
44+
modifier = modifier
45+
.fillMaxSize()
46+
.padding(bottom = 20.dp),
4347
contentAlignment = Alignment.Center
4448
) {
4549
when (data) {
4650
is SessionVerificationData.Decimals -> {
47-
val text = data.decimals.joinToString(separator = " - ") { it.toString() }
51+
val text = data.decimals.joinToString(separator = " - ")
4852
Text(
49-
modifier = Modifier.fillMaxWidth(),
53+
modifier = Modifier
54+
.fillMaxWidth()
55+
.semantics {
56+
contentDescription = data.decimals.joinToString()
57+
},
5058
text = text,
5159
style = ElementTheme.typography.fontHeadingLgBold,
5260
color = ElementTheme.colors.textPrimary,
@@ -57,7 +65,9 @@ internal fun VerificationContentVerifying(
5765
// We want each row to have up to 4 emojis
5866
val rows = data.emojis.chunked(4)
5967
Column(
60-
modifier = Modifier.fillMaxWidth(),
68+
modifier = Modifier
69+
.fillMaxWidth()
70+
.semantics(mergeDescendants = true) {},
6171
verticalArrangement = Arrangement.spacedBy(40.dp),
6272
) {
6373
rows.forEach { emojis ->

libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/molecules/TextWithLabelMolecule.kt

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,26 @@ package io.element.android.libraries.designsystem.atomic.molecules
1010
import androidx.compose.foundation.layout.Column
1111
import androidx.compose.runtime.Composable
1212
import androidx.compose.ui.Modifier
13+
import androidx.compose.ui.semantics.contentDescription
14+
import androidx.compose.ui.semantics.semantics
1315
import io.element.android.compound.theme.ElementTheme
1416
import io.element.android.libraries.designsystem.theme.components.Text
1517

18+
/**
19+
* Display a label and a text in a column.
20+
* @param label the label to display
21+
* @param text the text to display
22+
* @param modifier the modifier to apply to this layout
23+
* @param spellText if true, the text will be spelled out in the content description for accessibility.
24+
* Useful for deviceId for instance, that the screen reader will read as a list of letters instead of trying to read a
25+
* word of random characters.
26+
*/
1627
@Composable
1728
fun TextWithLabelMolecule(
1829
label: String,
1930
text: String,
2031
modifier: Modifier = Modifier,
32+
spellText: Boolean = false,
2133
) {
2234
Column(modifier = modifier) {
2335
Text(
@@ -26,6 +38,11 @@ fun TextWithLabelMolecule(
2638
color = ElementTheme.colors.textSecondary,
2739
)
2840
Text(
41+
modifier = Modifier.semantics {
42+
if (spellText) {
43+
contentDescription = text.toList().joinToString()
44+
}
45+
},
2946
text = text,
3047
style = ElementTheme.typography.fontBodyMdRegular,
3148
color = ElementTheme.colors.textPrimary,

0 commit comments

Comments
 (0)