Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Downloads: performance improvements and merge adapters #1145

Merged
merged 27 commits into from
Jun 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
d071059
Add swipe to delete downloads and minor performance improvements
Luna712 Jun 8, 2024
406c98f
Cleanup, fix bugs, and improve functionality and style
Luna712 Jun 9, 2024
b8a696b
Use runOnMainThread and cleanup
Luna712 Jun 10, 2024
81ae8b6
Improve UI/UX and use actually intended swipe not drag
Luna712 Jun 13, 2024
b3af98c
Use ACTION_UP
Luna712 Jun 13, 2024
0ceb273
Fix for multiple open items at once
Luna712 Jun 13, 2024
34c5c9f
Remove spaces for consistency
Luna712 Jun 14, 2024
3ceabb4
Minor performance optimization and split to new class file and cleanup
Luna712 Jun 14, 2024
312c2a7
Just use handleDownloadClick
Luna712 Jun 14, 2024
baaf932
Swipe delete without reloading all of the cards and add some comments…
Luna712 Jun 15, 2024
5cfd98e
Only on phone and emulator and a few fixes
Luna712 Jun 17, 2024
d4631bc
Merge adapters and support swipe to delete for episodes
Luna712 Jun 19, 2024
431a7e5
Fix
Luna712 Jun 19, 2024
fa14b5e
Remove swipe to delete
Luna712 Jun 19, 2024
aba5ea9
Use List
Luna712 Jun 19, 2024
452bdf6
Fix
Luna712 Jun 19, 2024
a386521
Cleanup and performance improvements
Luna712 Jun 20, 2024
ce8e0f9
Fix CodeFactor
Luna712 Jun 20, 2024
f3d775e
Fix
Luna712 Jun 20, 2024
6588f2c
Be extra careful and handle null in getItemViewType and other minor c…
Luna712 Jun 20, 2024
45ac920
Fix displaying go to child icon
Luna712 Jun 20, 2024
32d113f
Remove unneeded commented code from adapter
Luna712 Jun 20, 2024
ea0531f
Use isVisible instead of View
Luna712 Jun 20, 2024
2122677
Rewrite to use DiffUtil and massive cleanup
Luna712 Jun 21, 2024
bd9c884
Override hashCode
Luna712 Jun 21, 2024
43121e5
Fix updateStorageInfo messages
Luna712 Jun 21, 2024
b09dca9
Use @StringRes
Luna712 Jun 21, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,223 @@
package com.lagradost.cloudstream3.ui.download

import android.annotation.SuppressLint
import android.text.format.Formatter.formatShortFileSize
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.core.view.isVisible
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.ListAdapter
import androidx.recyclerview.widget.RecyclerView
import androidx.viewbinding.ViewBinding
import com.lagradost.cloudstream3.R
import com.lagradost.cloudstream3.databinding.DownloadChildEpisodeBinding
import com.lagradost.cloudstream3.databinding.DownloadHeaderEpisodeBinding
import com.lagradost.cloudstream3.mvvm.logError
import com.lagradost.cloudstream3.utils.AppUtils.getNameFull
import com.lagradost.cloudstream3.utils.DataStoreHelper
import com.lagradost.cloudstream3.utils.DataStoreHelper.fixVisual
import com.lagradost.cloudstream3.utils.UIHelper.setImage
import com.lagradost.cloudstream3.utils.VideoDownloadHelper

const val DOWNLOAD_ACTION_PLAY_FILE = 0
const val DOWNLOAD_ACTION_DELETE_FILE = 1
const val DOWNLOAD_ACTION_RESUME_DOWNLOAD = 2
const val DOWNLOAD_ACTION_PAUSE_DOWNLOAD = 3
const val DOWNLOAD_ACTION_DOWNLOAD = 4
const val DOWNLOAD_ACTION_LONG_CLICK = 5

abstract class VisualDownloadCached(
open val currentBytes: Long,
open val totalBytes: Long,
open val data: VideoDownloadHelper.DownloadCached
) {

// Just to be extra-safe with areContentsTheSame
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other !is VisualDownloadCached) return false

if (currentBytes != other.currentBytes) return false
if (totalBytes != other.totalBytes) return false
if (data != other.data) return false

return true
}

override fun hashCode(): Int {
var result = currentBytes.hashCode()
result = 31 * result + totalBytes.hashCode()
result = 31 * result + data.hashCode()
return result
}
}

data class VisualDownloadChildCached(
override val currentBytes: Long,
override val totalBytes: Long,
override val data: VideoDownloadHelper.DownloadEpisodeCached,
): VisualDownloadCached(currentBytes, totalBytes, data)

data class VisualDownloadHeaderCached(
override val currentBytes: Long,
override val totalBytes: Long,
override val data: VideoDownloadHelper.DownloadHeaderCached,
val child: VideoDownloadHelper.DownloadEpisodeCached?,
val currentOngoingDownloads: Int,
val totalDownloads: Int,
): VisualDownloadCached(currentBytes, totalBytes, data)

data class DownloadClickEvent(
val action: Int,
val data: VideoDownloadHelper.DownloadEpisodeCached
)

data class DownloadHeaderClickEvent(
val action: Int,
val data: VideoDownloadHelper.DownloadHeaderCached
)

class DownloadAdapter(
private val clickCallback: (DownloadHeaderClickEvent) -> Unit,
private val mediaClickCallback: (DownloadClickEvent) -> Unit,
) : ListAdapter<VisualDownloadCached, DownloadAdapter.DownloadViewHolder>(DiffCallback()) {

companion object {
private const val VIEW_TYPE_HEADER = 0
private const val VIEW_TYPE_CHILD = 1
}

inner class DownloadViewHolder(
private val binding: ViewBinding,
private val clickCallback: (DownloadHeaderClickEvent) -> Unit,
private val mediaClickCallback: (DownloadClickEvent) -> Unit,
) : RecyclerView.ViewHolder(binding.root) {

@SuppressLint("SetTextI18n")
fun bind(card: VisualDownloadCached?) {
when (binding) {
is DownloadHeaderEpisodeBinding -> binding.apply {
if (card == null || card !is VisualDownloadHeaderCached) return@apply
val d = card.data

downloadHeaderPoster.apply {
setImage(d.poster)
setOnClickListener {
clickCallback.invoke(DownloadHeaderClickEvent(1, d))
}
}

downloadHeaderTitle.text = d.name
val mbString = formatShortFileSize(itemView.context, card.totalBytes)

if (card.child != null) {
downloadHeaderGotoChild.isVisible = false

downloadButton.setDefaultClickListener(card.child, downloadHeaderInfo, mediaClickCallback)
downloadButton.isVisible = true

episodeHolder.setOnClickListener {
mediaClickCallback.invoke(
DownloadClickEvent(
DOWNLOAD_ACTION_PLAY_FILE,
card.child
)
)
}
} else {
downloadButton.isVisible = false
downloadHeaderGotoChild.isVisible = true

try {
downloadHeaderInfo.text =
downloadHeaderInfo.context.getString(R.string.extra_info_format)
.format(
card.totalDownloads,
if (card.totalDownloads == 1) downloadHeaderInfo.context.getString(
R.string.episode
) else downloadHeaderInfo.context.getString(
R.string.episodes
),
mbString
)
} catch (t: Throwable) {
// You probably formatted incorrectly
downloadHeaderInfo.text = "Error"
logError(t)
}

episodeHolder.setOnClickListener {
clickCallback.invoke(DownloadHeaderClickEvent(0, d))
}
}
}

is DownloadChildEpisodeBinding -> binding.apply {
if (card == null || card !is VisualDownloadChildCached) return@apply
val d = card.data

val posDur = DataStoreHelper.getViewPos(d.id)
downloadChildEpisodeProgress.apply {
if (posDur != null) {
val visualPos = posDur.fixVisual()
max = (visualPos.duration / 1000).toInt()
progress = (visualPos.position / 1000).toInt()
isVisible = true
} else isVisible = false
}

downloadButton.setDefaultClickListener(card.data, downloadChildEpisodeTextExtra, mediaClickCallback)

downloadChildEpisodeText.apply {
text = context.getNameFull(d.name, d.episode, d.season)
isSelected = true // Needed for text repeating
}

downloadChildEpisodeHolder.setOnClickListener {
mediaClickCallback.invoke(DownloadClickEvent(DOWNLOAD_ACTION_PLAY_FILE, d))
}
}
}
}
}

override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): DownloadViewHolder {
val binding = when (viewType) {
VIEW_TYPE_HEADER -> {
DownloadHeaderEpisodeBinding.inflate(
LayoutInflater.from(parent.context),
parent,
false
)
}
VIEW_TYPE_CHILD -> {
DownloadChildEpisodeBinding.inflate(
LayoutInflater.from(parent.context),
parent,
false
)
}
else -> throw IllegalArgumentException("Invalid view type")
}
return DownloadViewHolder(binding, clickCallback, mediaClickCallback)
}

override fun onBindViewHolder(holder: DownloadViewHolder, position: Int) {
holder.bind(getItem(position))
}

override fun getItemViewType(position: Int): Int {
val card = getItem(position)
return if (card is VisualDownloadChildCached) VIEW_TYPE_CHILD else VIEW_TYPE_HEADER
}

class DiffCallback : DiffUtil.ItemCallback<VisualDownloadCached>() {
override fun areItemsTheSame(oldItem: VisualDownloadCached, newItem: VisualDownloadCached): Boolean {
return oldItem.data.id == newItem.data.id
}

override fun areContentsTheSame(oldItem: VisualDownloadCached, newItem: VisualDownloadCached): Boolean {
return oldItem == newItem
}
}
}
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -22,7 +21,6 @@ import com.lagradost.cloudstream3.utils.VideoDownloadManager
object DownloadButtonSetup {
fun handleDownloadClick(click: DownloadClickEvent) {
val id = click.data.id
if (click.data !is VideoDownloadHelper.DownloadEpisodeCached) return
when (click.action) {
DOWNLOAD_ACTION_DELETE_FILE -> {
activity?.let { ctx ->
Expand Down

This file was deleted.

Loading