3
3
Fork 1
mirror of https://github.com/recloudstream/cloudstream.git synced 2024-08-15 01:53:11 +00:00

Simplification of VisualDownloadCached handling and fix a bug

This commit is contained in:
Luna712 2024-07-08 17:44:54 -06:00 committed by GitHub
parent 595588aeca
commit 5ed9eee407
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 64 additions and 99 deletions
app/src/main/java/com/lagradost/cloudstream3/ui/download

View file

@ -32,47 +32,27 @@ const val DOWNLOAD_ACTION_LONG_CLICK = 5
const val DOWNLOAD_ACTION_GO_TO_CHILD = 0
const val DOWNLOAD_ACTION_LOAD_RESULT = 1
abstract class VisualDownloadCached(
open val currentBytes: Long,
open val totalBytes: Long,
open val data: VideoDownloadHelper.DownloadCached
) {
sealed class VisualDownloadCached {
abstract val currentBytes: Long
abstract val totalBytes: Long
abstract 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
data class Child(
override val currentBytes: Long,
override val totalBytes: Long,
override val data: VideoDownloadHelper.DownloadEpisodeCached,
) : VisualDownloadCached()
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 Header(
override val currentBytes: Long,
override val totalBytes: Long,
override val data: VideoDownloadHelper.DownloadHeaderCached,
val child: VideoDownloadHelper.DownloadEpisodeCached?,
val currentOngoingDownloads: Int,
val totalDownloads: Int
) : VisualDownloadCached()
}
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
@ -83,16 +63,11 @@ data class DownloadHeaderClickEvent(
val data: VideoDownloadHelper.DownloadHeaderCached
)
sealed class VisualDownloadItem {
data class Header(val header: VisualDownloadHeaderCached) : VisualDownloadItem()
data class Child(val child: VisualDownloadChildCached) : VisualDownloadItem()
}
class DownloadAdapter(
private val headerClickCallback: (DownloadHeaderClickEvent) -> Unit,
private val mediaClickCallback: (DownloadClickEvent) -> Unit,
private val selectedChangedCallback: (VisualDownloadItem, Boolean) -> Unit,
private val multiDeleteStateCallback: (VisualDownloadItem) -> Unit,
private val selectedChangedCallback: (VisualDownloadCached, Boolean) -> Unit,
private val multiDeleteStateCallback: (VisualDownloadCached) -> Unit,
) : ListAdapter<VisualDownloadCached, DownloadAdapter.DownloadViewHolder>(DiffCallback()) {
private var isMultiDeleteState: Boolean = false
@ -109,12 +84,12 @@ class DownloadAdapter(
fun bind(card: VisualDownloadCached?) {
when (binding) {
is DownloadHeaderEpisodeBinding -> bindHeader(card as? VisualDownloadHeaderCached)
is DownloadChildEpisodeBinding -> bindChild(card as? VisualDownloadChildCached)
is DownloadHeaderEpisodeBinding -> bindHeader(card as? VisualDownloadCached.Header)
is DownloadChildEpisodeBinding -> bindChild(card as? VisualDownloadCached.Child)
}
}
private fun bindHeader(card: VisualDownloadHeaderCached?) {
private fun bindHeader(card: VisualDownloadCached.Header?) {
if (binding !is DownloadHeaderEpisodeBinding || card == null) return
val data = card.data
@ -123,7 +98,7 @@ class DownloadAdapter(
setImage(data.poster)
if (isMultiDeleteState) {
setOnClickListener {
toggleIsChecked(deleteCheckbox, VisualDownloadItem.Header(card))
toggleIsChecked(deleteCheckbox, card)
}
} else {
setOnClickListener {
@ -132,7 +107,7 @@ class DownloadAdapter(
}
setOnLongClickListener {
multiDeleteStateCallback.invoke(VisualDownloadItem.Header(card))
multiDeleteStateCallback.invoke(card)
true
}
}
@ -146,7 +121,7 @@ class DownloadAdapter(
if (isMultiDeleteState) {
deleteCheckbox.setOnCheckedChangeListener { _, isChecked ->
selectedIds[data.id] = isChecked
selectedChangedCallback.invoke(VisualDownloadItem.Header(card), isChecked)
selectedChangedCallback.invoke(card, isChecked)
}
} else deleteCheckbox.setOnCheckedChangeListener(null)
@ -158,7 +133,7 @@ class DownloadAdapter(
}
private fun DownloadHeaderEpisodeBinding.handleChildDownload(
card: VisualDownloadHeaderCached,
card: VisualDownloadCached.Header,
formattedSize: String
) {
card.child ?: return
@ -188,14 +163,14 @@ class DownloadAdapter(
downloadButton.isVisible = !isMultiDeleteState
downloadButton.setOnLongClickListener {
multiDeleteStateCallback.invoke(VisualDownloadItem.Header(card))
multiDeleteStateCallback.invoke(card)
true
}
episodeHolder.apply {
if (isMultiDeleteState) {
setOnClickListener {
toggleIsChecked(deleteCheckbox, VisualDownloadItem.Header(card))
toggleIsChecked(deleteCheckbox, card)
}
} else {
setOnClickListener {
@ -204,7 +179,7 @@ class DownloadAdapter(
}
setOnLongClickListener {
multiDeleteStateCallback.invoke(VisualDownloadItem.Header(card))
multiDeleteStateCallback.invoke(card)
true
}
}
@ -212,7 +187,7 @@ class DownloadAdapter(
@SuppressLint("SetTextI18n")
private fun DownloadHeaderEpisodeBinding.handleParentDownload(
card: VisualDownloadHeaderCached,
card: VisualDownloadCached.Header,
formattedSize: String
) {
downloadButton.isVisible = false
@ -232,7 +207,7 @@ class DownloadAdapter(
episodeHolder.apply {
if (isMultiDeleteState) {
setOnClickListener {
toggleIsChecked(deleteCheckbox, VisualDownloadItem.Header(card))
toggleIsChecked(deleteCheckbox, card)
}
} else {
setOnClickListener {
@ -241,13 +216,13 @@ class DownloadAdapter(
}
setOnLongClickListener {
multiDeleteStateCallback.invoke(VisualDownloadItem.Header(card))
multiDeleteStateCallback.invoke(card)
true
}
}
}
private fun bindChild(card: VisualDownloadChildCached?) {
private fun bindChild(card: VisualDownloadCached.Child?) {
if (binding !is DownloadChildEpisodeBinding || card == null) return
val data = card.data
@ -286,7 +261,7 @@ class DownloadAdapter(
downloadButton.isVisible = !isMultiDeleteState
downloadButton.setOnLongClickListener {
multiDeleteStateCallback.invoke(VisualDownloadItem.Child(card))
multiDeleteStateCallback.invoke(card)
true
}
@ -302,7 +277,7 @@ class DownloadAdapter(
downloadChildEpisodeHolder.apply {
if (isMultiDeleteState) {
setOnClickListener {
toggleIsChecked(deleteCheckbox, VisualDownloadItem.Child(card))
toggleIsChecked(deleteCheckbox, card)
}
} else {
setOnClickListener {
@ -311,7 +286,7 @@ class DownloadAdapter(
}
setOnLongClickListener {
multiDeleteStateCallback.invoke(VisualDownloadItem.Child(card))
multiDeleteStateCallback.invoke(card)
true
}
}
@ -319,7 +294,7 @@ class DownloadAdapter(
if (isMultiDeleteState) {
deleteCheckbox.setOnCheckedChangeListener { _, isChecked ->
selectedIds[data.id] = isChecked
selectedChangedCallback.invoke(VisualDownloadItem.Child(card), isChecked)
selectedChangedCallback.invoke(card, isChecked)
}
} else deleteCheckbox.setOnCheckedChangeListener(null)
@ -347,8 +322,8 @@ class DownloadAdapter(
override fun getItemViewType(position: Int): Int {
return when (getItem(position)) {
is VisualDownloadChildCached -> VIEW_TYPE_CHILD
is VisualDownloadHeaderCached -> VIEW_TYPE_HEADER
is VisualDownloadCached.Child -> VIEW_TYPE_CHILD
is VisualDownloadCached.Header -> VIEW_TYPE_HEADER
else -> throw IllegalArgumentException("Invalid data type at position $position")
}
}
@ -383,13 +358,10 @@ class DownloadAdapter(
}
}
private fun toggleIsChecked(checkbox: CheckBox, item: VisualDownloadItem) {
private fun toggleIsChecked(checkbox: CheckBox, item: VisualDownloadCached) {
val isChecked = !checkbox.isChecked
checkbox.isChecked = isChecked
when (item) {
is VisualDownloadItem.Header -> selectedIds[item.header.data.id] = isChecked
is VisualDownloadItem.Child -> selectedIds[item.child.data.id] = isChecked
}
selectedIds[item.data.id] = isChecked
selectedChangedCallback.invoke(item, isChecked)
}

View file

@ -67,7 +67,7 @@ class DownloadChildFragment : Fragment() {
}.mapNotNull {
val info = VideoDownloadManager.getDownloadFileInfoAndUpdateSettings(ctx, it.id)
?: return@mapNotNull null
VisualDownloadChildCached(
VisualDownloadCached.Child(
currentBytes = info.fileLength,
totalBytes = info.totalBytes,
data = it,
@ -131,10 +131,9 @@ class DownloadChildFragment : Fragment() {
} else downloadsViewModel.removeSelected(card)
},
{ card ->
if (card !is VisualDownloadItem.Child) return@DownloadAdapter
downloadsViewModel.addSelected(card)
(binding?.downloadChildList?.adapter as? DownloadAdapter)?.updateSelectedItem(
card.child.data.id,
card.data.id,
true
)
}
@ -154,7 +153,7 @@ class DownloadChildFragment : Fragment() {
updateList(folder)
}
private fun handleSelectedChange(selected: MutableList<VisualDownloadItem>) {
private fun handleSelectedChange(selected: MutableList<VisualDownloadCached>) {
val adapter = binding?.downloadChildList?.adapter as? DownloadAdapter
if (selected.isNotEmpty()) {
binding?.downloadDeleteAppbar?.isVisible = true

View file

@ -130,10 +130,9 @@ class DownloadFragment : Fragment() {
} else downloadsViewModel.removeSelected(card)
},
{ card ->
if (card !is VisualDownloadItem.Header) return@DownloadAdapter
downloadsViewModel.addSelected(card)
(binding?.downloadList?.adapter as? DownloadAdapter)?.updateSelectedItem(
card.header.data.id,
card.data.id,
true
)
}
@ -188,7 +187,7 @@ class DownloadFragment : Fragment() {
}
}
private fun handleSelectedChange(selected: MutableList<VisualDownloadItem>) {
private fun handleSelectedChange(selected: MutableList<VisualDownloadCached>) {
val adapter = binding?.downloadList?.adapter as? DownloadAdapter
if (selected.isNotEmpty()) {
binding?.downloadDeleteAppbar?.isVisible = true
@ -211,10 +210,10 @@ class DownloadFragment : Fragment() {
adapter?.setIsMultiDeleteState(true)
} else {
binding?.downloadDeleteAppbar?.isVisible = false
binding?.downloadStorageAppbar?.isVisible =
// Make sure we don't display it early
!downloadsViewModel.headerCards.value.isNullOrEmpty() &&
downloadsViewModel.usedBytes.value?.let { it > 0 } == true
// Make sure we don't display it early
if (downloadsViewModel.usedBytes.value?.let { it > 0 } == true) {
binding?.downloadStorageAppbar?.isVisible = true
}
adapter?.setIsMultiDeleteState(false)
downloadsViewModel.clearSelectedItems()

View file

@ -27,24 +27,24 @@ import kotlinx.coroutines.withContext
class DownloadViewModel : ViewModel() {
private val _headerCards =
MutableLiveData<List<VisualDownloadHeaderCached>>().apply { listOf<VisualDownloadHeaderCached>() }
val headerCards: LiveData<List<VisualDownloadHeaderCached>> = _headerCards
MutableLiveData<List<VisualDownloadCached.Header>>().apply { listOf<VisualDownloadCached.Header>() }
val headerCards: LiveData<List<VisualDownloadCached.Header>> = _headerCards
private val _usedBytes = MutableLiveData<Long>()
private val _availableBytes = MutableLiveData<Long>()
private val _downloadBytes = MutableLiveData<Long>()
private val _selectedItems = MutableLiveData<MutableList<VisualDownloadItem>>(mutableListOf())
private val _selectedItems = MutableLiveData<MutableList<VisualDownloadCached>>(mutableListOf())
val usedBytes: LiveData<Long> = _usedBytes
val availableBytes: LiveData<Long> = _availableBytes
val downloadBytes: LiveData<Long> = _downloadBytes
val selectedItems: LiveData<MutableList<VisualDownloadItem>> = _selectedItems
val selectedItems: LiveData<MutableList<VisualDownloadCached>> = _selectedItems
private var previousVisual: List<VisualDownloadHeaderCached>? = null
private var previousVisual: List<VisualDownloadCached.Header>? = null
fun addSelected(item: VisualDownloadItem) {
fun addSelected(item: VisualDownloadCached) {
val currentSelected = selectedItems.value ?: mutableListOf()
if (!currentSelected.contains(item)) {
currentSelected.add(item)
@ -52,7 +52,7 @@ class DownloadViewModel : ViewModel() {
}
}
fun removeSelected(item: VisualDownloadItem) {
fun removeSelected(item: VisualDownloadCached) {
selectedItems.value?.let { selected ->
selected.remove(item)
_selectedItems.postValue(selected)
@ -63,8 +63,8 @@ class DownloadViewModel : ViewModel() {
val currentSelected = selectedItems.value ?: mutableListOf()
val items = headerCards.value ?: return
items.forEach { item ->
if (!currentSelected.contains(VisualDownloadItem.Header(item))) {
currentSelected.add(VisualDownloadItem.Header(item))
if (!currentSelected.contains(item)) {
currentSelected.add(item)
}
}
_selectedItems.postValue(currentSelected)
@ -124,7 +124,7 @@ class DownloadViewModel : ViewModel() {
DOWNLOAD_EPISODE_CACHE,
getFolderName(it.id.toString(), it.id.toString())
)
VisualDownloadHeaderCached(
VisualDownloadCached.Header(
currentBytes = currentBytes,
totalBytes = bytes,
data = it,
@ -145,7 +145,7 @@ class DownloadViewModel : ViewModel() {
}
}
private fun updateStorageStats(visual: List<VisualDownloadHeaderCached>) {
private fun updateStorageStats(visual: List<VisualDownloadCached.Header>) {
try {
val stat = StatFs(Environment.getExternalStorageDirectory().path)
val localBytesAvailable = stat.availableBytes
@ -164,17 +164,12 @@ class DownloadViewModel : ViewModel() {
fun handleMultiDelete(context: Context) = viewModelScope.launchSafe {
val selectedItemsList = selectedItems.value ?: mutableListOf()
val ids = selectedItemsList.map {
when (it) {
is VisualDownloadItem.Header -> it.header.data.id
is VisualDownloadItem.Child -> it.child.data.id
}
}
val ids = selectedItemsList.map { it.data.id }
val names = selectedItemsList.mapNotNull {
when (it) {
is VisualDownloadItem.Header -> it.header.data.name
is VisualDownloadItem.Child -> it.child.data.name
is VisualDownloadCached.Header -> it.data.name
is VisualDownloadCached.Child -> it.data.name
}
}