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 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 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 } + } + }) +} 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 { 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..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 @@ -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,18 @@ class SortOptionsView @JvmOverloads constructor( viewTypeSelected.getOppositeViewType() ) } + 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, sortTitleText) + } else { + binding.sortTypeTitle.contentDescription = context.getString(R.string.content_description_sort_by_name_descending, sortTitleText) + } + } + }) + } fun selectAdditionalView(additionalView: AdditionalView) { 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 8c5715a1b71..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 @@ -280,8 +281,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 +859,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 +923,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()) @@ -1411,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/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 { 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) 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) { 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..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 -> { @@ -718,6 +727,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/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 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() { 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 { 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..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,6 +145,7 @@ android:id="@+id/fab_upload" android:layout_width="wrap_content" android:layout_height="wrap_content" + 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" @@ -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" diff --git a/owncloudApp/src/main/res/menu/drawer_menu.xml b/owncloudApp/src/main/res/menu/drawer_menu.xml index ebe3fd1795e..faefe5b1d6b 100644 --- a/owncloudApp/src/main/res/menu/drawer_menu.xml +++ b/owncloudApp/src/main/res/menu/drawer_menu.xml @@ -43,20 +43,6 @@ android:id="@+id/drawer_menu_privacy_policy" android:icon="@drawable/ic_privacy_policy" android:title="@string/prefs_privacy_policy" /> - - - - diff --git a/owncloudApp/src/main/res/values/strings.xml b/owncloudApp/src/main/res/values/strings.xml index b6a34d62231..ebeebbab4a5 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 %1$s ascending + sort by %1$s descending Create a shortcut URL @@ -818,4 +821,7 @@ Get in contact forum, chat with us or contribute on GitHub]]> + Link + Button +