Skip to content

Commit bacfa09

Browse files
authored
[a11y] Make more items focusable (#4605)
* Fix settings entry point not available when there is no avatar on the account. Fixes #4599. * Use Ktx extension `String.toUri()` * Allow screen reader to focus on the user avatar to allow editing it. * Fix import order
1 parent cb926ee commit bacfa09

File tree

15 files changed

+48
-24
lines changed

15 files changed

+48
-24
lines changed

features/call/impl/src/main/kotlin/io/element/android/features/call/impl/utils/CallIntentDataParser.kt

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
package io.element.android.features.call.impl.utils
99

1010
import android.net.Uri
11+
import androidx.core.net.toUri
1112
import javax.inject.Inject
1213

1314
class CallIntentDataParser @Inject constructor() {
@@ -17,7 +18,7 @@ class CallIntentDataParser @Inject constructor() {
1718
)
1819

1920
fun parse(data: String?): String? {
20-
val parsedUrl = data?.let { Uri.parse(data) } ?: return null
21+
val parsedUrl = data?.toUri() ?: return null
2122
val scheme = parsedUrl.scheme
2223
return when {
2324
scheme in validHttpSchemes -> parsedUrl
@@ -37,7 +38,7 @@ class CallIntentDataParser @Inject constructor() {
3738
private fun Uri.getUrlParameter(): Uri? {
3839
return getQueryParameter("url")
3940
?.let { urlParameter ->
40-
Uri.parse(urlParameter).takeIf { uri ->
41+
urlParameter.toUri().takeIf { uri ->
4142
uri.scheme in validHttpSchemes && !uri.host.isNullOrBlank()
4243
}
4344
}

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,11 @@ package io.element.android.features.login.impl.util
99

1010
import android.content.Context
1111
import android.content.Intent
12-
import android.net.Uri
12+
import androidx.core.net.toUri
1313
import io.element.android.appconfig.AuthenticationConfig
1414
import io.element.android.libraries.core.data.tryOrNull
1515

1616
fun openLearnMorePage(context: Context) {
17-
val intent = Intent(Intent.ACTION_VIEW, Uri.parse(AuthenticationConfig.SLIDING_SYNC_READ_MORE_URL))
17+
val intent = Intent(Intent.ACTION_VIEW, AuthenticationConfig.SLIDING_SYNC_READ_MORE_URL.toUri())
1818
tryOrNull { context.startActivity(intent) }
1919
}

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77

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

10-
import android.net.Uri
10+
import androidx.core.net.toUri
1111
import com.squareup.anvil.annotations.ContributesBinding
1212
import io.element.android.appconfig.AuthenticationConfig
1313
import io.element.android.features.login.impl.resolver.network.WellknownAPI
@@ -43,7 +43,7 @@ class DefaultWebClientUrlForAuthenticationRetriever @Inject constructor(
4343
}
4444
val registrationHelperUrl = result.registrationHelperUrl
4545
return if (registrationHelperUrl != null) {
46-
Uri.parse(registrationHelperUrl)
46+
registrationHelperUrl.toUri()
4747
.buildUpon()
4848
.appendQueryParameter("hs_url", homeServerUrl)
4949
.build()

features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/user/editprofile/EditUserProfilePresenter.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ class EditUserProfilePresenter @AssistedInject constructor(
5858
@Composable
5959
override fun present(): EditUserProfileState {
6060
val cameraPermissionState = cameraPermissionPresenter.present()
61-
var userAvatarUri by rememberSaveable { mutableStateOf(matrixUser.avatarUrl?.let { Uri.parse(it) }) }
61+
var userAvatarUri by rememberSaveable { mutableStateOf(matrixUser.avatarUrl?.toUri()) }
6262
var userDisplayName by rememberSaveable { mutableStateOf(matrixUser.displayName) }
6363
val cameraPhotoPicker = mediaPickerProvider.registerCameraPhotoPicker(
6464
onResult = { uri ->

features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/edit/RoomDetailsEditStateProvider.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ package io.element.android.features.roomdetails.impl.edit
99

1010
import android.net.Uri
1111
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
12+
import androidx.core.net.toUri
1213
import io.element.android.libraries.architecture.AsyncAction
1314
import io.element.android.libraries.matrix.api.core.RoomId
1415
import io.element.android.libraries.matrix.ui.media.AvatarAction
@@ -22,7 +23,7 @@ open class RoomDetailsEditStateProvider : PreviewParameterProvider<RoomDetailsEd
2223
aRoomDetailsEditState(),
2324
aRoomDetailsEditState(roomTopic = ""),
2425
aRoomDetailsEditState(roomRawName = ""),
25-
aRoomDetailsEditState(roomAvatarUrl = Uri.parse("example://uri")),
26+
aRoomDetailsEditState(roomAvatarUrl = "example://uri".toUri()),
2627
aRoomDetailsEditState(canChangeName = true, canChangeTopic = false, canChangeAvatar = true, saveButtonEnabled = false),
2728
aRoomDetailsEditState(canChangeName = false, canChangeTopic = true, canChangeAvatar = false, saveButtonEnabled = false),
2829
aRoomDetailsEditState(saveAction = AsyncAction.Loading),

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,12 @@ package io.element.android.libraries.androidutils.browser
99

1010
import android.app.Activity
1111
import android.content.ActivityNotFoundException
12-
import android.net.Uri
1312
import android.os.Bundle
1413
import android.provider.Browser
1514
import androidx.browser.customtabs.CustomTabColorSchemeParams
1615
import androidx.browser.customtabs.CustomTabsIntent
1716
import androidx.browser.customtabs.CustomTabsSession
17+
import androidx.core.net.toUri
1818
import io.element.android.libraries.androidutils.system.openUrlInExternalApp
1919
import java.util.Locale
2020

@@ -58,7 +58,7 @@ fun Activity.openUrlInChromeCustomTab(
5858
putString("Accept-Language", Locale.getDefault().toLanguageTag())
5959
})
6060
}
61-
.launchUrl(this, Uri.parse(url))
61+
.launchUrl(this, url.toUri())
6262
} catch (activityNotFoundException: ActivityNotFoundException) {
6363
openUrlInExternalApp(url)
6464
}

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

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import android.widget.Toast
1919
import androidx.activity.result.ActivityResultLauncher
2020
import androidx.annotation.RequiresApi
2121
import androidx.core.content.pm.PackageInfoCompat
22+
import androidx.core.net.toUri
2223
import io.element.android.libraries.androidutils.R
2324
import io.element.android.libraries.androidutils.compat.getApplicationInfoCompat
2425
import io.element.android.libraries.core.mimetype.MimeTypes
@@ -121,7 +122,7 @@ fun Context.startInstallFromSourceIntent(
121122
noActivityFoundMessage: String = getString(R.string.error_no_compatible_app_found),
122123
) {
123124
val intent = Intent(Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES)
124-
.setData(Uri.parse("package:$packageName"))
125+
.setData("package:$packageName".toUri())
125126
try {
126127
activityResultLauncher.launch(intent)
127128
} catch (activityNotFoundException: ActivityNotFoundException) {
@@ -165,7 +166,7 @@ fun Context.openUrlInExternalApp(
165166
url: String,
166167
errorMessage: String = getString(R.string.error_no_compatible_app_found),
167168
) {
168-
val intent = Intent(Intent.ACTION_VIEW, Uri.parse(url))
169+
val intent = Intent(Intent.ACTION_VIEW, url.toUri())
169170
if (this !is Activity) {
170171
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
171172
}

libraries/androidutils/src/main/kotlin/io/element/android/libraries/androidutils/uri/UriExtensions.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,8 @@
88
package io.element.android.libraries.androidutils.uri
99

1010
import android.net.Uri
11+
import androidx.core.net.toUri
1112

1213
const val IGNORED_SCHEMA = "ignored"
1314

14-
fun createIgnoredUri(path: String): Uri = Uri.parse("$IGNORED_SCHEMA://$path")
15+
fun createIgnoredUri(path: String): Uri = "$IGNORED_SCHEMA://$path".toUri()

libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/avatar/Avatar.kt

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import androidx.compose.ui.draw.clip
2323
import androidx.compose.ui.layout.ContentScale
2424
import androidx.compose.ui.platform.LocalInspectionMode
2525
import androidx.compose.ui.semantics.clearAndSetSemantics
26+
import androidx.compose.ui.semantics.contentDescription
2627
import androidx.compose.ui.tooling.preview.Preview
2728
import androidx.compose.ui.tooling.preview.PreviewParameter
2829
import androidx.compose.ui.unit.Dp
@@ -59,6 +60,7 @@ fun Avatar(
5960
avatarData = avatarData,
6061
forcedAvatarSize = forcedAvatarSize,
6162
modifier = commonModifier,
63+
contentDescription = contentDescription,
6264
)
6365
} else {
6466
ImageAvatar(
@@ -103,11 +105,13 @@ private fun ImageAvatar(
103105
InitialsAvatar(
104106
avatarData = avatarData,
105107
forcedAvatarSize = forcedAvatarSize,
108+
contentDescription = contentDescription,
106109
)
107110
}
108111
else -> InitialsAvatar(
109112
avatarData = avatarData,
110113
forcedAvatarSize = forcedAvatarSize,
114+
contentDescription = contentDescription,
111115
)
112116
}
113117
}
@@ -118,6 +122,7 @@ private fun ImageAvatar(
118122
private fun InitialsAvatar(
119123
avatarData: AvatarData,
120124
forcedAvatarSize: Dp?,
125+
contentDescription: String?,
121126
modifier: Modifier = Modifier,
122127
) {
123128
val avatarColors = AvatarColorsProvider.provide(avatarData.id)
@@ -130,7 +135,11 @@ private fun InitialsAvatar(
130135
val lineHeight = originalFont.lineHeight * ratio
131136
Text(
132137
modifier = Modifier
133-
.clearAndSetSemantics {}
138+
.clearAndSetSemantics {
139+
contentDescription?.let {
140+
this.contentDescription = it
141+
}
142+
}
134143
.align(Alignment.Center),
135144
text = avatarData.initial,
136145
style = originalFont.copy(fontSize = fontSize, lineHeight = lineHeight, letterSpacing = 0.sp),

libraries/fullscreenintent/impl/src/main/kotlin/io/element/android/libraries/fullscreenintent/impl/FullScreenIntentPermissionsPresenter.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,14 @@ package io.element.android.libraries.fullscreenintent.impl
99

1010
import android.content.ActivityNotFoundException
1111
import android.content.Intent
12-
import android.net.Uri
1312
import android.os.Build
1413
import android.provider.Settings
1514
import androidx.compose.runtime.Composable
1615
import androidx.compose.runtime.collectAsState
1716
import androidx.compose.runtime.getValue
1817
import androidx.compose.runtime.rememberCoroutineScope
1918
import androidx.core.app.NotificationManagerCompat
19+
import androidx.core.net.toUri
2020
import androidx.datastore.preferences.core.booleanPreferencesKey
2121
import androidx.datastore.preferences.core.edit
2222
import io.element.android.libraries.architecture.Presenter
@@ -77,7 +77,7 @@ class FullScreenIntentPermissionsPresenter @Inject constructor(
7777
try {
7878
val intent = Intent(
7979
Settings.ACTION_MANAGE_APP_USE_FULL_SCREEN_INTENT,
80-
Uri.parse("package:${buildMeta.applicationId}")
80+
"package:${buildMeta.applicationId}".toUri()
8181
)
8282
externalIntentLauncher.launch(intent)
8383
} catch (e: ActivityNotFoundException) {

0 commit comments

Comments
 (0)