Skip to content

Commit

Permalink
feat: Add accessibility string to OtherUserProfile and search partici…
Browse files Browse the repository at this point in the history
…pants [WPB-9784] (#3531)
  • Loading branch information
borichellow authored Oct 25, 2024
1 parent 499302d commit 669d39a
Show file tree
Hide file tree
Showing 24 changed files with 183 additions and 71 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ import androidx.compose.ui.platform.testTag
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.res.vectorResource
import androidx.compose.ui.semantics.onClick
import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import com.wire.android.BuildConfig
Expand Down Expand Up @@ -101,6 +103,8 @@ private fun DeviceItemContent(
shouldShowE2EIInfo: Boolean,
modifier: Modifier = Modifier,
) {
val openDetailsDescription = stringResource(id = R.string.content_description_user_profile_open_device_btn)

Row(
verticalAlignment = Alignment.Top,
modifier = modifier
Expand All @@ -109,6 +113,9 @@ private fun DeviceItemContent(
onClickAction?.invoke(device)
}
}
.semantics {
if (isWholeItemClickable) onClick(openDetailsDescription) { false }
}
) {
Row(
modifier = Modifier
Expand All @@ -118,7 +125,7 @@ private fun DeviceItemContent(
Icon(
modifier = Modifier.shimmerPlaceholder(visible = placeholder),
imageVector = ImageVector.vectorResource(id = R.drawable.ic_devices),
contentDescription = stringResource(R.string.content_description_remove_devices_screen_device_item_icon)
contentDescription = null
)
Column(
horizontalAlignment = Alignment.Start,
Expand Down Expand Up @@ -199,7 +206,8 @@ private fun ColumnScope.DeviceItemTexts(
ProteusVerifiedIcon(
Modifier
.wrapContentWidth()
.align(Alignment.CenterVertically))
.align(Alignment.CenterVertically)
)
}
}
}
Expand Down
8 changes: 5 additions & 3 deletions app/src/main/kotlin/com/wire/android/ui/common/CopyButton.kt
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

package com.wire.android.ui.common

import androidx.annotation.StringRes
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import com.wire.android.R
Expand All @@ -27,13 +28,14 @@ import com.wire.android.ui.common.button.WireSecondaryIconButton
@Composable
fun CopyButton(
onCopyClicked: () -> Unit,
state: WireButtonState = WireButtonState.Default,
modifier: Modifier = Modifier
modifier: Modifier = Modifier,
@StringRes contentDescription: Int = R.string.content_description_copy,
state: WireButtonState = WireButtonState.Default
) {
WireSecondaryIconButton(
onButtonClicked = onCopyClicked,
iconResource = R.drawable.ic_copy,
contentDescription = R.string.content_description_copy,
contentDescription = contentDescription,
state = state,
modifier = modifier
)
Expand Down
4 changes: 3 additions & 1 deletion app/src/main/kotlin/com/wire/android/ui/common/SearchBar.kt
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,8 @@ fun SearchBarInput(
placeholderAlignment: Alignment.Horizontal = Alignment.CenterHorizontally,
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
textStyle: TextStyle = LocalTextStyle.current,
isLoading: Boolean = false
isLoading: Boolean = false,
semanticDescription: String? = null
) {

WireTextField(
Expand Down Expand Up @@ -108,6 +109,7 @@ fun SearchBarInput(
placeholderAlignment = placeholderAlignment,
placeholderText = placeholderText,
lineLimits = TextFieldLineLimits.SingleLine,
semanticDescription = semanticDescription
)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ internal fun WireTextField(
labelText: String? = null,
labelMandatoryIcon: Boolean = false,
descriptionText: String? = null,
semanticDescription: String? = null,
leadingIcon: @Composable (() -> Unit)? = null,
trailingIcon: @Composable (() -> Unit)? = null,
state: WireTextFieldState = WireTextFieldState.Default,
Expand Down Expand Up @@ -115,6 +116,7 @@ internal fun WireTextField(
labelText = labelText,
labelMandatoryIcon = labelMandatoryIcon,
descriptionText = descriptionText,
semanticDescription = semanticDescription,
leadingIcon = leadingIcon,
trailingIcon = trailingIcon,
state = state,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.graphics.Shape
import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.platform.testTag
import androidx.compose.ui.semantics.clearAndSetSemantics
import androidx.compose.ui.semantics.contentDescription
import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.text.TextStyle
Expand All @@ -55,6 +56,12 @@ import com.wire.android.ui.theme.wireTypography
import com.wire.android.util.EMPTY
import io.github.esentsov.PackagePrivate

/**
* Priority in which fields are used for SemanticContentDescription:
* [semanticDescription] -> [labelText] -> [placeholderText] -> [descriptionText].
* If you need to make empty SemanticContentDescription (which is definitely bad idea for TextView)
* set [semanticDescription] = ""
*/
@PackagePrivate
@Composable
internal fun WireTextFieldLayout(
Expand All @@ -65,6 +72,7 @@ internal fun WireTextFieldLayout(
labelText: String? = null,
labelMandatoryIcon: Boolean = false,
descriptionText: String? = null,
semanticDescription: String? = null,
leadingIcon: @Composable (() -> Unit)? = null,
trailingIcon: @Composable (() -> Unit)? = null,
state: WireTextFieldState = WireTextFieldState.Default,
Expand Down Expand Up @@ -101,8 +109,8 @@ internal fun WireTextFieldLayout(
.fillMaxWidth()
.background(color = colors.backgroundColor(state).value, shape = shape)
.border(width = dimensions().spacing1x, color = colors.borderColor(state, interactionSource).value, shape = shape)
.semantics {
(labelText ?: placeholderText ?: descriptionText)?.let {
.semantics(mergeDescendants = true) {
(semanticDescription ?: labelText ?: placeholderText ?: descriptionText)?.let {
contentDescription = it
}
}
Expand Down Expand Up @@ -178,7 +186,9 @@ private fun InnerTextLayout(
text = placeholderText,
style = placeholderTextStyle,
color = colors.placeholderColor(style).value,
modifier = Modifier.align(placeholderAlignment.toAlignment())
modifier = Modifier
.align(placeholderAlignment.toAlignment())
.clearAndSetSemantics {}
)
}
Box(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ fun SearchTopBar(
searchQueryTextState: TextFieldState,
modifier: Modifier = Modifier,
isLoading: Boolean = false,
searchBarDescription: String? = null,
onCloseSearchClicked: (() -> Unit)? = null,
onActiveChanged: (isActive: Boolean) -> Unit = {},
bottomContent: @Composable ColumnScope.() -> Unit = {}
Expand Down Expand Up @@ -107,6 +108,7 @@ fun SearchTopBar(

SearchBarInput(
placeholderText = searchBarHint,
semanticDescription = searchBarDescription,
textState = searchQueryTextState,
isLoading = isLoading,
leadingIcon = {
Expand All @@ -118,7 +120,7 @@ fun SearchTopBar(
) {
Icon(
painter = painterResource(R.drawable.ic_search),
contentDescription = stringResource(R.string.content_description_conversation_search_icon),
contentDescription = null,
tint = MaterialTheme.wireColorScheme.onBackground,
)
}
Expand All @@ -129,7 +131,7 @@ fun SearchTopBar(
) {
Icon(
painter = rememberVectorPainter(image = Icons.Filled.ArrowBack),
contentDescription = stringResource(R.string.content_description_back_button),
contentDescription = stringResource(id = R.string.content_description_add_participants_back_btn),
tint = MaterialTheme.wireColorScheme.onBackground,
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,10 +43,11 @@ fun ConversationMediaButton(
leadingIcon = {
Icon(
painter = painterResource(id = R.drawable.ic_gallery),
contentDescription = stringResource(R.string.label_conversation_media),
contentDescription = null,
tint = MaterialTheme.colorScheme.onBackground,
modifier = Modifier.padding(end = dimensions().spacing8x)
)
}
},
onClickDescription = stringResource(id = R.string.content_description_see_media_in_conversation_btn)
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,10 @@ import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Alignment.Companion.CenterVertically
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.semantics.stateDescription
import com.wire.android.R
import com.wire.android.appLogger
import com.wire.android.model.Clickable
import com.wire.android.model.ItemActionType
Expand Down Expand Up @@ -53,16 +57,15 @@ fun InternalContactSearchResultItem(
membership: Membership,
searchQuery: String,
connectionState: ConnectionState,
onCheckChange: (Boolean) -> Unit,
onCheckClickable: Clickable,
isSelected: Boolean,
clickable: Clickable,
actionType: ItemActionType,
modifier: Modifier = Modifier
) {
val selectedDescription = stringResource(id = R.string.content_description_selected_label)
RowItemTemplate(
leadingIcon = {
UserProfileAvatar(avatarData)
},
leadingIcon = { UserProfileAvatar(avatarData) },
titleStartPadding = dimensions().spacing0x,
title = {
Row(verticalAlignment = CenterVertically) {
Expand Down Expand Up @@ -101,14 +104,16 @@ fun InternalContactSearchResultItem(
}
},
clickable =
if (actionType.clickable) {
clickable
} else {
Clickable {
onCheckChange(!isSelected)
}
},
modifier = modifier.padding(start = dimensions().spacing8x)
if (actionType.clickable) {
clickable
} else {
onCheckClickable
},
modifier = modifier
.padding(start = dimensions().spacing8x)
.semantics {
if (actionType.checkable && isSelected) stateDescription = selectedDescription
}
)
}

Expand Down Expand Up @@ -155,15 +160,20 @@ fun ExternalContactSearchResultItem(
when (connectionState) {
ConnectionState.NOT_CONNECTED, ConnectionState.CANCELLED ->
AddContactButton(userId, name)

ConnectionState.PENDING, ConnectionState.IGNORED ->
Box(modifier = Modifier.padding(horizontal = dimensions().spacing12x)) { ConnectRequestBadge() }

ConnectionState.SENT ->
Box(modifier = Modifier.padding(horizontal = dimensions().spacing12x)) { ConnectPendingRequestBadge() }

ConnectionState.BLOCKED -> {
}

ConnectionState.MISSING_LEGALHOLD_CONSENT -> {
appLogger.e("Unhandled ConnectionState.MISSING_LEGALHOLD_CONSENT in ExternalContactSearchResultItem")
}

ConnectionState.ACCEPTED -> {
appLogger.e("ConnectionState.ACCEPTED should not appear in ExternalContactSearchResultItem")
}
Expand All @@ -184,12 +194,13 @@ fun PreviewInternalContactSearchResultItemCheckable() = WireTheme {
membership = Membership.None,
searchQuery = "",
connectionState = ConnectionState.ACCEPTED,
onCheckChange = {},
onCheckClickable = Clickable {},
isSelected = false,
clickable = Clickable {},
actionType = ItemActionType.CHECK,
)
}

@PreviewMultipleThemes
@Composable
fun PreviewInternalContactSearchResultItemClickable() = WireTheme {
Expand All @@ -200,7 +211,7 @@ fun PreviewInternalContactSearchResultItemClickable() = WireTheme {
membership = Membership.None,
searchQuery = "",
connectionState = ConnectionState.ACCEPTED,
onCheckChange = {},
onCheckClickable = Clickable {},
isSelected = false,
clickable = Clickable {},
actionType = ItemActionType.CLICK,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,8 @@ import com.wire.android.ui.home.newconversation.model.Contact
import com.wire.android.ui.theme.WireTheme
import com.wire.android.util.extension.FolderType
import com.wire.android.util.extension.folderWithElements
import com.wire.android.util.ui.keepOnTopWhenNotScrolled
import com.wire.android.util.ui.PreviewMultipleThemes
import com.wire.android.util.ui.keepOnTopWhenNotScrolled
import com.wire.kalium.logic.data.user.ConnectionState
import com.wire.kalium.logic.data.user.UserId
import kotlinx.collections.immutable.ImmutableList
Expand Down Expand Up @@ -287,7 +287,13 @@ private fun LazyListScope.internalSuccessItem(
folderType = FolderType.Collapsing(expanded = expanded, onChanged = onExpansionChanged),
) { (contact, isSelected) ->
with(contact) {
val onClick = remember { { isChecked: Boolean -> onChecked(isChecked, this) } }
val onCheckDescription = stringResource(
id = if (isSelected) R.string.content_description_unselect_label else R.string.content_description_select_label
)
val clickDescription = stringResource(id = R.string.content_description_open_user_profile_label)
val onCheckClickable = remember(isSelected) {
Clickable(onClickDescription = onCheckDescription) { onChecked(!isSelected, this) }
}
InternalContactSearchResultItem(
avatarData = avatarData,
name = name,
Expand All @@ -296,9 +302,9 @@ private fun LazyListScope.internalSuccessItem(
searchQuery = searchQuery,
connectionState = connectionState,
isSelected = isSelected,
onCheckChange = onClick,
onCheckClickable = onCheckClickable,
actionType = actionType,
clickable = remember { Clickable(enabled = true) { onOpenUserProfile(contact) } }
clickable = remember { Clickable(onClickDescription = clickDescription) { onOpenUserProfile(contact) } }
)
}
}
Expand Down Expand Up @@ -344,6 +350,7 @@ private fun LazyListScope.externalSuccessItem(
folderType = FolderType.Collapsing(expanded = expanded, onChanged = onExpansionChanged),
) { contact ->
with(contact) {
val clickDescription = stringResource(id = R.string.content_description_open_user_profile_label)
ExternalContactSearchResultItem(
avatarData = avatarData,
userId = UserId(id, domain),
Expand All @@ -352,7 +359,7 @@ private fun LazyListScope.externalSuccessItem(
membership = membership,
connectionState = connectionState,
searchQuery = searchQuery,
clickable = remember { Clickable(enabled = true) { onOpenUserProfile(contact) } }
clickable = remember { Clickable(onClickDescription = clickDescription) { onOpenUserProfile(contact) } }
)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.hilt.navigation.compose.hiltViewModel
import com.wire.android.R
import com.wire.android.model.Clickable
Expand Down Expand Up @@ -110,6 +111,7 @@ private fun SuccessServicesList(
folderWithElements(
items = services.associateBy { it.id }
) {
val clickDescription = stringResource(id = R.string.content_description_open_service_label)
RowItemTemplate(
leadingIcon = {
Row {
Expand All @@ -132,7 +134,7 @@ private fun SuccessServicesList(
}
},
actions = {},
clickable = remember(it) { Clickable(enabled = true) { onServiceClicked(it) } },
clickable = remember(it) { Clickable(onClickDescription = clickDescription) { onServiceClicked(it) } },
modifier = Modifier.padding(start = dimensions().spacing8x)
)
}
Expand Down
Loading

0 comments on commit 669d39a

Please sign in to comment.