refactor: simplify swipe/drag listeners for recycler views

This commit is contained in:
Bnyro 2024-10-28 12:15:20 +01:00
parent 3a60a8d75b
commit 576b586075
7 changed files with 86 additions and 149 deletions

View File

@ -0,0 +1,48 @@
package com.github.libretube.extensions
import androidx.recyclerview.widget.ItemTouchHelper
import androidx.recyclerview.widget.RecyclerView
fun RecyclerView.setOnDismissListener(onDismissedListener: (position: Int) -> Unit) {
setActionListener(
swipeDirections = arrayOf(ItemTouchHelper.LEFT),
onDismissedListener = onDismissedListener
)
}
fun RecyclerView.setOnDraggedListener(onDragListener: (from: Int, to: Int) -> Unit) {
setActionListener(
dragDirections = arrayOf(ItemTouchHelper.DOWN, ItemTouchHelper.UP),
onDragListener = onDragListener
)
}
fun RecyclerView.setActionListener(
swipeDirections: Array<Int> = arrayOf(),
dragDirections: Array<Int> = arrayOf(),
onDragListener: (from: Int, to: Int) -> Unit = { _, _ -> },
onDismissedListener: (position: Int) -> Unit = {}
) {
val itemTouchCallback =
object : ItemTouchHelper.SimpleCallback(
dragDirections.fold(0) { a, b -> a or b },
swipeDirections.fold(0) { a, b -> a or b }
) {
override fun onMove(
recyclerView: RecyclerView,
viewHolder: RecyclerView.ViewHolder,
target: RecyclerView.ViewHolder
): Boolean {
if (dragDirections.isEmpty()) return false
onDragListener.invoke(viewHolder.absoluteAdapterPosition, target.absoluteAdapterPosition)
return true
}
override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {
onDismissedListener.invoke(viewHolder.absoluteAdapterPosition)
}
}
ItemTouchHelper(itemTouchCallback).attachToRecyclerView(this)
}

View File

@ -3,11 +3,10 @@ package com.github.libretube.ui.dialogs
import android.app.Dialog
import android.os.Bundle
import androidx.fragment.app.DialogFragment
import androidx.recyclerview.widget.ItemTouchHelper
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.github.libretube.R
import com.github.libretube.databinding.SimpleOptionsRecyclerBinding
import com.github.libretube.extensions.setOnDraggedListener
import com.github.libretube.helpers.NavBarHelper
import com.github.libretube.ui.adapters.NavBarOptionsAdapter
import com.google.android.material.dialog.MaterialAlertDialogBuilder
@ -21,41 +20,15 @@ class NavBarOptionsDialog : DialogFragment() {
NavBarHelper.getStartFragmentId(requireContext())
)
val itemTouchCallback = object : ItemTouchHelper.Callback() {
override fun getMovementFlags(
recyclerView: RecyclerView,
viewHolder: RecyclerView.ViewHolder
): Int {
val dragFlags = ItemTouchHelper.UP or ItemTouchHelper.DOWN
return makeMovementFlags(dragFlags, 0)
}
override fun onMove(
recyclerView: RecyclerView,
viewHolder: RecyclerView.ViewHolder,
target: RecyclerView.ViewHolder
): Boolean {
val itemToMove = adapter.items[viewHolder.absoluteAdapterPosition]
adapter.items.remove(itemToMove)
adapter.items.add(target.absoluteAdapterPosition, itemToMove)
adapter.notifyItemMoved(
viewHolder.absoluteAdapterPosition,
target.absoluteAdapterPosition
)
return true
}
override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {
// do nothing
}
}
binding.optionsRecycler.layoutManager = LinearLayoutManager(context)
binding.optionsRecycler.adapter = adapter
binding.optionsRecycler.setOnDraggedListener { from, to ->
val itemToMove = adapter.items[from]
adapter.items.remove(itemToMove)
adapter.items.add(to, itemToMove)
val itemTouchHelper = ItemTouchHelper(itemTouchCallback)
itemTouchHelper.attachToRecyclerView(binding.optionsRecycler)
adapter.notifyItemMoved(from, to)
}
return MaterialAlertDialogBuilder(requireContext())
.setTitle(R.string.navigation_bar)

View File

@ -17,7 +17,6 @@ import androidx.core.view.isVisible
import androidx.fragment.app.Fragment
import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.ItemTouchHelper
import androidx.recyclerview.widget.RecyclerView
import androidx.viewpager2.adapter.FragmentStateAdapter
import com.github.libretube.R
@ -31,6 +30,7 @@ import com.github.libretube.db.obj.filterByTab
import com.github.libretube.extensions.ceilHalf
import com.github.libretube.extensions.formatAsFileSize
import com.github.libretube.extensions.serializable
import com.github.libretube.extensions.setOnDismissListener
import com.github.libretube.helpers.BackgroundHelper
import com.github.libretube.helpers.DownloadHelper
import com.github.libretube.helpers.NavigationHelper
@ -216,29 +216,11 @@ class DownloadsFragmentPage : DynamicLayoutManagerFragment() {
}
binding.downloadsRecView.adapter = adapter
val itemTouchCallback =
object : ItemTouchHelper.SimpleCallback(0, ItemTouchHelper.LEFT) {
override fun getMovementFlags(
recyclerView: RecyclerView,
viewHolder: RecyclerView.ViewHolder
): Int = makeMovementFlags(0, ItemTouchHelper.LEFT)
override fun onMove(
recyclerView: RecyclerView,
viewHolder: RecyclerView.ViewHolder,
target: RecyclerView.ViewHolder
): Boolean = false
override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {
adapter.showDeleteDialog(
requireContext(),
viewHolder.absoluteAdapterPosition
)
// put the item back to the center, as it's currently out of the screen
adapter.restoreItem(viewHolder.absoluteAdapterPosition)
}
}
ItemTouchHelper(itemTouchCallback).attachToRecyclerView(binding.downloadsRecView)
binding.downloadsRecView.setOnDismissListener { position ->
adapter.showDeleteDialog(requireContext(), position)
// put the item back to the center, as it's currently out of the screen
adapter.restoreItem(position)
}
binding.downloadsRecView.adapter?.registerAdapterDataObserver(
object : RecyclerView.AdapterDataObserver() {

View File

@ -18,7 +18,6 @@ import androidx.fragment.app.activityViewModels
import androidx.lifecycle.lifecycleScope
import androidx.navigation.fragment.navArgs
import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.ItemTouchHelper
import androidx.recyclerview.widget.RecyclerView
import com.github.libretube.R
import com.github.libretube.api.PlaylistsHelper
@ -33,6 +32,7 @@ import com.github.libretube.enums.PlaylistType
import com.github.libretube.extensions.TAG
import com.github.libretube.extensions.ceilHalf
import com.github.libretube.extensions.dpToPx
import com.github.libretube.extensions.setOnDismissListener
import com.github.libretube.extensions.toastFromMainDispatcher
import com.github.libretube.helpers.ImageHelper
import com.github.libretube.helpers.NavigationHelper
@ -290,8 +290,9 @@ class PlaylistFragment : DynamicLayoutManagerFragment() {
if (!isBookmarked) return
withContext(Dispatchers.IO) {
// update the playlist thumbnail and title if bookmarked
val playlistBookmark = DatabaseHolder.Database.playlistBookmarkDao().findById(playlistId)
?: return@withContext
val playlistBookmark =
DatabaseHolder.Database.playlistBookmarkDao().findById(playlistId)
?: return@withContext
if (playlistBookmark.thumbnailUrl != playlist.thumbnailUrl ||
playlistBookmark.playlistName != playlist.name ||
playlistBookmark.videos != playlist.videos
@ -363,28 +364,10 @@ class PlaylistFragment : DynamicLayoutManagerFragment() {
// listener for swiping to the left or right
if (playlistType != PlaylistType.PUBLIC) {
val itemTouchCallback = object : ItemTouchHelper.SimpleCallback(
0,
ItemTouchHelper.LEFT
) {
override fun onMove(
recyclerView: RecyclerView,
viewHolder: RecyclerView.ViewHolder,
target: RecyclerView.ViewHolder
): Boolean {
return false
}
override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {
playlistAdapter!!.removeFromPlaylist(
_binding?.root ?: return,
viewHolder.absoluteAdapterPosition
)
}
binding.playlistRecView.setOnDismissListener { position ->
val rootView = _binding?.root ?: return@setOnDismissListener
playlistAdapter!!.removeFromPlaylist(rootView, position)
}
val itemTouchHelper = ItemTouchHelper(itemTouchCallback)
itemTouchHelper.attachToRecyclerView(binding.playlistRecView)
}
updatePlaylistDuration()

View File

@ -15,7 +15,6 @@ import androidx.core.view.updatePadding
import androidx.fragment.app.activityViewModels
import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.ItemTouchHelper
import androidx.recyclerview.widget.RecyclerView
import androidx.room.withTransaction
import com.github.libretube.R
@ -27,6 +26,7 @@ import com.github.libretube.db.DatabaseHolder.Database
import com.github.libretube.db.obj.WatchHistoryItem
import com.github.libretube.extensions.ceilHalf
import com.github.libretube.extensions.dpToPx
import com.github.libretube.extensions.setOnDismissListener
import com.github.libretube.helpers.NavigationHelper
import com.github.libretube.helpers.PreferenceHelper
import com.github.libretube.helpers.ProxyHelper
@ -198,27 +198,10 @@ class WatchHistoryFragment : DynamicLayoutManagerFragment() {
binding.historyEmpty.isGone = true
binding.historyContainer.isVisible = true
val itemTouchCallback = object : ItemTouchHelper.SimpleCallback(
0,
ItemTouchHelper.LEFT
) {
override fun onMove(
recyclerView: RecyclerView,
viewHolder: RecyclerView.ViewHolder,
target: RecyclerView.ViewHolder
): Boolean {
return false
}
override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {
val position = viewHolder.absoluteAdapterPosition
watchHistoryAdapter.removeFromWatchHistory(position)
}
binding.watchHistoryRecView.setOnDismissListener { position ->
watchHistoryAdapter.removeFromWatchHistory(position)
}
val itemTouchHelper = ItemTouchHelper(itemTouchCallback)
itemTouchHelper.attachToRecyclerView(binding.watchHistoryRecView)
// observe changes to indicate if the history is empty
watchHistoryAdapter.registerAdapterDataObserver(object :
RecyclerView.AdapterDataObserver() {

View File

@ -7,13 +7,12 @@ import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.activityViewModels
import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.ItemTouchHelper
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.github.libretube.databinding.DialogSubscriptionGroupsBinding
import com.github.libretube.db.DatabaseHolder
import com.github.libretube.db.obj.SubscriptionGroup
import com.github.libretube.extensions.move
import com.github.libretube.extensions.setOnDraggedListener
import com.github.libretube.ui.adapters.SubscriptionGroupsAdapter
import com.github.libretube.ui.models.EditChannelGroupsModel
import kotlinx.coroutines.CoroutineScope
@ -56,28 +55,11 @@ class ChannelGroupsSheet : ExpandedBottomSheet() {
dismiss()
}
val callback = object : ItemTouchHelper.SimpleCallback(
ItemTouchHelper.UP or ItemTouchHelper.DOWN,
0
) {
override fun onMove(
recyclerView: RecyclerView,
viewHolder: RecyclerView.ViewHolder,
target: RecyclerView.ViewHolder
): Boolean {
val from = viewHolder.absoluteAdapterPosition
val to = target.absoluteAdapterPosition
adapter.groups.move(from, to)
adapter.notifyItemMoved(from, to)
return true
}
override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {}
binding.groupsRV.setOnDraggedListener { from, to ->
adapter.groups.move(from, to)
adapter.notifyItemMoved(from, to)
}
val itemTouchHelper = ItemTouchHelper(callback)
itemTouchHelper.attachToRecyclerView(binding.groupsRV)
return binding.root
}
}

View File

@ -8,12 +8,12 @@ import android.view.ViewGroup
import androidx.media3.common.Player
import androidx.recyclerview.widget.ItemTouchHelper
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.github.libretube.R
import com.github.libretube.databinding.QueueBottomSheetBinding
import com.github.libretube.db.DatabaseHelper
import com.github.libretube.db.DatabaseHolder
import com.github.libretube.db.obj.WatchPosition
import com.github.libretube.extensions.setActionListener
import com.github.libretube.extensions.toID
import com.github.libretube.ui.adapters.PlayingQueueAdapter
import com.github.libretube.ui.dialogs.AddToPlaylistDialog
@ -84,37 +84,23 @@ class PlayingQueueSheet : ExpandedBottomSheet() {
showWatchPositionsOptions()
}
val callback = object : ItemTouchHelper.SimpleCallback(
ItemTouchHelper.UP or ItemTouchHelper.DOWN,
ItemTouchHelper.LEFT
) {
override fun onMove(
recyclerView: RecyclerView,
viewHolder: RecyclerView.ViewHolder,
target: RecyclerView.ViewHolder
): Boolean {
val from = viewHolder.absoluteAdapterPosition
val to = target.absoluteAdapterPosition
PlayingQueue.move(from, to)
adapter.notifyItemMoved(from, to)
return true
}
override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {
val position = viewHolder.absoluteAdapterPosition
binding.optionsRecycler.setActionListener(
swipeDirections = arrayOf(ItemTouchHelper.LEFT),
dragDirections = arrayOf(ItemTouchHelper.UP, ItemTouchHelper.DOWN),
onDismissedListener = { position ->
if (position == PlayingQueue.currentIndex()) {
adapter.notifyItemChanged(position)
return
return@setActionListener
}
PlayingQueue.remove(position)
adapter.notifyItemRemoved(position)
adapter.notifyItemRangeChanged(position, adapter.itemCount)
},
onDragListener = { from, to ->
PlayingQueue.move(from, to)
adapter.notifyItemMoved(from, to)
}
}
val itemTouchHelper = ItemTouchHelper(callback)
itemTouchHelper.attachToRecyclerView(binding.optionsRecycler)
)
}
private fun updateRepeatButton() {