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
|
@ -35,20 +35,23 @@ sealed class VisualDownloadCached {
|
||||||
abstract val currentBytes: Long
|
abstract val currentBytes: Long
|
||||||
abstract val totalBytes: Long
|
abstract val totalBytes: Long
|
||||||
abstract val data: VideoDownloadHelper.DownloadCached
|
abstract val data: VideoDownloadHelper.DownloadCached
|
||||||
|
abstract var isSelected: Boolean
|
||||||
|
|
||||||
data class Child(
|
data class Child(
|
||||||
override val currentBytes: Long,
|
override val currentBytes: Long,
|
||||||
override val totalBytes: Long,
|
override val totalBytes: Long,
|
||||||
override val data: VideoDownloadHelper.DownloadEpisodeCached,
|
override val data: VideoDownloadHelper.DownloadEpisodeCached,
|
||||||
|
override var isSelected: Boolean,
|
||||||
) : VisualDownloadCached()
|
) : VisualDownloadCached()
|
||||||
|
|
||||||
data class Header(
|
data class Header(
|
||||||
override val currentBytes: Long,
|
override val currentBytes: Long,
|
||||||
override val totalBytes: Long,
|
override val totalBytes: Long,
|
||||||
override val data: VideoDownloadHelper.DownloadHeaderCached,
|
override val data: VideoDownloadHelper.DownloadHeaderCached,
|
||||||
|
override var isSelected: Boolean,
|
||||||
val child: VideoDownloadHelper.DownloadEpisodeCached?,
|
val child: VideoDownloadHelper.DownloadEpisodeCached?,
|
||||||
val currentOngoingDownloads: Int,
|
val currentOngoingDownloads: Int,
|
||||||
val totalDownloads: Int
|
val totalDownloads: Int,
|
||||||
) : VisualDownloadCached()
|
) : VisualDownloadCached()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -69,7 +72,6 @@ class DownloadAdapter(
|
||||||
) : ListAdapter<VisualDownloadCached, DownloadAdapter.DownloadViewHolder>(DiffCallback()) {
|
) : ListAdapter<VisualDownloadCached, DownloadAdapter.DownloadViewHolder>(DiffCallback()) {
|
||||||
|
|
||||||
private var isMultiDeleteState: Boolean = false
|
private var isMultiDeleteState: Boolean = false
|
||||||
private val selectedIds: HashMap<Int, Boolean> = HashMap()
|
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private const val VIEW_TYPE_HEADER = 0
|
private const val VIEW_TYPE_HEADER = 0
|
||||||
|
@ -136,14 +138,13 @@ class DownloadAdapter(
|
||||||
|
|
||||||
if (isMultiDeleteState) {
|
if (isMultiDeleteState) {
|
||||||
deleteCheckbox.setOnCheckedChangeListener { _, isChecked ->
|
deleteCheckbox.setOnCheckedChangeListener { _, isChecked ->
|
||||||
selectedIds[data.id] = isChecked
|
|
||||||
onItemSelectionChanged.invoke(card, isChecked)
|
onItemSelectionChanged.invoke(card, isChecked)
|
||||||
}
|
}
|
||||||
} else deleteCheckbox.setOnCheckedChangeListener(null)
|
} else deleteCheckbox.setOnCheckedChangeListener(null)
|
||||||
|
|
||||||
deleteCheckbox.apply {
|
deleteCheckbox.apply {
|
||||||
isVisible = isMultiDeleteState
|
isVisible = isMultiDeleteState
|
||||||
isChecked = selectedIds[data.id] == true
|
isChecked = card.isSelected
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -310,14 +311,13 @@ class DownloadAdapter(
|
||||||
|
|
||||||
if (isMultiDeleteState) {
|
if (isMultiDeleteState) {
|
||||||
deleteCheckbox.setOnCheckedChangeListener { _, isChecked ->
|
deleteCheckbox.setOnCheckedChangeListener { _, isChecked ->
|
||||||
selectedIds[data.id] = isChecked
|
|
||||||
onItemSelectionChanged.invoke(card, isChecked)
|
onItemSelectionChanged.invoke(card, isChecked)
|
||||||
}
|
}
|
||||||
} else deleteCheckbox.setOnCheckedChangeListener(null)
|
} else deleteCheckbox.setOnCheckedChangeListener(null)
|
||||||
|
|
||||||
deleteCheckbox.apply {
|
deleteCheckbox.apply {
|
||||||
isVisible = isMultiDeleteState
|
isVisible = isMultiDeleteState
|
||||||
isChecked = selectedIds[data.id] == true
|
isChecked = card.isSelected
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -349,37 +349,30 @@ class DownloadAdapter(
|
||||||
if (isMultiDeleteState == value) return
|
if (isMultiDeleteState == value) return
|
||||||
isMultiDeleteState = value
|
isMultiDeleteState = value
|
||||||
if (!value) {
|
if (!value) {
|
||||||
selectedIds.clear()
|
|
||||||
currentList.forEachIndexed { index, _ ->
|
currentList.forEachIndexed { index, _ ->
|
||||||
notifyItemChanged(index)
|
notifyItemChanged(index)
|
||||||
}
|
}
|
||||||
} else notifyItemRangeChanged(0, itemCount)
|
} else notifyItemRangeChanged(0, itemCount)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun selectAllItems() {
|
fun notifyAllSelected() {
|
||||||
currentList.forEachIndexed { index, item ->
|
currentList.forEachIndexed { index, item ->
|
||||||
val id = item.data.id
|
if (item.isSelected) return@forEachIndexed
|
||||||
if (selectedIds[id] == true) return@forEachIndexed
|
|
||||||
|
|
||||||
selectedIds[id] = true
|
|
||||||
notifyItemChanged(index)
|
notifyItemChanged(index)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun clearSelectedItems() {
|
fun notifySelectionStates() {
|
||||||
val selectedPositions = selectedIds.keys.mapNotNull { id ->
|
currentList.indices.forEach { index ->
|
||||||
currentList.indexOfFirst { it.data.id == id }.takeIf { it != -1 }
|
if (currentList[index].isSelected) {
|
||||||
}
|
notifyItemChanged(index)
|
||||||
selectedIds.clear()
|
}
|
||||||
selectedPositions.forEach {
|
|
||||||
notifyItemChanged(it)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun toggleIsChecked(checkbox: CheckBox, item: VisualDownloadCached) {
|
private fun toggleIsChecked(checkbox: CheckBox, item: VisualDownloadCached) {
|
||||||
val isChecked = !checkbox.isChecked
|
val isChecked = !checkbox.isChecked
|
||||||
checkbox.isChecked = isChecked
|
checkbox.isChecked = isChecked
|
||||||
selectedIds[item.data.id] = isChecked
|
|
||||||
onItemSelectionChanged.invoke(item, isChecked)
|
onItemSelectionChanged.invoke(item, isChecked)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -185,12 +185,12 @@ class DownloadChildFragment : Fragment() {
|
||||||
|
|
||||||
binding?.btnToggleAll?.setOnClickListener {
|
binding?.btnToggleAll?.setOnClickListener {
|
||||||
val allSelected = downloadsViewModel.isAllSelected()
|
val allSelected = downloadsViewModel.isAllSelected()
|
||||||
val binding = binding?.downloadChildList?.adapter as? DownloadAdapter
|
val adapter = binding?.downloadChildList?.adapter as? DownloadAdapter
|
||||||
if (allSelected) {
|
if (allSelected) {
|
||||||
binding?.clearSelectedItems()
|
adapter?.notifySelectionStates()
|
||||||
downloadsViewModel.clearSelectedItems()
|
downloadsViewModel.clearSelectedItems()
|
||||||
} else {
|
} else {
|
||||||
binding?.selectAllItems()
|
adapter?.notifyAllSelected()
|
||||||
downloadsViewModel.selectAllItems()
|
downloadsViewModel.selectAllItems()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -133,7 +133,13 @@ class DownloadFragment : Fragment() {
|
||||||
binding?.downloadUsedTxt,
|
binding?.downloadUsedTxt,
|
||||||
binding?.downloadUsed
|
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) {
|
observe(downloadsViewModel.downloadBytes) {
|
||||||
updateStorageInfo(
|
updateStorageInfo(
|
||||||
|
@ -154,7 +160,8 @@ class DownloadFragment : Fragment() {
|
||||||
if (!isMultiDeleteState) {
|
if (!isMultiDeleteState) {
|
||||||
detachBackPressedCallback()
|
detachBackPressedCallback()
|
||||||
downloadsViewModel.clearSelectedItems()
|
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) {
|
if (downloadsViewModel.usedBytes.value?.let { it > 0 } == true) {
|
||||||
binding?.downloadStorageAppbar?.isVisible = true
|
binding?.downloadStorageAppbar?.isVisible = true
|
||||||
}
|
}
|
||||||
|
@ -260,12 +267,12 @@ class DownloadFragment : Fragment() {
|
||||||
|
|
||||||
binding?.btnToggleAll?.setOnClickListener {
|
binding?.btnToggleAll?.setOnClickListener {
|
||||||
val allSelected = downloadsViewModel.isAllSelected()
|
val allSelected = downloadsViewModel.isAllSelected()
|
||||||
val binding = binding?.downloadList?.adapter as? DownloadAdapter
|
val adapter = binding?.downloadList?.adapter as? DownloadAdapter
|
||||||
if (allSelected) {
|
if (allSelected) {
|
||||||
binding?.clearSelectedItems()
|
adapter?.notifySelectionStates()
|
||||||
downloadsViewModel.clearSelectedItems()
|
downloadsViewModel.clearSelectedItems()
|
||||||
} else {
|
} else {
|
||||||
binding?.selectAllItems()
|
adapter?.notifyAllSelected()
|
||||||
downloadsViewModel.selectAllItems()
|
downloadsViewModel.selectAllItems()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -67,6 +67,7 @@ class DownloadViewModel : ViewModel() {
|
||||||
currentSelected.add(item)
|
currentSelected.add(item)
|
||||||
_selectedItems.postValue(currentSelected)
|
_selectedItems.postValue(currentSelected)
|
||||||
updateSelectedBytes()
|
updateSelectedBytes()
|
||||||
|
updateSelectedCards()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -75,6 +76,7 @@ class DownloadViewModel : ViewModel() {
|
||||||
selected.remove(item)
|
selected.remove(item)
|
||||||
_selectedItems.postValue(selected)
|
_selectedItems.postValue(selected)
|
||||||
updateSelectedBytes()
|
updateSelectedBytes()
|
||||||
|
updateSelectedCards()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -89,12 +91,14 @@ class DownloadViewModel : ViewModel() {
|
||||||
}
|
}
|
||||||
_selectedItems.postValue(currentSelected)
|
_selectedItems.postValue(currentSelected)
|
||||||
updateSelectedBytes()
|
updateSelectedBytes()
|
||||||
|
updateSelectedCards()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun clearSelectedItems() {
|
fun clearSelectedItems() {
|
||||||
// We need this to be done immediately
|
// We need this to be done immediately
|
||||||
// so we can't use postValue
|
// so we can't use postValue
|
||||||
_selectedItems.value = mutableListOf()
|
_selectedItems.value = mutableListOf()
|
||||||
|
updateSelectedCards()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun isAllSelected(): Boolean {
|
fun isAllSelected(): Boolean {
|
||||||
|
@ -127,6 +131,27 @@ class DownloadViewModel : ViewModel() {
|
||||||
_selectedBytes.postValue(totalSelectedBytes)
|
_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 {
|
fun updateList(context: Context) = viewModelScope.launchSafe {
|
||||||
val children = withContext(Dispatchers.IO) {
|
val children = withContext(Dispatchers.IO) {
|
||||||
context.getKeys(DOWNLOAD_EPISODE_CACHE)
|
context.getKeys(DOWNLOAD_EPISODE_CACHE)
|
||||||
|
@ -174,6 +199,9 @@ class DownloadViewModel : ViewModel() {
|
||||||
val bytes = totalBytesUsedByChild[it.id] ?: 0
|
val bytes = totalBytesUsedByChild[it.id] ?: 0
|
||||||
val currentBytes = currentBytesUsedByChild[it.id] ?: 0
|
val currentBytes = currentBytesUsedByChild[it.id] ?: 0
|
||||||
if (bytes <= 0 || downloads <= 0) return@mapNotNull null
|
if (bytes <= 0 || downloads <= 0) return@mapNotNull null
|
||||||
|
val isSelected = selectedItems.value?.any { header ->
|
||||||
|
it.id == header.data.id
|
||||||
|
} ?: false
|
||||||
val movieEpisode =
|
val movieEpisode =
|
||||||
if (!it.type.isMovieType()) null
|
if (!it.type.isMovieType()) null
|
||||||
else context.getKey<VideoDownloadHelper.DownloadEpisodeCached>(
|
else context.getKey<VideoDownloadHelper.DownloadEpisodeCached>(
|
||||||
|
@ -187,6 +215,7 @@ class DownloadViewModel : ViewModel() {
|
||||||
child = movieEpisode,
|
child = movieEpisode,
|
||||||
currentOngoingDownloads = 0,
|
currentOngoingDownloads = 0,
|
||||||
totalDownloads = downloads,
|
totalDownloads = downloads,
|
||||||
|
isSelected = isSelected,
|
||||||
)
|
)
|
||||||
}.sortedBy {
|
}.sortedBy {
|
||||||
(it.child?.episode ?: 0) + (it.child?.season?.times(10000) ?: 0)
|
(it.child?.episode ?: 0) + (it.child?.season?.times(10000) ?: 0)
|
||||||
|
@ -212,9 +241,13 @@ class DownloadViewModel : ViewModel() {
|
||||||
}.mapNotNull {
|
}.mapNotNull {
|
||||||
val info = getDownloadFileInfoAndUpdateSettings(context, it.id)
|
val info = getDownloadFileInfoAndUpdateSettings(context, it.id)
|
||||||
?: return@mapNotNull null
|
?: return@mapNotNull null
|
||||||
|
val isSelected = selectedItems.value?.any { child ->
|
||||||
|
it.id == child.data.id
|
||||||
|
} ?: false
|
||||||
VisualDownloadCached.Child(
|
VisualDownloadCached.Child(
|
||||||
currentBytes = info.fileLength,
|
currentBytes = info.fileLength,
|
||||||
totalBytes = info.totalBytes,
|
totalBytes = info.totalBytes,
|
||||||
|
isSelected = isSelected,
|
||||||
data = it,
|
data = it,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue