From 87b765e1dfd9c8c93029859f1c6fcbbe98242691 Mon Sep 17 00:00:00 2001 From: Aitorbp Date: Wed, 14 Aug 2024 15:39:53 +0100 Subject: [PATCH 01/17] feat: added roles relate to the drawer: menu and links --- .../android/ui/activity/DrawerActivity.kt | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/owncloudApp/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.kt b/owncloudApp/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.kt index bfebd6cafa3..b104cc2da54 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.kt +++ b/owncloudApp/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.kt @@ -59,6 +59,7 @@ import com.owncloud.android.domain.utils.Event import com.owncloud.android.extensions.goToUrl import com.owncloud.android.extensions.openPrivacyPolicy import com.owncloud.android.extensions.sendEmailOrOpenFeedbackDialogAction +import com.owncloud.android.extensions.setAccessibilityRole import com.owncloud.android.lib.common.OwnCloudAccount import com.owncloud.android.presentation.authentication.AccountUtils import com.owncloud.android.presentation.avatar.AvatarUtils @@ -112,10 +113,12 @@ abstract class DrawerActivity : ToolbarActivity() { getDrawerLinkIcon()?.apply { isVisible = true setOnClickListener { openDrawerLink() } + setAccessibilityRole(roleDescription = context.getString(R.string.link_role_accessibility)) } getDrawerLinkText()?.apply { isVisible = true setOnClickListener { openDrawerLink() } + setAccessibilityRole(roleDescription = context.getString(R.string.link_role_accessibility)) } } else { getDrawerLogo()?.setImageResource(R.drawable.drawer_logo) @@ -189,6 +192,7 @@ abstract class DrawerActivity : ToolbarActivity() { } true } + setRolesAccessibilityToMenuItems() } fun setCheckedItemAtBottomBar(checkedMenuItem: Int) { @@ -415,6 +419,19 @@ abstract class DrawerActivity : ToolbarActivity() { }, Handler(), false) } + private fun setRolesAccessibilityToMenuItems() { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + val navViewMenu = getNavView()?.menu ?: return + val roleAccessibilityDescription = getString(R.string.button_role_accessibility) + navViewMenu.apply { + findItem(R.id.nav_settings)?.contentDescription = getString(R.string.actionbar_settings) + roleAccessibilityDescription + findItem(R.id.drawer_menu_feedback)?.contentDescription = getString(R.string.drawer_feedback) + roleAccessibilityDescription + findItem(R.id.drawer_menu_help)?.contentDescription = getString(R.string.prefs_help) + roleAccessibilityDescription + findItem(R.id.drawer_menu_privacy_policy)?.contentDescription = getString(R.string.prefs_privacy_policy) + roleAccessibilityDescription + } + } + } + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) if (savedInstanceState != null) { From 9ba9153eafdda366ae64e34e2c3ae6beed358b92 Mon Sep 17 00:00:00 2001 From: Aitorbp Date: Wed, 14 Aug 2024 15:43:57 +0100 Subject: [PATCH 02/17] feat: added roles to FAB and the contentDescription to the floating buttons --- .../files/filelist/MainFileListFragment.kt | 13 +++++++++++-- .../android/ui/activity/FileDisplayActivity.kt | 1 + .../src/main/res/layout/main_file_list_fragment.xml | 4 ++++ 3 files changed, 16 insertions(+), 2 deletions(-) diff --git a/owncloudApp/src/main/java/com/owncloud/android/presentation/files/filelist/MainFileListFragment.kt b/owncloudApp/src/main/java/com/owncloud/android/presentation/files/filelist/MainFileListFragment.kt index 8c5715a1b71..f87f68db4d7 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/presentation/files/filelist/MainFileListFragment.kt +++ b/owncloudApp/src/main/java/com/owncloud/android/presentation/files/filelist/MainFileListFragment.kt @@ -280,8 +280,7 @@ class MainFileListFragment : Fragment(), showOrHideFab(requireArguments().getParcelable(ARG_FILE_LIST_OPTION)!!, requireArguments().getParcelable(ARG_INITIAL_FOLDER_TO_DISPLAY)!!) - binding.fabMain.findViewById(com.getbase.floatingactionbutton.R.id.fab_expand_menu_button).contentDescription = - getString(R.string.content_description_add_new_content) + setFabMainContentDescription() setTextHintRootToolbar() } @@ -859,6 +858,11 @@ class MainFileListFragment : Fragment(), fabMkdir.isFocusable = isFabExpanded() fabNewfile.isFocusable = isFabExpanded() fabNewshortcut.isFocusable = isFabExpanded() + if (fabMain.isExpanded) { + binding.fabMain.findViewById(com.getbase.floatingactionbutton.R.id.fab_expand_menu_button).contentDescription = getString(R.string.content_description_add_new_content_expanded) + } else { + setFabMainContentDescription() + } } } } @@ -918,6 +922,11 @@ class MainFileListFragment : Fragment(), fun isFabExpanded() = binding.fabMain.isExpanded + fun setFabMainContentDescription() { + binding.fabMain.findViewById(com.getbase.floatingactionbutton.R.id.fab_expand_menu_button).contentDescription = + getString(R.string.content_description_add_new_content) + } + private fun openBottomSheetToUploadFiles() { val uploadBottomSheet = layoutInflater.inflate(R.layout.upload_bottom_sheet_fragment, null) val dialog = BottomSheetDialog(requireContext()) diff --git a/owncloudApp/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.kt b/owncloudApp/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.kt index 2ce5a788f0f..72ab42637e6 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.kt +++ b/owncloudApp/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.kt @@ -718,6 +718,7 @@ class FileDisplayActivity : FileActivity(), } else if (!isDrawerOpen() && isFabOpen) { // close fab mainFileListFragment?.collapseFab() + mainFileListFragment?.setFabMainContentDescription() } else { // Every single menu is collapsed. We can navigate up. if (secondFragment != null) { diff --git a/owncloudApp/src/main/res/layout/main_file_list_fragment.xml b/owncloudApp/src/main/res/layout/main_file_list_fragment.xml index 2561add2b6f..b00d5f262eb 100644 --- a/owncloudApp/src/main/res/layout/main_file_list_fragment.xml +++ b/owncloudApp/src/main/res/layout/main_file_list_fragment.xml @@ -145,6 +145,7 @@ android:id="@+id/fab_upload" android:layout_width="wrap_content" android:layout_height="wrap_content" + android:contentDescription="@string/actionbar_mkdir" fab:fab_colorNormal="@color/primary_button_background_color" fab:fab_colorPressed="@color/owncloud_blue" fab:fab_icon="@drawable/ic_action_upload" @@ -155,6 +156,7 @@ android:id="@+id/fab_mkdir" android:layout_width="wrap_content" android:layout_height="wrap_content" + android:contentDescription="@string/actionbar_mkdir" fab:fab_colorNormal="@color/primary_button_background_color" fab:fab_colorPressed="@color/owncloud_blue" fab:fab_icon="@drawable/ic_action_create_dir" @@ -165,6 +167,7 @@ android:id="@+id/fab_newfile" android:layout_width="wrap_content" android:layout_height="wrap_content" + android:contentDescription="@string/fab_new_file" fab:fab_colorNormal="@color/primary_button_background_color" fab:fab_colorPressed="@color/owncloud_blue" fab:fab_icon="@drawable/ic_action_create_file" @@ -175,6 +178,7 @@ android:id="@+id/fab_newshortcut" android:layout_width="wrap_content" android:layout_height="wrap_content" + android:contentDescription="@string/fab_new_shortcut" fab:fab_colorNormal="@color/primary_button_background_color" fab:fab_colorPressed="@color/owncloud_blue" fab:fab_icon="@drawable/ic_action_open_shortcut" From 67e41900e44744cf8896ee2dc8b7862f5a42d65d Mon Sep 17 00:00:00 2001 From: Aitorbp Date: Wed, 14 Aug 2024 15:44:40 +0100 Subject: [PATCH 03/17] feat: added roles in Manage Accounts --- .../android/presentation/accounts/ManageAccountsAdapter.kt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/owncloudApp/src/main/java/com/owncloud/android/presentation/accounts/ManageAccountsAdapter.kt b/owncloudApp/src/main/java/com/owncloud/android/presentation/accounts/ManageAccountsAdapter.kt index 5c22686da17..9713a7237cb 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/presentation/accounts/ManageAccountsAdapter.kt +++ b/owncloudApp/src/main/java/com/owncloud/android/presentation/accounts/ManageAccountsAdapter.kt @@ -25,10 +25,12 @@ import android.accounts.Account import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import android.widget.Button import androidx.recyclerview.widget.RecyclerView import com.owncloud.android.R import com.owncloud.android.databinding.AccountActionBinding import com.owncloud.android.databinding.AccountItemBinding +import com.owncloud.android.extensions.setAccessibilityRole import com.owncloud.android.lib.common.OwnCloudAccount import com.owncloud.android.presentation.authentication.AccountUtils import com.owncloud.android.presentation.avatar.AvatarUtils @@ -46,6 +48,7 @@ class ManageAccountsAdapter(private val accountListener: AccountAdapterListener) return if (viewType == AccountManagementRecyclerItemViewType.ITEM_VIEW_ACCOUNT.ordinal) { val view = inflater.inflate(R.layout.account_item, parent, false) view.filterTouchesWhenObscured = PreferenceUtils.shouldDisallowTouchesWithOtherVisibleWindows(parent.context) + view.setAccessibilityRole(className = Button::class.java) AccountManagementViewHolder(view) } else { val view = inflater.inflate(R.layout.account_action, parent, false) @@ -119,6 +122,7 @@ class ManageAccountsAdapter(private val accountListener: AccountAdapterListener) is NewAccountViewHolder -> { holder.binding.icon.setImageResource(R.drawable.ic_account_plus) holder.binding.name.setText(R.string.prefs_add_account) + holder.binding.name.setAccessibilityRole(className = Button::class.java) // bind action listener holder.binding.constraintLayoutAction.setOnClickListener { From 9632561afd62db5584215d57a09ca7b0713c5266 Mon Sep 17 00:00:00 2001 From: Aitorbp Date: Wed, 14 Aug 2024 15:55:40 +0100 Subject: [PATCH 04/17] feat: added role to sort options and ascending/descending behaviour --- .../presentation/files/SortOptionsView.kt | 24 +++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/owncloudApp/src/main/java/com/owncloud/android/presentation/files/SortOptionsView.kt b/owncloudApp/src/main/java/com/owncloud/android/presentation/files/SortOptionsView.kt index 6676559e99b..319fbd2617b 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/presentation/files/SortOptionsView.kt +++ b/owncloudApp/src/main/java/com/owncloud/android/presentation/files/SortOptionsView.kt @@ -2,7 +2,9 @@ * ownCloud Android client application * * @author Abel García de Prada - * Copyright (C) 2020 ownCloud GmbH. + * @author Aitor Ballesteros Pavón + * + * Copyright (C) 2024 ownCloud GmbH. *

* This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2, @@ -21,13 +23,20 @@ package com.owncloud.android.presentation.files import android.content.Context import android.util.AttributeSet import android.view.LayoutInflater +import android.view.View +import android.widget.Button import androidx.constraintlayout.widget.ConstraintLayout import androidx.core.content.ContextCompat +import androidx.core.view.AccessibilityDelegateCompat +import androidx.core.view.ViewCompat +import androidx.core.view.accessibility.AccessibilityNodeInfoCompat import com.owncloud.android.R import com.owncloud.android.data.providers.SharedPreferencesProvider import com.owncloud.android.data.providers.implementation.OCSharedPreferencesProvider import com.owncloud.android.databinding.SortOptionsLayoutBinding +import com.owncloud.android.extensions.setAccessibilityRole import com.owncloud.android.presentation.files.SortOrder.Companion.PREF_FILE_LIST_SORT_ORDER +import com.owncloud.android.presentation.files.SortOrder.SORT_ORDER_ASCENDING import com.owncloud.android.presentation.files.SortType.Companion.PREF_FILE_LIST_SORT_TYPE class SortOptionsView @JvmOverloads constructor( @@ -75,7 +84,7 @@ class SortOptionsView @JvmOverloads constructor( // Select sort type and order according to preferences. sortTypeSelected = SortType.values()[sharedPreferencesProvider.getInt(PREF_FILE_LIST_SORT_TYPE, SortType.SORT_TYPE_BY_NAME.ordinal)] sortOrderSelected = SortOrder.values()[sharedPreferencesProvider.getInt(PREF_FILE_LIST_SORT_ORDER, SortOrder.SORT_ORDER_ASCENDING.ordinal)] - + binding.sortTypeTitle.setAccessibilityRole(className = Button::class.java) binding.sortTypeSelector.setOnClickListener { onSortOptionsListener?.onSortTypeListener( sortTypeSelected, @@ -87,6 +96,17 @@ class SortOptionsView @JvmOverloads constructor( viewTypeSelected.getOppositeViewType() ) } + ViewCompat.setAccessibilityDelegate(binding.sortTypeSelector, object : AccessibilityDelegateCompat() { + override fun onInitializeAccessibilityNodeInfo(v: View, info: AccessibilityNodeInfoCompat) { + super.onInitializeAccessibilityNodeInfo(v, info) + if (sortOrderSelected == SORT_ORDER_ASCENDING) { + binding.sortTypeTitle.contentDescription = context.getString(R.string.content_description_sort_by_name_ascending) + } else { + binding.sortTypeTitle.contentDescription = context.getString(R.string.content_description_sort_by_name_descending) + } + } + }) + } fun selectAdditionalView(additionalView: AdditionalView) { From b612b7b952ec6c5ae9876b83795d39748e91fa97 Mon Sep 17 00:00:00 2001 From: Aitorbp Date: Wed, 14 Aug 2024 15:57:34 +0100 Subject: [PATCH 05/17] feat: created extension file ViewExt.kt to handle the role --- .../owncloud/android/extensions/ViewExt.kt | 36 +++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 owncloudApp/src/main/java/com/owncloud/android/extensions/ViewExt.kt diff --git a/owncloudApp/src/main/java/com/owncloud/android/extensions/ViewExt.kt b/owncloudApp/src/main/java/com/owncloud/android/extensions/ViewExt.kt new file mode 100644 index 00000000000..df76ce0ae12 --- /dev/null +++ b/owncloudApp/src/main/java/com/owncloud/android/extensions/ViewExt.kt @@ -0,0 +1,36 @@ +/** + * ownCloud Android client application + * + * @author Aitor Ballesteros Pavón + * + * Copyright (C) 2024 ownCloud GmbH. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.owncloud.android.extensions + +import android.view.View +import androidx.core.view.AccessibilityDelegateCompat +import androidx.core.view.ViewCompat +import androidx.core.view.accessibility.AccessibilityNodeInfoCompat + +fun View.setAccessibilityRole(className: Class<*>? = null, roleDescription: String? = null) { + ViewCompat.setAccessibilityDelegate(this, object : AccessibilityDelegateCompat() { + override fun onInitializeAccessibilityNodeInfo(v: View, info: AccessibilityNodeInfoCompat) { + super.onInitializeAccessibilityNodeInfo(v, info) + className?.let { info.className = it.name } + roleDescription?.let { info.roleDescription = it } + } + }) +} From efbdcda7266bd253d062fbf8212254842b22bf22 Mon Sep 17 00:00:00 2001 From: Aitorbp Date: Wed, 14 Aug 2024 15:58:44 +0100 Subject: [PATCH 06/17] feat: created string for specific roles and contentDescription --- owncloudApp/src/main/res/values/strings.xml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/owncloudApp/src/main/res/values/strings.xml b/owncloudApp/src/main/res/values/strings.xml index b6a34d62231..1b4970747df 100644 --- a/owncloudApp/src/main/res/values/strings.xml +++ b/owncloudApp/src/main/res/values/strings.xml @@ -799,12 +799,15 @@ Edit public link Remove account Add new content + "Add new content expanded" Clean account storage Logo Add share Edit share Delete share %1$s operations + Sort by name ascending + sort by name descending Create a shortcut URL @@ -818,4 +821,7 @@ Get in contact forum, chat with us or contribute on GitHub]]> + Link + Button + From a6c2f0df93c726e391411914f964cc5ce27ce930 Mon Sep 17 00:00:00 2001 From: Aitorbp Date: Wed, 14 Aug 2024 15:59:36 +0100 Subject: [PATCH 07/17] feat: added role to space item --- .../android/presentation/spaces/SpacesListAdapter.kt | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/owncloudApp/src/main/java/com/owncloud/android/presentation/spaces/SpacesListAdapter.kt b/owncloudApp/src/main/java/com/owncloud/android/presentation/spaces/SpacesListAdapter.kt index 2c2ed2547a8..0e6e88eda06 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/presentation/spaces/SpacesListAdapter.kt +++ b/owncloudApp/src/main/java/com/owncloud/android/presentation/spaces/SpacesListAdapter.kt @@ -3,8 +3,9 @@ * * @author Juan Carlos Garrote Gascón * @author Manuel Plazas Palacio + * @author Aitor Balleteros Pavón * - * Copyright (C) 2023 ownCloud GmbH. + * Copyright (C) 2024 ownCloud GmbH. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2, @@ -23,6 +24,7 @@ package com.owncloud.android.presentation.spaces import android.view.LayoutInflater import android.view.ViewGroup +import android.widget.Button import androidx.core.content.ContextCompat import androidx.recyclerview.widget.DiffUtil import androidx.recyclerview.widget.RecyclerView @@ -31,6 +33,7 @@ import coil.load import com.owncloud.android.R import com.owncloud.android.databinding.SpacesListItemBinding import com.owncloud.android.domain.spaces.model.OCSpace +import com.owncloud.android.extensions.setAccessibilityRole import com.owncloud.android.presentation.thumbnails.ThumbnailsRequester import com.owncloud.android.utils.PreferenceUtils @@ -54,6 +57,7 @@ class SpacesListAdapter( spacesListItemCard.setOnClickListener { listener.onItemClick(space) } + spacesListItemCard.setAccessibilityRole(className = Button::class.java) if (space.isPersonal) { spacesListItemName.text = holder.itemView.context.getString(R.string.bottom_nav_personal) From 6d66ef8b73a01c2ad44c2c159a4bbd8c15507278 Mon Sep 17 00:00:00 2001 From: Aitorbp Date: Wed, 14 Aug 2024 15:59:57 +0100 Subject: [PATCH 08/17] feat: added role to toolbar title --- .../java/com/owncloud/android/ui/activity/ToolbarActivity.kt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/owncloudApp/src/main/java/com/owncloud/android/ui/activity/ToolbarActivity.kt b/owncloudApp/src/main/java/com/owncloud/android/ui/activity/ToolbarActivity.kt index 6470329b8fd..68e723c08bd 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/ui/activity/ToolbarActivity.kt +++ b/owncloudApp/src/main/java/com/owncloud/android/ui/activity/ToolbarActivity.kt @@ -27,6 +27,7 @@ package com.owncloud.android.ui.activity import android.view.Menu import android.view.View import android.view.View.VISIBLE +import android.widget.Button import android.widget.EditText import android.widget.ImageView import android.widget.TextView @@ -36,6 +37,7 @@ import androidx.constraintlayout.widget.ConstraintLayout import androidx.core.content.ContextCompat import androidx.core.view.isVisible import com.owncloud.android.R +import com.owncloud.android.extensions.setAccessibilityRole import com.owncloud.android.presentation.accounts.ManageAccountsDialogFragment import com.owncloud.android.presentation.accounts.ManageAccountsDialogFragment.Companion.MANAGE_ACCOUNTS_DIALOG import com.owncloud.android.presentation.authentication.AccountUtils @@ -93,6 +95,7 @@ abstract class ToolbarActivity : BaseActivity() { toolbarTitle.setCompoundDrawablesWithIntrinsicBounds(0, 0, 0, 0) } } + toolbarTitle.setAccessibilityRole(className = Button::class.java) searchView.apply { isVisible = false From 127c77fe91728796e4820fe8eec844e66dfa6c4a Mon Sep 17 00:00:00 2001 From: Aitorbp Date: Mon, 19 Aug 2024 13:14:08 +0100 Subject: [PATCH 09/17] feat: added role MenuItems in fragment --- .../files/details/FileDetailsFragment.kt | 20 ++++++++--- .../files/filelist/MainFileListFragment.kt | 21 +++++++++++ .../android/ui/fragment/FileFragment.java | 35 ++++++++++++++++++- .../ui/preview/PreviewAudioFragment.kt | 20 ++++++----- .../ui/preview/PreviewImageFragment.kt | 20 ++++++----- .../android/ui/preview/PreviewTextFragment.kt | 15 +++++--- 6 files changed, 102 insertions(+), 29 deletions(-) diff --git a/owncloudApp/src/main/java/com/owncloud/android/presentation/files/details/FileDetailsFragment.kt b/owncloudApp/src/main/java/com/owncloud/android/presentation/files/details/FileDetailsFragment.kt index 0d215a0ce69..1e1a6a5d9cd 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/presentation/files/details/FileDetailsFragment.kt +++ b/owncloudApp/src/main/java/com/owncloud/android/presentation/files/details/FileDetailsFragment.kt @@ -26,10 +26,10 @@ import android.accounts.Account import android.content.Intent import android.graphics.Bitmap import android.net.Uri +import android.os.Build import android.os.Bundle import android.view.LayoutInflater import android.view.Menu -import android.view.MenuInflater import android.view.MenuItem import android.view.View import android.view.ViewGroup @@ -219,10 +219,6 @@ class FileDetailsFragment : FileFragment() { fileDetailsViewModel.checkOnGoingTransfersWhenOpening() } - override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) { - super.onCreateOptionsMenu(menu, inflater) - inflater.inflate(R.menu.file_actions_menu, menu) - } override fun onPrepareOptionsMenu(menu: Menu) { super.onPrepareOptionsMenu(menu) @@ -241,6 +237,20 @@ class FileDetailsFragment : FileFragment() { val appRegistryProviders = fileDetailsViewModel.appRegistryMimeType.value?.appProviders openInWebProviders = addOpenInWebMenuOptions(menu, openInWebProviders, appRegistryProviders) + + setRolesAccessibilityToMenuItems(menu) + } + + private fun setRolesAccessibilityToMenuItems(menu: Menu) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + val roleAccessibilityDescription = getString(R.string.button_role_accessibility) + menu.findItem(R.id.action_rename_file).setContentDescription( + getString(R.string.common_rename) + roleAccessibilityDescription + ) + menu.findItem(R.id.action_remove_file).setContentDescription( + getString(R.string.common_remove) + roleAccessibilityDescription + ) + } } override fun onOptionsItemSelected(item: MenuItem): Boolean { diff --git a/owncloudApp/src/main/java/com/owncloud/android/presentation/files/filelist/MainFileListFragment.kt b/owncloudApp/src/main/java/com/owncloud/android/presentation/files/filelist/MainFileListFragment.kt index f87f68db4d7..9783789ed80 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/presentation/files/filelist/MainFileListFragment.kt +++ b/owncloudApp/src/main/java/com/owncloud/android/presentation/files/filelist/MainFileListFragment.kt @@ -31,6 +31,7 @@ import android.content.Intent import android.content.res.ColorStateList import android.graphics.drawable.Drawable import android.net.Uri +import android.os.Build import android.os.Bundle import android.view.LayoutInflater import android.view.Menu @@ -1420,10 +1421,30 @@ class MainFileListFragment : Fragment(), openInWebProviders = emptyMap() } } + setRolesAccessibilityToMenuItems() return true } + private fun setRolesAccessibilityToMenuItems() { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + val roleAccessibilityDescription = getString(R.string.button_role_accessibility) + menu?.apply { + findItem(R.id.file_action_select_all)?.contentDescription = getString(R.string.actionbar_select_all) + roleAccessibilityDescription + findItem(R.id.action_select_inverse)?.contentDescription = getString(R.string.actionbar_select_inverse) + roleAccessibilityDescription + findItem(R.id.action_open_file_with)?.contentDescription = getString(R.string.actionbar_open_with) + roleAccessibilityDescription + findItem(R.id.action_rename_file)?.contentDescription = getString(R.string.common_rename) + roleAccessibilityDescription + findItem(R.id.action_move)?.contentDescription = getString(R.string.actionbar_move) + roleAccessibilityDescription + findItem(R.id.action_copy)?.contentDescription = getString(R.string.copy) + roleAccessibilityDescription + findItem(R.id.action_send_file)?.contentDescription = getString(R.string.actionbar_send_file) + roleAccessibilityDescription + findItem(R.id.action_set_available_offline)?.contentDescription = getString(R.string.set_available_offline) + roleAccessibilityDescription + findItem(R.id.action_unset_available_offline)?.contentDescription = getString(R.string.unset_available_offline) + roleAccessibilityDescription + findItem(R.id.action_see_details)?.contentDescription = getString(R.string.actionbar_see_details) + roleAccessibilityDescription + findItem(R.id.action_remove_file)?.contentDescription = getString(R.string.common_remove) + roleAccessibilityDescription + } + } + } + override fun onActionItemClicked(mode: ActionMode?, item: MenuItem?): Boolean { return onFileActionChosen(item?.itemId) } diff --git a/owncloudApp/src/main/java/com/owncloud/android/ui/fragment/FileFragment.java b/owncloudApp/src/main/java/com/owncloud/android/ui/fragment/FileFragment.java index b49fdbe6dc2..1ac41e88c03 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/ui/fragment/FileFragment.java +++ b/owncloudApp/src/main/java/com/owncloud/android/ui/fragment/FileFragment.java @@ -4,7 +4,9 @@ * @author David A. Velasco * @author Christian Schabesberger * @author David González Verdugo - * Copyright (C) 2020 ownCloud GmbH. + * @author Aitor Ballesteros Pavón + * + * Copyright (C) 2024 ownCloud GmbH. *

* This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2, @@ -22,8 +24,13 @@ package com.owncloud.android.ui.fragment; import android.content.Context; +import android.os.Build; +import android.view.Menu; +import android.view.MenuInflater; +import androidx.annotation.NonNull; import androidx.fragment.app.Fragment; +import com.owncloud.android.R; import com.owncloud.android.domain.files.model.OCFile; import com.owncloud.android.ui.activity.ComponentsGetter; @@ -144,4 +151,30 @@ public interface ContainerActivity extends ComponentsGetter { // inside the fragment, MAYBE activity is interested --> unify in notification method } + + @Override + public void onCreateOptionsMenu(@NonNull Menu menu, @NonNull MenuInflater inflater) { + super.onCreateOptionsMenu(menu, inflater); + inflater.inflate(R.menu.file_actions_menu, menu); + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + String roleAccessibilityDescription = getString(R.string.button_role_accessibility); + + menu.findItem(R.id.action_open_file_with).setContentDescription( + getString(R.string.actionbar_open_with) + roleAccessibilityDescription + ); + + menu.findItem(R.id.action_send_file).setContentDescription( + getString(R.string.actionbar_send_file) + roleAccessibilityDescription + ); + + menu.findItem(R.id.action_set_available_offline).setContentDescription( + getString(R.string.set_available_offline) + roleAccessibilityDescription + ); + + menu.findItem(R.id.action_unset_available_offline).setContentDescription( + getString(R.string.set_available_offline) + roleAccessibilityDescription + ); + } + } } diff --git a/owncloudApp/src/main/java/com/owncloud/android/ui/preview/PreviewAudioFragment.kt b/owncloudApp/src/main/java/com/owncloud/android/ui/preview/PreviewAudioFragment.kt index c986a107dfa..9a73b05a4b8 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/ui/preview/PreviewAudioFragment.kt +++ b/owncloudApp/src/main/java/com/owncloud/android/ui/preview/PreviewAudioFragment.kt @@ -33,11 +33,11 @@ import android.content.Intent import android.content.ServiceConnection import android.graphics.BitmapFactory import android.media.MediaMetadataRetriever +import android.os.Build import android.os.Bundle import android.os.IBinder import android.view.LayoutInflater import android.view.Menu -import android.view.MenuInflater import android.view.MenuItem import android.view.View import android.view.ViewGroup @@ -218,14 +218,6 @@ class PreviewAudioFragment : FileFragment() { // Nothing to do here, sync is not shown in previews } - /** - * {@inheritDoc} - */ - override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) { - super.onCreateOptionsMenu(menu, inflater) - inflater.inflate(R.menu.file_actions_menu, menu) - } - /** * {@inheritDoc} */ @@ -244,6 +236,16 @@ class PreviewAudioFragment : FileFragment() { isVisible = false isEnabled = false } + + setRolesAccessibilityToMenuItems(menu) + } + + private fun setRolesAccessibilityToMenuItems(menu: Menu) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + menu.findItem(R.id.action_see_details).setContentDescription( + getString(R.string.actionbar_see_details) + getString(R.string.button_role_accessibility) + ) + } } /** diff --git a/owncloudApp/src/main/java/com/owncloud/android/ui/preview/PreviewImageFragment.kt b/owncloudApp/src/main/java/com/owncloud/android/ui/preview/PreviewImageFragment.kt index f61b261aed5..076c0f77246 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/ui/preview/PreviewImageFragment.kt +++ b/owncloudApp/src/main/java/com/owncloud/android/ui/preview/PreviewImageFragment.kt @@ -30,10 +30,10 @@ import android.accounts.Account import android.graphics.Bitmap import android.graphics.Color import android.graphics.drawable.Drawable +import android.os.Build import android.os.Bundle import android.view.LayoutInflater import android.view.Menu -import android.view.MenuInflater import android.view.MenuItem import android.view.View import android.view.ViewGroup @@ -171,14 +171,6 @@ class PreviewImageFragment : FileFragment() { currentFilePreviewing = file } - /** - * {@inheritDoc} - */ - override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) { - super.onCreateOptionsMenu(menu, inflater) - inflater.inflate(R.menu.file_actions_menu, menu) - } - /** * {@inheritDoc} */ @@ -194,6 +186,16 @@ class PreviewImageFragment : FileFragment() { val hasWritePermission = safeFile.hasWritePermission menu.filterMenuOptions(menuOptions, hasWritePermission) } + + setRolesAccessibilityToMenuItems(menu) + } + + private fun setRolesAccessibilityToMenuItems(menu: Menu) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + menu.findItem(R.id.action_see_details).setContentDescription( + getString(R.string.actionbar_see_details) + getString(R.string.button_role_accessibility) + ) + } } /** diff --git a/owncloudApp/src/main/java/com/owncloud/android/ui/preview/PreviewTextFragment.kt b/owncloudApp/src/main/java/com/owncloud/android/ui/preview/PreviewTextFragment.kt index cae95c5d3f9..9380707a0ba 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/ui/preview/PreviewTextFragment.kt +++ b/owncloudApp/src/main/java/com/owncloud/android/ui/preview/PreviewTextFragment.kt @@ -27,10 +27,10 @@ package com.owncloud.android.ui.preview import android.accounts.Account import android.os.AsyncTask +import android.os.Build import android.os.Bundle import android.view.LayoutInflater import android.view.Menu -import android.view.MenuInflater import android.view.MenuItem import android.view.View import android.view.ViewGroup @@ -208,10 +208,6 @@ class PreviewTextFragment : FileFragment() { // Nothing to do here, sync is not shown in previews } - override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) { - inflater.inflate(R.menu.file_actions_menu, menu) - } - override fun onPrepareOptionsMenu(menu: Menu) { mContainerActivity.storageManager?.let { val safeFile = file @@ -229,6 +225,15 @@ class PreviewTextFragment : FileFragment() { isEnabled = false } + setRolesAccessibilityToMenuItems(menu) + } + + private fun setRolesAccessibilityToMenuItems(menu: Menu) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + menu.findItem(R.id.action_see_details).setContentDescription( + getString(R.string.actionbar_see_details) + getString(R.string.button_role_accessibility) + ) + } } private fun loadAndShowTextPreview() { From 97f0d26708ebea00e1b5f91645977864affaf640 Mon Sep 17 00:00:00 2001 From: Aitorbp Date: Mon, 19 Aug 2024 13:14:50 +0100 Subject: [PATCH 10/17] feat: added role MenuItems in activities --- .../android/ui/activity/FileDisplayActivity.kt | 9 +++++++++ .../android/ui/preview/PreviewVideoActivity.kt | 15 +++++++++++++++ 2 files changed, 24 insertions(+) diff --git a/owncloudApp/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.kt b/owncloudApp/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.kt index 72ab42637e6..923afc08687 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.kt +++ b/owncloudApp/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.kt @@ -557,9 +557,18 @@ class FileDisplayActivity : FileActivity(), menu.removeItem(shareFileMenuItem.itemId) } + setRolesAccessibilityToMenuItems() + return true } + private fun setRolesAccessibilityToMenuItems() { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + selectAllMenuItem?.contentDescription = + getString(R.string.actionbar_select_all) + getString(R.string.button_role_accessibility) + } + } + override fun onOptionsItemSelected(item: MenuItem): Boolean { when (item.itemId) { android.R.id.home -> { diff --git a/owncloudApp/src/main/java/com/owncloud/android/ui/preview/PreviewVideoActivity.kt b/owncloudApp/src/main/java/com/owncloud/android/ui/preview/PreviewVideoActivity.kt index 330f12d2c2a..3647ca7863c 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/ui/preview/PreviewVideoActivity.kt +++ b/owncloudApp/src/main/java/com/owncloud/android/ui/preview/PreviewVideoActivity.kt @@ -328,6 +328,21 @@ class PreviewVideoActivity : FileActivity(), Player.Listener, OnPrepareVideoPlay val hasWritePermission: Boolean = safeFile.hasWritePermission menu.filterMenuOptions(menuOptions, hasWritePermission) } + setRolesAccessibilityToMenuItems(menu) + } + + private fun setRolesAccessibilityToMenuItems(menu: Menu) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + val roleAccessibilityDescription = getString(R.string.button_role_accessibility) + menu.apply { + menu.findItem(R.id.action_open_file_with)?.contentDescription = getString(R.string.actionbar_open_with) + roleAccessibilityDescription + menu.findItem(R.id.action_send_file)?.contentDescription = getString(R.string.actionbar_send_file) + roleAccessibilityDescription + menu.findItem(R.id.action_set_available_offline)?.contentDescription = getString(R.string.set_available_offline) + roleAccessibilityDescription + menu.findItem(R.id.action_unset_available_offline)?.contentDescription = getString(R.string.unset_available_offline) + roleAccessibilityDescription + menu.findItem(R.id.action_see_details)?.contentDescription = getString(R.string.actionbar_see_details) + roleAccessibilityDescription + menu.findItem(R.id.action_remove_file)?.contentDescription = getString(R.string.common_remove) + roleAccessibilityDescription + } + } } override fun onMenuItemSelected(menuItem: MenuItem): Boolean { From f6edd0362c0cd237bce12893e7c1c48dcb580074 Mon Sep 17 00:00:00 2001 From: Aitorbp Date: Mon, 19 Aug 2024 13:15:06 +0100 Subject: [PATCH 11/17] chore: added calens file --- changelog/unreleased/4454 | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 changelog/unreleased/4454 diff --git a/changelog/unreleased/4454 b/changelog/unreleased/4454 new file mode 100644 index 00000000000..3f8133aa7f5 --- /dev/null +++ b/changelog/unreleased/4454 @@ -0,0 +1,6 @@ +Enhancement: Roles added to some elements to improve accessibility + +Roles have been added to specific elements within the following views: Toolbar, Spaces, Drawer Menu, Manage accounts and Floating Action Button. + +https://github.com/owncloud/android/issues/4373 +https://github.com/owncloud/android/pull/4454 From cb45ba18bce1b1fa26a0fc3eab64531611cddcdc Mon Sep 17 00:00:00 2001 From: Aitorbp Date: Mon, 19 Aug 2024 12:25:49 +0000 Subject: [PATCH 12/17] docs: calens changelog updated --- CHANGELOG.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6c07627831e..90a0a70c55e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -37,6 +37,7 @@ ownCloud admins and users. * Enhancement - Improved accessibility of information and relationships: [#4362](https://github.com/owncloud/android/issues/4362) * Enhancement - Changed the color of some elements to improve accessibility: [#4364](https://github.com/owncloud/android/issues/4364) * Enhancement - Improved SearchView accessibility: [#4365](https://github.com/owncloud/android/issues/4365) +* Enhancement - Roles added to some elements to improve accessibility: [#4373](https://github.com/owncloud/android/issues/4373) * Enhancement - Hardware keyboard support: [#4438](https://github.com/owncloud/android/pull/4438) * Enhancement - Hardware keyboard support for passcode view: [#4447](https://github.com/owncloud/android/issues/4447) @@ -88,6 +89,14 @@ ownCloud admins and users. https://github.com/owncloud/android/issues/4365 https://github.com/owncloud/android/pull/4433 +* Enhancement - Roles added to some elements to improve accessibility: [#4373](https://github.com/owncloud/android/issues/4373) + + Roles have been added to specific elements within the following views: Toolbar, + Spaces, Drawer Menu, Manage accounts and Floating Action Button. + + https://github.com/owncloud/android/issues/4373 + https://github.com/owncloud/android/pull/4454 + * Enhancement - Hardware keyboard support: [#4438](https://github.com/owncloud/android/pull/4438) Navigation via hardware keyboard has been improved so that now focus order has a From 05a5489396bd86dfcc3ce8ba2f5859842b813a2a Mon Sep 17 00:00:00 2001 From: Aitorbp Date: Thu, 22 Aug 2024 07:31:15 +0100 Subject: [PATCH 13/17] fix: sort by name correctly --- .../owncloud/android/presentation/files/SortOptionsView.kt | 5 +++-- owncloudApp/src/main/res/values/strings.xml | 4 ++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/owncloudApp/src/main/java/com/owncloud/android/presentation/files/SortOptionsView.kt b/owncloudApp/src/main/java/com/owncloud/android/presentation/files/SortOptionsView.kt index 319fbd2617b..4b433b7243c 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/presentation/files/SortOptionsView.kt +++ b/owncloudApp/src/main/java/com/owncloud/android/presentation/files/SortOptionsView.kt @@ -99,10 +99,11 @@ class SortOptionsView @JvmOverloads constructor( ViewCompat.setAccessibilityDelegate(binding.sortTypeSelector, object : AccessibilityDelegateCompat() { override fun onInitializeAccessibilityNodeInfo(v: View, info: AccessibilityNodeInfoCompat) { super.onInitializeAccessibilityNodeInfo(v, info) + val sortTitleText = binding.sortTypeTitle.text if (sortOrderSelected == SORT_ORDER_ASCENDING) { - binding.sortTypeTitle.contentDescription = context.getString(R.string.content_description_sort_by_name_ascending) + binding.sortTypeTitle.contentDescription = context.getString(R.string.content_description_sort_by_name_ascending, sortTitleText) } else { - binding.sortTypeTitle.contentDescription = context.getString(R.string.content_description_sort_by_name_descending) + binding.sortTypeTitle.contentDescription = context.getString(R.string.content_description_sort_by_name_descending, sortTitleText) } } }) diff --git a/owncloudApp/src/main/res/values/strings.xml b/owncloudApp/src/main/res/values/strings.xml index 1b4970747df..ebeebbab4a5 100644 --- a/owncloudApp/src/main/res/values/strings.xml +++ b/owncloudApp/src/main/res/values/strings.xml @@ -806,8 +806,8 @@ Edit share Delete share %1$s operations - Sort by name ascending - sort by name descending + Sort by %1$s ascending + sort by %1$s descending Create a shortcut URL From 472c584e2a74691bd556e6932c5790dba34d36fb Mon Sep 17 00:00:00 2001 From: Aitorbp Date: Thu, 22 Aug 2024 07:33:25 +0100 Subject: [PATCH 14/17] fix: changed string "New folder" by "Upload" --- owncloudApp/src/main/res/layout/main_file_list_fragment.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/owncloudApp/src/main/res/layout/main_file_list_fragment.xml b/owncloudApp/src/main/res/layout/main_file_list_fragment.xml index b00d5f262eb..155e2e9af01 100644 --- a/owncloudApp/src/main/res/layout/main_file_list_fragment.xml +++ b/owncloudApp/src/main/res/layout/main_file_list_fragment.xml @@ -145,7 +145,7 @@ android:id="@+id/fab_upload" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:contentDescription="@string/actionbar_mkdir" + android:contentDescription="@string/actionbar_upload" fab:fab_colorNormal="@color/primary_button_background_color" fab:fab_colorPressed="@color/owncloud_blue" fab:fab_icon="@drawable/ic_action_upload" From 4760e71ccd5104e0e118cea32de4c02f426776ad Mon Sep 17 00:00:00 2001 From: Aitorbp Date: Thu, 22 Aug 2024 07:35:59 +0100 Subject: [PATCH 15/17] fix: added attribute visible=false to avoid items for accessibility --- owncloudApp/src/main/res/menu/drawer_menu.xml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/owncloudApp/src/main/res/menu/drawer_menu.xml b/owncloudApp/src/main/res/menu/drawer_menu.xml index ebe3fd1795e..ef0d98238b0 100644 --- a/owncloudApp/src/main/res/menu/drawer_menu.xml +++ b/owncloudApp/src/main/res/menu/drawer_menu.xml @@ -49,14 +49,16 @@ android:checkable="false" android:enabled="false" android:orderInCategory="200" - android:title="" /> + android:title="" + android:visible="false"/> + android:title="" + android:visible="false"/> From e6e665868ace4ae73abaeeedb254d97c39b56ce9 Mon Sep 17 00:00:00 2001 From: Aitorbp Date: Thu, 22 Aug 2024 07:54:46 +0100 Subject: [PATCH 16/17] fix: added Button rol to shareViaLinkExpirationValue --- .../presentation/sharing/shares/PublicShareDialogFragment.kt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/owncloudApp/src/main/java/com/owncloud/android/presentation/sharing/shares/PublicShareDialogFragment.kt b/owncloudApp/src/main/java/com/owncloud/android/presentation/sharing/shares/PublicShareDialogFragment.kt index ab342033357..5ee96c8656d 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/presentation/sharing/shares/PublicShareDialogFragment.kt +++ b/owncloudApp/src/main/java/com/owncloud/android/presentation/sharing/shares/PublicShareDialogFragment.kt @@ -37,6 +37,7 @@ import android.view.View import android.view.ViewGroup import android.view.WindowManager import android.view.inputmethod.InputMethodManager +import android.widget.Button import android.widget.CompoundButton import android.widget.TextView import androidx.appcompat.widget.SwitchCompat @@ -55,6 +56,7 @@ import com.owncloud.android.domain.sharing.shares.model.OCShare import com.owncloud.android.domain.utils.Event.EventObserver import com.owncloud.android.extensions.avoidScreenshotsIfNeeded import com.owncloud.android.extensions.parseError +import com.owncloud.android.extensions.setAccessibilityRole import com.owncloud.android.extensions.showMessageInSnackbar import com.owncloud.android.lib.resources.shares.RemoteShare import com.owncloud.android.lib.resources.status.OwnCloudVersion @@ -122,6 +124,7 @@ class PublicShareDialogFragment : DialogFragment() { val expirationDateValueInMillis: Long get() { var publicLinkExpirationDateInMillis: Long = -1 + binding.shareViaLinkExpirationValue.setAccessibilityRole(className = Button::class.java) val expirationDate = binding.shareViaLinkExpirationValue.text.toString() if (expirationDate.isNotEmpty()) { try { From 4e99414543fbf4b4d8cfb16386709def6f6c5da5 Mon Sep 17 00:00:00 2001 From: Aitorbp Date: Thu, 22 Aug 2024 09:17:29 +0100 Subject: [PATCH 17/17] fix: deleted free slot footer_spacer_1 and footer_spacer_2 --- owncloudApp/src/main/res/menu/drawer_menu.xml | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/owncloudApp/src/main/res/menu/drawer_menu.xml b/owncloudApp/src/main/res/menu/drawer_menu.xml index ef0d98238b0..faefe5b1d6b 100644 --- a/owncloudApp/src/main/res/menu/drawer_menu.xml +++ b/owncloudApp/src/main/res/menu/drawer_menu.xml @@ -43,22 +43,6 @@ android:id="@+id/drawer_menu_privacy_policy" android:icon="@drawable/ic_privacy_policy" android:title="@string/prefs_privacy_policy" /> - - - -