Initial basic support for child downloads

This commit is contained in:
Luna712 2024-07-07 16:05:27 -06:00 committed by GitHub
parent 9a1949e452
commit f6bfdefe15
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 80 additions and 61 deletions

View file

@ -85,10 +85,16 @@ 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: (Int, String, Boolean) -> Unit,
private val selectedChangedCallback: (VisualDownloadItem, Boolean) -> Unit,
private val multiDeleteStateCallback: (VisualDownloadItem) -> Unit,
) : ListAdapter<VisualDownloadCached, DownloadAdapter.DownloadViewHolder>(DiffCallback()) {
private var isMultiDeleteState: Boolean = false
@ -119,7 +125,7 @@ class DownloadAdapter(
setImage(data.poster)
if (isMultiDeleteState) {
setOnClickListener {
toggleIsChecked(deleteCheckbox, data.id, data.name)
toggleIsChecked(deleteCheckbox, VisualDownloadItem.Header(card))
}
} else {
setOnClickListener {
@ -128,7 +134,7 @@ class DownloadAdapter(
}
setOnLongClickListener {
headerClickCallback.invoke(DownloadHeaderClickEvent(DOWNLOAD_ACTION_LONG_CLICK, data))
multiDeleteStateCallback.invoke(VisualDownloadItem.Header(card))
true
}
}
@ -142,7 +148,7 @@ class DownloadAdapter(
if (isMultiDeleteState) {
deleteCheckbox.setOnCheckedChangeListener { _, isChecked ->
selectedIds[data.id] = isChecked
selectedChangedCallback.invoke(data.id, data.name, isChecked)
selectedChangedCallback.invoke(VisualDownloadItem.Header(card), isChecked)
}
} else deleteCheckbox.setOnCheckedChangeListener(null)
@ -183,10 +189,15 @@ class DownloadAdapter(
downloadButton.setDefaultClickListener(card.child, downloadHeaderInfo, mediaClickCallback)
downloadButton.isVisible = !isMultiDeleteState
downloadButton.setOnLongClickListener {
multiDeleteStateCallback.invoke(VisualDownloadItem.Header(card))
true
}
episodeHolder.apply {
if (isMultiDeleteState) {
setOnClickListener {
toggleIsChecked(deleteCheckbox, card.data.id, card.data.name)
toggleIsChecked(deleteCheckbox, VisualDownloadItem.Header(card))
}
} else {
setOnClickListener {
@ -195,7 +206,7 @@ class DownloadAdapter(
}
setOnLongClickListener {
mediaClickCallback.invoke(DownloadClickEvent(DOWNLOAD_ACTION_LONG_CLICK, card.child))
multiDeleteStateCallback.invoke(VisualDownloadItem.Header(card))
true
}
}
@ -223,7 +234,7 @@ class DownloadAdapter(
episodeHolder.apply {
if (isMultiDeleteState) {
setOnClickListener {
toggleIsChecked(deleteCheckbox, card.data.id, card.data.name)
toggleIsChecked(deleteCheckbox, VisualDownloadItem.Header(card))
}
} else {
setOnClickListener {
@ -232,7 +243,7 @@ class DownloadAdapter(
}
setOnLongClickListener {
headerClickCallback.invoke(DownloadHeaderClickEvent(DOWNLOAD_ACTION_LONG_CLICK, card.data))
multiDeleteStateCallback.invoke(VisualDownloadItem.Header(card))
true
}
}
@ -340,11 +351,14 @@ class DownloadAdapter(
}
}
private fun toggleIsChecked(checkbox: CheckBox, id: Int, name: String) {
private fun toggleIsChecked(checkbox: CheckBox, item: VisualDownloadItem) {
val isChecked = !checkbox.isChecked
checkbox.isChecked = isChecked
selectedIds[id] = isChecked
selectedChangedCallback.invoke(id, name, isChecked)
when (item) {
is VisualDownloadItem.Header -> selectedIds[item.header.data.id] = isChecked
is VisualDownloadItem.Child -> selectedIds[item.child.data.id] = isChecked
}
selectedChangedCallback.invoke(item, isChecked)
}
class DiffCallback : DiffUtil.ItemCallback<VisualDownloadCached>() {

View file

@ -1,6 +1,5 @@
package com.lagradost.cloudstream3.ui.download
import android.content.Context
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
@ -113,7 +112,9 @@ class DownloadChildFragment : Fragment() {
if (downloadClickEvent.action == DOWNLOAD_ACTION_DELETE_FILE) {
setUpDownloadDeleteListener(folder)
}
}, { _, _, _ -> }
},
{ _, _ -> },
{ _ -> }
)
binding?.downloadChildList?.apply {

View file

@ -93,7 +93,7 @@ class DownloadFragment : Fragment() {
// We always want fresh selections
// when navigating to downloads
downloadsViewModel.clearSelectedIds()
downloadsViewModel.clearSelectedItems()
observe(downloadsViewModel.headerCards) {
(binding?.downloadList?.adapter as? DownloadAdapter)?.submitList(it)
@ -110,7 +110,7 @@ class DownloadFragment : Fragment() {
observe(downloadsViewModel.downloadBytes) {
updateStorageInfo(view.context, it, R.string.app_storage, binding?.downloadAppTxt, binding?.downloadApp)
}
observe(downloadsViewModel.selectedIds) {
observe(downloadsViewModel.selectedItems) {
handleSelectedChange(it)
binding?.btnDelete?.text =
getString(R.string.delete_count).format(it.count())
@ -120,24 +120,22 @@ class DownloadFragment : Fragment() {
{ click -> handleItemClick(click) },
{ downloadClickEvent ->
handleDownloadClick(downloadClickEvent)
when (downloadClickEvent.action) {
DOWNLOAD_ACTION_DELETE_FILE -> setUpDownloadDeleteListener()
DOWNLOAD_ACTION_LONG_CLICK -> downloadClickEvent.data.name?.let {
downloadsViewModel.addSelected(
downloadClickEvent.data.id,
it
)
(binding?.downloadList?.adapter as? DownloadAdapter)?.updateSelectedItem(
downloadClickEvent.data.id,
true
)
}
if (downloadClickEvent.action == DOWNLOAD_ACTION_DELETE_FILE) {
setUpDownloadDeleteListener()
}
},
{ id, name, isChecked ->
{ card, isChecked ->
if (isChecked) {
downloadsViewModel.addSelected(id, name)
} else downloadsViewModel.removeSelected(id)
downloadsViewModel.addSelected(card)
} 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,
true
)
}
)
@ -187,20 +185,10 @@ class DownloadFragment : Fragment() {
DOWNLOAD_ACTION_LOAD_RESULT -> {
(activity as AppCompatActivity?)?.loadResult(click.data.url, click.data.apiName)
}
DOWNLOAD_ACTION_LONG_CLICK -> {
downloadsViewModel.addSelected(
click.data.id,
click.data.name
)
(binding?.downloadList?.adapter as? DownloadAdapter)?.updateSelectedItem(
click.data.id,
true
)
}
}
}
private fun handleSelectedChange(selected: HashMap<Int, String>) {
private fun handleSelectedChange(selected: MutableList<VisualDownloadItem>) {
val adapter = binding?.downloadList?.adapter as? DownloadAdapter
if (selected.isNotEmpty()) {
binding?.downloadDeleteAppbar?.isVisible = true
@ -212,7 +200,7 @@ class DownloadFragment : Fragment() {
binding?.btnCancel?.setOnClickListener {
adapter?.setIsMultiDeleteState(false)
downloadsViewModel.clearSelectedIds()
downloadsViewModel.clearSelectedItems()
}
binding?.btnSelectAll?.setOnClickListener {
@ -229,7 +217,7 @@ class DownloadFragment : Fragment() {
downloadsViewModel.usedBytes.value?.let { it > 0 } == true
adapter?.setIsMultiDeleteState(false)
downloadsViewModel.clearSelectedIds()
downloadsViewModel.clearSelectedItems()
}
}

View file

@ -34,41 +34,44 @@ class DownloadViewModel : ViewModel() {
private val _availableBytes = MutableLiveData<Long>()
private val _downloadBytes = MutableLiveData<Long>()
private val _selectedIds = MutableLiveData<HashMap<Int, String>>(HashMap())
private val _selectedItems = MutableLiveData<MutableList<VisualDownloadItem>>(mutableListOf())
val usedBytes: LiveData<Long> = _usedBytes
val availableBytes: LiveData<Long> = _availableBytes
val downloadBytes: LiveData<Long> = _downloadBytes
val selectedIds: LiveData<HashMap<Int, String>> = _selectedIds
val selectedItems: LiveData<MutableList<VisualDownloadItem>> = _selectedItems
private var previousVisual: List<VisualDownloadHeaderCached>? = null
fun addSelected(id: Int, name: String) {
val currentSelected = selectedIds.value ?: HashMap()
currentSelected[id] = name
_selectedIds.postValue(currentSelected)
fun addSelected(item: VisualDownloadItem) {
val currentSelected = selectedItems.value ?: mutableListOf()
if (!currentSelected.contains(item)) {
currentSelected.add(item)
_selectedItems.postValue(currentSelected)
}
}
fun removeSelected(id: Int) {
selectedIds.value?.let { selectedIds ->
selectedIds.remove(id)
_selectedIds.postValue(selectedIds)
fun removeSelected(item: VisualDownloadItem) {
selectedItems.value?.let { selected ->
selected.remove(item)
_selectedItems.postValue(selected)
}
}
fun selectAllItems() {
val currentSelected = selectedIds.value ?: HashMap()
val currentSelected = selectedItems.value ?: mutableListOf()
val items = headerCards.value ?: return
items.forEach { item ->
if (currentSelected.containsKey(item.data.id)) return@forEach
currentSelected[item.data.id] = item.data.name
if (!currentSelected.contains(VisualDownloadItem.Header(item))) {
currentSelected.add(VisualDownloadItem.Header(item))
}
}
_selectedIds.postValue(currentSelected)
_selectedItems.postValue(currentSelected)
}
fun clearSelectedIds() {
_selectedIds.postValue(HashMap())
fun clearSelectedItems() {
_selectedItems.postValue(mutableListOf())
}
fun updateList(context: Context) = viewModelScope.launchSafe {
@ -159,8 +162,21 @@ class DownloadViewModel : ViewModel() {
}
fun handleMultiDelete(context: Context) = viewModelScope.launchSafe {
val ids: List<Int> = selectedIds.value?.keys?.toList() ?: emptyList()
val names: List<String> = selectedIds.value?.values?.toList() ?: emptyList()
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 names = selectedItemsList.mapNotNull {
when (it) {
is VisualDownloadItem.Header -> it.header.data.name
is VisualDownloadItem.Child -> it.child.data.name
}
}
showDeleteConfirmationDialog(context, ids, names)
}