From 95a215271461a8be39df2a6e2e2025668a5c9833 Mon Sep 17 00:00:00 2001 From: jonnyandrew Date: Tue, 31 Oct 2023 13:00:08 +0000 Subject: [PATCH] Fix long press on voice message with screen reader (#1704) As a workaround, disable seeking within the waveform so that it does not interfere with the long press menu. Seeking behaviour is already suboptimal given that there is no spoken feedback about the current seek position. No core functionality is lost as voice messages can be played using a screen reader. --- .../components/event/TimelineItemVoiceView.kt | 4 ++ .../androidutils/accessibility/ContextExt.kt | 39 +++++++++++++++++++ 2 files changed, 43 insertions(+) create mode 100644 libraries/androidutils/src/main/kotlin/io/element/android/libraries/androidutils/accessibility/ContextExt.kt diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemVoiceView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemVoiceView.kt index 0c98321047..e7762fe278 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemVoiceView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemVoiceView.kt @@ -29,6 +29,7 @@ import androidx.compose.material3.IconButtonDefaults import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource import androidx.compose.ui.semantics.contentDescription import androidx.compose.ui.semantics.semantics @@ -42,6 +43,7 @@ import io.element.android.features.messages.impl.timeline.model.event.TimelineIt import io.element.android.features.messages.impl.voicemessages.timeline.VoiceMessageEvents import io.element.android.features.messages.impl.voicemessages.timeline.VoiceMessageState import io.element.android.features.messages.impl.voicemessages.timeline.VoiceMessageStateProvider +import io.element.android.libraries.androidutils.accessibility.isScreenReaderEnabled import io.element.android.libraries.designsystem.components.media.WaveformPlaybackView import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.PreviewsDayNight @@ -86,6 +88,7 @@ fun TimelineItemVoiceView( overflow = TextOverflow.Ellipsis, ) Spacer(Modifier.width(8.dp)) + val context = LocalContext.current WaveformPlaybackView( showCursor = state.button == VoiceMessageState.Button.Pause, playbackProgress = state.progress, @@ -93,6 +96,7 @@ fun TimelineItemVoiceView( modifier = Modifier .height(34.dp) .weight(1f), + seekEnabled = !context.isScreenReaderEnabled(), onSeek = { state.eventSink(VoiceMessageEvents.Seek(it)) }, ) Spacer(Modifier.width(extraPadding.getDpSize())) diff --git a/libraries/androidutils/src/main/kotlin/io/element/android/libraries/androidutils/accessibility/ContextExt.kt b/libraries/androidutils/src/main/kotlin/io/element/android/libraries/androidutils/accessibility/ContextExt.kt new file mode 100644 index 0000000000..bb223e43fb --- /dev/null +++ b/libraries/androidutils/src/main/kotlin/io/element/android/libraries/androidutils/accessibility/ContextExt.kt @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2023 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.element.android.libraries.androidutils.accessibility + +import android.content.Context +import android.view.accessibility.AccessibilityManager +import androidx.core.content.getSystemService + +/** + * Whether a screen reader is enabled. + * + * Avoid changing UI or app behavior based on the state of accessibility. + * See [AccessibilityManager.isTouchExplorationEnabled] for more details. + * + * @return true if the screen reader is enabled. + */ +fun Context.isScreenReaderEnabled(): Boolean { + val accessibilityManager = getSystemService() + ?: return false + + return accessibilityManager.let { + it.isEnabled && it.isTouchExplorationEnabled + } +} +