[a11y] Ensure that the focus is not lost when the send button state change.
This commit is contained in:
@@ -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()
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
<string name="a11y_jump_to_bottom">"Jump to bottom"</string>
|
||||
<string name="a11y_notifications_mentions_only">"Mentions only"</string>
|
||||
<string name="a11y_notifications_muted">"Muted"</string>
|
||||
<string name="a11y_other_user_avatar">"Other user avatar"</string>
|
||||
<string name="a11y_other_user_avatar">"Other user\'s avatar"</string>
|
||||
<string name="a11y_page_n">"Page %1$d"</string>
|
||||
<string name="a11y_pause">"Pause"</string>
|
||||
<string name="a11y_paused_voice_message">"Voice message, duration: %1$s, current position: %2$s"</string>
|
||||
@@ -125,7 +125,9 @@
|
||||
<string name="action_save">"Save"</string>
|
||||
<string name="action_search">"Search"</string>
|
||||
<string name="action_send">"Send"</string>
|
||||
<string name="action_send_edited_message">"Send edited message"</string>
|
||||
<string name="action_send_message">"Send message"</string>
|
||||
<string name="action_send_voice_message">"Send voice message"</string>
|
||||
<string name="action_share">"Share"</string>
|
||||
<string name="action_share_link">"Share link"</string>
|
||||
<string name="action_show">"Show"</string>
|
||||
|
||||
Reference in New Issue
Block a user