3
3
Fork 1
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:
Luna712 2024-07-16 17:24:17 -06:00 committed by GitHub
parent b601704764
commit e41ca6318a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 61 additions and 28 deletions
app/src/main/java/com/lagradost/cloudstream3/ui/download

View file

@ -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)
}

View file

@ -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()
}
}

View file

@ -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()
}
}

View file

@ -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,
)
}