From c8fbb8851167310f1ac8eec95c449f1a3347105e Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 4 May 2023 15:51:13 +0200 Subject: [PATCH 1/9] Let the focus go to the room list and the actions at the bottom, useful when navigating with the keyboard. --- .../main/res/values/styles_app_bar_layout.xml | 5 ++++- .../src/main/res/values/styles_toolbar.xml | 19 ++++++++++++++++++- .../src/main/res/values/theme_dark.xml | 3 +++ .../src/main/res/values/theme_light.xml | 3 +++ .../res/layout/activity_filtered_rooms.xml | 2 +- .../res/layout/fragment_new_home_detail.xml | 3 +-- 6 files changed, 30 insertions(+), 5 deletions(-) diff --git a/library/ui-styles/src/main/res/values/styles_app_bar_layout.xml b/library/ui-styles/src/main/res/values/styles_app_bar_layout.xml index 973a2c5e4a..431480ed6b 100644 --- a/library/ui-styles/src/main/res/values/styles_app_bar_layout.xml +++ b/library/ui-styles/src/main/res/values/styles_app_bar_layout.xml @@ -4,6 +4,9 @@ - \ No newline at end of file + diff --git a/library/ui-styles/src/main/res/values/styles_toolbar.xml b/library/ui-styles/src/main/res/values/styles_toolbar.xml index 893de92aae..cc00c5ab63 100644 --- a/library/ui-styles/src/main/res/values/styles_toolbar.xml +++ b/library/ui-styles/src/main/res/values/styles_toolbar.xml @@ -12,6 +12,9 @@ @style/TextAppearance.Vector.Widget.ActionBarSubTitle ?vctr_content_secondary + + + false @@ -43,10 +46,24 @@ + + + + diff --git a/library/ui-styles/src/main/res/values/theme_dark.xml b/library/ui-styles/src/main/res/values/theme_dark.xml index 9665b7335c..24758edc62 100644 --- a/library/ui-styles/src/main/res/values/theme_dark.xml +++ b/library/ui-styles/src/main/res/values/theme_dark.xml @@ -83,6 +83,9 @@ @style/Widget.Vector.TextView.Body @style/Widget.Vector.Button @style/Widget.Vector.Toolbar + @style/Widget.Vector.CollapsingToolbar + @style/Widget.Vector.CollapsingToolbar.Medium + @style/Widget.Vector.CollapsingToolbar.Large @style/BottomNavigation.Vector @style/Widget.Vector.SearchView @style/Widget.Vector.TextInputLayout diff --git a/library/ui-styles/src/main/res/values/theme_light.xml b/library/ui-styles/src/main/res/values/theme_light.xml index c19fe8a111..23782ee34b 100644 --- a/library/ui-styles/src/main/res/values/theme_light.xml +++ b/library/ui-styles/src/main/res/values/theme_light.xml @@ -83,6 +83,9 @@ @style/Widget.Vector.TextView.Body @style/Widget.Vector.Button @style/Widget.Vector.Toolbar + @style/Widget.Vector.CollapsingToolbar + @style/Widget.Vector.CollapsingToolbar.Medium + @style/Widget.Vector.CollapsingToolbar.Large @style/BottomNavigation.Vector @style/Widget.Vector.SearchView @style/Widget.Vector.TextInputLayout diff --git a/vector/src/main/res/layout/activity_filtered_rooms.xml b/vector/src/main/res/layout/activity_filtered_rooms.xml index 07a2bc7a2e..5a3dae13dc 100644 --- a/vector/src/main/res/layout/activity_filtered_rooms.xml +++ b/vector/src/main/res/layout/activity_filtered_rooms.xml @@ -43,4 +43,4 @@ - \ No newline at end of file + diff --git a/vector/src/main/res/layout/fragment_new_home_detail.xml b/vector/src/main/res/layout/fragment_new_home_detail.xml index 8f603e57ab..d20223a382 100644 --- a/vector/src/main/res/layout/fragment_new_home_detail.xml +++ b/vector/src/main/res/layout/fragment_new_home_detail.xml @@ -49,15 +49,14 @@ app:layout_constraintTop_toBottomOf="@id/syncStateView"> Date: Wed, 10 May 2023 14:49:05 +0200 Subject: [PATCH 2/9] Improve accessibility for avatar of users, rooms and spaces. --- .../src/main/res/values/strings.xml | 3 ++ .../app/features/home/AvatarRenderer.kt | 33 ++++++++++++++++++- 2 files changed, 35 insertions(+), 1 deletion(-) diff --git a/library/ui-strings/src/main/res/values/strings.xml b/library/ui-strings/src/main/res/values/strings.xml index 1a1546459e..3d1a36d4c3 100644 --- a/library/ui-strings/src/main/res/values/strings.xml +++ b/library/ui-strings/src/main/res/values/strings.xml @@ -1470,6 +1470,9 @@ Reason: %1$s Avatar + Avatar of space %1$s + Avatar of room %1$s + Profile picture of user %1$s To continue using the %1$s homeserver you must review and agree to the terms and conditions. diff --git a/vector/src/main/java/im/vector/app/features/home/AvatarRenderer.kt b/vector/src/main/java/im/vector/app/features/home/AvatarRenderer.kt index f4ffbb826a..7214db41ef 100644 --- a/vector/src/main/java/im/vector/app/features/home/AvatarRenderer.kt +++ b/vector/src/main/java/im/vector/app/features/home/AvatarRenderer.kt @@ -35,13 +35,16 @@ import com.bumptech.glide.request.RequestOptions import com.bumptech.glide.request.target.DrawableImageViewTarget import com.bumptech.glide.request.target.Target import com.bumptech.glide.signature.ObjectKey +import im.vector.app.R import im.vector.app.core.contacts.MappedContact import im.vector.app.core.di.ActiveSessionHolder import im.vector.app.core.glide.AvatarPlaceholder import im.vector.app.core.glide.GlideApp import im.vector.app.core.glide.GlideRequest import im.vector.app.core.glide.GlideRequests +import im.vector.app.core.resources.StringProvider import im.vector.app.core.utils.DimensionConverter +import im.vector.app.features.displayname.getBestName import im.vector.app.features.home.room.detail.timeline.helper.MatrixItemColorProvider import jp.wasabeef.glide.transformations.BlurTransformation import jp.wasabeef.glide.transformations.ColorFilterTransformation @@ -58,7 +61,8 @@ import javax.inject.Inject class AvatarRenderer @Inject constructor( private val activeSessionHolder: ActiveSessionHolder, private val matrixItemColorProvider: MatrixItemColorProvider, - private val dimensionConverter: DimensionConverter + private val dimensionConverter: DimensionConverter, + private val stringProvider: StringProvider, ) { companion object { @@ -67,6 +71,7 @@ class AvatarRenderer @Inject constructor( @UiThread fun render(matrixItem: MatrixItem, imageView: ImageView) { + imageView.setContentDescription(matrixItem) render( GlideApp.with(imageView), matrixItem, @@ -100,6 +105,7 @@ class AvatarRenderer @Inject constructor( @UiThread fun render(matrixItem: MatrixItem, imageView: ImageView, glideRequests: GlideRequests) { + imageView.setContentDescription(matrixItem) render( glideRequests, matrixItem, @@ -109,6 +115,7 @@ class AvatarRenderer @Inject constructor( @UiThread fun render(matrixItem: MatrixItem, localUri: Uri?, imageView: ImageView) { + imageView.setContentDescription(matrixItem) val placeholder = getPlaceholderDrawable(matrixItem) GlideApp.with(imageView) .load(localUri?.let { File(localUri.path!!) }) @@ -295,4 +302,28 @@ class AvatarRenderer @Inject constructor( return activeSessionHolder.getSafeActiveSession()?.contentUrlResolver() ?.resolveThumbnail(avatarUrl, THUMBNAIL_SIZE, THUMBNAIL_SIZE, ContentUrlResolver.ThumbnailMethod.SCALE) } + + /** + * Accessibility management. + */ + private fun ImageView.setContentDescription(matrixItem: MatrixItem) { + // Do not set contentDescription if the ImageView should be ignored regarding accessibility. + if (isImportantForAccessibility.not()) return + when (matrixItem) { + is MatrixItem.SpaceItem -> { + contentDescription = stringProvider.getString(R.string.avatar_of_space, matrixItem.getBestName()) + } + is MatrixItem.RoomAliasItem, + is MatrixItem.RoomItem -> { + contentDescription = stringProvider.getString(R.string.avatar_of_room, matrixItem.getBestName()) + } + is MatrixItem.UserItem -> { + contentDescription = stringProvider.getString(R.string.avatar_of_user, matrixItem.getBestName()) + } + is MatrixItem.EveryoneInRoomItem, + is MatrixItem.EventItem -> { + // NA + } + } + } } From 71a2a4d31a28e4a11a26fad91f9899cd7cea229d Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 10 May 2023 16:43:54 +0200 Subject: [PATCH 3/9] a11y: add custom action to be able to close the alert. --- .../app/features/popup/PopupAlertManager.kt | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/vector/src/main/java/im/vector/app/features/popup/PopupAlertManager.kt b/vector/src/main/java/im/vector/app/features/popup/PopupAlertManager.kt index d3c6e83589..29aa623c9b 100644 --- a/vector/src/main/java/im/vector/app/features/popup/PopupAlertManager.kt +++ b/vector/src/main/java/im/vector/app/features/popup/PopupAlertManager.kt @@ -21,9 +21,11 @@ import android.os.Handler import android.os.Looper import android.view.View import android.view.WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS +import androidx.core.view.ViewCompat import com.tapadoo.alerter.Alerter import im.vector.app.R import im.vector.app.core.platform.VectorBaseActivity +import im.vector.app.core.resources.StringProvider import im.vector.app.core.utils.isAnimationEnabled import im.vector.app.features.MainActivity import im.vector.app.features.analytics.ui.consent.AnalyticsOptInActivity @@ -46,6 +48,7 @@ import javax.inject.Singleton @Singleton class PopupAlertManager @Inject constructor( private val clock: Clock, + private val stringProvider: StringProvider, ) { companion object { @@ -282,6 +285,9 @@ class PopupAlertManager @Inject constructor( } currentIsDismissed() } + .setOnShowListener { + handleAccessibility(activity) + } .enableSwipeToDismiss() .enableInfiniteDuration(true) .apply { @@ -297,6 +303,24 @@ class PopupAlertManager @Inject constructor( .show() } + /* a11y */ + private fun handleAccessibility(activity: Activity) { + activity.window.decorView.findViewById(R.id.llAlertBackground)?.let { alertView -> + alertView.importantForAccessibility = View.IMPORTANT_FOR_ACCESSIBILITY_YES + + // Add close action for a11y (same action than swipe). User can select the action by swiping on the screen vertically, + // and double tap to perform the action + ViewCompat.addAccessibilityAction( + alertView, + stringProvider.getString(R.string.action_close) + ) { _, _ -> + currentIsDismissed() + Alerter.hide() + true + } + } + } + private fun currentIsDismissed() { // current alert has been hidden if (currentAlerter?.isLight == false) { From 6c942a757589c753fad1dc0bffcbe696882a8329 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 10 May 2023 17:00:07 +0200 Subject: [PATCH 4/9] Give accessibility focus to the alerts. --- .../im/vector/app/core/extensions/ViewExtensions.kt | 11 +++++++++++ .../im/vector/app/features/popup/PopupAlertManager.kt | 10 ++++++++-- 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/vector/src/main/java/im/vector/app/core/extensions/ViewExtensions.kt b/vector/src/main/java/im/vector/app/core/extensions/ViewExtensions.kt index 156809d5ad..55ad5294d9 100644 --- a/vector/src/main/java/im/vector/app/core/extensions/ViewExtensions.kt +++ b/vector/src/main/java/im/vector/app/core/extensions/ViewExtensions.kt @@ -20,6 +20,8 @@ import android.graphics.drawable.Drawable import android.text.InputType import android.view.View import android.view.ViewGroup +import android.view.accessibility.AccessibilityEvent +import android.view.accessibility.AccessibilityNodeInfo import android.widget.EditText import android.widget.ImageView import androidx.annotation.AttrRes @@ -28,6 +30,7 @@ import androidx.annotation.DrawableRes import androidx.appcompat.widget.SearchView import androidx.core.content.ContextCompat import androidx.core.graphics.drawable.DrawableCompat +import androidx.core.view.ViewCompat import androidx.core.view.isVisible import androidx.transition.ChangeBounds import androidx.transition.Fade @@ -97,6 +100,14 @@ fun View.setAttributeBackground(@AttrRes attributeId: Int) { setBackgroundResource(attribute.resourceId) } +/** + * Inspired from https://stackoverflow.com/a/64597532/1472514. Safer to call the 2 available API. + */ +fun View.giveAccessibilityFocus() { + ViewCompat.performAccessibilityAction(this, AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS, null) + sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_SELECTED) +} + fun ViewGroup.animateLayoutChange(animationDuration: Long, transitionComplete: (() -> Unit)? = null) { val transition = TransitionSet().apply { ordering = TransitionSet.ORDERING_SEQUENTIAL diff --git a/vector/src/main/java/im/vector/app/features/popup/PopupAlertManager.kt b/vector/src/main/java/im/vector/app/features/popup/PopupAlertManager.kt index 29aa623c9b..02275c933e 100644 --- a/vector/src/main/java/im/vector/app/features/popup/PopupAlertManager.kt +++ b/vector/src/main/java/im/vector/app/features/popup/PopupAlertManager.kt @@ -24,6 +24,7 @@ import android.view.WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS import androidx.core.view.ViewCompat import com.tapadoo.alerter.Alerter import im.vector.app.R +import im.vector.app.core.extensions.giveAccessibilityFocus import im.vector.app.core.platform.VectorBaseActivity import im.vector.app.core.resources.StringProvider import im.vector.app.core.utils.isAnimationEnabled @@ -286,7 +287,7 @@ class PopupAlertManager @Inject constructor( currentIsDismissed() } .setOnShowListener { - handleAccessibility(activity) + handleAccessibility(activity, animate) } .enableSwipeToDismiss() .enableInfiniteDuration(true) @@ -304,7 +305,7 @@ class PopupAlertManager @Inject constructor( } /* a11y */ - private fun handleAccessibility(activity: Activity) { + private fun handleAccessibility(activity: Activity, giveFocus: Boolean) { activity.window.decorView.findViewById(R.id.llAlertBackground)?.let { alertView -> alertView.importantForAccessibility = View.IMPORTANT_FOR_ACCESSIBILITY_YES @@ -318,6 +319,11 @@ class PopupAlertManager @Inject constructor( Alerter.hide() true } + + // And give focus to the alert right now, only for first display, i.e. when there is an animation. + if (giveFocus) { + alertView.giveAccessibilityFocus() + } } } From db76cd5899583955d51eb21b30718040450b0041 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 10 May 2023 17:27:25 +0200 Subject: [PATCH 5/9] Give accessibility focus to title of bottom sheet, which can be updated asynchronously. --- .../app/core/platform/VectorBaseFragment.kt | 16 ++++++++++++++++ .../recover/BootstrapConclusionFragment.kt | 1 + .../BootstrapConfirmPassphraseFragment.kt | 1 + .../recover/BootstrapEnterPassphraseFragment.kt | 1 + .../recover/BootstrapMigrateBackupFragment.kt | 1 + .../crypto/recover/BootstrapReAuthFragment.kt | 1 + .../recover/BootstrapSaveRecoveryKeyFragment.kt | 1 + .../recover/BootstrapSetupRecoveryKeyFragment.kt | 1 + .../crypto/recover/BootstrapWaitingFragment.kt | 1 + 9 files changed, 24 insertions(+) diff --git a/vector/src/main/java/im/vector/app/core/platform/VectorBaseFragment.kt b/vector/src/main/java/im/vector/app/core/platform/VectorBaseFragment.kt index a82cef54e5..b57f288c09 100644 --- a/vector/src/main/java/im/vector/app/core/platform/VectorBaseFragment.kt +++ b/vector/src/main/java/im/vector/app/core/platform/VectorBaseFragment.kt @@ -45,6 +45,7 @@ import im.vector.app.R import im.vector.app.core.di.ActivityEntryPoint import im.vector.app.core.dialogs.UnrecognizedCertificateDialog import im.vector.app.core.error.ErrorFormatter +import im.vector.app.core.extensions.giveAccessibilityFocus import im.vector.app.core.extensions.singletonEntryPoint import im.vector.app.core.extensions.toMvRxBundle import im.vector.app.core.utils.ToolbarConfig @@ -318,4 +319,19 @@ abstract class VectorBaseFragment : Fragment(), MavericksView .setPositiveButton(R.string.ok, null) .show() } + + /* ========================================================================================== + * Accessibility - a11y + * ========================================================================================== */ + + private var hasBeenAccessibilityFocused = false + + /** + * Ensure the View get the accessibility focus. This method has effect only once per fragment instance. + */ + protected fun View.giveAccessibilityFocusOnce() { + if (hasBeenAccessibilityFocused) return + hasBeenAccessibilityFocused = true + giveAccessibilityFocus() + } } diff --git a/vector/src/main/java/im/vector/app/features/crypto/recover/BootstrapConclusionFragment.kt b/vector/src/main/java/im/vector/app/features/crypto/recover/BootstrapConclusionFragment.kt index 22ecd3dafd..fd1eb821db 100644 --- a/vector/src/main/java/im/vector/app/features/crypto/recover/BootstrapConclusionFragment.kt +++ b/vector/src/main/java/im/vector/app/features/crypto/recover/BootstrapConclusionFragment.kt @@ -60,5 +60,6 @@ class BootstrapConclusionFragment : .toSpannable() .colorizeMatchingText(getString(R.string.recovery_passphrase), colorProvider.getColorFromAttribute(android.R.attr.textColorLink)) .colorizeMatchingText(getString(R.string.message_key), colorProvider.getColorFromAttribute(android.R.attr.textColorLink)) + views.bootstrapConclusionText.giveAccessibilityFocusOnce() } } diff --git a/vector/src/main/java/im/vector/app/features/crypto/recover/BootstrapConfirmPassphraseFragment.kt b/vector/src/main/java/im/vector/app/features/crypto/recover/BootstrapConfirmPassphraseFragment.kt index 285721ee75..3942fc6528 100644 --- a/vector/src/main/java/im/vector/app/features/crypto/recover/BootstrapConfirmPassphraseFragment.kt +++ b/vector/src/main/java/im/vector/app/features/crypto/recover/BootstrapConfirmPassphraseFragment.kt @@ -52,6 +52,7 @@ class BootstrapConfirmPassphraseFragment : views.ssssPassphraseSecurityProgress.isGone = true views.bootstrapDescriptionText.text = getString(R.string.set_a_security_phrase_again_notice) + views.bootstrapDescriptionText.giveAccessibilityFocusOnce() views.ssssPassphraseEnterEdittext.hint = getString(R.string.set_a_security_phrase_hint) withState(sharedViewModel) { diff --git a/vector/src/main/java/im/vector/app/features/crypto/recover/BootstrapEnterPassphraseFragment.kt b/vector/src/main/java/im/vector/app/features/crypto/recover/BootstrapEnterPassphraseFragment.kt index 78f0bc6284..262abd306d 100644 --- a/vector/src/main/java/im/vector/app/features/crypto/recover/BootstrapEnterPassphraseFragment.kt +++ b/vector/src/main/java/im/vector/app/features/crypto/recover/BootstrapEnterPassphraseFragment.kt @@ -118,5 +118,6 @@ class BootstrapEnterPassphraseFragment : } } } + views.bootstrapDescriptionText.giveAccessibilityFocusOnce() } } diff --git a/vector/src/main/java/im/vector/app/features/crypto/recover/BootstrapMigrateBackupFragment.kt b/vector/src/main/java/im/vector/app/features/crypto/recover/BootstrapMigrateBackupFragment.kt index 2802c872ee..8df5ce7ad3 100644 --- a/vector/src/main/java/im/vector/app/features/crypto/recover/BootstrapMigrateBackupFragment.kt +++ b/vector/src/main/java/im/vector/app/features/crypto/recover/BootstrapMigrateBackupFragment.kt @@ -141,6 +141,7 @@ class BootstrapMigrateBackupFragment : views.bootstrapMigrateUseFile.isVisible = false } + views.bootstrapDescriptionText.giveAccessibilityFocusOnce() } private val importFileStartForActivityResult = registerStartForActivityResult { activityResult -> diff --git a/vector/src/main/java/im/vector/app/features/crypto/recover/BootstrapReAuthFragment.kt b/vector/src/main/java/im/vector/app/features/crypto/recover/BootstrapReAuthFragment.kt index d5e60631a5..f32ba735a1 100644 --- a/vector/src/main/java/im/vector/app/features/crypto/recover/BootstrapReAuthFragment.kt +++ b/vector/src/main/java/im/vector/app/features/crypto/recover/BootstrapReAuthFragment.kt @@ -78,5 +78,6 @@ class BootstrapReAuthFragment : views.bootstrapCancelButton.isVisible = true views.bootstrapRetryButton.isVisible = true } + views.bootstrapDescriptionText.giveAccessibilityFocusOnce() } } diff --git a/vector/src/main/java/im/vector/app/features/crypto/recover/BootstrapSaveRecoveryKeyFragment.kt b/vector/src/main/java/im/vector/app/features/crypto/recover/BootstrapSaveRecoveryKeyFragment.kt index 21d68dfe99..88db55a291 100644 --- a/vector/src/main/java/im/vector/app/features/crypto/recover/BootstrapSaveRecoveryKeyFragment.kt +++ b/vector/src/main/java/im/vector/app/features/crypto/recover/BootstrapSaveRecoveryKeyFragment.kt @@ -117,5 +117,6 @@ class BootstrapSaveRecoveryKeyFragment : views.recoveryContinue.isVisible = step.isSaved views.bootstrapRecoveryKeyText.text = state.recoveryKeyCreationInfo?.recoveryKey?.formatRecoveryKey() + views.bootstrapSaveText.giveAccessibilityFocusOnce() } } diff --git a/vector/src/main/java/im/vector/app/features/crypto/recover/BootstrapSetupRecoveryKeyFragment.kt b/vector/src/main/java/im/vector/app/features/crypto/recover/BootstrapSetupRecoveryKeyFragment.kt index b03cbe87a8..a28a7c8f56 100644 --- a/vector/src/main/java/im/vector/app/features/crypto/recover/BootstrapSetupRecoveryKeyFragment.kt +++ b/vector/src/main/java/im/vector/app/features/crypto/recover/BootstrapSetupRecoveryKeyFragment.kt @@ -68,6 +68,7 @@ class BootstrapSetupRecoveryKeyFragment : // Choose between create a passphrase or use a recovery key renderBackupMethodActions(firstFormStep.methods) } + views.bootstrapSetupSecureText.giveAccessibilityFocusOnce() } private fun renderStateWithExistingKeyBackup() = with(views) { diff --git a/vector/src/main/java/im/vector/app/features/crypto/recover/BootstrapWaitingFragment.kt b/vector/src/main/java/im/vector/app/features/crypto/recover/BootstrapWaitingFragment.kt index 310bb5ac37..9d9cf6cdd2 100644 --- a/vector/src/main/java/im/vector/app/features/crypto/recover/BootstrapWaitingFragment.kt +++ b/vector/src/main/java/im/vector/app/features/crypto/recover/BootstrapWaitingFragment.kt @@ -52,5 +52,6 @@ class BootstrapWaitingFragment : views.bootstrapDescriptionText.isVisible = false } } + views.bootstrapDescriptionText.giveAccessibilityFocusOnce() } } From 20fedc87febc7915b4d2998b1a7812415b6a1fca Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 10 May 2023 17:50:56 +0200 Subject: [PATCH 6/9] Remove dead code --- .../verification/QuadSLoadingFragment.kt | 31 ------------------- .../src/main/res/layout/fragment_progress.xml | 14 --------- 2 files changed, 45 deletions(-) delete mode 100644 vector/src/main/java/im/vector/app/features/crypto/verification/QuadSLoadingFragment.kt delete mode 100644 vector/src/main/res/layout/fragment_progress.xml diff --git a/vector/src/main/java/im/vector/app/features/crypto/verification/QuadSLoadingFragment.kt b/vector/src/main/java/im/vector/app/features/crypto/verification/QuadSLoadingFragment.kt deleted file mode 100644 index 5b6902df98..0000000000 --- a/vector/src/main/java/im/vector/app/features/crypto/verification/QuadSLoadingFragment.kt +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright (c) 2020 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 im.vector.app.features.crypto.verification - -import android.view.LayoutInflater -import android.view.ViewGroup -import dagger.hilt.android.AndroidEntryPoint -import im.vector.app.core.platform.VectorBaseFragment -import im.vector.app.databinding.FragmentProgressBinding - -@AndroidEntryPoint -class QuadSLoadingFragment : - VectorBaseFragment() { - override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentProgressBinding { - return FragmentProgressBinding.inflate(inflater, container, false) - } -} diff --git a/vector/src/main/res/layout/fragment_progress.xml b/vector/src/main/res/layout/fragment_progress.xml deleted file mode 100644 index a7a2076209..0000000000 --- a/vector/src/main/res/layout/fragment_progress.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - From 7a65a51ee15e8f72afa87ed4d095c3b19874246f Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 10 May 2023 18:45:25 +0200 Subject: [PATCH 7/9] Attempt to give accessibility focus to the first item of the RecyclerView when the recycler view is updated (screen change), to improve screen reader behavior. --- .../self/SelfVerificationFragment.kt | 61 ++++++++++++++++++- .../user/UserVerificationFragment.kt | 54 +++++++++++++++- 2 files changed, 113 insertions(+), 2 deletions(-) diff --git a/vector/src/main/java/im/vector/app/features/crypto/verification/self/SelfVerificationFragment.kt b/vector/src/main/java/im/vector/app/features/crypto/verification/self/SelfVerificationFragment.kt index ae20955d45..8badddba8b 100644 --- a/vector/src/main/java/im/vector/app/features/crypto/verification/self/SelfVerificationFragment.kt +++ b/vector/src/main/java/im/vector/app/features/crypto/verification/self/SelfVerificationFragment.kt @@ -21,12 +21,18 @@ import android.os.Bundle import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import com.airbnb.epoxy.OnModelBuildFinishedListener +import com.airbnb.mvrx.Fail +import com.airbnb.mvrx.Loading +import com.airbnb.mvrx.Success +import com.airbnb.mvrx.Uninitialized import com.airbnb.mvrx.parentFragmentViewModel import com.airbnb.mvrx.withState import dagger.hilt.android.AndroidEntryPoint import im.vector.app.R import im.vector.app.core.extensions.cleanup import im.vector.app.core.extensions.configureWith +import im.vector.app.core.extensions.giveAccessibilityFocus import im.vector.app.core.extensions.registerStartForActivityResult import im.vector.app.core.platform.VectorBaseFragment import im.vector.app.core.utils.PERMISSIONS_FOR_TAKING_PHOTO @@ -36,15 +42,26 @@ import im.vector.app.core.utils.registerForPermissionsResult import im.vector.app.databinding.BottomSheetVerificationChildFragmentBinding import im.vector.app.features.crypto.verification.VerificationAction import im.vector.app.features.qrcode.QrCodeScannerActivity +import org.matrix.android.sdk.api.session.crypto.verification.EVerificationState import timber.log.Timber import javax.inject.Inject @AndroidEntryPoint -class SelfVerificationFragment : VectorBaseFragment(), +class SelfVerificationFragment : VectorBaseFragment(), SelfVerificationController.InteractionListener { @Inject lateinit var controller: SelfVerificationController + private var requestAccessibilityFocus: Boolean = false + private val modelBuildListener: OnModelBuildFinishedListener = OnModelBuildFinishedListener { + if (requestAccessibilityFocus) { + // Do not use giveAccessibilityFocusOnce() here. + views.bottomSheetVerificationRecyclerView.layoutManager?.findViewByPosition(0)?.giveAccessibilityFocus() + requestAccessibilityFocus = false + // Note: it does not work when the recycler view is displayed for the first time, because findViewByPosition(0) is null + } + } + private val viewModel by parentFragmentViewModel(SelfVerificationViewModel::class) override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): BottomSheetVerificationChildFragmentBinding { @@ -58,17 +75,22 @@ class SelfVerificationFragment : VectorBaseFragment // Timber.w("VALR: invalidate with State: ${state.pendingRequest}") + if (state.isNewScreen()) { + requestAccessibilityFocus = true + } controller.update(state) } @@ -176,4 +198,41 @@ class SelfVerificationFragment : VectorBaseFragment 30 + UserAction.None -> 31 + } + } else { + when (pendingRequest) { + is Fail -> 0 + is Loading -> 1 + is Success -> when (pendingRequest.invoke().state) { + EVerificationState.WaitingForReady -> 10 + EVerificationState.Requested -> 11 + EVerificationState.Ready -> 12 + EVerificationState.Started -> 13 + EVerificationState.WeStarted -> 14 + EVerificationState.WaitingForDone -> 15 + EVerificationState.Done -> 16 + EVerificationState.Cancelled -> 17 + EVerificationState.HandledByOtherSession -> 18 + } + Uninitialized -> 2 + } + } + } } diff --git a/vector/src/main/java/im/vector/app/features/crypto/verification/user/UserVerificationFragment.kt b/vector/src/main/java/im/vector/app/features/crypto/verification/user/UserVerificationFragment.kt index 415b8069e9..7aac6dcb58 100644 --- a/vector/src/main/java/im/vector/app/features/crypto/verification/user/UserVerificationFragment.kt +++ b/vector/src/main/java/im/vector/app/features/crypto/verification/user/UserVerificationFragment.kt @@ -21,12 +21,18 @@ import android.os.Bundle import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import com.airbnb.epoxy.OnModelBuildFinishedListener +import com.airbnb.mvrx.Fail +import com.airbnb.mvrx.Loading +import com.airbnb.mvrx.Success +import com.airbnb.mvrx.Uninitialized import com.airbnb.mvrx.parentFragmentViewModel import com.airbnb.mvrx.withState import dagger.hilt.android.AndroidEntryPoint import im.vector.app.R import im.vector.app.core.extensions.cleanup import im.vector.app.core.extensions.configureWith +import im.vector.app.core.extensions.giveAccessibilityFocus import im.vector.app.core.extensions.registerStartForActivityResult import im.vector.app.core.platform.VectorBaseFragment import im.vector.app.core.utils.PERMISSIONS_FOR_TAKING_PHOTO @@ -36,6 +42,7 @@ import im.vector.app.core.utils.registerForPermissionsResult import im.vector.app.databinding.BottomSheetVerificationChildFragmentBinding import im.vector.app.features.crypto.verification.VerificationAction import im.vector.app.features.qrcode.QrCodeScannerActivity +import org.matrix.android.sdk.api.session.crypto.verification.EVerificationState import timber.log.Timber import javax.inject.Inject @@ -45,6 +52,16 @@ class UserVerificationFragment : VectorBaseFragment // Timber.w("VALR: invalidate with State: ${state.pendingRequest}") + if (state.isNewScreen()) { + requestAccessibilityFocus = true + } controller.update(state) } @@ -142,10 +164,40 @@ class UserVerificationFragment : VectorBaseFragment 0 + is Loading -> 1 + is Success -> when (pendingRequest.invoke().state) { + EVerificationState.WaitingForReady -> 10 + EVerificationState.Requested -> 11 + EVerificationState.Ready -> 12 + EVerificationState.Started -> 13 + EVerificationState.WeStarted -> 14 + EVerificationState.WaitingForDone -> 15 + EVerificationState.Done -> 16 + EVerificationState.Cancelled -> 17 + EVerificationState.HandledByOtherSession -> 18 + } + Uninitialized -> 2 + } + } } From 6542eae2588efb017bba896552508602cc9fda89 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 11 May 2023 10:17:59 +0200 Subject: [PATCH 8/9] Changelog --- changelog.d/8426.misc | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog.d/8426.misc diff --git a/changelog.d/8426.misc b/changelog.d/8426.misc new file mode 100644 index 0000000000..6e3b5013c8 --- /dev/null +++ b/changelog.d/8426.misc @@ -0,0 +1 @@ +Improve keyboard navigation and accessibility when using a screen reader. From 14884b768a528072cc125c177856d50ea0108d56 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 11 May 2023 14:32:52 +0200 Subject: [PATCH 9/9] Remove unused resource --- library/ui-styles/src/main/res/values/styles_toolbar.xml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/library/ui-styles/src/main/res/values/styles_toolbar.xml b/library/ui-styles/src/main/res/values/styles_toolbar.xml index cc00c5ab63..dfe5c33733 100644 --- a/library/ui-styles/src/main/res/values/styles_toolbar.xml +++ b/library/ui-styles/src/main/res/values/styles_toolbar.xml @@ -42,10 +42,6 @@ 12sp - - -