From 068b218f7a16061435e71c9bb85a4815ef8d093b Mon Sep 17 00:00:00 2001 From: Luna712 <142361265+Luna712@users.noreply.github.com> Date: Sat, 6 Jul 2024 15:10:43 -0600 Subject: [PATCH] Push initial UI --- .../ui/download/DownloadAdapter.kt | 187 ++++++++++-------- .../ui/download/DownloadButtonSetup.kt | 43 ++-- .../ui/download/DownloadChildFragment.kt | 28 +-- .../ui/download/DownloadFragment.kt | 80 ++++++-- .../ui/download/DownloadViewModel.kt | 39 +++- .../res/layout/download_delete_toolbar.xml | 41 ++++ .../res/layout/download_header_episode.xml | 11 ++ .../main/res/layout/fragment_downloads.xml | 4 + 8 files changed, 276 insertions(+), 157 deletions(-) create mode 100644 app/src/main/res/layout/download_delete_toolbar.xml 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 e8f969e8..a15feceb 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 @@ -27,7 +27,6 @@ const val DOWNLOAD_ACTION_RESUME_DOWNLOAD = 2 const val DOWNLOAD_ACTION_PAUSE_DOWNLOAD = 3 const val DOWNLOAD_ACTION_DOWNLOAD = 4 const val DOWNLOAD_ACTION_LONG_CLICK = 5 -const val DOWNLOAD_ACTION_DELETE_MULTIPLE_FILES = 6 const val DOWNLOAD_ACTION_GO_TO_CHILD = 0 const val DOWNLOAD_ACTION_LOAD_RESULT = 1 @@ -58,15 +57,11 @@ abstract class VisualDownloadCached( } } -abstract class DownloadActionEventBase( - open val action: Int, - open val data: VideoDownloadHelper.DownloadEpisodeCached? -) - data class VisualDownloadChildCached( override val currentBytes: Long, override val totalBytes: Long, override val data: VideoDownloadHelper.DownloadEpisodeCached, + val selected: Boolean = false, ): VisualDownloadCached(currentBytes, totalBytes, data) data class VisualDownloadHeaderCached( @@ -76,17 +71,13 @@ data class VisualDownloadHeaderCached( val child: VideoDownloadHelper.DownloadEpisodeCached?, val currentOngoingDownloads: Int, val totalDownloads: Int, + val selected: Boolean = false, ): VisualDownloadCached(currentBytes, totalBytes, data) data class DownloadClickEvent( - override val action: Int, - override val data: VideoDownloadHelper.DownloadEpisodeCached -): DownloadActionEventBase(action, data) - -data class DownloadDeleteEvent( - override val action: Int, - val items: List -): DownloadActionEventBase(action, null) + val action: Int, + val data: VideoDownloadHelper.DownloadEpisodeCached +) data class DownloadHeaderClickEvent( val action: Int, @@ -94,19 +85,20 @@ data class DownloadHeaderClickEvent( ) class DownloadAdapter( - private val actionCallback: (DownloadActionEventBase) -> Unit, - private val clickCallback: (DownloadHeaderClickEvent) -> Unit, + private val headerClickCallback: (DownloadHeaderClickEvent) -> Unit, + private val mediaClickCallback: (DownloadClickEvent) -> Unit, + private val selectedChangedCallback: (Int, String, Boolean) -> Unit, ) : ListAdapter(DiffCallback()) { + private var showDeleteCheckbox: Boolean = false + companion object { private const val VIEW_TYPE_HEADER = 0 private const val VIEW_TYPE_CHILD = 1 } inner class DownloadViewHolder( - private val binding: ViewBinding, - private val actionCallback: (DownloadActionEventBase) -> Unit, - private val clickCallback: (DownloadHeaderClickEvent) -> Unit, + private val binding: ViewBinding ) : RecyclerView.ViewHolder(binding.root) { fun bind(card: VisualDownloadCached?) { @@ -116,86 +108,105 @@ class DownloadAdapter( } } - @SuppressLint("SetTextI18n") private fun bindHeader(card: VisualDownloadHeaderCached?) { - if (binding !is DownloadHeaderEpisodeBinding) return - card ?: return - val d = card.data + if (binding !is DownloadHeaderEpisodeBinding || card == null) return + val data = card.data binding.apply { downloadHeaderPoster.apply { - setImage(d.poster) + setImage(data.poster) setOnClickListener { - clickCallback.invoke(DownloadHeaderClickEvent(DOWNLOAD_ACTION_LOAD_RESULT, d)) + headerClickCallback.invoke(DownloadHeaderClickEvent(DOWNLOAD_ACTION_LOAD_RESULT, data)) } } - - downloadHeaderTitle.text = d.name - val formattedSizeString = formatShortFileSize(itemView.context, card.totalBytes) + downloadHeaderTitle.text = data.name + val formattedSize = formatShortFileSize(itemView.context, card.totalBytes) if (card.child != null) { - downloadHeaderGotoChild.isVisible = false + handleChildDownload(card, formattedSize) + } else handleParentDownload(card, formattedSize) + } + } - val status = downloadButton.getStatus(card.child.id, card.currentBytes, card.totalBytes) - if (status == DownloadStatusTell.IsDone) { - // We do this here instead if we are finished downloading - // so that we can use the value from the view model - // rather than extra unneeded disk operations and to prevent a - // delay in updating download icon state. - downloadButton.setProgress(card.currentBytes, card.totalBytes) - downloadButton.applyMetaData(card.child.id, card.currentBytes, card.totalBytes) - // We will let the view model handle this - downloadButton.doSetProgress = false - downloadButton.progressBar.progressDrawable = - downloadButton.getDrawableFromStatus(status) - ?.let { ContextCompat.getDrawable(downloadButton.context, it) } - downloadHeaderInfo.text = formattedSizeString - } else { - downloadButton.doSetProgress = true - downloadButton.progressBar.progressDrawable = - ContextCompat.getDrawable(downloadButton.context, downloadButton.progressDrawable) - } + private fun DownloadHeaderEpisodeBinding.handleChildDownload( + card: VisualDownloadHeaderCached, + formattedSize: String + ) { + card.child ?: return + downloadHeaderGotoChild.isVisible = false - downloadButton.setDefaultClickListener(card.child, downloadHeaderInfo, actionCallback) - downloadButton.isVisible = true + val status = downloadButton.getStatus(card.child.id, card.currentBytes, card.totalBytes) + if (status == DownloadStatusTell.IsDone) { + // We do this here instead if we are finished downloading + // so that we can use the value from the view model + // rather than extra unneeded disk operations and to prevent a + // delay in updating download icon state. + downloadButton.setProgress(card.currentBytes, card.totalBytes) + downloadButton.applyMetaData(card.child.id, card.currentBytes, card.totalBytes) + // We will let the view model handle this + downloadButton.doSetProgress = false + downloadButton.progressBar.progressDrawable = + downloadButton.getDrawableFromStatus(status) + ?.let { ContextCompat.getDrawable(downloadButton.context, it) } + downloadHeaderInfo.text = formattedSize + } else { + downloadButton.doSetProgress = true + downloadButton.progressBar.progressDrawable = + ContextCompat.getDrawable(downloadButton.context, downloadButton.progressDrawable) + } - episodeHolder.setOnClickListener { - actionCallback.invoke(DownloadClickEvent(DOWNLOAD_ACTION_PLAY_FILE, card.child)) - } - } else { - downloadButton.isVisible = false - downloadHeaderGotoChild.isVisible = true + downloadButton.setDefaultClickListener(card.child, downloadHeaderInfo, mediaClickCallback) + downloadButton.isVisible = !showDeleteCheckbox - try { - downloadHeaderInfo.text = downloadHeaderInfo.context.getString(R.string.extra_info_format) - .format( - card.totalDownloads, - downloadHeaderInfo.context.resources.getQuantityString( - R.plurals.episodes, - card.totalDownloads - ), - formattedSizeString - ) - } catch (e: Exception) { - // You probably formatted incorrectly - downloadHeaderInfo.text = "Error" - logError(e) - } + episodeHolder.apply { + setOnClickListener { + mediaClickCallback.invoke(DownloadClickEvent(DOWNLOAD_ACTION_PLAY_FILE, card.child)) + } + setOnLongClickListener { + mediaClickCallback.invoke(DownloadClickEvent(DOWNLOAD_ACTION_LONG_CLICK, card.child)) + true + } + } - episodeHolder.setOnClickListener { - clickCallback.invoke(DownloadHeaderClickEvent(DOWNLOAD_ACTION_GO_TO_CHILD, d)) - } + deleteCheckbox.apply { + isVisible = showDeleteCheckbox + isChecked = card.selected + setOnCheckedChangeListener { _, isChecked -> + selectedChangedCallback.invoke(card.data.id, card.data.name, isChecked) } } } - private fun bindChild(card: VisualDownloadChildCached?) { - if (binding !is DownloadChildEpisodeBinding) return - card ?: return - val d = card.data + @SuppressLint("SetTextI18n") + private fun DownloadHeaderEpisodeBinding.handleParentDownload( + card: VisualDownloadHeaderCached, + formattedSize: String + ) { + downloadButton.isVisible = false + downloadHeaderGotoChild.isVisible = true + try { + downloadHeaderInfo.text = downloadHeaderInfo.context.getString(R.string.extra_info_format).format( + card.totalDownloads, + downloadHeaderInfo.context.resources.getQuantityString(R.plurals.episodes, card.totalDownloads), + formattedSize + ) + } catch (e: Exception) { + downloadHeaderInfo.text = "Error" + logError(e) + } + + episodeHolder.setOnClickListener { + headerClickCallback.invoke(DownloadHeaderClickEvent(DOWNLOAD_ACTION_GO_TO_CHILD, card.data)) + } + } + + private fun bindChild(card: VisualDownloadChildCached?) { + if (binding !is DownloadChildEpisodeBinding || card == null) return + + val data = card.data binding.apply { - val posDur = getViewPos(d.id) + val posDur = getViewPos(data.id) downloadChildEpisodeProgress.apply { isVisible = posDur != null posDur?.let { @@ -205,14 +216,14 @@ class DownloadAdapter( } } - val status = downloadButton.getStatus(d.id, card.currentBytes, card.totalBytes) + val status = downloadButton.getStatus(data.id, card.currentBytes, card.totalBytes) if (status == DownloadStatusTell.IsDone) { // We do this here instead if we are finished downloading // so that we can use the value from the view model // rather than extra unneeded disk operations and to prevent a // delay in updating download icon state. downloadButton.setProgress(card.currentBytes, card.totalBytes) - downloadButton.applyMetaData(d.id, card.currentBytes, card.totalBytes) + downloadButton.applyMetaData(data.id, card.currentBytes, card.totalBytes) // We will let the view model handle this downloadButton.doSetProgress = false downloadButton.progressBar.progressDrawable = @@ -225,16 +236,16 @@ class DownloadAdapter( ContextCompat.getDrawable(downloadButton.context, downloadButton.progressDrawable) } - downloadButton.setDefaultClickListener(d, downloadChildEpisodeTextExtra, actionCallback) + downloadButton.setDefaultClickListener(data, downloadChildEpisodeTextExtra, mediaClickCallback) downloadButton.isVisible = true downloadChildEpisodeText.apply { - text = context.getNameFull(d.name, d.episode, d.season) + text = context.getNameFull(data.name, data.episode, data.season) isSelected = true // Needed for text repeating } downloadChildEpisodeHolder.setOnClickListener { - actionCallback.invoke(DownloadClickEvent(DOWNLOAD_ACTION_PLAY_FILE, d)) + mediaClickCallback.invoke(DownloadClickEvent(DOWNLOAD_ACTION_PLAY_FILE, data)) } } } @@ -247,7 +258,7 @@ class DownloadAdapter( VIEW_TYPE_CHILD -> DownloadChildEpisodeBinding.inflate(inflater, parent, false) else -> throw IllegalArgumentException("Invalid view type") } - return DownloadViewHolder(binding, actionCallback, clickCallback) + return DownloadViewHolder(binding) } override fun onBindViewHolder(holder: DownloadViewHolder, position: Int) { @@ -262,6 +273,12 @@ class DownloadAdapter( } } + fun setDeleteCheckboxVisibility(visible: Boolean) { + if (showDeleteCheckbox == visible) return + showDeleteCheckbox = visible + notifyItemRangeChanged(0, itemCount) + } + class DiffCallback : DiffUtil.ItemCallback() { override fun areItemsTheSame(oldItem: VisualDownloadCached, newItem: VisualDownloadCached): Boolean { return oldItem.data.id == newItem.data.id diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/download/DownloadButtonSetup.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/download/DownloadButtonSetup.kt index 62622a89..7d39afab 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/download/DownloadButtonSetup.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/download/DownloadButtonSetup.kt @@ -20,11 +20,10 @@ import com.lagradost.cloudstream3.utils.VideoDownloadManager import kotlinx.coroutines.MainScope object DownloadButtonSetup { - fun handleDownloadClick(click: DownloadActionEventBase) { + fun handleDownloadClick(click: DownloadClickEvent) { + val id = click.data.id when (click.action) { DOWNLOAD_ACTION_DELETE_FILE -> { - if (click !is DownloadDeleteEvent) return - val id = click.items.firstOrNull()?.id ?: return activity?.let { ctx -> val builder: AlertDialog.Builder = AlertDialog.Builder(ctx) val dialogClickListener = @@ -43,9 +42,9 @@ object DownloadButtonSetup { .setMessage( ctx.getString(R.string.delete_message).format( ctx.getNameFull( - click.items.firstOrNull()?.name, - click.items.firstOrNull()?.episode, - click.items.firstOrNull()?.season + click.data.name, + click.data.episode, + click.data.season ) ) ) @@ -59,17 +58,15 @@ object DownloadButtonSetup { } } DOWNLOAD_ACTION_PAUSE_DOWNLOAD -> { - val id = click.data?.id ?: return VideoDownloadManager.downloadEvent.invoke( - Pair(id, VideoDownloadManager.DownloadActionType.Pause) + Pair(click.data.id, VideoDownloadManager.DownloadActionType.Pause) ) } DOWNLOAD_ACTION_RESUME_DOWNLOAD -> { - val id = click.data?.id ?: return activity?.let { ctx -> if (VideoDownloadManager.downloadStatus.containsKey(id) && VideoDownloadManager.downloadStatus[id] == VideoDownloadManager.DownloadType.IsPaused) { VideoDownloadManager.downloadEvent.invoke( - Pair(id, VideoDownloadManager.DownloadActionType.Resume) + Pair(click.data.id, VideoDownloadManager.DownloadActionType.Resume) ) } else { val pkg = VideoDownloadManager.getDownloadResumePackage(ctx, id) @@ -77,19 +74,18 @@ object DownloadButtonSetup { VideoDownloadManager.downloadFromResumeUsingWorker(ctx, pkg) } else { VideoDownloadManager.downloadEvent.invoke( - Pair(id, VideoDownloadManager.DownloadActionType.Resume) + Pair(click.data.id, VideoDownloadManager.DownloadActionType.Resume) ) } } } } DOWNLOAD_ACTION_LONG_CLICK -> { - val id = click.data?.id ?: return activity?.let { act -> val length = VideoDownloadManager.getDownloadFileInfoAndUpdateSettings( act, - id + click.data.id )?.fileLength ?: 0 if (length > 0) { @@ -100,20 +96,19 @@ object DownloadButtonSetup { } } DOWNLOAD_ACTION_PLAY_FILE -> { - val id = click.data?.id ?: return activity?.let { act -> val info = VideoDownloadManager.getDownloadFileInfoAndUpdateSettings( act, - id + click.data.id ) ?: return val keyInfo = getKey( VideoDownloadManager.KEY_DOWNLOAD_INFO, - id.toString() + click.data.id.toString() ) ?: return val parent = getKey( DOWNLOAD_HEADER_CACHE, - click.data?.parentId.toString() + click.data.parentId.toString() ) ?: return act.navigate( @@ -123,11 +118,11 @@ object DownloadButtonSetup { ExtractorUri( uri = info.path, - id = id, - parentId = click.data?.parentId, + id = click.data.id, + parentId = click.data.parentId, name = act.getString(R.string.downloaded_file), //click.data.name ?: keyInfo.displayName - season = click.data?.season, - episode = click.data?.episode, + season = click.data.season, + episode = click.data.episode, headerName = parent.name, tvType = parent.type, @@ -144,13 +139,13 @@ object DownloadButtonSetup { // keyInfo.basePath, // keyInfo.relativePath, // keyInfo.displayName, - // click.data?.parentId, - // click.data?.id, + // click.data.parentId, + // click.data.id, // headerName ?: "null", // if (click.data.episode <= 0) null else click.data.episode, // click.data.season // ), - // getViewPos(click.data?.id)?.position ?: 0 + // getViewPos(click.data.id)?.position ?: 0 //) ) } 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 94e4d249..9457c366 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 @@ -107,8 +107,13 @@ class DownloadChildFragment : Fragment() { } val adapter = DownloadAdapter( - { actionEvent -> handleActionEvent(folder, actionEvent, context) }, - {} + {}, + { downloadClickEvent -> + handleDownloadClick(downloadClickEvent) + if (downloadClickEvent.action == DOWNLOAD_ACTION_DELETE_FILE) { + setUpDownloadDeleteListener(folder) + } + }, { _, _, _ -> } ) binding?.downloadChildList?.apply { @@ -125,25 +130,6 @@ class DownloadChildFragment : Fragment() { updateList(folder) } - private fun handleActionEvent(folder: String, actionEvent: DownloadActionEventBase, context: Context?) { - when (actionEvent.action) { - DOWNLOAD_ACTION_DELETE_MULTIPLE_FILES -> { - if (actionEvent is DownloadDeleteEvent) { - context?.let { downloadsViewModel.handleMultiDelete(it, actionEvent) } - } - } - DOWNLOAD_ACTION_DELETE_FILE -> { - val downloadDeleteEvent = DownloadDeleteEvent( - action = DOWNLOAD_ACTION_DELETE_FILE, - items = listOf(actionEvent.data) - ) - handleDownloadClick(downloadDeleteEvent) - setUpDownloadDeleteListener(folder) - } - else -> handleDownloadClick(actionEvent) - } - } - private fun setUpDownloadDeleteListener(folder: String) { downloadDeleteEventListener = { id: Int -> val list = (binding?.downloadChildList?.adapter as? DownloadAdapter)?.currentList 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 8abc1a3d..bb2a3c5e 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 @@ -91,6 +91,10 @@ class DownloadFragment : Fragment() { hideKeyboard() binding?.downloadStorageAppbar?.setAppBarNoScrollFlagsOnTV() + // We always want fresh selections + // when navigating to downloads + downloadsViewModel.resetSelected() + observe(downloadsViewModel.headerCards) { (binding?.downloadList?.adapter as? DownloadAdapter)?.submitList(it) binding?.downloadLoading?.isVisible = false @@ -106,10 +110,32 @@ class DownloadFragment : Fragment() { observe(downloadsViewModel.downloadBytes) { updateStorageInfo(view.context, it, R.string.app_storage, binding?.downloadAppTxt, binding?.downloadApp) } + observe(downloadsViewModel.selectedIds) { + handleSelectedChange(it) + updateSelectedState(it) + binding?.downloadDeleteToolbar?.btnDelete?.text = + getString(R.string.delete_count).format(it.count()) + } val adapter = DownloadAdapter( - { actionEvent -> handleActionEvent(actionEvent, context) }, - { click -> handleItemClick(click) } + { 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 + ) + } + } + }, + { id, name, isChecked -> + if (isChecked) { + downloadsViewModel.addSelected(id, name) + } else downloadsViewModel.removeSelected(id) + } ) binding?.downloadList?.apply { @@ -144,25 +170,6 @@ class DownloadFragment : Fragment() { fixPaddingStatusbar(binding?.downloadRoot) } - private fun handleActionEvent(actionEvent: DownloadActionEventBase, context: Context?) { - when (actionEvent.action) { - DOWNLOAD_ACTION_DELETE_MULTIPLE_FILES -> { - if (actionEvent is DownloadDeleteEvent) { - context?.let { downloadsViewModel.handleMultiDelete(it, actionEvent) } - } - } - DOWNLOAD_ACTION_DELETE_FILE -> { - val downloadDeleteEvent = DownloadDeleteEvent( - action = DOWNLOAD_ACTION_DELETE_FILE, - items = listOf(actionEvent.data) - ) - handleDownloadClick(downloadDeleteEvent) - setUpDownloadDeleteListener() - } - else -> handleDownloadClick(actionEvent) - } - } - private fun handleItemClick(click: DownloadHeaderClickEvent) { when (click.action) { DOWNLOAD_ACTION_GO_TO_CHILD -> { @@ -180,6 +187,37 @@ class DownloadFragment : Fragment() { } } + private fun handleSelectedChange(selected: HashMap) { + val adapter = (binding?.downloadList?.adapter as? DownloadAdapter) + if (selected.isNotEmpty()) { + binding?.downloadDeleteToolbar?.downloadDeleteToolbar?.isVisible = true + binding?.downloadStorageAppbar?.isVisible = false + binding?.downloadDeleteToolbar?.btnDelete?.setOnClickListener { + context?.let { ctx -> downloadsViewModel.handleMultiDelete(ctx) } + } + binding?.downloadDeleteToolbar?.btnCancel?.setOnClickListener { + downloadsViewModel.resetSelected() + } + + adapter?.setDeleteCheckboxVisibility(true) + } else { + binding?.downloadDeleteToolbar?.downloadDeleteToolbar?.isVisible = false + binding?.downloadStorageAppbar?.isVisible = + // Make sure we don't display it early + !downloadsViewModel.headerCards.value.isNullOrEmpty() && + downloadsViewModel.usedBytes.value?.let { it > 0 } == true + adapter?.setDeleteCheckboxVisibility(false) + } + } + + private fun updateSelectedState(selected: HashMap) { + val currentList = downloadsViewModel.headerCards.value ?: return + val updatedList = currentList.map { header -> + header.copy(selected = selected.keys.contains(header.data.id)) + } + (binding?.downloadList?.adapter as? DownloadAdapter)?.submitList(updatedList) + } + private fun setUpDownloadDeleteListener() { downloadDeleteEventListener = { id -> val list = (binding?.downloadList?.adapter as? DownloadAdapter)?.currentList 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 15851da0..da9801b4 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,12 +34,42 @@ class DownloadViewModel : ViewModel() { private val _availableBytes = MutableLiveData() private val _downloadBytes = MutableLiveData() + private val _selectedIds = MutableLiveData>(HashMap()) + val usedBytes: LiveData = _usedBytes val availableBytes: LiveData = _availableBytes val downloadBytes: LiveData = _downloadBytes + val selectedIds: LiveData> = _selectedIds + private var previousVisual: List? = null + fun addSelected(id: Int, name: String) { + _selectedIds.value?.let { selectedIds -> + selectedIds[id] = name + _selectedIds.postValue(selectedIds) + } + } + + fun removeSelected(id: Int) { + _selectedIds.value?.let { selectedIds -> + selectedIds.remove(id) + _selectedIds.postValue(selectedIds) + } + } + + fun resetSelected() { + _selectedIds.postValue(HashMap()) + } + + private fun getSelectedIds(): List { + return _selectedIds.value?.keys?.toList() ?: emptyList() + } + + private fun getSelectedNames(): List { + return _selectedIds.value?.values?.toList() ?: emptyList() + } + fun updateList(context: Context) = viewModelScope.launchSafe { val children = withContext(Dispatchers.IO) { context.getKeys(DOWNLOAD_EPISODE_CACHE) @@ -127,12 +157,9 @@ class DownloadViewModel : ViewModel() { } } - fun handleMultiDelete(context: Context, event: DownloadDeleteEvent) = viewModelScope.launchSafe { - val ids: List = event.items.mapNotNull { it?.id } - .takeIf { it.isNotEmpty() } ?: return@launchSafe - - val names: List = event.items.mapNotNull { it?.name } - .takeIf { it.isNotEmpty() } ?: return@launchSafe + fun handleMultiDelete(context: Context) = viewModelScope.launchSafe { + val ids: List = getSelectedIds() + val names: List = getSelectedNames() showDeleteConfirmationDialog(context, ids, names) } diff --git a/app/src/main/res/layout/download_delete_toolbar.xml b/app/src/main/res/layout/download_delete_toolbar.xml new file mode 100644 index 00000000..43001c67 --- /dev/null +++ b/app/src/main/res/layout/download_delete_toolbar.xml @@ -0,0 +1,41 @@ + + + + + + + + +