mirror of
https://github.com/recloudstream/cloudstream.git
synced 2024-08-15 01:53:11 +00:00
Initial basic support for child downloads
This commit is contained in:
parent
9a1949e452
commit
f6bfdefe15
4 changed files with 80 additions and 61 deletions
|
@ -85,10 +85,16 @@ data class DownloadHeaderClickEvent(
|
||||||
val data: VideoDownloadHelper.DownloadHeaderCached
|
val data: VideoDownloadHelper.DownloadHeaderCached
|
||||||
)
|
)
|
||||||
|
|
||||||
|
sealed class VisualDownloadItem {
|
||||||
|
data class Header(val header: VisualDownloadHeaderCached) : VisualDownloadItem()
|
||||||
|
data class Child(val child: VisualDownloadChildCached) : VisualDownloadItem()
|
||||||
|
}
|
||||||
|
|
||||||
class DownloadAdapter(
|
class DownloadAdapter(
|
||||||
private val headerClickCallback: (DownloadHeaderClickEvent) -> Unit,
|
private val headerClickCallback: (DownloadHeaderClickEvent) -> Unit,
|
||||||
private val mediaClickCallback: (DownloadClickEvent) -> 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()) {
|
) : ListAdapter<VisualDownloadCached, DownloadAdapter.DownloadViewHolder>(DiffCallback()) {
|
||||||
|
|
||||||
private var isMultiDeleteState: Boolean = false
|
private var isMultiDeleteState: Boolean = false
|
||||||
|
@ -119,7 +125,7 @@ class DownloadAdapter(
|
||||||
setImage(data.poster)
|
setImage(data.poster)
|
||||||
if (isMultiDeleteState) {
|
if (isMultiDeleteState) {
|
||||||
setOnClickListener {
|
setOnClickListener {
|
||||||
toggleIsChecked(deleteCheckbox, data.id, data.name)
|
toggleIsChecked(deleteCheckbox, VisualDownloadItem.Header(card))
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
setOnClickListener {
|
setOnClickListener {
|
||||||
|
@ -128,7 +134,7 @@ class DownloadAdapter(
|
||||||
}
|
}
|
||||||
|
|
||||||
setOnLongClickListener {
|
setOnLongClickListener {
|
||||||
headerClickCallback.invoke(DownloadHeaderClickEvent(DOWNLOAD_ACTION_LONG_CLICK, data))
|
multiDeleteStateCallback.invoke(VisualDownloadItem.Header(card))
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -142,7 +148,7 @@ class DownloadAdapter(
|
||||||
if (isMultiDeleteState) {
|
if (isMultiDeleteState) {
|
||||||
deleteCheckbox.setOnCheckedChangeListener { _, isChecked ->
|
deleteCheckbox.setOnCheckedChangeListener { _, isChecked ->
|
||||||
selectedIds[data.id] = isChecked
|
selectedIds[data.id] = isChecked
|
||||||
selectedChangedCallback.invoke(data.id, data.name, isChecked)
|
selectedChangedCallback.invoke(VisualDownloadItem.Header(card), isChecked)
|
||||||
}
|
}
|
||||||
} else deleteCheckbox.setOnCheckedChangeListener(null)
|
} else deleteCheckbox.setOnCheckedChangeListener(null)
|
||||||
|
|
||||||
|
@ -183,10 +189,15 @@ class DownloadAdapter(
|
||||||
downloadButton.setDefaultClickListener(card.child, downloadHeaderInfo, mediaClickCallback)
|
downloadButton.setDefaultClickListener(card.child, downloadHeaderInfo, mediaClickCallback)
|
||||||
downloadButton.isVisible = !isMultiDeleteState
|
downloadButton.isVisible = !isMultiDeleteState
|
||||||
|
|
||||||
|
downloadButton.setOnLongClickListener {
|
||||||
|
multiDeleteStateCallback.invoke(VisualDownloadItem.Header(card))
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
episodeHolder.apply {
|
episodeHolder.apply {
|
||||||
if (isMultiDeleteState) {
|
if (isMultiDeleteState) {
|
||||||
setOnClickListener {
|
setOnClickListener {
|
||||||
toggleIsChecked(deleteCheckbox, card.data.id, card.data.name)
|
toggleIsChecked(deleteCheckbox, VisualDownloadItem.Header(card))
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
setOnClickListener {
|
setOnClickListener {
|
||||||
|
@ -195,7 +206,7 @@ class DownloadAdapter(
|
||||||
}
|
}
|
||||||
|
|
||||||
setOnLongClickListener {
|
setOnLongClickListener {
|
||||||
mediaClickCallback.invoke(DownloadClickEvent(DOWNLOAD_ACTION_LONG_CLICK, card.child))
|
multiDeleteStateCallback.invoke(VisualDownloadItem.Header(card))
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -223,7 +234,7 @@ class DownloadAdapter(
|
||||||
episodeHolder.apply {
|
episodeHolder.apply {
|
||||||
if (isMultiDeleteState) {
|
if (isMultiDeleteState) {
|
||||||
setOnClickListener {
|
setOnClickListener {
|
||||||
toggleIsChecked(deleteCheckbox, card.data.id, card.data.name)
|
toggleIsChecked(deleteCheckbox, VisualDownloadItem.Header(card))
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
setOnClickListener {
|
setOnClickListener {
|
||||||
|
@ -232,7 +243,7 @@ class DownloadAdapter(
|
||||||
}
|
}
|
||||||
|
|
||||||
setOnLongClickListener {
|
setOnLongClickListener {
|
||||||
headerClickCallback.invoke(DownloadHeaderClickEvent(DOWNLOAD_ACTION_LONG_CLICK, card.data))
|
multiDeleteStateCallback.invoke(VisualDownloadItem.Header(card))
|
||||||
true
|
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
|
val isChecked = !checkbox.isChecked
|
||||||
checkbox.isChecked = isChecked
|
checkbox.isChecked = isChecked
|
||||||
selectedIds[id] = isChecked
|
when (item) {
|
||||||
selectedChangedCallback.invoke(id, name, isChecked)
|
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>() {
|
class DiffCallback : DiffUtil.ItemCallback<VisualDownloadCached>() {
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
package com.lagradost.cloudstream3.ui.download
|
package com.lagradost.cloudstream3.ui.download
|
||||||
|
|
||||||
import android.content.Context
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.View
|
import android.view.View
|
||||||
|
@ -113,7 +112,9 @@ class DownloadChildFragment : Fragment() {
|
||||||
if (downloadClickEvent.action == DOWNLOAD_ACTION_DELETE_FILE) {
|
if (downloadClickEvent.action == DOWNLOAD_ACTION_DELETE_FILE) {
|
||||||
setUpDownloadDeleteListener(folder)
|
setUpDownloadDeleteListener(folder)
|
||||||
}
|
}
|
||||||
}, { _, _, _ -> }
|
},
|
||||||
|
{ _, _ -> },
|
||||||
|
{ _ -> }
|
||||||
)
|
)
|
||||||
|
|
||||||
binding?.downloadChildList?.apply {
|
binding?.downloadChildList?.apply {
|
||||||
|
|
|
@ -93,7 +93,7 @@ class DownloadFragment : Fragment() {
|
||||||
|
|
||||||
// We always want fresh selections
|
// We always want fresh selections
|
||||||
// when navigating to downloads
|
// when navigating to downloads
|
||||||
downloadsViewModel.clearSelectedIds()
|
downloadsViewModel.clearSelectedItems()
|
||||||
|
|
||||||
observe(downloadsViewModel.headerCards) {
|
observe(downloadsViewModel.headerCards) {
|
||||||
(binding?.downloadList?.adapter as? DownloadAdapter)?.submitList(it)
|
(binding?.downloadList?.adapter as? DownloadAdapter)?.submitList(it)
|
||||||
|
@ -110,7 +110,7 @@ class DownloadFragment : Fragment() {
|
||||||
observe(downloadsViewModel.downloadBytes) {
|
observe(downloadsViewModel.downloadBytes) {
|
||||||
updateStorageInfo(view.context, it, R.string.app_storage, binding?.downloadAppTxt, binding?.downloadApp)
|
updateStorageInfo(view.context, it, R.string.app_storage, binding?.downloadAppTxt, binding?.downloadApp)
|
||||||
}
|
}
|
||||||
observe(downloadsViewModel.selectedIds) {
|
observe(downloadsViewModel.selectedItems) {
|
||||||
handleSelectedChange(it)
|
handleSelectedChange(it)
|
||||||
binding?.btnDelete?.text =
|
binding?.btnDelete?.text =
|
||||||
getString(R.string.delete_count).format(it.count())
|
getString(R.string.delete_count).format(it.count())
|
||||||
|
@ -120,24 +120,22 @@ class DownloadFragment : Fragment() {
|
||||||
{ click -> handleItemClick(click) },
|
{ click -> handleItemClick(click) },
|
||||||
{ downloadClickEvent ->
|
{ downloadClickEvent ->
|
||||||
handleDownloadClick(downloadClickEvent)
|
handleDownloadClick(downloadClickEvent)
|
||||||
when (downloadClickEvent.action) {
|
if (downloadClickEvent.action == DOWNLOAD_ACTION_DELETE_FILE) {
|
||||||
DOWNLOAD_ACTION_DELETE_FILE -> setUpDownloadDeleteListener()
|
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
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{ id, name, isChecked ->
|
{ card, isChecked ->
|
||||||
if (isChecked) {
|
if (isChecked) {
|
||||||
downloadsViewModel.addSelected(id, name)
|
downloadsViewModel.addSelected(card)
|
||||||
} else downloadsViewModel.removeSelected(id)
|
} 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 -> {
|
DOWNLOAD_ACTION_LOAD_RESULT -> {
|
||||||
(activity as AppCompatActivity?)?.loadResult(click.data.url, click.data.apiName)
|
(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
|
val adapter = binding?.downloadList?.adapter as? DownloadAdapter
|
||||||
if (selected.isNotEmpty()) {
|
if (selected.isNotEmpty()) {
|
||||||
binding?.downloadDeleteAppbar?.isVisible = true
|
binding?.downloadDeleteAppbar?.isVisible = true
|
||||||
|
@ -212,7 +200,7 @@ class DownloadFragment : Fragment() {
|
||||||
|
|
||||||
binding?.btnCancel?.setOnClickListener {
|
binding?.btnCancel?.setOnClickListener {
|
||||||
adapter?.setIsMultiDeleteState(false)
|
adapter?.setIsMultiDeleteState(false)
|
||||||
downloadsViewModel.clearSelectedIds()
|
downloadsViewModel.clearSelectedItems()
|
||||||
}
|
}
|
||||||
|
|
||||||
binding?.btnSelectAll?.setOnClickListener {
|
binding?.btnSelectAll?.setOnClickListener {
|
||||||
|
@ -229,7 +217,7 @@ class DownloadFragment : Fragment() {
|
||||||
downloadsViewModel.usedBytes.value?.let { it > 0 } == true
|
downloadsViewModel.usedBytes.value?.let { it > 0 } == true
|
||||||
|
|
||||||
adapter?.setIsMultiDeleteState(false)
|
adapter?.setIsMultiDeleteState(false)
|
||||||
downloadsViewModel.clearSelectedIds()
|
downloadsViewModel.clearSelectedItems()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -34,41 +34,44 @@ class DownloadViewModel : ViewModel() {
|
||||||
private val _availableBytes = MutableLiveData<Long>()
|
private val _availableBytes = MutableLiveData<Long>()
|
||||||
private val _downloadBytes = 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 usedBytes: LiveData<Long> = _usedBytes
|
||||||
val availableBytes: LiveData<Long> = _availableBytes
|
val availableBytes: LiveData<Long> = _availableBytes
|
||||||
val downloadBytes: LiveData<Long> = _downloadBytes
|
val downloadBytes: LiveData<Long> = _downloadBytes
|
||||||
|
|
||||||
val selectedIds: LiveData<HashMap<Int, String>> = _selectedIds
|
val selectedItems: LiveData<MutableList<VisualDownloadItem>> = _selectedItems
|
||||||
|
|
||||||
private var previousVisual: List<VisualDownloadHeaderCached>? = null
|
private var previousVisual: List<VisualDownloadHeaderCached>? = null
|
||||||
|
|
||||||
fun addSelected(id: Int, name: String) {
|
fun addSelected(item: VisualDownloadItem) {
|
||||||
val currentSelected = selectedIds.value ?: HashMap()
|
val currentSelected = selectedItems.value ?: mutableListOf()
|
||||||
currentSelected[id] = name
|
if (!currentSelected.contains(item)) {
|
||||||
_selectedIds.postValue(currentSelected)
|
currentSelected.add(item)
|
||||||
|
_selectedItems.postValue(currentSelected)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun removeSelected(id: Int) {
|
fun removeSelected(item: VisualDownloadItem) {
|
||||||
selectedIds.value?.let { selectedIds ->
|
selectedItems.value?.let { selected ->
|
||||||
selectedIds.remove(id)
|
selected.remove(item)
|
||||||
_selectedIds.postValue(selectedIds)
|
_selectedItems.postValue(selected)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun selectAllItems() {
|
fun selectAllItems() {
|
||||||
val currentSelected = selectedIds.value ?: HashMap()
|
val currentSelected = selectedItems.value ?: mutableListOf()
|
||||||
val items = headerCards.value ?: return
|
val items = headerCards.value ?: return
|
||||||
items.forEach { item ->
|
items.forEach { item ->
|
||||||
if (currentSelected.containsKey(item.data.id)) return@forEach
|
if (!currentSelected.contains(VisualDownloadItem.Header(item))) {
|
||||||
currentSelected[item.data.id] = item.data.name
|
currentSelected.add(VisualDownloadItem.Header(item))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
_selectedIds.postValue(currentSelected)
|
_selectedItems.postValue(currentSelected)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun clearSelectedIds() {
|
fun clearSelectedItems() {
|
||||||
_selectedIds.postValue(HashMap())
|
_selectedItems.postValue(mutableListOf())
|
||||||
}
|
}
|
||||||
|
|
||||||
fun updateList(context: Context) = viewModelScope.launchSafe {
|
fun updateList(context: Context) = viewModelScope.launchSafe {
|
||||||
|
@ -159,8 +162,21 @@ class DownloadViewModel : ViewModel() {
|
||||||
}
|
}
|
||||||
|
|
||||||
fun handleMultiDelete(context: Context) = viewModelScope.launchSafe {
|
fun handleMultiDelete(context: Context) = viewModelScope.launchSafe {
|
||||||
val ids: List<Int> = selectedIds.value?.keys?.toList() ?: emptyList()
|
val selectedItemsList = selectedItems.value ?: mutableListOf()
|
||||||
val names: List<String> = selectedIds.value?.values?.toList() ?: emptyList()
|
|
||||||
|
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)
|
showDeleteConfirmationDialog(context, ids, names)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue