mirror of
				https://github.com/mihonapp/mihon.git
				synced 2025-11-04 08:08:55 +01:00 
			
		
		
		
	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
This commit is contained in:
		
				
					committed by
					
						
						GitHub
					
				
			
			
				
	
			
			
			
						parent
						
							bc8753da85
						
					
				
				
					commit
					6a310bbaa9
				
			@@ -37,6 +37,7 @@ class ChaptersController : NucleusController<ChaptersPresenter>(),
 | 
			
		||||
        SetDisplayModeDialog.Listener,
 | 
			
		||||
        SetSortingDialog.Listener,
 | 
			
		||||
        DownloadChaptersDialog.Listener,
 | 
			
		||||
        DownloadCustomChaptersDialog.Listener,
 | 
			
		||||
        DeleteChaptersDialog.Listener {
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
@@ -210,7 +211,7 @@ class ChaptersController : NucleusController<ChaptersPresenter>(),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fun fetchChaptersFromSource() {
 | 
			
		||||
    private fun fetchChaptersFromSource() {
 | 
			
		||||
        swipe_refresh?.isRefreshing = true
 | 
			
		||||
        presenter.fetchChaptersFromSource()
 | 
			
		||||
    }
 | 
			
		||||
@@ -272,18 +273,18 @@ class ChaptersController : NucleusController<ChaptersPresenter>(),
 | 
			
		||||
        actionMode?.invalidate()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fun getSelectedChapters(): List<ChapterItem> {
 | 
			
		||||
    private fun getSelectedChapters(): List<ChapterItem> {
 | 
			
		||||
        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<ChaptersPresenter>(),
 | 
			
		||||
 | 
			
		||||
    // SELECTION MODE ACTIONS
 | 
			
		||||
 | 
			
		||||
    fun selectAll() {
 | 
			
		||||
    private fun selectAll() {
 | 
			
		||||
        val adapter = adapter ?: return
 | 
			
		||||
        adapter.selectAll()
 | 
			
		||||
        selectedItems.addAll(adapter.items)
 | 
			
		||||
        actionMode?.invalidate()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fun markAsRead(chapters: List<ChapterItem>) {
 | 
			
		||||
    private fun markAsRead(chapters: List<ChapterItem>) {
 | 
			
		||||
        presenter.markChaptersRead(chapters, true)
 | 
			
		||||
        if (presenter.preferences.removeAfterMarkedAsRead()) {
 | 
			
		||||
            deleteChapters(chapters)
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fun markAsUnread(chapters: List<ChapterItem>) {
 | 
			
		||||
    private fun markAsUnread(chapters: List<ChapterItem>) {
 | 
			
		||||
        presenter.markChaptersRead(chapters, false)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fun downloadChapters(chapters: List<ChapterItem>) {
 | 
			
		||||
    private fun downloadChapters(chapters: List<ChapterItem>) {
 | 
			
		||||
        val view = view
 | 
			
		||||
        destroyActionModeIfNeeded()
 | 
			
		||||
        presenter.downloadChapters(chapters)
 | 
			
		||||
@@ -372,6 +373,7 @@ class ChaptersController : NucleusController<ChaptersPresenter>(),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    private fun showDeleteChaptersConfirmationDialog() {
 | 
			
		||||
        DeleteChaptersDialog(this).showDialog(router)
 | 
			
		||||
    }
 | 
			
		||||
@@ -380,7 +382,7 @@ class ChaptersController : NucleusController<ChaptersPresenter>(),
 | 
			
		||||
        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<ChaptersPresenter>(),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fun bookmarkChapters(chapters: List<ChapterItem>, bookmarked: Boolean) {
 | 
			
		||||
    private fun bookmarkChapters(chapters: List<ChapterItem>, bookmarked: Boolean) {
 | 
			
		||||
        destroyActionModeIfNeeded()
 | 
			
		||||
        presenter.bookmarkChapters(chapters, bookmarked)
 | 
			
		||||
    }
 | 
			
		||||
@@ -412,7 +414,7 @@ class ChaptersController : NucleusController<ChaptersPresenter>(),
 | 
			
		||||
        Timber.e(error)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fun dismissDeletingDialog() {
 | 
			
		||||
    private fun dismissDeletingDialog() {
 | 
			
		||||
        router.popControllerWithTag(DeletingChaptersDialog.TAG)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -441,29 +443,44 @@ class ChaptersController : NucleusController<ChaptersPresenter>(),
 | 
			
		||||
        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)
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -21,12 +21,12 @@ class DownloadChaptersDialog<T>(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, _ ->
 | 
			
		||||
 
 | 
			
		||||
@@ -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<T> : 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"
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -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
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user