mirror of
https://github.com/recloudstream/cloudstream.git
synced 2024-08-15 01:53:11 +00:00
FINALLY make selected state fully managed by the view model
This commit is contained in:
parent
b601704764
commit
e41ca6318a
4 changed files with 61 additions and 28 deletions
app/src/main/java/com/lagradost/cloudstream3/ui/download
|
@ -35,20 +35,23 @@ sealed class VisualDownloadCached {
|
|||
abstract val currentBytes: Long
|
||||
abstract val totalBytes: Long
|
||||
abstract val data: VideoDownloadHelper.DownloadCached
|
||||
abstract var isSelected: Boolean
|
||||
|
||||
data class Child(
|
||||
override val currentBytes: Long,
|
||||
override val totalBytes: Long,
|
||||
override val data: VideoDownloadHelper.DownloadEpisodeCached,
|
||||
override var isSelected: Boolean,
|
||||
) : VisualDownloadCached()
|
||||
|
||||
data class Header(
|
||||
override val currentBytes: Long,
|
||||
override val totalBytes: Long,
|
||||
override val data: VideoDownloadHelper.DownloadHeaderCached,
|
||||
override var isSelected: Boolean,
|
||||
val child: VideoDownloadHelper.DownloadEpisodeCached?,
|
||||
val currentOngoingDownloads: Int,
|
||||
val totalDownloads: Int
|
||||
val totalDownloads: Int,
|
||||
) : VisualDownloadCached()
|
||||
}
|
||||
|
||||
|
@ -69,7 +72,6 @@ class DownloadAdapter(
|
|||
) : ListAdapter<VisualDownloadCached, DownloadAdapter.DownloadViewHolder>(DiffCallback()) {
|
||||
|
||||
private var isMultiDeleteState: Boolean = false
|
||||
private val selectedIds: HashMap<Int, Boolean> = HashMap()
|
||||
|
||||
companion object {
|
||||
private const val VIEW_TYPE_HEADER = 0
|
||||
|
@ -136,14 +138,13 @@ class DownloadAdapter(
|
|||
|
||||
if (isMultiDeleteState) {
|
||||
deleteCheckbox.setOnCheckedChangeListener { _, isChecked ->
|
||||
selectedIds[data.id] = isChecked
|
||||
onItemSelectionChanged.invoke(card, isChecked)
|
||||
}
|
||||
} else deleteCheckbox.setOnCheckedChangeListener(null)
|
||||
|
||||
deleteCheckbox.apply {
|
||||
isVisible = isMultiDeleteState
|
||||
isChecked = selectedIds[data.id] == true
|
||||
isChecked = card.isSelected
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -310,14 +311,13 @@ class DownloadAdapter(
|
|||
|
||||
if (isMultiDeleteState) {
|
||||
deleteCheckbox.setOnCheckedChangeListener { _, isChecked ->
|
||||
selectedIds[data.id] = isChecked
|
||||
onItemSelectionChanged.invoke(card, isChecked)
|
||||
}
|
||||
} else deleteCheckbox.setOnCheckedChangeListener(null)
|
||||
|
||||
deleteCheckbox.apply {
|
||||
isVisible = isMultiDeleteState
|
||||
isChecked = selectedIds[data.id] == true
|
||||
isChecked = card.isSelected
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -349,37 +349,30 @@ class DownloadAdapter(
|
|||
if (isMultiDeleteState == value) return
|
||||
isMultiDeleteState = value
|
||||
if (!value) {
|
||||
selectedIds.clear()
|
||||
currentList.forEachIndexed { index, _ ->
|
||||
notifyItemChanged(index)
|
||||
}
|
||||
} else notifyItemRangeChanged(0, itemCount)
|
||||
}
|
||||
|
||||
fun selectAllItems() {
|
||||
fun notifyAllSelected() {
|
||||
currentList.forEachIndexed { index, item ->
|
||||
val id = item.data.id
|
||||
if (selectedIds[id] == true) return@forEachIndexed
|
||||
|
||||
selectedIds[id] = true
|
||||
if (item.isSelected) return@forEachIndexed
|
||||
notifyItemChanged(index)
|
||||
}
|
||||
}
|
||||
|
||||
fun clearSelectedItems() {
|
||||
val selectedPositions = selectedIds.keys.mapNotNull { id ->
|
||||
currentList.indexOfFirst { it.data.id == id }.takeIf { it != -1 }
|
||||
}
|
||||
selectedIds.clear()
|
||||
selectedPositions.forEach {
|
||||
notifyItemChanged(it)
|
||||
fun notifySelectionStates() {
|
||||
currentList.indices.forEach { index ->
|
||||
if (currentList[index].isSelected) {
|
||||
notifyItemChanged(index)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun toggleIsChecked(checkbox: CheckBox, item: VisualDownloadCached) {
|
||||
val isChecked = !checkbox.isChecked
|
||||
checkbox.isChecked = isChecked
|
||||
selectedIds[item.data.id] = isChecked
|
||||
onItemSelectionChanged.invoke(item, isChecked)
|
||||
}
|
||||
|
||||
|
|
|
@ -185,12 +185,12 @@ class DownloadChildFragment : Fragment() {
|
|||
|
||||
binding?.btnToggleAll?.setOnClickListener {
|
||||
val allSelected = downloadsViewModel.isAllSelected()
|
||||
val binding = binding?.downloadChildList?.adapter as? DownloadAdapter
|
||||
val adapter = binding?.downloadChildList?.adapter as? DownloadAdapter
|
||||
if (allSelected) {
|
||||
binding?.clearSelectedItems()
|
||||
adapter?.notifySelectionStates()
|
||||
downloadsViewModel.clearSelectedItems()
|
||||
} else {
|
||||
binding?.selectAllItems()
|
||||
adapter?.notifyAllSelected()
|
||||
downloadsViewModel.selectAllItems()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -133,7 +133,13 @@ class DownloadFragment : Fragment() {
|
|||
binding?.downloadUsedTxt,
|
||||
binding?.downloadUsed
|
||||
)
|
||||
binding?.downloadStorageAppbar?.isVisible = it > 0
|
||||
|
||||
// Prevent race condition and make sure
|
||||
// we don't display it early
|
||||
if (
|
||||
downloadsViewModel.isMultiDeleteState.value == null ||
|
||||
downloadsViewModel.isMultiDeleteState.value == false
|
||||
) binding?.downloadStorageAppbar?.isVisible = it > 0
|
||||
}
|
||||
observe(downloadsViewModel.downloadBytes) {
|
||||
updateStorageInfo(
|
||||
|
@ -154,7 +160,8 @@ class DownloadFragment : Fragment() {
|
|||
if (!isMultiDeleteState) {
|
||||
detachBackPressedCallback()
|
||||
downloadsViewModel.clearSelectedItems()
|
||||
// Make sure we don't display it early
|
||||
// Prevent race condition and make sure
|
||||
// we don't display it early
|
||||
if (downloadsViewModel.usedBytes.value?.let { it > 0 } == true) {
|
||||
binding?.downloadStorageAppbar?.isVisible = true
|
||||
}
|
||||
|
@ -260,12 +267,12 @@ class DownloadFragment : Fragment() {
|
|||
|
||||
binding?.btnToggleAll?.setOnClickListener {
|
||||
val allSelected = downloadsViewModel.isAllSelected()
|
||||
val binding = binding?.downloadList?.adapter as? DownloadAdapter
|
||||
val adapter = binding?.downloadList?.adapter as? DownloadAdapter
|
||||
if (allSelected) {
|
||||
binding?.clearSelectedItems()
|
||||
adapter?.notifySelectionStates()
|
||||
downloadsViewModel.clearSelectedItems()
|
||||
} else {
|
||||
binding?.selectAllItems()
|
||||
adapter?.notifyAllSelected()
|
||||
downloadsViewModel.selectAllItems()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -67,6 +67,7 @@ class DownloadViewModel : ViewModel() {
|
|||
currentSelected.add(item)
|
||||
_selectedItems.postValue(currentSelected)
|
||||
updateSelectedBytes()
|
||||
updateSelectedCards()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -75,6 +76,7 @@ class DownloadViewModel : ViewModel() {
|
|||
selected.remove(item)
|
||||
_selectedItems.postValue(selected)
|
||||
updateSelectedBytes()
|
||||
updateSelectedCards()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -89,12 +91,14 @@ class DownloadViewModel : ViewModel() {
|
|||
}
|
||||
_selectedItems.postValue(currentSelected)
|
||||
updateSelectedBytes()
|
||||
updateSelectedCards()
|
||||
}
|
||||
|
||||
fun clearSelectedItems() {
|
||||
// We need this to be done immediately
|
||||
// so we can't use postValue
|
||||
_selectedItems.value = mutableListOf()
|
||||
updateSelectedCards()
|
||||
}
|
||||
|
||||
fun isAllSelected(): Boolean {
|
||||
|
@ -127,6 +131,27 @@ class DownloadViewModel : ViewModel() {
|
|||
_selectedBytes.postValue(totalSelectedBytes)
|
||||
}
|
||||
|
||||
private fun updateSelectedCards() = viewModelScope.launchSafe {
|
||||
val currentSelected = selectedItems.value ?: return@launchSafe
|
||||
val updatedHeaderCards = headerCards.value?.toMutableList()
|
||||
val updatedChildCards = childCards.value?.toMutableList()
|
||||
|
||||
updatedHeaderCards?.forEach { header ->
|
||||
header.isSelected = currentSelected.any {
|
||||
it.data.id == header.data.id
|
||||
}
|
||||
}
|
||||
|
||||
updatedChildCards?.forEach { child ->
|
||||
child.isSelected = currentSelected.any {
|
||||
it.data.id == child.data.id
|
||||
}
|
||||
}
|
||||
|
||||
_headerCards.postValue(updatedHeaderCards)
|
||||
_childCards.postValue(updatedChildCards)
|
||||
}
|
||||
|
||||
fun updateList(context: Context) = viewModelScope.launchSafe {
|
||||
val children = withContext(Dispatchers.IO) {
|
||||
context.getKeys(DOWNLOAD_EPISODE_CACHE)
|
||||
|
@ -174,6 +199,9 @@ class DownloadViewModel : ViewModel() {
|
|||
val bytes = totalBytesUsedByChild[it.id] ?: 0
|
||||
val currentBytes = currentBytesUsedByChild[it.id] ?: 0
|
||||
if (bytes <= 0 || downloads <= 0) return@mapNotNull null
|
||||
val isSelected = selectedItems.value?.any { header ->
|
||||
it.id == header.data.id
|
||||
} ?: false
|
||||
val movieEpisode =
|
||||
if (!it.type.isMovieType()) null
|
||||
else context.getKey<VideoDownloadHelper.DownloadEpisodeCached>(
|
||||
|
@ -187,6 +215,7 @@ class DownloadViewModel : ViewModel() {
|
|||
child = movieEpisode,
|
||||
currentOngoingDownloads = 0,
|
||||
totalDownloads = downloads,
|
||||
isSelected = isSelected,
|
||||
)
|
||||
}.sortedBy {
|
||||
(it.child?.episode ?: 0) + (it.child?.season?.times(10000) ?: 0)
|
||||
|
@ -212,9 +241,13 @@ class DownloadViewModel : ViewModel() {
|
|||
}.mapNotNull {
|
||||
val info = getDownloadFileInfoAndUpdateSettings(context, it.id)
|
||||
?: return@mapNotNull null
|
||||
val isSelected = selectedItems.value?.any { child ->
|
||||
it.id == child.data.id
|
||||
} ?: false
|
||||
VisualDownloadCached.Child(
|
||||
currentBytes = info.fileLength,
|
||||
totalBytes = info.totalBytes,
|
||||
isSelected = isSelected,
|
||||
data = it,
|
||||
)
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue