From d0710596d409855dbbbe482f93b55f5c4d05ba79 Mon Sep 17 00:00:00 2001 From: Luna712 <142361265+Luna712@users.noreply.github.com> Date: Sat, 8 Jun 2024 13:56:32 -0600 Subject: [PATCH] Add swipe to delete downloads and minor performance improvements This could honestly be a horrible way to impliment this (or maybe you don't want it at all) but figured I would give it a try as I often am used to you being able to on other apps so often out of habbit try like this and of course does not work. This also seems to fix an issue where downloaded icon becomes mixed with in progress and downloaded overlapping on scroll if you have inprogress downloads as well as size becoming inaccurate while scrolling (it used the wrong file's size sometimes) which seems to be fixed with `setItemViewCacheSize`. `setHasFixedSize` also improves performance by reducing the need to continue re-requesting layout when it does not need to (testing did not show any issues with it anyway, but not sure if this is the best way to do either of these things. Known issues: * Does not work for series/individual episodes * Delete icon UI needs improvements TODO (for further download improvements; maybe in follow-ups): * Show more information (IE synopsis/descriptions) for downloaded content * Further performance improvements for UI threads etc... If the entire concept of this idea is undesired I suppose we can close it and I will just do some of the other things to improve performance etc... (IE when having a moderate amount of downloads scroll is very laggy which this also seems to at least improve quite a lot. --- .../ui/download/DownloadButtonSetup.kt | 7 +- .../ui/download/DownloadFragment.kt | 97 ++++++++++++++++++- 2 files changed, 102 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/download/DownloadButtonSetup.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/download/DownloadButtonSetup.kt index 10ce67a7..c6f27667 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/download/DownloadButtonSetup.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/download/DownloadButtonSetup.kt @@ -1,6 +1,5 @@ package com.lagradost.cloudstream3.ui.download -import android.app.Activity import android.content.DialogInterface import android.widget.Toast import androidx.appcompat.app.AlertDialog @@ -21,6 +20,10 @@ import com.lagradost.cloudstream3.utils.VideoDownloadManager object DownloadButtonSetup { fun handleDownloadClick(click: DownloadClickEvent) { + handleDownloadClick(click) {} + } + + fun handleDownloadClick(click: DownloadClickEvent, deleteCallback: (Boolean) -> Unit) { val id = click.data.id if (click.data !is VideoDownloadHelper.DownloadEpisodeCached) return when (click.action) { @@ -32,8 +35,10 @@ object DownloadButtonSetup { when (which) { DialogInterface.BUTTON_POSITIVE -> { VideoDownloadManager.deleteFileAndUpdateSettings(ctx, id) + deleteCallback.invoke(true) } DialogInterface.BUTTON_NEGATIVE -> { + deleteCallback.invoke(false) } } } diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/download/DownloadFragment.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/download/DownloadFragment.kt index 31790b0f..f6e7961b 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/download/DownloadFragment.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/download/DownloadFragment.kt @@ -1,8 +1,13 @@ package com.lagradost.cloudstream3.ui.download +import android.annotation.SuppressLint import android.app.Dialog import android.content.ClipboardManager import android.content.Context +import android.graphics.Canvas +import android.graphics.Color +import android.graphics.drawable.ColorDrawable +import android.graphics.drawable.Drawable import android.os.Build import android.os.Bundle import android.text.format.Formatter.formatShortFileSize @@ -12,11 +17,13 @@ import android.view.ViewGroup import android.widget.LinearLayout import android.widget.Toast import androidx.appcompat.app.AppCompatActivity +import androidx.core.content.ContextCompat import androidx.core.view.isGone import androidx.core.view.isVisible import androidx.core.widget.doOnTextChanged import androidx.fragment.app.Fragment import androidx.lifecycle.ViewModelProvider +import androidx.recyclerview.widget.ItemTouchHelper import androidx.recyclerview.widget.RecyclerView import com.lagradost.cloudstream3.CommonActivity.showToast import com.lagradost.cloudstream3.R @@ -44,6 +51,9 @@ import com.lagradost.cloudstream3.utils.UIHelper.navigate import com.lagradost.cloudstream3.utils.UIHelper.setAppBarNoScrollFlagsOnTV import com.lagradost.cloudstream3.utils.VideoDownloadHelper import com.lagradost.cloudstream3.utils.VideoDownloadManager +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch import java.net.URI @@ -61,6 +71,7 @@ class DownloadFragment : Fragment() { this.layoutParams = param } + @SuppressLint("NotifyDataSetChanged") private fun setList(list: List) { main { (binding?.downloadList?.adapter as DownloadHeaderAdapter?)?.cardList = list @@ -182,7 +193,6 @@ class DownloadFragment : Fragment() { if (list != null) { if (list.any { it.data.id == id }) { context?.let { ctx -> - setList(ArrayList()) downloadsViewModel.updateList(ctx) } } @@ -192,6 +202,8 @@ class DownloadFragment : Fragment() { downloadDeleteEventListener?.let { VideoDownloadManager.downloadDeleteEvent += it } binding?.downloadList?.apply { + setHasFixedSize(true) + setItemViewCacheSize(20) this.adapter = adapter setLinearListLayout( isHorizontal = false, @@ -200,6 +212,9 @@ class DownloadFragment : Fragment() { nextDown = FOCUS_SELF ) //layoutManager = GridLayoutManager(context, 1) + + val itemTouchHelper = ItemTouchHelper(SwipeToDeleteCallback(this.adapter as DownloadHeaderAdapter)) + itemTouchHelper.attachToRecyclerView(binding?.downloadList) } // Should be visible in emulator layout @@ -280,4 +295,84 @@ class DownloadFragment : Fragment() { fixPaddingStatusbar(binding?.downloadRoot) } +} + +class SwipeToDeleteCallback(private val adapter: DownloadHeaderAdapter) : + ItemTouchHelper.SimpleCallback(ItemTouchHelper.LEFT, 0) { + + private var isActive = false + + 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.bindingAdapterPosition + val item = adapter.cardList[position] + + CoroutineScope(Dispatchers.Main).launch { + item.child?.let { clickEvent -> + handleDownloadClick( + DownloadClickEvent( + DOWNLOAD_ACTION_DELETE_FILE, + clickEvent + ) + ) { + if (it) adapter.notifyItemRemoved(viewHolder.absoluteAdapterPosition) + isActive = false + } + } + } + } + + override fun onChildDraw( + c: Canvas, + recyclerView: RecyclerView, + viewHolder: RecyclerView.ViewHolder, + dX: Float, + dY: Float, + actionState: Int, + isCurrentlyActive: Boolean + ) { + super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive) + + val deleteIcon: Drawable? = ContextCompat.getDrawable(recyclerView.context, R.drawable.ic_baseline_delete_outline_24) + val background: ColorDrawable = ColorDrawable(Color.RED) + + val itemView = viewHolder.itemView + val backgroundCornerOffset = 20 + + val iconMargin = (itemView.height - deleteIcon?.intrinsicHeight!!) / 2 + val iconTop = itemView.top + (itemView.height - deleteIcon.intrinsicHeight) / 2 + val iconBottom = iconTop + deleteIcon.intrinsicHeight + + if (dX < 0) { // Swiping to the left + val iconLeft = itemView.right - iconMargin - deleteIcon.intrinsicWidth + val iconRight = itemView.right - iconMargin + deleteIcon.setBounds(iconLeft, iconTop, iconRight, iconBottom) + + background.setBounds( + itemView.right + dX.toInt() - backgroundCornerOffset, + itemView.top, + itemView.right, + itemView.bottom + ) + } else background.setBounds(0, 0, 0, 0) + + background.draw(c) + deleteIcon.draw(c) + + val swipeDistance = itemView.width / 3 + + if (dX <= -swipeDistance && !isCurrentlyActive && !isActive) { + // If the item is dragged by at least one-third of its width to the left + // Trigger onSwiped action + onSwiped(viewHolder, ItemTouchHelper.LEFT) + isActive = true + } + } } \ No newline at end of file