Skip to content

Commit 5d6c85e

Browse files
committed
fixed keyboard navigation and deletion for list entries
1 parent 895c4fc commit 5d6c85e

4 files changed

Lines changed: 88 additions & 9 deletions

File tree

app/src/main/java/com/teleteh/xplayer2/MainActivity.kt

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@ import android.view.KeyEvent
2424
import android.widget.Button
2525
import android.widget.EditText
2626
import android.view.ViewGroup
27-
import androidx.recyclerview.widget.LinearLayoutManager
2827
import androidx.recyclerview.widget.RecyclerView
2928

3029

@@ -50,6 +49,16 @@ class MainActivity : AppCompatActivity() {
5049
return false
5150
}
5251

52+
private fun findRecyclerItemView(focused: View, recyclerView: RecyclerView): View? {
53+
var current: View? = focused
54+
while (current != null) {
55+
val parent = current.parent
56+
if (parent === recyclerView) return current
57+
current = if (parent is View) parent else null
58+
}
59+
return null
60+
}
61+
5362
private fun shouldReturnToTabsOnUp(fragmentView: View): Boolean {
5463
val focused = currentFocus ?: return false
5564

@@ -65,9 +74,11 @@ class MainActivity : AppCompatActivity() {
6574
val recyclerView: RecyclerView? = fragmentView.findViewById<RecyclerView?>(R.id.rvRecent)
6675
?: fragmentView.findViewById<RecyclerView?>(R.id.rvNetwork)
6776
if (recyclerView != null && isDescendantOf(focused, recyclerView)) {
68-
val lm = recyclerView.layoutManager as? LinearLayoutManager
69-
val atTop = lm?.findFirstVisibleItemPosition()?.let { it <= 0 } == true
70-
if (atTop) return true
77+
val focusedItemView = findRecyclerItemView(focused, recyclerView)
78+
if (focusedItemView == null) return true
79+
80+
val focusedPosition = recyclerView.getChildAdapterPosition(focusedItemView)
81+
if (focusedPosition == 0) return true
7182
}
7283

7384
return false

app/src/main/java/com/teleteh/xplayer2/ui/network/NetworkAdapter.kt

Lines changed: 52 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package com.teleteh.xplayer2.ui.network
22

33
import android.net.Uri
4+
import android.view.KeyEvent
45
import android.view.LayoutInflater
56
import android.view.MotionEvent
67
import android.view.View
@@ -15,7 +16,8 @@ import com.teleteh.xplayer2.R
1516
import com.teleteh.xplayer2.data.network.NetworkItem
1617

1718
class NetworkAdapter(
18-
private val onClick: (NetworkItem) -> Unit
19+
private val onClick: (NetworkItem) -> Unit,
20+
private val onDelete: (NetworkItem.SmbShare) -> Unit = {}
1921
) : ListAdapter<NetworkItem, NetworkAdapter.VH>(DIFF) {
2022

2123
companion object {
@@ -42,25 +44,32 @@ class NetworkAdapter(
4244

4345
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): VH {
4446
val v = LayoutInflater.from(parent.context).inflate(R.layout.item_network, parent, false)
45-
return VH(v, onClick)
47+
return VH(v, onClick, onDelete)
4648
}
4749

4850
override fun onBindViewHolder(holder: VH, position: Int) {
4951
holder.bind(getItem(position))
5052
}
5153

52-
class VH(itemView: View, private val onClick: (NetworkItem) -> Unit) :
54+
class VH(
55+
itemView: View,
56+
private val onClick: (NetworkItem) -> Unit,
57+
private val onDelete: (NetworkItem.SmbShare) -> Unit
58+
) :
5359
RecyclerView.ViewHolder(itemView) {
5460
private val title: TextView = itemView.findViewById(R.id.tvTitle)
5561
private val sub: TextView = itemView.findViewById(R.id.tvSubtitle)
5662
private val iconBg: ImageView = itemView.findViewById(R.id.ivIconBg)
5763
private val icon: ImageView = itemView.findViewById(R.id.ivIcon)
64+
private val deleteButton: View = itemView.findViewById(R.id.btnDelete)
5865
private var current: NetworkItem? = null
5966

6067
init {
68+
itemView.isFocusable = true
69+
itemView.isFocusableInTouchMode = true
6170
itemView.isLongClickable = false
6271
itemView.setOnClickListener { current?.let(onClick) }
63-
72+
6473
// Single-tap activation without double-click requirement
6574
itemView.setOnTouchListener { v, event ->
6675
if (event.action == MotionEvent.ACTION_UP) {
@@ -71,6 +80,40 @@ class NetworkAdapter(
7180
false
7281
}
7382
}
83+
84+
itemView.setOnKeyListener { _, keyCode, event ->
85+
if (event.action == KeyEvent.ACTION_DOWN && keyCode == KeyEvent.KEYCODE_DPAD_RIGHT) {
86+
if (deleteButton.visibility == View.VISIBLE) {
87+
deleteButton.requestFocus()
88+
return@setOnKeyListener true
89+
}
90+
}
91+
false
92+
}
93+
94+
deleteButton.isFocusable = true
95+
deleteButton.isFocusableInTouchMode = true
96+
deleteButton.setOnClickListener {
97+
(current as? NetworkItem.SmbShare)?.let(onDelete)
98+
}
99+
deleteButton.setOnKeyListener { _, keyCode, event ->
100+
if (event.action != KeyEvent.ACTION_DOWN) return@setOnKeyListener false
101+
when (keyCode) {
102+
KeyEvent.KEYCODE_DPAD_LEFT -> {
103+
itemView.requestFocus()
104+
true
105+
}
106+
107+
KeyEvent.KEYCODE_DPAD_CENTER,
108+
KeyEvent.KEYCODE_ENTER,
109+
KeyEvent.KEYCODE_NUMPAD_ENTER -> {
110+
deleteButton.performClick()
111+
true
112+
}
113+
114+
else -> false
115+
}
116+
}
74117
}
75118

76119
fun bind(item: NetworkItem) {
@@ -81,12 +124,14 @@ class NetworkAdapter(
81124
sub.text = formatSmbSubtitle(item.uri)
82125
iconBg.setImageResource(R.drawable.bg_circle_smb)
83126
icon.setImageResource(R.drawable.ic_smb_24)
127+
deleteButton.visibility = View.VISIBLE
84128
}
85129

86130
is NetworkItem.DlnaDevice -> {
87131
title.text = item.friendlyName.ifBlank { "DLNA Device" }
88132
sub.text = formatHttpSubtitle(item.location)
89133
iconBg.setImageResource(R.drawable.bg_circle_dlna)
134+
deleteButton.visibility = View.GONE
90135
val iurl = item.iconUrl
91136
if (!iurl.isNullOrBlank()) {
92137
icon.load(iurl) {
@@ -104,19 +149,22 @@ class NetworkAdapter(
104149
sub.text = "Вверх"
105150
iconBg.setImageResource(R.drawable.bg_circle_dlna)
106151
icon.setImageResource(R.drawable.ic_folder_24)
152+
deleteButton.visibility = View.GONE
107153
}
108154

109155
is NetworkItem.DlnaContainer -> {
110156
title.text = item.title
111157
sub.text = "Папка"
112158
iconBg.setImageResource(R.drawable.bg_circle_dlna)
113159
icon.setImageResource(R.drawable.ic_folder_24)
160+
deleteButton.visibility = View.GONE
114161
}
115162

116163
is NetworkItem.DlnaMedia -> {
117164
title.text = item.title
118165
sub.text = item.mime ?: item.url
119166
iconBg.setImageResource(R.drawable.bg_circle_dlna)
167+
deleteButton.visibility = View.GONE
120168
val mime = item.mime ?: ""
121169
icon.setImageResource(
122170
if (mime.startsWith("audio")) R.drawable.ic_audio_24 else R.drawable.ic_video_24

app/src/main/java/com/teleteh/xplayer2/ui/network/NetworkFragment.kt

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,13 @@ class NetworkFragment : Fragment(R.layout.fragment_network) {
5353
rv = view.findViewById(R.id.rvNetwork)
5454
val fab: FloatingActionButton = view.findViewById(R.id.fabAddShare)
5555

56-
adapter = NetworkAdapter(onClick = { item -> onItemClick(item) })
56+
adapter = NetworkAdapter(
57+
onClick = { item -> onItemClick(item) },
58+
onDelete = { share ->
59+
smbStorage.remove(share.name)
60+
reloadShares()
61+
}
62+
)
5763
rv.layoutManager = LinearLayoutManager(requireContext())
5864
rv.adapter = adapter
5965

app/src/main/res/layout/item_network.xml

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,4 +63,18 @@
6363
android:text="Subtitle" />
6464
</LinearLayout>
6565

66+
<ImageButton
67+
android:id="@+id/btnDelete"
68+
android:layout_width="36dp"
69+
android:layout_height="36dp"
70+
android:layout_marginStart="8dp"
71+
android:background="?android:attr/selectableItemBackgroundBorderless"
72+
android:contentDescription="@string/delete"
73+
android:focusable="true"
74+
android:focusableInTouchMode="true"
75+
android:clickable="true"
76+
android:nextFocusLeft="@id/tvTitle"
77+
android:src="@android:drawable/ic_menu_delete"
78+
android:visibility="gone" />
79+
6680
</LinearLayout>

0 commit comments

Comments
 (0)