From 9106fc5b940e8b55b0299888ee0914cae2bec829 Mon Sep 17 00:00:00 2001 From: Ivan Iskandar <12537387+ivaniskandar@users.noreply.github.com> Date: Sat, 9 Oct 2021 22:41:45 +0700 Subject: [PATCH] Grouped chapter download list by source (#5575) --- .../tachiyomi/ui/download/DownloadAdapter.kt | 8 +- .../ui/download/DownloadController.kt | 85 ++++++++++--------- .../ui/download/DownloadHeaderHolder.kt | 35 ++++++++ .../ui/download/DownloadHeaderItem.kt | 52 ++++++++++++ .../tachiyomi/ui/download/DownloadHolder.kt | 3 - .../tachiyomi/ui/download/DownloadItem.kt | 7 +- .../ui/download/DownloadPresenter.kt | 10 ++- app/src/main/res/layout/download_header.xml | 42 +++++++++ app/src/main/res/layout/download_item.xml | 21 +---- 9 files changed, 199 insertions(+), 64 deletions(-) create mode 100644 app/src/main/java/eu/kanade/tachiyomi/ui/download/DownloadHeaderHolder.kt create mode 100644 app/src/main/java/eu/kanade/tachiyomi/ui/download/DownloadHeaderItem.kt create mode 100644 app/src/main/res/layout/download_header.xml diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/download/DownloadAdapter.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/download/DownloadAdapter.kt index 01c72f2df6..9ba345141b 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/download/DownloadAdapter.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/download/DownloadAdapter.kt @@ -2,13 +2,14 @@ package eu.kanade.tachiyomi.ui.download import android.view.MenuItem import eu.davidea.flexibleadapter.FlexibleAdapter +import eu.davidea.flexibleadapter.items.AbstractFlexibleItem /** * Adapter storing a list of downloads. * * @param context the context of the fragment containing this adapter. */ -class DownloadAdapter(controller: DownloadController) : FlexibleAdapter( +class DownloadAdapter(controller: DownloadController) : FlexibleAdapter>( null, controller, true @@ -19,6 +20,11 @@ class DownloadAdapter(controller: DownloadController) : FlexibleAdapter> reorderQueue(selector: (DownloadItem) -> R, reverse: Boolean = false) { val adapter = adapter ?: return - val items = adapter.currentItems.sortedBy(selector).toMutableList() - if (reverse) { - items.reverse() + val newDownloads = mutableListOf() + adapter.headerItems.forEach { headerItem -> + headerItem as DownloadHeaderItem + headerItem.subItems = headerItem.subItems.sortedBy(selector).toMutableList().apply { + if (reverse) { + reverse() + } + } + newDownloads.addAll(headerItem.subItems.map { it.download }) } - adapter.updateDataSet(items) - val downloads = items.mapNotNull { it.download } - presenter.reorder(downloads) + presenter.reorder(newDownloads) } /** @@ -254,7 +258,7 @@ class DownloadController : * * @param downloads the downloads from the queue. */ - fun onNextDownloads(downloads: List) { + fun onNextDownloads(downloads: List) { activity?.invalidateOptionsMenu() setInformationView() adapter?.updateDataSet(downloads) @@ -327,7 +331,11 @@ class DownloadController : */ override fun onItemReleased(position: Int) { val adapter = adapter ?: return - val downloads = (0 until adapter.itemCount).mapNotNull { adapter.getItem(it)?.download } + val downloads = adapter.headerItems.flatMap { header -> + adapter.getSectionItems(header).map { item -> + (item as DownloadItem).download + } + } presenter.reorder(downloads) } @@ -338,38 +346,37 @@ class DownloadController : * @param menuItem The menu Item pressed */ override fun onMenuItemClick(position: Int, menuItem: MenuItem) { - when (menuItem.itemId) { - R.id.move_to_top, R.id.move_to_bottom -> { - val download = adapter?.getItem(position) ?: return - val items = adapter?.currentItems?.toMutableList() ?: return - items.remove(download) - if (menuItem.itemId == R.id.move_to_top) { - items.add(0, download) - } else { - items.add(download) + val item = adapter?.getItem(position) ?: return + if (item is DownloadItem) { + when (menuItem.itemId) { + R.id.move_to_top, R.id.move_to_bottom -> { + val headerItems = adapter?.headerItems ?: return + val newDownloads = mutableListOf() + headerItems.forEach { headerItem -> + headerItem as DownloadHeaderItem + if (headerItem == item.header) { + headerItem.removeSubItem(item) + if (menuItem.itemId == R.id.move_to_top) { + headerItem.addSubItem(0, item) + } else { + headerItem.addSubItem(item) + } + } + newDownloads.addAll(headerItem.subItems.map { it.download }) + } + presenter.reorder(newDownloads) } - - val adapter = adapter ?: return - adapter.updateDataSet(items) - val downloads = adapter.currentItems.mapNotNull { it?.download } - presenter.reorder(downloads) - } - R.id.cancel_download -> { - val download = adapter?.getItem(position)?.download ?: return - presenter.cancelDownload(download) - - val adapter = adapter ?: return - adapter.removeItem(position) - val downloads = adapter.currentItems.mapNotNull { it?.download } - presenter.reorder(downloads) - } - R.id.cancel_series -> { - val download = adapter?.getItem(position)?.download ?: return - val allDownloadsForSeries = adapter?.currentItems - ?.filter { download.manga.id == it.download.manga.id } - ?.map(DownloadItem::download) - if (!allDownloadsForSeries.isNullOrEmpty()) { - presenter.cancelDownloads(allDownloadsForSeries) + R.id.cancel_download -> { + presenter.cancelDownload(item.download) + } + R.id.cancel_series -> { + val allDownloadsForSeries = adapter?.currentItems + ?.filterIsInstance() + ?.filter { item.download.manga.id == it.download.manga.id } + ?.map(DownloadItem::download) + if (!allDownloadsForSeries.isNullOrEmpty()) { + presenter.cancelDownloads(allDownloadsForSeries) + } } } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/download/DownloadHeaderHolder.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/download/DownloadHeaderHolder.kt new file mode 100644 index 0000000000..59d604974b --- /dev/null +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/download/DownloadHeaderHolder.kt @@ -0,0 +1,35 @@ +package eu.kanade.tachiyomi.ui.download + +import android.annotation.SuppressLint +import android.view.View +import androidx.recyclerview.widget.ItemTouchHelper +import eu.davidea.flexibleadapter.FlexibleAdapter +import eu.davidea.viewholders.ExpandableViewHolder +import eu.kanade.tachiyomi.databinding.DownloadHeaderBinding + +class DownloadHeaderHolder(view: View, adapter: FlexibleAdapter<*>) : ExpandableViewHolder(view, adapter) { + + private val binding = DownloadHeaderBinding.bind(view) + + @SuppressLint("SetTextI18n") + fun bind(item: DownloadHeaderItem) { + setDragHandleView(binding.reorder) + binding.title.text = "${item.name} (${item.size})" + } + + override fun onActionStateChanged(position: Int, actionState: Int) { + super.onActionStateChanged(position, actionState) + if (actionState == ItemTouchHelper.ACTION_STATE_DRAG) { + binding.container.isDragged = true + mAdapter.collapseAll() + } + } + + override fun onItemReleased(position: Int) { + super.onItemReleased(position) + binding.container.isDragged = false + mAdapter as DownloadAdapter + mAdapter.expandAll() + mAdapter.downloadItemListener.onItemReleased(position) + } +} diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/download/DownloadHeaderItem.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/download/DownloadHeaderItem.kt new file mode 100644 index 0000000000..3d8a47380c --- /dev/null +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/download/DownloadHeaderItem.kt @@ -0,0 +1,52 @@ +package eu.kanade.tachiyomi.ui.download + +import android.view.View +import androidx.recyclerview.widget.RecyclerView +import eu.davidea.flexibleadapter.FlexibleAdapter +import eu.davidea.flexibleadapter.items.AbstractExpandableHeaderItem +import eu.davidea.flexibleadapter.items.IFlexible +import eu.kanade.tachiyomi.R + +data class DownloadHeaderItem( + val name: String, + val size: Int +) : AbstractExpandableHeaderItem() { + + override fun getLayoutRes(): Int { + return R.layout.download_header + } + + override fun createViewHolder( + view: View, + adapter: FlexibleAdapter> + ): DownloadHeaderHolder { + return DownloadHeaderHolder(view, adapter) + } + + override fun bindViewHolder( + adapter: FlexibleAdapter>, + holder: DownloadHeaderHolder, + position: Int, + payloads: List? + ) { + holder.bind(this) + } + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other is DownloadHeaderItem) { + return name == other.name + } + return false + } + + override fun hashCode(): Int { + return name.hashCode() + } + + init { + isHidden = false + isExpanded = true + isSelectable = false + } +} diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/download/DownloadHolder.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/download/DownloadHolder.kt index f0ff9c3b72..371869fe68 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/download/DownloadHolder.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/download/DownloadHolder.kt @@ -40,9 +40,6 @@ class DownloadHolder(private val view: View, val adapter: DownloadAdapter) : // Update the manga title binding.mangaFullTitle.text = download.manga.title - // Update the manga source - binding.mangaSource.text = download.source.name - // Update the progress bar and the number of downloaded pages val pages = download.pages if (pages == null) { diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/download/DownloadItem.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/download/DownloadItem.kt index e523bbe02f..3393d7c12d 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/download/DownloadItem.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/download/DownloadItem.kt @@ -3,12 +3,15 @@ package eu.kanade.tachiyomi.ui.download import android.view.View import androidx.recyclerview.widget.RecyclerView import eu.davidea.flexibleadapter.FlexibleAdapter -import eu.davidea.flexibleadapter.items.AbstractFlexibleItem +import eu.davidea.flexibleadapter.items.AbstractSectionableItem import eu.davidea.flexibleadapter.items.IFlexible import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.data.download.model.Download -class DownloadItem(val download: Download) : AbstractFlexibleItem() { +class DownloadItem( + val download: Download, + header: DownloadHeaderItem +) : AbstractSectionableItem(header) { override fun getLayoutRes(): Int { return R.layout.download_item diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/download/DownloadPresenter.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/download/DownloadPresenter.kt index 08fecbc213..0dc8f3eb2e 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/download/DownloadPresenter.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/download/DownloadPresenter.kt @@ -29,7 +29,15 @@ class DownloadPresenter : BasePresenter() { downloadQueue.getUpdatedObservable() .observeOn(AndroidSchedulers.mainThread()) - .map { it.map(::DownloadItem) } + .map { downloads -> + downloads + .groupBy { it.source } + .map { entry -> + DownloadHeaderItem(entry.key.name, entry.value.size).apply { + addSubItems(0, entry.value.map { DownloadItem(it, this) }) + } + } + } .subscribeLatestCache(DownloadController::onNextDownloads) { _, error -> logcat(LogPriority.ERROR, error) } diff --git a/app/src/main/res/layout/download_header.xml b/app/src/main/res/layout/download_header.xml new file mode 100644 index 0000000000..191e7e315f --- /dev/null +++ b/app/src/main/res/layout/download_header.xml @@ -0,0 +1,42 @@ + + + + + + + + + + + + diff --git a/app/src/main/res/layout/download_item.xml b/app/src/main/res/layout/download_item.xml index d3e5124c0c..a7b47d396e 100644 --- a/app/src/main/res/layout/download_item.xml +++ b/app/src/main/res/layout/download_item.xml @@ -5,14 +5,14 @@ android:id="@+id/container" android:layout_width="match_parent" android:layout_height="wrap_content" - android:layout_marginTop="8dp" app:cardBackgroundColor="?android:attr/colorBackground" app:cardElevation="0dp" app:cardForegroundColor="@color/draggable_card_foreground"> + android:layout_height="wrap_content" + android:paddingVertical="4dp"> @@ -84,20 +83,6 @@ app:layout_constraintTop_toTopOf="@+id/manga_full_title" tools:text="0/10" /> - -