diff --git a/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/TextComposer.kt b/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/TextComposer.kt
index 47216d7e88..91e36de12c 100644
--- a/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/TextComposer.kt
+++ b/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/TextComposer.kt
@@ -28,6 +28,7 @@ import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.runtime.ReadOnlyComposable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
@@ -39,7 +40,11 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.platform.LocalView
import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.semantics.SemanticsPropertyReceiver
+import androidx.compose.ui.semantics.clearAndSetSemantics
+import androidx.compose.ui.semantics.contentDescription
import androidx.compose.ui.semantics.hideFromAccessibility
+import androidx.compose.ui.semantics.onClick
import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.tooling.preview.PreviewParameter
@@ -280,6 +285,13 @@ fun TextComposer(
else -> sendButton
}
+ val endButtonA11y = endButtonA11y(
+ composerMode = composerMode,
+ voiceMessageState = voiceMessageState,
+ enableVoiceMessages = enableVoiceMessages,
+ canSendMessage = canSendMessage,
+ )
+
val voiceRecording = @Composable {
when (voiceMessageState) {
is VoiceMessageState.Preview ->
@@ -323,6 +335,7 @@ fun TextComposer(
)
},
textFormatting = textFormattingOptions,
+ endButtonA11y = endButtonA11y,
sendButton = sendButton,
)
} else {
@@ -334,6 +347,7 @@ fun TextComposer(
composerOptionsButton = composerOptionsButton,
textInput = textInput,
endButton = sendOrRecordButton,
+ endButtonA11y = endButtonA11y,
voiceRecording = voiceRecording,
voiceDeleteButton = voiceDeleteButton,
)
@@ -359,6 +373,40 @@ fun TextComposer(
}
}
+@ReadOnlyComposable
+@Composable
+private fun endButtonA11y(
+ composerMode: MessageComposerMode,
+ voiceMessageState: VoiceMessageState,
+ enableVoiceMessages: Boolean,
+ canSendMessage: Boolean,
+): (SemanticsPropertyReceiver) -> Unit {
+ val a11ySendButtonDescription = stringResource(
+ id = when {
+ enableVoiceMessages && !canSendMessage ->
+ when (voiceMessageState) {
+ VoiceMessageState.Idle,
+ is VoiceMessageState.Recording -> if (voiceMessageState is VoiceMessageState.Recording) {
+ CommonStrings.a11y_voice_message_stop_recording
+ } else {
+ CommonStrings.a11y_voice_message_record
+ }
+ is VoiceMessageState.Preview -> when (voiceMessageState.isSending) {
+ true -> CommonStrings.common_sending
+ false -> CommonStrings.action_send_voice_message
+ }
+ }
+ composerMode.isEditing -> CommonStrings.action_send_edited_message
+ else -> CommonStrings.action_send_message
+ }
+ )
+ val endButtonA11y: (SemanticsPropertyReceiver.() -> Unit) = {
+ contentDescription = a11ySendButtonDescription
+ onClick(null, null)
+ }
+ return endButtonA11y
+}
+
@Composable
private fun StandardLayout(
voiceMessageState: VoiceMessageState,
@@ -369,6 +417,7 @@ private fun StandardLayout(
voiceRecording: @Composable () -> Unit,
voiceDeleteButton: @Composable () -> Unit,
endButton: @Composable () -> Unit,
+ endButtonA11y: (SemanticsPropertyReceiver.() -> Unit),
modifier: Modifier = Modifier,
) {
Column(modifier = modifier) {
@@ -416,7 +465,8 @@ private fun StandardLayout(
Box(
Modifier
.padding(bottom = 5.dp, top = 5.dp, end = 6.dp, start = 6.dp)
- .size(48.dp),
+ .size(48.dp)
+ .clearAndSetSemantics(endButtonA11y),
contentAlignment = Alignment.Center,
) {
endButton()
@@ -454,6 +504,7 @@ private fun TextFormattingLayout(
dismissTextFormattingButton: @Composable () -> Unit,
textFormatting: @Composable () -> Unit,
sendButton: @Composable () -> Unit,
+ endButtonA11y: (SemanticsPropertyReceiver.() -> Unit),
modifier: Modifier = Modifier
) {
Column(
@@ -485,10 +536,12 @@ private fun TextFormattingLayout(
textFormatting()
}
Box(
- modifier = Modifier.padding(
- start = 14.dp,
- end = 6.dp
- )
+ modifier = Modifier
+ .padding(
+ start = 14.dp,
+ end = 6.dp,
+ )
+ .clearAndSetSemantics(endButtonA11y)
) {
sendButton()
}
diff --git a/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/components/SendButton.kt b/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/components/SendButton.kt
index aeeda86269..55e4350fb2 100644
--- a/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/components/SendButton.kt
+++ b/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/components/SendButton.kt
@@ -21,7 +21,6 @@ import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.LinearGradientShader
import androidx.compose.ui.graphics.ShaderBrush
-import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import io.element.android.compound.theme.ElementTheme
import io.element.android.compound.tokens.generated.CompoundIcons
@@ -32,7 +31,6 @@ import io.element.android.libraries.designsystem.theme.components.IconButton
import io.element.android.libraries.matrix.api.core.EventId
import io.element.android.libraries.matrix.api.timeline.item.event.toEventOrTransactionId
import io.element.android.libraries.textcomposer.model.MessageComposerMode
-import io.element.android.libraries.ui.strings.CommonStrings
/**
* Send button for the message composer.
@@ -60,10 +58,6 @@ internal fun SendButton(
composerMode.isEditing -> 0.dp
else -> 2.dp
}
- val contentDescription = when {
- composerMode.isEditing -> stringResource(CommonStrings.action_edit)
- else -> stringResource(CommonStrings.action_send)
- }
Box(
modifier = Modifier
.clip(CircleShape)
@@ -81,7 +75,8 @@ internal fun SendButton(
.padding(start = iconStartPadding)
.align(Alignment.Center),
imageVector = iconVector,
- contentDescription = contentDescription,
+ // Note: accessibility is managed in TextComposer.
+ contentDescription = null,
tint = if (canSendMessage) {
if (ElementTheme.colors.isLight) {
ElementTheme.colors.iconOnSolidPrimary
diff --git a/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/components/VoiceMessageRecorderButton.kt b/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/components/VoiceMessageRecorderButton.kt
index 03965566e5..3a92dfbeb0 100644
--- a/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/components/VoiceMessageRecorderButton.kt
+++ b/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/components/VoiceMessageRecorderButton.kt
@@ -16,7 +16,6 @@ import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.hapticfeedback.HapticFeedbackType
import androidx.compose.ui.platform.LocalHapticFeedback
-import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import io.element.android.compound.theme.ElementTheme
import io.element.android.compound.tokens.generated.CompoundIcons
@@ -26,7 +25,6 @@ import io.element.android.libraries.designsystem.theme.components.Icon
import io.element.android.libraries.designsystem.theme.components.IconButton
import io.element.android.libraries.designsystem.utils.CommonDrawables
import io.element.android.libraries.textcomposer.model.VoiceMessageRecorderEvent
-import io.element.android.libraries.ui.strings.CommonStrings
@Composable
internal fun VoiceMessageRecorderButton(
@@ -70,7 +68,8 @@ private fun StartButton(
Icon(
modifier = Modifier.size(24.dp),
imageVector = CompoundIcons.MicOn(),
- contentDescription = stringResource(CommonStrings.a11y_voice_message_record),
+ // Note: accessibility is managed in TextComposer.
+ contentDescription = null,
tint = ElementTheme.colors.iconSecondary,
)
}
@@ -95,7 +94,8 @@ private fun StopButton(
Icon(
modifier = Modifier.size(24.dp),
resourceId = CommonDrawables.ic_stop,
- contentDescription = stringResource(CommonStrings.a11y_voice_message_stop_recording),
+ // Note: accessibility is managed in TextComposer.
+ contentDescription = null,
tint = ElementTheme.colors.iconOnSolidPrimary,
)
}
diff --git a/libraries/ui-strings/src/main/res/values/localazy.xml b/libraries/ui-strings/src/main/res/values/localazy.xml
index 41e87f83c5..f57bcef62d 100644
--- a/libraries/ui-strings/src/main/res/values/localazy.xml
+++ b/libraries/ui-strings/src/main/res/values/localazy.xml
@@ -12,7 +12,7 @@
"Jump to bottom"
"Mentions only"
"Muted"
- "Other user avatar"
+ "Other user\'s avatar"
"Page %1$d"
"Pause"
"Voice message, duration: %1$s, current position: %2$s"
@@ -125,7 +125,9 @@
"Save"
"Search"
"Send"
+ "Send edited message"
"Send message"
+ "Send voice message"
"Share"
"Share link"
"Show"