diff --git a/CHANGELOG.md b/CHANGELOG.md index 280d41e0ea0..c115af71519 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -34,6 +34,7 @@ ownCloud admins and users. * 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 - Hardware keyboard support: [#4438](https://github.com/owncloud/android/pull/4438) ## Details @@ -55,6 +56,18 @@ ownCloud admins and users. https://github.com/owncloud/android/issues/4365 https://github.com/owncloud/android/pull/4433 +* 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 + logical path, every element is reachable and there are no traps. These + improvements have been applied in main file list, spaces list, drawer menu, + share view and image preview. + + https://github.com/owncloud/android/issues/4366 + https://github.com/owncloud/android/issues/4367 + https://github.com/owncloud/android/issues/4368 + https://github.com/owncloud/android/pull/4438 + # Changelog for ownCloud Android Client [4.3.1] (2024-07-22) The following sections list the changes in ownCloud Android Client 4.3.1 relevant to diff --git a/changelog/unreleased/4438 b/changelog/unreleased/4438 new file mode 100644 index 00000000000..532bd7df6b2 --- /dev/null +++ b/changelog/unreleased/4438 @@ -0,0 +1,10 @@ +Enhancement: Hardware keyboard support + +Navigation via hardware keyboard has been improved so that now focus order has a logical path, every element +is reachable and there are no traps. These improvements have been applied in main file list, spaces list, +drawer menu, share view and image preview. + +https://github.com/owncloud/android/pull/4438 +https://github.com/owncloud/android/issues/4366 +https://github.com/owncloud/android/issues/4367 +https://github.com/owncloud/android/issues/4368 diff --git a/owncloudApp/src/main/AndroidManifest.xml b/owncloudApp/src/main/AndroidManifest.xml index b2669ed6d7e..b52f7fc1f6a 100644 --- a/owncloudApp/src/main/AndroidManifest.xml +++ b/owncloudApp/src/main/AndroidManifest.xml @@ -199,7 +199,7 @@ android:exported="false" android:label="@string/share_dialog_title" android:launchMode="singleTop" - android:theme="@style/Theme.ownCloud" + android:theme="@style/Theme.ownCloud.Toolbar" android:windowSoftInputMode="adjustResize"> 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 d0159e32a26..8c5715a1b71 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 @@ -824,9 +824,16 @@ class MainFileListFragment : Fragment(), } else if (!currentFolder.hasAddSubdirectoriesPermission) { binding.fabMkdir.isVisible = false } + registerFabMainListener() registerFabUploadListener() registerFabMkDirListener() registerFabNewShortcutListener() + binding.apply { + fabUpload.isFocusable = false + fabMkdir.isFocusable = false + fabNewfile.isFocusable = false + fabNewshortcut.isFocusable = false + } } } @@ -844,6 +851,18 @@ class MainFileListFragment : Fragment(), binding.fabMkdir.isVisible = shouldBeShown } + private fun registerFabMainListener() { + binding.apply { + fabMain.findViewById(R.id.fab_expand_menu_button).setOnClickListener { + fabMain.toggle() + fabUpload.isFocusable = isFabExpanded() + fabMkdir.isFocusable = isFabExpanded() + fabNewfile.isFocusable = isFabExpanded() + fabNewshortcut.isFocusable = isFabExpanded() + } + } + } + /** * Registers [android.view.View.OnClickListener] on the 'Upload' mini FAB for the linked action. */ @@ -887,7 +906,14 @@ class MainFileListFragment : Fragment(), } fun collapseFab() { - binding.fabMain.collapse() + binding.apply { + fabMain.collapse() + fabUpload.isFocusable = false + fabMkdir.isFocusable = false + fabNewfile.isFocusable = false + fabNewshortcut.isFocusable = false + } + } fun isFabExpanded() = binding.fabMain.isExpanded @@ -1323,6 +1349,8 @@ class MainFileListFragment : Fragment(), setDrawerStatus(enabled = false) actionMode = mode + requireActivity().findViewById(R.id.owncloud_app_bar).isFocusableInTouchMode = false + val inflater = requireActivity().menuInflater inflater.inflate(R.menu.file_actions_menu, menu) this@MainFileListFragment.menu = menu @@ -1395,6 +1423,8 @@ class MainFileListFragment : Fragment(), setDrawerStatus(enabled = true) actionMode = null + requireActivity().findViewById(R.id.owncloud_app_bar).isFocusableInTouchMode = true + // reset to previous color requireActivity().window.statusBarColor = statusBarColor!! diff --git a/owncloudApp/src/main/java/com/owncloud/android/presentation/sharing/ShareActivity.kt b/owncloudApp/src/main/java/com/owncloud/android/presentation/sharing/ShareActivity.kt index 07c2348b2b3..14ae905174b 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/presentation/sharing/ShareActivity.kt +++ b/owncloudApp/src/main/java/com/owncloud/android/presentation/sharing/ShareActivity.kt @@ -7,6 +7,7 @@ * @author David González Verdugo * @author Christian Schabesberger * @author Aitor Ballesteros Pavón + * @author Juan Carlos Garrote Gascón * * Copyright (C) 2024 ownCloud GmbH. * @@ -29,8 +30,10 @@ package com.owncloud.android.presentation.sharing import android.app.SearchManager import android.content.Intent import android.os.Bundle +import android.view.KeyEvent import android.view.Menu import android.view.MenuItem +import android.view.View import androidx.fragment.app.transaction import com.owncloud.android.R import com.owncloud.android.domain.files.model.OCFile @@ -66,9 +69,14 @@ class ShareActivity : FileActivity(), ShareFragmentListener { setContentView(R.layout.share_activity) - // Set back button - supportActionBar?.setDisplayHomeAsUpEnabled(true) + setupStandardToolbar( + title = null, + displayHomeAsUpEnabled = true, + homeButtonEnabled = true, + displayShowTitleEnabled = true, + ) supportActionBar?.setHomeActionContentDescription(R.string.common_back) + supportFragmentManager.transaction { if (savedInstanceState == null && file != null && account != null) { // Add Share fragment on first creation @@ -304,6 +312,18 @@ class ShareActivity : FileActivity(), ShareFragmentListener { return false } + override fun onKeyUp(keyCode: Int, event: KeyEvent?): Boolean { + return when (keyCode) { + KeyEvent.KEYCODE_DPAD_DOWN -> { + if (findViewById(R.id.owncloud_app_bar).hasFocus()) { + findViewById(R.id.share_fragment_container).requestFocus() + } + true + } + else -> super.onKeyUp(keyCode, event) + } + } + companion object { const val TAG_SHARE_FRAGMENT = "SHARE_FRAGMENT" const val TAG_SEARCH_FRAGMENT = "SEARCH_USER_AND_GROUPS_FRAGMENT" diff --git a/owncloudApp/src/main/java/com/owncloud/android/presentation/sharing/ShareFileFragment.kt b/owncloudApp/src/main/java/com/owncloud/android/presentation/sharing/ShareFileFragment.kt index daae5824c06..1339dc4298d 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/presentation/sharing/ShareFileFragment.kt +++ b/owncloudApp/src/main/java/com/owncloud/android/presentation/sharing/ShareFileFragment.kt @@ -6,7 +6,9 @@ * @author Juan Carlos González Cabrero * @author David González Verdugo * @author Christian Schabesberger - * Copyright (C) 2020 ownCloud GmbH. + * @author Juan Carlos Garrote Gascó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, @@ -273,8 +275,6 @@ class ShareFileFragment : Fragment(), ShareUserListAdapter.ShareUserAdapterListe super.onActivityCreated(savedInstanceState) Timber.d("onActivityCreated") - activity?.setTitle(R.string.share_dialog_title) - observeCapabilities() // Get capabilities to update some UI elements depending on them observeShares() } 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 6db7affc6e8..bfebd6cafa3 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 @@ -280,6 +280,7 @@ abstract class DrawerActivity : ToolbarActivity() { */ open fun openDrawer() { getDrawerLayout()?.openDrawer(GravityCompat.START) + findViewById(R.id.nav_view).requestFocus() } /** 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 8469a3afc77..2ce5a788f0f 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 @@ -41,6 +41,7 @@ import android.os.Build import android.os.Bundle import android.os.RemoteException import android.util.TypedValue +import android.view.KeyEvent import android.view.Menu import android.view.MenuItem import android.view.View @@ -1965,6 +1966,18 @@ class FileDisplayActivity : FileActivity(), } } + override fun onKeyUp(keyCode: Int, event: KeyEvent?): Boolean { + return when (keyCode) { + KeyEvent.KEYCODE_DPAD_DOWN -> { + if (findViewById(R.id.owncloud_app_bar).hasFocus()) { + findViewById(R.id.left_fragment_container).requestFocus() + } + true + } + else -> super.onKeyUp(keyCode, event) + } + } + companion object { private const val TAG_LIST_OF_FILES = "LIST_OF_FILES" private const val TAG_LIST_OF_SPACES = "LIST_OF_SPACES" 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 a1f7961844b..6470329b8fd 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 @@ -159,6 +159,7 @@ abstract class ToolbarActivity : BaseActivity() { searchText.setHintTextColor(getColor(R.color.search_view_hint_text)) closeButton.setColorFilter(getColor(R.color.white)) background = getDrawable(R.drawable.rounded_search_view) + isFocusable = false } return true } diff --git a/owncloudApp/src/main/java/com/owncloud/android/ui/activity/UploadListActivity.java b/owncloudApp/src/main/java/com/owncloud/android/ui/activity/UploadListActivity.java index ff3a96b3cbb..eabf08fec95 100755 --- a/owncloudApp/src/main/java/com/owncloud/android/ui/activity/UploadListActivity.java +++ b/owncloudApp/src/main/java/com/owncloud/android/ui/activity/UploadListActivity.java @@ -31,6 +31,7 @@ import android.content.Intent; import android.net.Uri; import android.os.Bundle; +import android.view.KeyEvent; import android.view.Menu; import android.view.View; @@ -177,4 +178,18 @@ protected void onAccountSet(boolean stateWasRecovered) { public boolean onCreateOptionsMenu(Menu menu) { return false; } + + @Override + public boolean onKeyUp(int keyCode, KeyEvent event) { + if (keyCode == KeyEvent.KEYCODE_DPAD_DOWN) { + if (findViewById(R.id.owncloud_app_bar).hasFocus()) { + boolean nonEmptyView = findViewById(R.id.left_fragment_container).requestFocus(); + if (!nonEmptyView) { + findViewById(R.id.bottom_nav_view).requestFocus(); + } + return true; + } + } + return super.onKeyUp(keyCode, event); + } } diff --git a/owncloudApp/src/main/java/com/owncloud/android/ui/preview/PreviewImageActivity.kt b/owncloudApp/src/main/java/com/owncloud/android/ui/preview/PreviewImageActivity.kt index dc21e3d8f63..aaef496e7d2 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/ui/preview/PreviewImageActivity.kt +++ b/owncloudApp/src/main/java/com/owncloud/android/ui/preview/PreviewImageActivity.kt @@ -5,6 +5,7 @@ * @author David González Verdugo * @author Christian Schabesberger * @author Aitor Ballesteros Pavón + * @author Juan Carlos Garrote Gascón * * Copyright (C) 2024 ownCloud GmbH. * @@ -23,12 +24,14 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see //www.gnu.org/licenses/>. */ + package com.owncloud.android.ui.preview import android.content.Intent import android.os.Bundle import android.os.Handler import android.os.Message +import android.view.KeyEvent import android.view.Menu import android.view.MenuItem import android.view.View @@ -397,6 +400,16 @@ class PreviewImageActivity : FileActivity(), return false } + override fun onKeyUp(keyCode: Int, event: KeyEvent?): Boolean { + return when (keyCode) { + KeyEvent.KEYCODE_TAB -> { + showSystemUI(fullScreenAnchorView) + true + } + else -> super.onKeyUp(keyCode, event) + } + } + companion object { private const val INITIAL_HIDE_DELAY = 0 // immediate hide } diff --git a/owncloudApp/src/main/res/layout/owncloud_toolbar.xml b/owncloudApp/src/main/res/layout/owncloud_toolbar.xml index a32958a439c..1ca157d4c99 100644 --- a/owncloudApp/src/main/res/layout/owncloud_toolbar.xml +++ b/owncloudApp/src/main/res/layout/owncloud_toolbar.xml @@ -17,7 +17,8 @@ android:id="@+id/owncloud_app_bar" android:layout_width="match_parent" android:layout_height="?android:actionBarSize" - android:theme="@style/ownCloud.Appbar"> + android:theme="@style/ownCloud.Appbar" + android:focusableInTouchMode="true"> - \ No newline at end of file + diff --git a/owncloudApp/src/main/res/layout/share_activity.xml b/owncloudApp/src/main/res/layout/share_activity.xml index 425a53fd13e..4dc73ec7bce 100644 --- a/owncloudApp/src/main/res/layout/share_activity.xml +++ b/owncloudApp/src/main/res/layout/share_activity.xml @@ -1,6 +1,6 @@