diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 5e6f58a..a21f609 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -3,6 +3,8 @@ + + diff --git a/app/src/main/java/ru/nsu/bobrofon/easysshfs/mountpointlist/MountpointFragment.kt b/app/src/main/java/ru/nsu/bobrofon/easysshfs/mountpointlist/MountpointFragment.kt index 496ac75..69c7820 100644 --- a/app/src/main/java/ru/nsu/bobrofon/easysshfs/mountpointlist/MountpointFragment.kt +++ b/app/src/main/java/ru/nsu/bobrofon/easysshfs/mountpointlist/MountpointFragment.kt @@ -2,6 +2,7 @@ package ru.nsu.bobrofon.easysshfs.mountpointlist import android.os.Bundle +import android.view.KeyEvent import android.view.LayoutInflater import android.view.Menu import android.view.MenuInflater @@ -10,6 +11,7 @@ import android.view.View import android.view.ViewGroup import android.widget.AbsListView import android.widget.AdapterView +import android.widget.Button import android.widget.ListAdapter import androidx.core.view.MenuHost import androidx.core.view.MenuProvider @@ -28,6 +30,96 @@ class MountpointFragment : EasySSHFSFragment(), AdapterView.OnItemClickListener, private lateinit var listAdapter: ListAdapter private lateinit var mountPointsList: MountPointsList + /** + * buttonOnKeyListener is used when a mount button is focused, to implement custom D-pad + * navigation between the button and an enclosing ListView's items. + * + * The listener expects the ListView item to look like this: + * |----------------------| + * | +--------+| + * | Item view | Button || + * | +--------+| + * |----------------------| + */ + private val buttonOnKeyListener = View.OnKeyListener { _, keycode, event -> + if (event.action != KeyEvent.ACTION_DOWN) { + return@OnKeyListener false + } + + val dpadNavigationKeys = arrayOf( + KeyEvent.KEYCODE_DPAD_RIGHT, + KeyEvent.KEYCODE_DPAD_LEFT, + KeyEvent.KEYCODE_DPAD_UP, + KeyEvent.KEYCODE_DPAD_DOWN + ) + if (!dpadNavigationKeys.contains(keycode)) { + return@OnKeyListener false + } + + // We are leaving the button, so we need to pass the focus back. + listView.descendantFocusability = ViewGroup.FOCUS_BLOCK_DESCENDANTS + listView.requestFocus() + + // The button is on the right side of the ListView item. + // Moving left means "go back to the item containing this button". + if (keycode == KeyEvent.KEYCODE_DPAD_LEFT) { + // Focus is already passed to the original ListView item. + true + } else { + // Allow ListView to select the next item. Otherwise, the currently selected item will + // remain selected after moving out of the button. + listView.onKeyDown(keycode, event) + } + } + + /** + * buttonOnFocusChangeListener implements custom focus logic for buttons inside a ListView. + */ + private val buttonOnFocusChangeListener = View.OnFocusChangeListener { buttonView, hasFocus -> + if (hasFocus) { + return@OnFocusChangeListener + } + + // We should keep ButtonView unfocusable to allow ListView/GridView to handle onItemClick + // properly. + buttonView.isFocusable = false + + // Restore the original state in case the button lost the focus not via buttonOnKeyListener. + listView.descendantFocusability = ViewGroup.FOCUS_BLOCK_DESCENDANTS + } + + /** + * listViewOnKeyListener is used when a mount button is not focused, to implement custom D-pad + * navigation between the selected ListView item and the button inside it. + * + * The listener expects the ListView item to look like this: + * |----------------------| + * | +--------+| + * | Item view | Button || + * | +--------+| + * |----------------------| + */ + private val listViewOnKeyListener = View.OnKeyListener { _, keycode, event -> + val selectedItemView = listView.selectedView ?: return@OnKeyListener false + + if (event.action != KeyEvent.ACTION_DOWN) { + return@OnKeyListener false + } + if (keycode != KeyEvent.KEYCODE_DPAD_RIGHT) { + return@OnKeyListener false + } + // The button is on the right side of the item view. + // Moving right means "moving to the button". + + listView.descendantFocusability = ViewGroup.FOCUS_AFTER_DESCENDANTS + + val button = selectedItemView.findViewById