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
* 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
* 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" />
-
-