Skip to content

[a11y] Make more items focusable #4605

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Apr 22, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
package io.element.android.features.call.impl.utils

import android.net.Uri
import androidx.core.net.toUri
import javax.inject.Inject

class CallIntentDataParser @Inject constructor() {
Expand All @@ -17,7 +18,7 @@ class CallIntentDataParser @Inject constructor() {
)

fun parse(data: String?): String? {
val parsedUrl = data?.let { Uri.parse(data) } ?: return null
val parsedUrl = data?.toUri() ?: return null
val scheme = parsedUrl.scheme
return when {
scheme in validHttpSchemes -> parsedUrl
Expand All @@ -37,7 +38,7 @@ class CallIntentDataParser @Inject constructor() {
private fun Uri.getUrlParameter(): Uri? {
return getQueryParameter("url")
?.let { urlParameter ->
Uri.parse(urlParameter).takeIf { uri ->
urlParameter.toUri().takeIf { uri ->
uri.scheme in validHttpSchemes && !uri.host.isNullOrBlank()
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,11 @@

import android.content.Context
import android.content.Intent
import android.net.Uri
import androidx.core.net.toUri
import io.element.android.appconfig.AuthenticationConfig
import io.element.android.libraries.core.data.tryOrNull

fun openLearnMorePage(context: Context) {
val intent = Intent(Intent.ACTION_VIEW, Uri.parse(AuthenticationConfig.SLIDING_SYNC_READ_MORE_URL))
val intent = Intent(Intent.ACTION_VIEW, AuthenticationConfig.SLIDING_SYNC_READ_MORE_URL.toUri())

Check warning on line 17 in features/login/impl/src/main/kotlin/io/element/android/features/login/impl/util/Util.kt

View check run for this annotation

Codecov / codecov/patch

features/login/impl/src/main/kotlin/io/element/android/features/login/impl/util/Util.kt#L17

Added line #L17 was not covered by tests
tryOrNull { context.startActivity(intent) }
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

package io.element.android.features.login.impl.web

import android.net.Uri
import androidx.core.net.toUri
import com.squareup.anvil.annotations.ContributesBinding
import io.element.android.appconfig.AuthenticationConfig
import io.element.android.features.login.impl.resolver.network.WellknownAPI
Expand Down Expand Up @@ -43,7 +43,7 @@
}
val registrationHelperUrl = result.registrationHelperUrl
return if (registrationHelperUrl != null) {
Uri.parse(registrationHelperUrl)
registrationHelperUrl.toUri()

Check warning on line 46 in features/login/impl/src/main/kotlin/io/element/android/features/login/impl/web/WebClientUrlForAuthenticationRetriever.kt

View check run for this annotation

Codecov / codecov/patch

features/login/impl/src/main/kotlin/io/element/android/features/login/impl/web/WebClientUrlForAuthenticationRetriever.kt#L46

Added line #L46 was not covered by tests
.buildUpon()
.appendQueryParameter("hs_url", homeServerUrl)
.build()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ class EditUserProfilePresenter @AssistedInject constructor(
@Composable
override fun present(): EditUserProfileState {
val cameraPermissionState = cameraPermissionPresenter.present()
var userAvatarUri by rememberSaveable { mutableStateOf(matrixUser.avatarUrl?.let { Uri.parse(it) }) }
var userAvatarUri by rememberSaveable { mutableStateOf(matrixUser.avatarUrl?.toUri()) }
var userDisplayName by rememberSaveable { mutableStateOf(matrixUser.displayName) }
val cameraPhotoPicker = mediaPickerProvider.registerCameraPhotoPicker(
onResult = { uri ->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ package io.element.android.features.roomdetails.impl.edit

import android.net.Uri
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
import androidx.core.net.toUri
import io.element.android.libraries.architecture.AsyncAction
import io.element.android.libraries.matrix.api.core.RoomId
import io.element.android.libraries.matrix.ui.media.AvatarAction
Expand All @@ -22,7 +23,7 @@ open class RoomDetailsEditStateProvider : PreviewParameterProvider<RoomDetailsEd
aRoomDetailsEditState(),
aRoomDetailsEditState(roomTopic = ""),
aRoomDetailsEditState(roomRawName = ""),
aRoomDetailsEditState(roomAvatarUrl = Uri.parse("example://uri")),
aRoomDetailsEditState(roomAvatarUrl = "example://uri".toUri()),
aRoomDetailsEditState(canChangeName = true, canChangeTopic = false, canChangeAvatar = true, saveButtonEnabled = false),
aRoomDetailsEditState(canChangeName = false, canChangeTopic = true, canChangeAvatar = false, saveButtonEnabled = false),
aRoomDetailsEditState(saveAction = AsyncAction.Loading),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,12 @@

import android.app.Activity
import android.content.ActivityNotFoundException
import android.net.Uri
import android.os.Bundle
import android.provider.Browser
import androidx.browser.customtabs.CustomTabColorSchemeParams
import androidx.browser.customtabs.CustomTabsIntent
import androidx.browser.customtabs.CustomTabsSession
import androidx.core.net.toUri
import io.element.android.libraries.androidutils.system.openUrlInExternalApp
import java.util.Locale

Expand Down Expand Up @@ -58,7 +58,7 @@
putString("Accept-Language", Locale.getDefault().toLanguageTag())
})
}
.launchUrl(this, Uri.parse(url))
.launchUrl(this, url.toUri())

Check warning on line 61 in libraries/androidutils/src/main/kotlin/io/element/android/libraries/androidutils/browser/ChromeCustomTab.kt

View check run for this annotation

Codecov / codecov/patch

libraries/androidutils/src/main/kotlin/io/element/android/libraries/androidutils/browser/ChromeCustomTab.kt#L61

Added line #L61 was not covered by tests
} catch (activityNotFoundException: ActivityNotFoundException) {
openUrlInExternalApp(url)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import androidx.activity.result.ActivityResultLauncher
import androidx.annotation.RequiresApi
import androidx.core.content.pm.PackageInfoCompat
import androidx.core.net.toUri
import io.element.android.libraries.androidutils.R
import io.element.android.libraries.androidutils.compat.getApplicationInfoCompat
import io.element.android.libraries.core.mimetype.MimeTypes
Expand Down Expand Up @@ -121,7 +122,7 @@
noActivityFoundMessage: String = getString(R.string.error_no_compatible_app_found),
) {
val intent = Intent(Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES)
.setData(Uri.parse("package:$packageName"))
.setData("package:$packageName".toUri())

Check warning on line 125 in libraries/androidutils/src/main/kotlin/io/element/android/libraries/androidutils/system/SystemUtils.kt

View check run for this annotation

Codecov / codecov/patch

libraries/androidutils/src/main/kotlin/io/element/android/libraries/androidutils/system/SystemUtils.kt#L125

Added line #L125 was not covered by tests
try {
activityResultLauncher.launch(intent)
} catch (activityNotFoundException: ActivityNotFoundException) {
Expand Down Expand Up @@ -165,7 +166,7 @@
url: String,
errorMessage: String = getString(R.string.error_no_compatible_app_found),
) {
val intent = Intent(Intent.ACTION_VIEW, Uri.parse(url))
val intent = Intent(Intent.ACTION_VIEW, url.toUri())

Check warning on line 169 in libraries/androidutils/src/main/kotlin/io/element/android/libraries/androidutils/system/SystemUtils.kt

View check run for this annotation

Codecov / codecov/patch

libraries/androidutils/src/main/kotlin/io/element/android/libraries/androidutils/system/SystemUtils.kt#L169

Added line #L169 was not covered by tests
if (this !is Activity) {
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@
package io.element.android.libraries.androidutils.uri

import android.net.Uri
import androidx.core.net.toUri

const val IGNORED_SCHEMA = "ignored"

fun createIgnoredUri(path: String): Uri = Uri.parse("$IGNORED_SCHEMA://$path")
fun createIgnoredUri(path: String): Uri = "$IGNORED_SCHEMA://$path".toUri()
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import androidx.compose.ui.draw.clip
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.platform.LocalInspectionMode
import androidx.compose.ui.semantics.clearAndSetSemantics
import androidx.compose.ui.semantics.contentDescription
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.unit.Dp
Expand Down Expand Up @@ -59,6 +60,7 @@ fun Avatar(
avatarData = avatarData,
forcedAvatarSize = forcedAvatarSize,
modifier = commonModifier,
contentDescription = contentDescription,
)
} else {
ImageAvatar(
Expand Down Expand Up @@ -103,11 +105,13 @@ private fun ImageAvatar(
InitialsAvatar(
avatarData = avatarData,
forcedAvatarSize = forcedAvatarSize,
contentDescription = contentDescription,
)
}
else -> InitialsAvatar(
avatarData = avatarData,
forcedAvatarSize = forcedAvatarSize,
contentDescription = contentDescription,
)
}
}
Expand All @@ -118,6 +122,7 @@ private fun ImageAvatar(
private fun InitialsAvatar(
avatarData: AvatarData,
forcedAvatarSize: Dp?,
contentDescription: String?,
modifier: Modifier = Modifier,
) {
val avatarColors = AvatarColorsProvider.provide(avatarData.id)
Expand All @@ -130,7 +135,11 @@ private fun InitialsAvatar(
val lineHeight = originalFont.lineHeight * ratio
Text(
modifier = Modifier
.clearAndSetSemantics {}
.clearAndSetSemantics {
contentDescription?.let {
this.contentDescription = it
}
}
.align(Alignment.Center),
text = avatarData.initial,
style = originalFont.copy(fontSize = fontSize, lineHeight = lineHeight, letterSpacing = 0.sp),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,14 @@ package io.element.android.libraries.fullscreenintent.impl

import android.content.ActivityNotFoundException
import android.content.Intent
import android.net.Uri
import android.os.Build
import android.provider.Settings
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.rememberCoroutineScope
import androidx.core.app.NotificationManagerCompat
import androidx.core.net.toUri
import androidx.datastore.preferences.core.booleanPreferencesKey
import androidx.datastore.preferences.core.edit
import io.element.android.libraries.architecture.Presenter
Expand Down Expand Up @@ -77,7 +77,7 @@ class FullScreenIntentPermissionsPresenter @Inject constructor(
try {
val intent = Intent(
Settings.ACTION_MANAGE_APP_USE_FULL_SCREEN_INTENT,
Uri.parse("package:${buildMeta.applicationId}")
"package:${buildMeta.applicationId}".toUri()
)
externalIntentLauncher.launch(intent)
} catch (e: ActivityNotFoundException) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
package io.element.android.libraries.matrix.impl.permalink

import android.net.Uri
import androidx.core.net.toUri
import com.squareup.anvil.annotations.ContributesBinding
import io.element.android.appconfig.MatrixConfiguration
import io.element.android.libraries.core.extensions.replacePrefix
Expand Down Expand Up @@ -43,7 +44,7 @@ class DefaultMatrixToConverter @Inject constructor() : MatrixToConverter {
// Web or client url
SUPPORTED_PATHS.any { it in uriString } -> {
val path = SUPPORTED_PATHS.first { it in uriString }
Uri.parse(baseUrl + uriString.substringAfter(path))
(baseUrl + uriString.substringAfter(path)).toUri()
}
// URL is not supported
else -> null
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

package io.element.android.libraries.matrix.impl.permalink

import android.net.Uri
import androidx.core.net.toUri
import com.squareup.anvil.annotations.ContributesBinding
import io.element.android.libraries.di.AppScope
import io.element.android.libraries.matrix.api.core.EventId
Expand Down Expand Up @@ -38,7 +38,7 @@
* https://github.com/matrix-org/matrix-doc/blob/master/proposals/1704-matrix.to-permalinks.md
*/
override fun parse(uriString: String): PermalinkData {
val uri = Uri.parse(uriString)
val uri = uriString.toUri()

Check warning on line 41 in libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/permalink/DefaultPermalinkParser.kt

View check run for this annotation

Codecov / codecov/patch

libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/permalink/DefaultPermalinkParser.kt#L41

Added line #L41 was not covered by tests
// the client or element-based domain permalinks (e.g. https://app.element.io/#/user/@chagai95:matrix.org) don't have the
// mxid in the first param (like matrix.to does - https://matrix.to/#/@chagai95:matrix.org) but rather in the second after /user/ so /user/mxid
// so convert URI to matrix.to to simplify parsing process
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,13 @@ import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.semantics.clearAndSetSemantics
import androidx.compose.ui.semantics.contentDescription
import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
import androidx.compose.ui.unit.dp
import androidx.core.net.toUri
import io.element.android.compound.theme.ElementTheme
import io.element.android.compound.tokens.generated.CompoundIcons
import io.element.android.libraries.designsystem.components.avatar.Avatar
Expand All @@ -36,6 +40,7 @@ import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.designsystem.theme.components.Icon
import io.element.android.libraries.testtags.TestTags
import io.element.android.libraries.testtags.testTag
import io.element.android.libraries.ui.strings.CommonStrings

@Composable
fun EditableAvatarView(
Expand All @@ -50,6 +55,7 @@ fun EditableAvatarView(
modifier = modifier.fillMaxWidth(),
horizontalAlignment = Alignment.CenterHorizontally,
) {
val a11yAvatar = stringResource(CommonStrings.a11y_avatar)
Box(
modifier = Modifier
.size(avatarSize.dp)
Expand All @@ -59,6 +65,9 @@ fun EditableAvatarView(
indication = ripple(bounded = false),
)
.testTag(TestTags.editAvatar)
.clearAndSetSemantics {
contentDescription = a11yAvatar
},
) {
when (avatarUrl?.scheme) {
null, "mxc" -> {
Expand Down Expand Up @@ -112,7 +121,7 @@ open class EditableAvatarViewUriProvider : PreviewParameterProvider<Uri?> {
override val values: Sequence<Uri?>
get() = sequenceOf(
null,
Uri.parse("mxc://matrix.org/123456"),
Uri.parse("https://example.com/avatar.jpg"),
"mxc://matrix.org/123456".toUri(),
"https://example.com/avatar.jpg".toUri(),
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,10 @@ package io.element.android.libraries.oidc.impl.customtab
import android.app.Activity
import android.content.ComponentName
import android.content.Context
import android.net.Uri
import androidx.browser.customtabs.CustomTabsClient
import androidx.browser.customtabs.CustomTabsServiceConnection
import androidx.browser.customtabs.CustomTabsSession
import androidx.core.net.toUri
import io.element.android.libraries.androidutils.browser.openUrlInChromeCustomTab
import io.element.android.libraries.di.ApplicationContext
import javax.inject.Inject
Expand Down Expand Up @@ -55,7 +55,7 @@ class CustomTabHandler @Inject constructor(
customTabsSession = customTabsClient?.newSession(null)
}

customTabsSession?.mayLaunchUrl(Uri.parse(url), null, null)
customTabsSession?.mayLaunchUrl(url.toUri(), null, null)
}

fun disposeCustomTab() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
package io.element.android.libraries.push.impl.notifications.model

import android.net.Uri
import androidx.core.net.toUri
import io.element.android.libraries.matrix.api.core.EventId
import io.element.android.libraries.matrix.api.core.RoomId
import io.element.android.libraries.matrix.api.core.SessionId
Expand Down Expand Up @@ -52,7 +53,7 @@ data class NotifiableMessageEvent(
// Example of value:
// content://io.element.android.x.debug.notifications.fileprovider/downloads/temp/notif/matrix.org/XGItzSDOnSyXjYtOPfiKexDJ
val imageUri: Uri?
get() = imageUriString?.let { Uri.parse(it) }
get() = imageUriString?.toUri()
}

/**
Expand Down
Loading