Skip to content

Commit 01716a6

Browse files
authored
Merge pull request #4438 from owncloud/feature/support_keyboard_navigation
[a11y] Hardware keyboard support
2 parents a9c05ec + 56d2ffa commit 01716a6

File tree

14 files changed

+134
-12
lines changed

14 files changed

+134
-12
lines changed

CHANGELOG.md

+13
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ ownCloud admins and users.
3434

3535
* Enhancement - Changed the color of some elements to improve accessibility: [#4364](https://github.com/owncloud/android/issues/4364)
3636
* Enhancement - Improved SearchView accessibility: [#4365](https://github.com/owncloud/android/issues/4365)
37+
* Enhancement - Hardware keyboard support: [#4438](https://github.com/owncloud/android/pull/4438)
3738

3839
## Details
3940

@@ -55,6 +56,18 @@ ownCloud admins and users.
5556
https://github.com/owncloud/android/issues/4365
5657
https://github.com/owncloud/android/pull/4433
5758

59+
* Enhancement - Hardware keyboard support: [#4438](https://github.com/owncloud/android/pull/4438)
60+
61+
Navigation via hardware keyboard has been improved so that now focus order has a
62+
logical path, every element is reachable and there are no traps. These
63+
improvements have been applied in main file list, spaces list, drawer menu,
64+
share view and image preview.
65+
66+
https://github.com/owncloud/android/issues/4366
67+
https://github.com/owncloud/android/issues/4367
68+
https://github.com/owncloud/android/issues/4368
69+
https://github.com/owncloud/android/pull/4438
70+
5871
# Changelog for ownCloud Android Client [4.3.1] (2024-07-22)
5972

6073
The following sections list the changes in ownCloud Android Client 4.3.1 relevant to

changelog/unreleased/4438

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
Enhancement: Hardware keyboard support
2+
3+
Navigation via hardware keyboard has been improved so that now focus order has a logical path, every element
4+
is reachable and there are no traps. These improvements have been applied in main file list, spaces list,
5+
drawer menu, share view and image preview.
6+
7+
https://github.com/owncloud/android/pull/4438
8+
https://github.com/owncloud/android/issues/4366
9+
https://github.com/owncloud/android/issues/4367
10+
https://github.com/owncloud/android/issues/4368

owncloudApp/src/main/AndroidManifest.xml

+1-1
Original file line numberDiff line numberDiff line change
@@ -199,7 +199,7 @@
199199
android:exported="false"
200200
android:label="@string/share_dialog_title"
201201
android:launchMode="singleTop"
202-
android:theme="@style/Theme.ownCloud"
202+
android:theme="@style/Theme.ownCloud.Toolbar"
203203
android:windowSoftInputMode="adjustResize">
204204
<intent-filter>
205205
<action android:name="android.intent.action.SEARCH" />

owncloudApp/src/main/java/com/owncloud/android/presentation/files/filelist/MainFileListFragment.kt

+31-1
Original file line numberDiff line numberDiff line change
@@ -824,9 +824,16 @@ class MainFileListFragment : Fragment(),
824824
} else if (!currentFolder.hasAddSubdirectoriesPermission) {
825825
binding.fabMkdir.isVisible = false
826826
}
827+
registerFabMainListener()
827828
registerFabUploadListener()
828829
registerFabMkDirListener()
829830
registerFabNewShortcutListener()
831+
binding.apply {
832+
fabUpload.isFocusable = false
833+
fabMkdir.isFocusable = false
834+
fabNewfile.isFocusable = false
835+
fabNewshortcut.isFocusable = false
836+
}
830837
}
831838
}
832839

@@ -844,6 +851,18 @@ class MainFileListFragment : Fragment(),
844851
binding.fabMkdir.isVisible = shouldBeShown
845852
}
846853

854+
private fun registerFabMainListener() {
855+
binding.apply {
856+
fabMain.findViewById<View>(R.id.fab_expand_menu_button).setOnClickListener {
857+
fabMain.toggle()
858+
fabUpload.isFocusable = isFabExpanded()
859+
fabMkdir.isFocusable = isFabExpanded()
860+
fabNewfile.isFocusable = isFabExpanded()
861+
fabNewshortcut.isFocusable = isFabExpanded()
862+
}
863+
}
864+
}
865+
847866
/**
848867
* Registers [android.view.View.OnClickListener] on the 'Upload' mini FAB for the linked action.
849868
*/
@@ -887,7 +906,14 @@ class MainFileListFragment : Fragment(),
887906
}
888907

889908
fun collapseFab() {
890-
binding.fabMain.collapse()
909+
binding.apply {
910+
fabMain.collapse()
911+
fabUpload.isFocusable = false
912+
fabMkdir.isFocusable = false
913+
fabNewfile.isFocusable = false
914+
fabNewshortcut.isFocusable = false
915+
}
916+
891917
}
892918

893919
fun isFabExpanded() = binding.fabMain.isExpanded
@@ -1323,6 +1349,8 @@ class MainFileListFragment : Fragment(),
13231349
setDrawerStatus(enabled = false)
13241350
actionMode = mode
13251351

1352+
requireActivity().findViewById<View>(R.id.owncloud_app_bar).isFocusableInTouchMode = false
1353+
13261354
val inflater = requireActivity().menuInflater
13271355
inflater.inflate(R.menu.file_actions_menu, menu)
13281356
this@MainFileListFragment.menu = menu
@@ -1395,6 +1423,8 @@ class MainFileListFragment : Fragment(),
13951423
setDrawerStatus(enabled = true)
13961424
actionMode = null
13971425

1426+
requireActivity().findViewById<View>(R.id.owncloud_app_bar).isFocusableInTouchMode = true
1427+
13981428
// reset to previous color
13991429
requireActivity().window.statusBarColor = statusBarColor!!
14001430

owncloudApp/src/main/java/com/owncloud/android/presentation/sharing/ShareActivity.kt

+22-2
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
* @author David González Verdugo
88
* @author Christian Schabesberger
99
* @author Aitor Ballesteros Pavón
10+
* @author Juan Carlos Garrote Gascón
1011
*
1112
* Copyright (C) 2024 ownCloud GmbH.
1213
*
@@ -29,8 +30,10 @@ package com.owncloud.android.presentation.sharing
2930
import android.app.SearchManager
3031
import android.content.Intent
3132
import android.os.Bundle
33+
import android.view.KeyEvent
3234
import android.view.Menu
3335
import android.view.MenuItem
36+
import android.view.View
3437
import androidx.fragment.app.transaction
3538
import com.owncloud.android.R
3639
import com.owncloud.android.domain.files.model.OCFile
@@ -66,9 +69,14 @@ class ShareActivity : FileActivity(), ShareFragmentListener {
6669

6770
setContentView(R.layout.share_activity)
6871

69-
// Set back button
70-
supportActionBar?.setDisplayHomeAsUpEnabled(true)
72+
setupStandardToolbar(
73+
title = null,
74+
displayHomeAsUpEnabled = true,
75+
homeButtonEnabled = true,
76+
displayShowTitleEnabled = true,
77+
)
7178
supportActionBar?.setHomeActionContentDescription(R.string.common_back)
79+
7280
supportFragmentManager.transaction {
7381
if (savedInstanceState == null && file != null && account != null) {
7482
// Add Share fragment on first creation
@@ -304,6 +312,18 @@ class ShareActivity : FileActivity(), ShareFragmentListener {
304312
return false
305313
}
306314

315+
override fun onKeyUp(keyCode: Int, event: KeyEvent?): Boolean {
316+
return when (keyCode) {
317+
KeyEvent.KEYCODE_DPAD_DOWN -> {
318+
if (findViewById<View>(R.id.owncloud_app_bar).hasFocus()) {
319+
findViewById<View>(R.id.share_fragment_container).requestFocus()
320+
}
321+
true
322+
}
323+
else -> super.onKeyUp(keyCode, event)
324+
}
325+
}
326+
307327
companion object {
308328
const val TAG_SHARE_FRAGMENT = "SHARE_FRAGMENT"
309329
const val TAG_SEARCH_FRAGMENT = "SEARCH_USER_AND_GROUPS_FRAGMENT"

owncloudApp/src/main/java/com/owncloud/android/presentation/sharing/ShareFileFragment.kt

+3-3
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,9 @@
66
* @author Juan Carlos González Cabrero
77
* @author David González Verdugo
88
* @author Christian Schabesberger
9-
* Copyright (C) 2020 ownCloud GmbH.
9+
* @author Juan Carlos Garrote Gascón
10+
*
11+
* Copyright (C) 2024 ownCloud GmbH.
1012
*
1113
* This program is free software: you can redistribute it and/or modify
1214
* it under the terms of the GNU General Public License version 2,
@@ -273,8 +275,6 @@ class ShareFileFragment : Fragment(), ShareUserListAdapter.ShareUserAdapterListe
273275
super.onActivityCreated(savedInstanceState)
274276
Timber.d("onActivityCreated")
275277

276-
activity?.setTitle(R.string.share_dialog_title)
277-
278278
observeCapabilities() // Get capabilities to update some UI elements depending on them
279279
observeShares()
280280
}

owncloudApp/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.kt

+1
Original file line numberDiff line numberDiff line change
@@ -280,6 +280,7 @@ abstract class DrawerActivity : ToolbarActivity() {
280280
*/
281281
open fun openDrawer() {
282282
getDrawerLayout()?.openDrawer(GravityCompat.START)
283+
findViewById<View>(R.id.nav_view).requestFocus()
283284
}
284285

285286
/**

owncloudApp/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.kt

+13
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ import android.os.Build
4141
import android.os.Bundle
4242
import android.os.RemoteException
4343
import android.util.TypedValue
44+
import android.view.KeyEvent
4445
import android.view.Menu
4546
import android.view.MenuItem
4647
import android.view.View
@@ -1965,6 +1966,18 @@ class FileDisplayActivity : FileActivity(),
19651966
}
19661967
}
19671968

1969+
override fun onKeyUp(keyCode: Int, event: KeyEvent?): Boolean {
1970+
return when (keyCode) {
1971+
KeyEvent.KEYCODE_DPAD_DOWN -> {
1972+
if (findViewById<View>(R.id.owncloud_app_bar).hasFocus()) {
1973+
findViewById<View>(R.id.left_fragment_container).requestFocus()
1974+
}
1975+
true
1976+
}
1977+
else -> super.onKeyUp(keyCode, event)
1978+
}
1979+
}
1980+
19681981
companion object {
19691982
private const val TAG_LIST_OF_FILES = "LIST_OF_FILES"
19701983
private const val TAG_LIST_OF_SPACES = "LIST_OF_SPACES"

owncloudApp/src/main/java/com/owncloud/android/ui/activity/ToolbarActivity.kt

+1
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,7 @@ abstract class ToolbarActivity : BaseActivity() {
159159
searchText.setHintTextColor(getColor(R.color.search_view_hint_text))
160160
closeButton.setColorFilter(getColor(R.color.white))
161161
background = getDrawable(R.drawable.rounded_search_view)
162+
isFocusable = false
162163
}
163164
return true
164165
}

owncloudApp/src/main/java/com/owncloud/android/ui/activity/UploadListActivity.java

+15
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
import android.content.Intent;
3232
import android.net.Uri;
3333
import android.os.Bundle;
34+
import android.view.KeyEvent;
3435
import android.view.Menu;
3536
import android.view.View;
3637

@@ -177,4 +178,18 @@ protected void onAccountSet(boolean stateWasRecovered) {
177178
public boolean onCreateOptionsMenu(Menu menu) {
178179
return false;
179180
}
181+
182+
@Override
183+
public boolean onKeyUp(int keyCode, KeyEvent event) {
184+
if (keyCode == KeyEvent.KEYCODE_DPAD_DOWN) {
185+
if (findViewById(R.id.owncloud_app_bar).hasFocus()) {
186+
boolean nonEmptyView = findViewById(R.id.left_fragment_container).requestFocus();
187+
if (!nonEmptyView) {
188+
findViewById(R.id.bottom_nav_view).requestFocus();
189+
}
190+
return true;
191+
}
192+
}
193+
return super.onKeyUp(keyCode, event);
194+
}
180195
}

owncloudApp/src/main/java/com/owncloud/android/ui/preview/PreviewImageActivity.kt

+13
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
* @author David González Verdugo
66
* @author Christian Schabesberger
77
* @author Aitor Ballesteros Pavón
8+
* @author Juan Carlos Garrote Gascón
89
*
910
* Copyright (C) 2024 ownCloud GmbH.
1011
*
@@ -23,12 +24,14 @@
2324
* You should have received a copy of the GNU General Public License
2425
* along with this program. If not, see <http:></http:>//www.gnu.org/licenses/>.
2526
*/
27+
2628
package com.owncloud.android.ui.preview
2729

2830
import android.content.Intent
2931
import android.os.Bundle
3032
import android.os.Handler
3133
import android.os.Message
34+
import android.view.KeyEvent
3235
import android.view.Menu
3336
import android.view.MenuItem
3437
import android.view.View
@@ -397,6 +400,16 @@ class PreviewImageActivity : FileActivity(),
397400
return false
398401
}
399402

403+
override fun onKeyUp(keyCode: Int, event: KeyEvent?): Boolean {
404+
return when (keyCode) {
405+
KeyEvent.KEYCODE_TAB -> {
406+
showSystemUI(fullScreenAnchorView)
407+
true
408+
}
409+
else -> super.onKeyUp(keyCode, event)
410+
}
411+
}
412+
400413
companion object {
401414
private const val INITIAL_HIDE_DELAY = 0 // immediate hide
402415
}

owncloudApp/src/main/res/layout/owncloud_toolbar.xml

+3-2
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,8 @@
1717
android:id="@+id/owncloud_app_bar"
1818
android:layout_width="match_parent"
1919
android:layout_height="?android:actionBarSize"
20-
android:theme="@style/ownCloud.Appbar">
20+
android:theme="@style/ownCloud.Appbar"
21+
android:focusableInTouchMode="true">
2122

2223
<androidx.constraintlayout.widget.ConstraintLayout
2324
android:id="@+id/root_toolbar"
@@ -96,4 +97,4 @@
9697
android:layout_marginEnd="@dimen/standard_margin"
9798
app:navigationContentDescription="@string/common_back"
9899
app:popupTheme="?attr/actionBarPopupTheme" />
99-
</com.google.android.material.appbar.AppBarLayout>
100+
</com.google.android.material.appbar.AppBarLayout>

owncloudApp/src/main/res/layout/share_activity.xml

+4-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<?xml version="1.0" encoding="utf-8"?><!--
22
ownCloud Android client application
3-
Copyright (C) 2020 ownCloud GmbH.
3+
Copyright (C) 2024 ownCloud GmbH.
44
55
This program is free software: you can redistribute it and/or modify
66
it under the terms of the GNU General Public License version 2,
@@ -23,8 +23,11 @@
2323
android:layout_height="match_parent"
2424
tools:context=".presentation.sharing.ShareActivity"
2525
android:filterTouchesWhenObscured="true"
26+
android:orientation="vertical"
2627
>
2728

29+
<include layout="@layout/owncloud_toolbar" />
30+
2831
<FrameLayout
2932
android:layout_width="match_parent"
3033
android:layout_height="match_parent"

owncloudApp/src/main/res/layout/share_public_dialog.xml

+4-2
Original file line numberDiff line numberDiff line change
@@ -289,7 +289,8 @@
289289
android:paddingTop="@dimen/standard_half_padding"
290290
android:text="@string/share_via_link_expiration_date_label"
291291
android:textColor="@color/black"
292-
android:textSize="15sp" />
292+
android:textSize="15sp"
293+
android:focusable="false"/>
293294

294295
<TextView
295296
android:id="@+id/shareViaLinkExpirationValue"
@@ -303,7 +304,8 @@
303304
android:paddingRight="@dimen/standard_half_padding"
304305
android:textSize="12sp"
305306
android:textColor="@color/list_item_lastmod_and_filesize_text"
306-
android:visibility="gone" />
307+
android:visibility="gone"
308+
android:focusable="false"/>
307309

308310
<TextView
309311
android:id="@+id/shareViaLinkExpirationExplanationLabel"

0 commit comments

Comments
 (0)