From f6bfdefe15b1b5e36c5ea64f0066e2a057b820d7 Mon Sep 17 00:00:00 2001 From: Luna712 <142361265+Luna712@users.noreply.github.com> Date: Sun, 7 Jul 2024 16:05:27 -0600 Subject: [PATCH] Initial basic support for child downloads --- .../ui/download/DownloadAdapter.kt | 36 +++++++++---- .../ui/download/DownloadChildFragment.kt | 5 +- .../ui/download/DownloadFragment.kt | 48 +++++++---------- .../ui/download/DownloadViewModel.kt | 52 ++++++++++++------- 4 files changed, 80 insertions(+), 61 deletions(-) diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/download/DownloadAdapter.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/download/DownloadAdapter.kt index 074caeda..0c43935a 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/download/DownloadAdapter.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/download/DownloadAdapter.kt @@ -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(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() { diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/download/DownloadChildFragment.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/download/DownloadChildFragment.kt index 9457c366..5cf3f1cb 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/download/DownloadChildFragment.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/download/DownloadChildFragment.kt @@ -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 { 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 485a8b95..fa193850 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 @@ -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) { + private fun handleSelectedChange(selected: MutableList) { 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() } } diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/download/DownloadViewModel.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/download/DownloadViewModel.kt index 15c8b1ef..d3723f65 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/download/DownloadViewModel.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/download/DownloadViewModel.kt @@ -34,41 +34,44 @@ class DownloadViewModel : ViewModel() { private val _availableBytes = MutableLiveData() private val _downloadBytes = MutableLiveData() - private val _selectedIds = MutableLiveData>(HashMap()) + private val _selectedItems = MutableLiveData>(mutableListOf()) val usedBytes: LiveData = _usedBytes val availableBytes: LiveData = _availableBytes val downloadBytes: LiveData = _downloadBytes - val selectedIds: LiveData> = _selectedIds + val selectedItems: LiveData> = _selectedItems private var previousVisual: List? = 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 = selectedIds.value?.keys?.toList() ?: emptyList() - val names: List = 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) }