From 6a310bbaa960e91db7e665befca471fa97cc85bc Mon Sep 17 00:00:00 2001 From: Bram van de Kerkhof Date: Tue, 23 Jan 2018 21:18:55 +0100 Subject: [PATCH] Added custom download option (#1185) * Added custom download option * Implemented new design. TODO comments (like always...) * W00t comments * Implemented code review. * Fixed commit breaking mistake :O * Small design fix --- .../ui/manga/chapter/ChaptersController.kt | 77 ++++++++----- .../manga/chapter/DownloadChaptersDialog.kt | 2 +- .../chapter/DownloadCustomChaptersDialog.kt | 77 +++++++++++++ .../widget/DialogCustomDownloadView.kt | 109 ++++++++++++++++++ .../drawable/ic_chevron_left_black_24dp.xml | 9 ++ .../ic_chevron_left_double_black_24dp.xml | 12 ++ .../drawable/ic_chevron_right_black_24dp.xml | 9 ++ .../ic_chevron_right_double_black_24dp.xml | 12 ++ .../res/layout/download_custom_amount.xml | 55 +++++++++ app/src/main/res/values/strings.xml | 3 + 10 files changed, 334 insertions(+), 31 deletions(-) create mode 100644 app/src/main/java/eu/kanade/tachiyomi/ui/manga/chapter/DownloadCustomChaptersDialog.kt create mode 100644 app/src/main/java/eu/kanade/tachiyomi/widget/DialogCustomDownloadView.kt create mode 100644 app/src/main/res/drawable/ic_chevron_left_black_24dp.xml create mode 100644 app/src/main/res/drawable/ic_chevron_left_double_black_24dp.xml create mode 100644 app/src/main/res/drawable/ic_chevron_right_black_24dp.xml create mode 100644 app/src/main/res/drawable/ic_chevron_right_double_black_24dp.xml create mode 100644 app/src/main/res/layout/download_custom_amount.xml diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/chapter/ChaptersController.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/chapter/ChaptersController.kt index 827c9a9220..199ece7e92 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/chapter/ChaptersController.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/chapter/ChaptersController.kt @@ -37,6 +37,7 @@ class ChaptersController : NucleusController(), SetDisplayModeDialog.Listener, SetSortingDialog.Listener, DownloadChaptersDialog.Listener, + DownloadCustomChaptersDialog.Listener, DeleteChaptersDialog.Listener { /** @@ -210,7 +211,7 @@ class ChaptersController : NucleusController(), } } - fun fetchChaptersFromSource() { + private fun fetchChaptersFromSource() { swipe_refresh?.isRefreshing = true presenter.fetchChaptersFromSource() } @@ -272,18 +273,18 @@ class ChaptersController : NucleusController(), actionMode?.invalidate() } - fun getSelectedChapters(): List { + private fun getSelectedChapters(): List { val adapter = adapter ?: return emptyList() return adapter.selectedPositions.mapNotNull { adapter.getItem(it) } } - fun createActionModeIfNeeded() { + private fun createActionModeIfNeeded() { if (actionMode == null) { actionMode = (activity as? AppCompatActivity)?.startSupportActionMode(this) } } - fun destroyActionModeIfNeeded() { + private fun destroyActionModeIfNeeded() { actionMode?.finish() } @@ -341,25 +342,25 @@ class ChaptersController : NucleusController(), // SELECTION MODE ACTIONS - fun selectAll() { + private fun selectAll() { val adapter = adapter ?: return adapter.selectAll() selectedItems.addAll(adapter.items) actionMode?.invalidate() } - fun markAsRead(chapters: List) { + private fun markAsRead(chapters: List) { presenter.markChaptersRead(chapters, true) if (presenter.preferences.removeAfterMarkedAsRead()) { deleteChapters(chapters) } } - fun markAsUnread(chapters: List) { + private fun markAsUnread(chapters: List) { presenter.markChaptersRead(chapters, false) } - fun downloadChapters(chapters: List) { + private fun downloadChapters(chapters: List) { val view = view destroyActionModeIfNeeded() presenter.downloadChapters(chapters) @@ -372,6 +373,7 @@ class ChaptersController : NucleusController(), } } + private fun showDeleteChaptersConfirmationDialog() { DeleteChaptersDialog(this).showDialog(router) } @@ -380,7 +382,7 @@ class ChaptersController : NucleusController(), deleteChapters(getSelectedChapters()) } - fun markPreviousAsRead(chapter: ChapterItem) { + private fun markPreviousAsRead(chapter: ChapterItem) { val adapter = adapter ?: return val chapters = if (presenter.sortDescending()) adapter.items.reversed() else adapter.items val chapterPos = chapters.indexOf(chapter) @@ -389,7 +391,7 @@ class ChaptersController : NucleusController(), } } - fun bookmarkChapters(chapters: List, bookmarked: Boolean) { + private fun bookmarkChapters(chapters: List, bookmarked: Boolean) { destroyActionModeIfNeeded() presenter.bookmarkChapters(chapters, bookmarked) } @@ -412,7 +414,7 @@ class ChaptersController : NucleusController(), Timber.e(error) } - fun dismissDeletingDialog() { + private fun dismissDeletingDialog() { router.popControllerWithTag(DeletingChaptersDialog.TAG) } @@ -441,29 +443,44 @@ class ChaptersController : NucleusController(), DownloadChaptersDialog(this).showDialog(router) } - override fun downloadChapters(choice: Int) { - fun getUnreadChaptersSorted() = presenter.chapters - .filter { !it.read && it.status == Download.NOT_DOWNLOADED } - .distinctBy { it.name } - .sortedByDescending { it.source_order } - - // i = 0: Download 1 - // i = 1: Download 5 - // i = 2: Download 10 - // i = 3: Download unread - // i = 4: Download all - val chaptersToDownload = when (choice) { - 0 -> getUnreadChaptersSorted().take(1) - 1 -> getUnreadChaptersSorted().take(5) - 2 -> getUnreadChaptersSorted().take(10) - 3 -> presenter.chapters.filter { !it.read } - 4 -> presenter.chapters - else -> emptyList() - } + private fun getUnreadChaptersSorted() = presenter.chapters + .filter { !it.read && it.status == Download.NOT_DOWNLOADED } + .distinctBy { it.name } + .sortedByDescending { it.source_order } + override fun downloadCustomChapters(amount: Int) { + val chaptersToDownload = getUnreadChaptersSorted().take(amount) if (chaptersToDownload.isNotEmpty()) { downloadChapters(chaptersToDownload) } } + private fun showCustomDownloadDialog() { + DownloadCustomChaptersDialog(this, presenter.chapters.size).showDialog(router) + } + + + override fun downloadChapters(choice: Int) { + // i = 0: Download 1 + // i = 1: Download 5 + // i = 2: Download 10 + // i = 3: Download x + // i = 4: Download unread + // i = 5: Download all + val chaptersToDownload = when (choice) { + 0 -> getUnreadChaptersSorted().take(1) + 1 -> getUnreadChaptersSorted().take(5) + 2 -> getUnreadChaptersSorted().take(10) + 3 -> { + showCustomDownloadDialog() + return + } + 4 -> presenter.chapters.filter { !it.read } + 5 -> presenter.chapters + else -> emptyList() + } + if (chaptersToDownload.isNotEmpty()) { + downloadChapters(chaptersToDownload) + } + } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/chapter/DownloadChaptersDialog.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/chapter/DownloadChaptersDialog.kt index c54797a1f9..c3016841c8 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/chapter/DownloadChaptersDialog.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/chapter/DownloadChaptersDialog.kt @@ -21,12 +21,12 @@ class DownloadChaptersDialog(bundle: Bundle? = null) : DialogController(bundl R.string.download_1, R.string.download_5, R.string.download_10, + R.string.download_custom, R.string.download_unread, R.string.download_all ).map { activity.getString(it) } return MaterialDialog.Builder(activity) - .title(R.string.manga_download) .negativeText(android.R.string.cancel) .items(choices) .itemsCallback { _, _, position, _ -> diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/chapter/DownloadCustomChaptersDialog.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/chapter/DownloadCustomChaptersDialog.kt new file mode 100644 index 0000000000..22ddee7bf8 --- /dev/null +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/chapter/DownloadCustomChaptersDialog.kt @@ -0,0 +1,77 @@ +package eu.kanade.tachiyomi.ui.manga.chapter + +import android.app.Dialog +import android.os.Bundle +import com.afollestad.materialdialogs.MaterialDialog +import com.bluelinelabs.conductor.Controller +import eu.kanade.tachiyomi.R +import eu.kanade.tachiyomi.ui.base.controller.DialogController +import eu.kanade.tachiyomi.widget.DialogCustomDownloadView + +/** + * Dialog used to let user select amount of chapters to download. + */ +class DownloadCustomChaptersDialog : DialogController + where T : Controller, T : DownloadCustomChaptersDialog.Listener { + + /** + * Maximum number of chapters to download in download chooser. + */ + private val maxChapters: Int + + /** + * Initialize dialog. + * @param maxChapters maximal number of chapters that user can download. + */ + constructor(target: T, maxChapters: Int) : super(Bundle().apply { + // Add maximum number of chapters to download value to bundle. + putInt(KEY_ITEM_MAX, maxChapters) + }) { + targetController = target + this.maxChapters = maxChapters + } + + /** + * Restore dialog. + * @param bundle bundle containing data from state restore. + */ + @Suppress("unused") + constructor(bundle: Bundle) : super(bundle) { + // Get maximum chapters to download from bundle + val maxChapters = bundle.getInt(KEY_ITEM_MAX, 0) + this.maxChapters = maxChapters + } + + /** + * Called when dialog is being created. + */ + override fun onCreateDialog(savedViewState: Bundle?): Dialog { + val activity = activity!! + + // Initialize view that lets user select number of chapters to download. + val view = DialogCustomDownloadView(activity).apply { + setMinMax(0, maxChapters) + } + + // Build dialog. + // when positive dialog is pressed call custom listener. + return MaterialDialog.Builder(activity) + .title(R.string.custom_download) + .customView(view, true) + .positiveText(android.R.string.ok) + .negativeText(android.R.string.cancel) + .onPositive { _, _ -> + (targetController as? Listener)?.downloadCustomChapters(view.amount) + } + .build() + } + + interface Listener { + fun downloadCustomChapters(amount: Int) + } + + private companion object { + // Key to retrieve max chapters from bundle on process death. + const val KEY_ITEM_MAX = "DownloadCustomChaptersDialog.int.maxChapters" + } +} \ No newline at end of file diff --git a/app/src/main/java/eu/kanade/tachiyomi/widget/DialogCustomDownloadView.kt b/app/src/main/java/eu/kanade/tachiyomi/widget/DialogCustomDownloadView.kt new file mode 100644 index 0000000000..03896b9008 --- /dev/null +++ b/app/src/main/java/eu/kanade/tachiyomi/widget/DialogCustomDownloadView.kt @@ -0,0 +1,109 @@ +package eu.kanade.tachiyomi.widget + +import android.content.Context +import android.text.SpannableStringBuilder +import android.util.AttributeSet +import android.view.View +import android.widget.LinearLayout +import eu.kanade.tachiyomi.R +import eu.kanade.tachiyomi.util.inflate +import kotlinx.android.synthetic.main.download_custom_amount.view.* +import timber.log.Timber + +/** + * Custom dialog to select how many chapters to download. + */ +class DialogCustomDownloadView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null) : + LinearLayout(context, attrs) { + + /** + * Current amount of custom download chooser. + */ + var amount: Int = 0 + private set + + /** + * Minimal value of custom download chooser. + */ + private var min = 0 + + /** + * Maximal value of custom download chooser. + */ + private var max = 0 + + init { + // Add view to stack + addView(inflate(R.layout.download_custom_amount)) + } + + + /** + * Called when view is added + * + * @param child + */ + override fun onViewAdded(child: View) { + super.onViewAdded(child) + + // Set download count to 0. + myNumber.text = SpannableStringBuilder(getAmount(0).toString()) + + // When user presses button decrease amount by 10. + btn_decrease_10.setOnClickListener { + myNumber.text = SpannableStringBuilder(getAmount(amount - 10).toString()) + } + + // When user presses button increase amount by 10. + btn_increase_10.setOnClickListener { + myNumber.text = SpannableStringBuilder(getAmount(amount + 10).toString()) + } + + // When user presses button decrease amount by 1. + btn_decrease.setOnClickListener { + myNumber.text = SpannableStringBuilder(getAmount(amount - 1).toString()) + } + + // When user presses button increase amount by 1. + btn_increase.setOnClickListener { + myNumber.text = SpannableStringBuilder(getAmount(amount + 1).toString()) + } + + // When user inputs custom number set amount equal to input. + myNumber.addTextChangedListener(object : SimpleTextWatcher() { + override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) { + try { + amount = getAmount(Integer.parseInt(s.toString())) + } catch (error: NumberFormatException) { + // Catch NumberFormatException to prevent parse exception when input is empty. + Timber.e(error) + } + } + }) + } + + /** + * Set min max of custom download amount chooser. + * @param min minimal downloads + * @param max maximal downloads + */ + fun setMinMax(min: Int, max: Int) { + this.min = min + this.max = max + } + + /** + * Returns amount to download. + * if minimal downloads is less than input return minimal downloads. + * if Maximal downloads is more than input return maximal downloads. + * + * @return amount to download. + */ + private fun getAmount(input: Int): Int { + return when { + input > max -> max + input < min -> min + else -> input + } + } +} diff --git a/app/src/main/res/drawable/ic_chevron_left_black_24dp.xml b/app/src/main/res/drawable/ic_chevron_left_black_24dp.xml new file mode 100644 index 0000000000..b48c93db38 --- /dev/null +++ b/app/src/main/res/drawable/ic_chevron_left_black_24dp.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_chevron_left_double_black_24dp.xml b/app/src/main/res/drawable/ic_chevron_left_double_black_24dp.xml new file mode 100644 index 0000000000..1af4b96382 --- /dev/null +++ b/app/src/main/res/drawable/ic_chevron_left_double_black_24dp.xml @@ -0,0 +1,12 @@ + + + + diff --git a/app/src/main/res/drawable/ic_chevron_right_black_24dp.xml b/app/src/main/res/drawable/ic_chevron_right_black_24dp.xml new file mode 100644 index 0000000000..125885ad41 --- /dev/null +++ b/app/src/main/res/drawable/ic_chevron_right_black_24dp.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_chevron_right_double_black_24dp.xml b/app/src/main/res/drawable/ic_chevron_right_double_black_24dp.xml new file mode 100644 index 0000000000..27d42fdbbd --- /dev/null +++ b/app/src/main/res/drawable/ic_chevron_right_double_black_24dp.xml @@ -0,0 +1,12 @@ + + + + diff --git a/app/src/main/res/layout/download_custom_amount.xml b/app/src/main/res/layout/download_custom_amount.xml new file mode 100644 index 0000000000..d26ddd75ec --- /dev/null +++ b/app/src/main/res/layout/download_custom_amount.xml @@ -0,0 +1,55 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index ea01d7a8ec..fc534a47cb 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -341,9 +341,12 @@ By source By chapter number Download + Download custom amount + amount Download next chapter Download next 5 chapters Download next 10 chapters + Download custom Download all Download unread Are you sure you want to delete selected chapters?