mirror of
https://github.com/recloudstream/cloudstream.git
synced 2024-08-15 01:53:11 +00:00
Swipe delete without reloading all of the cards and add some comments and other cleanup
We can use adapter.notifyItemRemoved and remove it from the cardList in adapater directly since we do understand the position in this case. It works better because it does not require reloading everything and causes less bugs with the view model. I am not sure if this was the proper way to do this. If not I can revert the change to this method It also removes the need for a callback in DownloadButtonSetup
This commit is contained in:
parent
312c2a738b
commit
baaf9320e9
4 changed files with 106 additions and 37 deletions
|
@ -1,5 +1,6 @@
|
|||
package com.lagradost.cloudstream3.ui.download
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.DialogInterface
|
||||
import android.widget.Toast
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
|
@ -19,7 +20,7 @@ import com.lagradost.cloudstream3.utils.VideoDownloadHelper
|
|||
import com.lagradost.cloudstream3.utils.VideoDownloadManager
|
||||
|
||||
object DownloadButtonSetup {
|
||||
fun handleDownloadClick(click: DownloadClickEvent, deleteCallback: () -> Unit = {}) {
|
||||
fun handleDownloadClick(click: DownloadClickEvent) {
|
||||
val id = click.data.id
|
||||
if (click.data !is VideoDownloadHelper.DownloadEpisodeCached) return
|
||||
when (click.action) {
|
||||
|
@ -31,7 +32,6 @@ object DownloadButtonSetup {
|
|||
when (which) {
|
||||
DialogInterface.BUTTON_POSITIVE -> {
|
||||
VideoDownloadManager.deleteFileAndUpdateSettings(ctx, id)
|
||||
deleteCallback.invoke()
|
||||
}
|
||||
DialogInterface.BUTTON_NEGATIVE -> {
|
||||
}
|
||||
|
|
|
@ -66,7 +66,7 @@ class DownloadFragment : Fragment() {
|
|||
@SuppressLint("NotifyDataSetChanged")
|
||||
private fun setList(list: List<VisualDownloadHeaderCached>) {
|
||||
main {
|
||||
(binding?.downloadList?.adapter as DownloadHeaderAdapter?)?.cardList = list
|
||||
(binding?.downloadList?.adapter as DownloadHeaderAdapter?)?.cardList = list.toMutableList()
|
||||
binding?.downloadList?.adapter?.notifyDataSetChanged()
|
||||
}
|
||||
}
|
||||
|
@ -147,7 +147,7 @@ class DownloadFragment : Fragment() {
|
|||
when (click.action) {
|
||||
0 -> {
|
||||
if (click.data.type.isMovieType()) {
|
||||
//wont be called
|
||||
// Won't be called
|
||||
} else {
|
||||
val folder = DataStore.getFolderName(
|
||||
DOWNLOAD_EPISODE_CACHE,
|
||||
|
@ -173,13 +173,6 @@ class DownloadFragment : Fragment() {
|
|||
if (downloadClickEvent.data !is VideoDownloadHelper.DownloadEpisodeCached) return@DownloadHeaderAdapter
|
||||
handleDownloadClick(downloadClickEvent)
|
||||
if (downloadClickEvent.action == DOWNLOAD_ACTION_DELETE_FILE) {
|
||||
context?.let { ctx ->
|
||||
downloadsViewModel.updateList(ctx)
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
downloadDeleteEventListener = { id ->
|
||||
val list = (binding?.downloadList?.adapter as DownloadHeaderAdapter?)?.cardList
|
||||
if (list != null) {
|
||||
|
@ -190,8 +183,10 @@ class DownloadFragment : Fragment() {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
downloadDeleteEventListener?.let { VideoDownloadManager.downloadDeleteEvent += it }
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
binding?.downloadList?.apply {
|
||||
setHasFixedSize(true)
|
||||
|
@ -203,10 +198,9 @@ class DownloadFragment : Fragment() {
|
|||
nextUp = FOCUS_SELF,
|
||||
nextDown = FOCUS_SELF
|
||||
)
|
||||
//layoutManager = GridLayoutManager(context, 1)
|
||||
|
||||
val itemTouchHelper = ItemTouchHelper(
|
||||
DownloadSwipeToDeleteCallback(
|
||||
DownloadSwipeDeleteCallback(
|
||||
this.adapter as DownloadHeaderAdapter,
|
||||
context
|
||||
)
|
||||
|
|
|
@ -29,7 +29,7 @@ data class DownloadHeaderClickEvent(
|
|||
)
|
||||
|
||||
class DownloadHeaderAdapter(
|
||||
var cardList: List<VisualDownloadHeaderCached>,
|
||||
var cardList: MutableList<VisualDownloadHeaderCached>,
|
||||
private val clickCallback: (DownloadHeaderClickEvent) -> Unit,
|
||||
private val movieClickCallback: (DownloadClickEvent) -> Unit,
|
||||
) : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
|
||||
|
@ -55,7 +55,7 @@ class DownloadHeaderAdapter(
|
|||
}
|
||||
|
||||
override fun getItemCount(): Int {
|
||||
return cardList.size
|
||||
return cardList.count()
|
||||
}
|
||||
|
||||
class DownloadHeaderViewHolder
|
||||
|
|
|
@ -16,12 +16,15 @@ import com.lagradost.cloudstream3.R
|
|||
import com.lagradost.cloudstream3.isEpisodeBased
|
||||
import com.lagradost.cloudstream3.ui.download.DownloadButtonSetup.handleDownloadClick
|
||||
import com.lagradost.cloudstream3.utils.Coroutines.runOnMainThread
|
||||
import com.lagradost.cloudstream3.utils.VideoDownloadManager.downloadDeleteEvent
|
||||
|
||||
class DownloadSwipeToDeleteCallback(
|
||||
class DownloadSwipeDeleteCallback(
|
||||
private val adapter: DownloadHeaderAdapter,
|
||||
private val context: Context
|
||||
) : ItemTouchHelper.Callback() {
|
||||
|
||||
private var downloadDeleteEventListener: ((Int) -> Unit)? = null
|
||||
|
||||
private val swipeOpenItems: MutableSet<Int> = mutableSetOf()
|
||||
private val deleteIcon: Drawable? by lazy {
|
||||
ContextCompat.getDrawable(context, R.drawable.ic_baseline_delete_outline_24)
|
||||
|
@ -39,7 +42,8 @@ class DownloadSwipeToDeleteCallback(
|
|||
val position = viewHolder.bindingAdapterPosition
|
||||
val item = adapter.cardList[position]
|
||||
return if (item.data.type.isEpisodeBased()) 0 else {
|
||||
makeMovementFlags(0, ItemTouchHelper.LEFT or ItemTouchHelper.RIGHT)
|
||||
val swipeFlags = ItemTouchHelper.LEFT or ItemTouchHelper.RIGHT
|
||||
makeMovementFlags(0, swipeFlags)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -102,7 +106,12 @@ class DownloadSwipeToDeleteCallback(
|
|||
itemView.right.toFloat(),
|
||||
itemView.bottom.toFloat()
|
||||
),
|
||||
floatArrayOf(0f, 0f, 20f, 20f, 20f, 20f, 0f, 0f),
|
||||
floatArrayOf(
|
||||
0f, 0f, // Top-left corner
|
||||
20f, 20f, // Top-right corner
|
||||
20f, 20f, // Bottom-right corner
|
||||
0f, 0f // Bottom-left corner
|
||||
),
|
||||
Path.Direction.CW
|
||||
)
|
||||
}
|
||||
|
@ -127,6 +136,14 @@ class DownloadSwipeToDeleteCallback(
|
|||
}
|
||||
}
|
||||
|
||||
override fun clearView(
|
||||
recyclerView: RecyclerView,
|
||||
viewHolder: RecyclerView.ViewHolder
|
||||
) {
|
||||
clearDownloadDeleteEvent()
|
||||
super.clearView(recyclerView, viewHolder)
|
||||
}
|
||||
|
||||
@SuppressLint("ClickableViewAccessibility")
|
||||
private fun setRecyclerViewTouchListener(
|
||||
recyclerView: RecyclerView,
|
||||
|
@ -136,14 +153,17 @@ class DownloadSwipeToDeleteCallback(
|
|||
if (event.action == MotionEvent.ACTION_UP) {
|
||||
val x = event.x.toInt()
|
||||
val y = event.y.toInt()
|
||||
swipeOpenItems.forEach { pos ->
|
||||
// We use toList() to avoid a very rare edge case
|
||||
// where it gives concurrent modification errors
|
||||
swipeOpenItems.toList().forEach { pos ->
|
||||
val vh = recyclerView.findViewHolderForAdapterPosition(pos)
|
||||
vh?.itemView?.let { swipeItemView ->
|
||||
val backgroundLeft: Int = swipeItemView.right - swipeDistance.toInt()
|
||||
val backgroundXRange: IntRange = backgroundLeft..swipeItemView.right
|
||||
val backgroundYRange: IntRange = swipeItemView.top..swipeItemView.bottom
|
||||
if (x in backgroundXRange && y in backgroundYRange) {
|
||||
handleDelete(pos)
|
||||
handleDeleteAction(pos)
|
||||
addDownloadDeleteEvent(pos, recyclerView)
|
||||
return@setOnTouchListener true
|
||||
}
|
||||
}
|
||||
|
@ -154,11 +174,19 @@ class DownloadSwipeToDeleteCallback(
|
|||
}
|
||||
|
||||
@SuppressLint("ClickableViewAccessibility")
|
||||
private fun removeRecyclerViewTouchListener(
|
||||
recyclerView: RecyclerView
|
||||
): Unit = recyclerView.setOnTouchListener(null)
|
||||
private fun removeRecyclerViewTouchListener(recyclerView: RecyclerView) {
|
||||
// We don't want to unnecessarily listen to unused touch events
|
||||
recyclerView.setOnTouchListener(null)
|
||||
|
||||
private fun handleDelete(position: Int) {
|
||||
/**
|
||||
* If we are not listening to touch events, then
|
||||
* we should clear the delete event as it will
|
||||
* not be used at the moment.
|
||||
*/
|
||||
clearDownloadDeleteEvent()
|
||||
}
|
||||
|
||||
private fun handleDeleteAction(position: Int) {
|
||||
val item = adapter.cardList[position]
|
||||
runOnMainThread {
|
||||
item.child?.let { clickEvent ->
|
||||
|
@ -167,8 +195,55 @@ class DownloadSwipeToDeleteCallback(
|
|||
DOWNLOAD_ACTION_DELETE_FILE,
|
||||
clickEvent
|
||||
)
|
||||
) { adapter.notifyItemRemoved(position) }
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun addDownloadDeleteEvent(
|
||||
position: Int,
|
||||
recyclerView: RecyclerView
|
||||
) {
|
||||
// Clear any old events as we don't want to get
|
||||
// concurrent modification errors
|
||||
clearDownloadDeleteEvent()
|
||||
downloadDeleteEventListener = { id: Int ->
|
||||
val list = adapter.cardList
|
||||
if (list.any { it.data.id == id }) {
|
||||
/**
|
||||
* Seamlessly remove now-deleted item from adapter.
|
||||
* We don't need to reload from the viewModel,
|
||||
* that just causes more unnecessary actions and
|
||||
* unreliable data to be returned oftentimes,
|
||||
* as it would cause it to reload the entire
|
||||
* view model (which is all items) we only want
|
||||
* to target one item and this provides a more seamless
|
||||
* and performant solution to it since we do have access to
|
||||
* the position we need to target here.
|
||||
*/
|
||||
adapter.cardList.removeAt(position)
|
||||
adapter.notifyItemRemoved(position)
|
||||
adapter.notifyItemRangeChanged(
|
||||
position,
|
||||
adapter.cardList.size
|
||||
) // rebind to new positions
|
||||
|
||||
/**
|
||||
* we need to clear the listener now since nothing should be open
|
||||
* and it was closed outside of on onChildDraw so we don't
|
||||
* want to have random unexpected touch events.
|
||||
*/
|
||||
removeRecyclerViewTouchListener(recyclerView)
|
||||
}
|
||||
}
|
||||
|
||||
downloadDeleteEventListener?.let { downloadDeleteEvent += it }
|
||||
}
|
||||
|
||||
private fun clearDownloadDeleteEvent() {
|
||||
if (downloadDeleteEventListener != null) {
|
||||
downloadDeleteEvent -= downloadDeleteEventListener!!
|
||||
downloadDeleteEventListener = null
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue