From 72f8c4d5e21ac8de3f3103b6c73826a7a91faf03 Mon Sep 17 00:00:00 2001 From: Bram van de Kerkhof Date: Sun, 8 Jan 2017 20:07:19 +0100 Subject: [PATCH] Added add to library dialog when downloading from catalogue (#618) * Now show snackbar when adding from catalogue * Code cleanup + added manga favorite event to update favorite drawable when added via snack * Update SettingsAdvancedFragment.kt Forgot to check optimize import. I think(hope) I got them all ;). * Now uses PublishRelay. Manga favorite is now handled in info presenter * Update MangaInfoFragment.kt --- .../tachiyomi/ui/manga/MangaPresenter.kt | 3 ++ .../ui/manga/chapter/ChaptersFragment.kt | 9 ++++ .../ui/manga/chapter/ChaptersPresenter.kt | 47 +++++++++++-------- .../ui/manga/info/ChapterCountEvent.kt | 2 +- .../ui/manga/info/MangaFavoriteEvent.kt | 16 +++++++ .../ui/manga/info/MangaInfoPresenter.kt | 19 ++++++-- .../ui/recently_read/RecentlyReadHolder.kt | 16 ++++--- .../tachiyomi/util/ChapterRecognition.kt | 6 +-- .../tachiyomi/widget/DialogCheckboxView.kt | 28 +++++++++++ ..._recently.xml => dialog_with_checkbox.xml} | 14 +++--- app/src/main/res/values-es/strings.xml | 4 +- app/src/main/res/values-it/strings.xml | 4 +- app/src/main/res/values-pt/strings.xml | 4 +- app/src/main/res/values/strings.xml | 11 +++-- 14 files changed, 134 insertions(+), 49 deletions(-) create mode 100644 app/src/main/java/eu/kanade/tachiyomi/ui/manga/info/MangaFavoriteEvent.kt create mode 100644 app/src/main/java/eu/kanade/tachiyomi/widget/DialogCheckboxView.kt rename app/src/main/res/layout/{dialog_remove_recently.xml => dialog_with_checkbox.xml} (56%) diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaPresenter.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaPresenter.kt index 022ee880b..08a8ed626 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaPresenter.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaPresenter.kt @@ -6,6 +6,7 @@ import eu.kanade.tachiyomi.data.database.models.Manga import eu.kanade.tachiyomi.data.track.TrackManager import eu.kanade.tachiyomi.ui.base.presenter.BasePresenter import eu.kanade.tachiyomi.ui.manga.info.ChapterCountEvent +import eu.kanade.tachiyomi.ui.manga.info.MangaFavoriteEvent import eu.kanade.tachiyomi.util.SharedData import rx.Observable import rx.Subscription @@ -38,6 +39,8 @@ class MangaPresenter : BasePresenter() { // Prepare a subject to communicate the chapters and info presenters for the chapter count. SharedData.put(ChapterCountEvent()) + // Prepare a subject to communicate the chapters and info presenters for the chapter favorite. + SharedData.put(MangaFavoriteEvent()) } fun setMangaEvent(event: MangaEvent) { diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/chapter/ChaptersFragment.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/chapter/ChaptersFragment.kt index 540b95d95..1b400f5c8 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/chapter/ChaptersFragment.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/chapter/ChaptersFragment.kt @@ -4,6 +4,7 @@ import android.animation.Animator import android.animation.AnimatorListenerAdapter import android.content.Intent import android.os.Bundle +import android.support.design.widget.Snackbar import android.support.v4.app.DialogFragment import android.support.v7.view.ActionMode import android.support.v7.widget.DividerItemDecoration @@ -20,6 +21,7 @@ import eu.kanade.tachiyomi.ui.base.fragment.BaseRxFragment import eu.kanade.tachiyomi.ui.manga.MangaActivity import eu.kanade.tachiyomi.ui.reader.ReaderActivity import eu.kanade.tachiyomi.util.getCoordinates +import eu.kanade.tachiyomi.util.snack import eu.kanade.tachiyomi.util.toast import eu.kanade.tachiyomi.widget.DeletingChaptersDialog import kotlinx.android.synthetic.main.fragment_manga_chapters.* @@ -370,6 +372,13 @@ class ChaptersFragment : BaseRxFragment(), ActionMode.Callbac fun downloadChapters(chapters: List) { destroyActionModeIfNeeded() presenter.downloadChapters(chapters) + if (!presenter.manga.favorite){ + recycler.snack(getString(R.string.snack_add_to_library), Snackbar.LENGTH_INDEFINITE) { + setAction(R.string.action_add) { + presenter.addToLibrary() + } + } + } } fun bookmarkChapters(chapters: List, bookmarked: Boolean) { diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/chapter/ChaptersPresenter.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/chapter/ChaptersPresenter.kt index 16fdc9c05..cb3303f39 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/chapter/ChaptersPresenter.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/chapter/ChaptersPresenter.kt @@ -13,6 +13,7 @@ import eu.kanade.tachiyomi.data.source.SourceManager import eu.kanade.tachiyomi.ui.base.presenter.BasePresenter import eu.kanade.tachiyomi.ui.manga.MangaEvent import eu.kanade.tachiyomi.ui.manga.info.ChapterCountEvent +import eu.kanade.tachiyomi.ui.manga.info.MangaFavoriteEvent import eu.kanade.tachiyomi.util.SharedData import eu.kanade.tachiyomi.util.syncChaptersWithSource import rx.Observable @@ -68,7 +69,8 @@ class ChaptersPresenter : BasePresenter() { /** * Subject of list of chapters to allow updating the view without going to DB. */ - val chaptersSubject by lazy { PublishSubject.create>() } + val chaptersSubject: PublishSubject> + by lazy { PublishSubject.create>() } /** * Whether the chapter list has been requested to the source. @@ -100,23 +102,23 @@ class ChaptersPresenter : BasePresenter() { // On each subject emission, apply filters and sort then update the view. { chaptersSubject .flatMap { applyChapterFilters(it) } - .observeOn(AndroidSchedulers.mainThread()) }, - { view, chapters -> view.onNextChapters(chapters) }) + .observeOn(AndroidSchedulers.mainThread()) + }, ChaptersFragment::onNextChapters) startableFirst(FETCH_CHAPTERS, { getRemoteChaptersObservable() }, { view, result -> view.onFetchChaptersDone() }, - { view, error -> view.onFetchChaptersError(error) }) + ChaptersFragment::onFetchChaptersError) startableLatestCache(CHAPTER_STATUS_CHANGES, { getChapterStatusObservable() }, - { view, download -> view.onChapterStatusChange(download) }, + ChaptersFragment::onChapterStatusChange, { view, error -> Timber.e(error) }) // Find the active manga from the shared data or return. manga = SharedData.get(MangaEvent::class.java)?.manga ?: return Observable.just(manga) - .subscribeLatestCache({ view, manga -> view.onNextManga(manga) }) + .subscribeLatestCache(ChaptersFragment::onNextManga) // Find the source for this manga. source = sourceManager.get(manga.source)!! @@ -197,18 +199,20 @@ class ChaptersPresenter : BasePresenter() { /** * Returns an observable that updates the chapter list with the latest from the source. */ - fun getRemoteChaptersObservable() = Observable.defer { source.fetchChapterList(manga) } - .subscribeOn(Schedulers.io()) - .map { syncChaptersWithSource(db, it, manga, source) } - .observeOn(AndroidSchedulers.mainThread()) + fun getRemoteChaptersObservable(): Observable, List>> = + Observable.defer { source.fetchChapterList(manga) } + .subscribeOn(Schedulers.io()) + .map { syncChaptersWithSource(db, it, manga, source) } + .observeOn(AndroidSchedulers.mainThread()) /** * Returns an observable that listens to download queue status changes. */ - fun getChapterStatusObservable() = downloadManager.queue.getStatusObservable() - .observeOn(AndroidSchedulers.mainThread()) - .filter { download -> download.manga.id == manga.id } - .doOnNext { onDownloadStatusChange(it) } + fun getChapterStatusObservable(): Observable = + downloadManager.queue.getStatusObservable() + .observeOn(AndroidSchedulers.mainThread()) + .filter { download -> download.manga.id == manga.id } + .doOnNext { onDownloadStatusChange(it) } /** * Applies the view filters to the list of chapters obtained from the database. @@ -231,11 +235,11 @@ class ChaptersPresenter : BasePresenter() { } val sortFunction: (Chapter, Chapter) -> Int = when (manga.sorting) { Manga.SORTING_SOURCE -> when (sortDescending()) { - true -> { c1, c2 -> c1.source_order.compareTo(c2.source_order) } + true -> { c1, c2 -> c1.source_order.compareTo(c2.source_order) } false -> { c1, c2 -> c2.source_order.compareTo(c1.source_order) } } Manga.SORTING_NUMBER -> when (sortDescending()) { - true -> { c1, c2 -> c2.chapter_number.compareTo(c1.chapter_number) } + true -> { c1, c2 -> c2.chapter_number.compareTo(c1.chapter_number) } false -> { c1, c2 -> c1.chapter_number.compareTo(c2.chapter_number) } } else -> throw NotImplementedError("Unimplemented sorting method") @@ -325,9 +329,7 @@ class ChaptersPresenter : BasePresenter() { .observeOn(AndroidSchedulers.mainThread()) .subscribeFirst({ view, result -> view.onChaptersDeleted() - }, { view, error -> - view.onChaptersDeletedError(error) - }) + }, ChaptersFragment::onChaptersDeletedError) } /** @@ -401,6 +403,13 @@ class ChaptersPresenter : BasePresenter() { refreshChapters() } + /** + * Adds manga to library + */ + fun addToLibrary() { + SharedData.get(MangaFavoriteEvent::class.java)?.call(true) + } + /** * Sets the active display mode. * @param mode the mode to set. diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/info/ChapterCountEvent.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/info/ChapterCountEvent.kt index 14a025df6..d307941bc 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/info/ChapterCountEvent.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/info/ChapterCountEvent.kt @@ -3,7 +3,7 @@ package eu.kanade.tachiyomi.ui.manga.info import rx.Observable import rx.subjects.BehaviorSubject -class ChapterCountEvent() { +class ChapterCountEvent { private val subject = BehaviorSubject.create() diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/info/MangaFavoriteEvent.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/info/MangaFavoriteEvent.kt new file mode 100644 index 000000000..75beb742c --- /dev/null +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/info/MangaFavoriteEvent.kt @@ -0,0 +1,16 @@ +package eu.kanade.tachiyomi.ui.manga.info + +import com.jakewharton.rxrelay.PublishRelay +import rx.Observable + +class MangaFavoriteEvent { + + private val subject = PublishRelay.create() + + val observable: Observable + get() = subject + + fun call(favorite: Boolean) { + subject.call(favorite) + } +} diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/info/MangaInfoPresenter.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/info/MangaInfoPresenter.kt index 91f09f45c..950b8e430 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/info/MangaInfoPresenter.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/info/MangaInfoPresenter.kt @@ -77,11 +77,14 @@ class MangaInfoPresenter : BasePresenter() { refreshManga() // Update chapter count - SharedData.get(ChapterCountEvent::class.java)?.let { - it.observable - .observeOn(AndroidSchedulers.mainThread()) - .subscribeLatestCache({ view, count -> view.setChapterCount(count) }) - } + SharedData.get(ChapterCountEvent::class.java)?.observable + ?.observeOn(AndroidSchedulers.mainThread()) + ?.subscribeLatestCache(MangaInfoFragment::setChapterCount) + + // Update favorite status + SharedData.get(MangaFavoriteEvent::class.java)?.observable + ?.observeOn(AndroidSchedulers.mainThread()) + ?.subscribe{setFavorite(it)} } /** @@ -123,6 +126,12 @@ class MangaInfoPresenter : BasePresenter() { refreshManga() } + private fun setFavorite(favorite:Boolean){ + if (manga.favorite == favorite) + return + toggleFavorite() + } + /** * Refresh MangaInfo view. */ diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/recently_read/RecentlyReadHolder.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/recently_read/RecentlyReadHolder.kt index 9aa7c79de..2cef6d76e 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/recently_read/RecentlyReadHolder.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/recently_read/RecentlyReadHolder.kt @@ -7,7 +7,7 @@ import com.bumptech.glide.Glide import com.bumptech.glide.load.engine.DiskCacheStrategy import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.data.database.models.MangaChapterHistory -import kotlinx.android.synthetic.main.dialog_remove_recently.view.* +import eu.kanade.tachiyomi.widget.DialogCheckboxView import kotlinx.android.synthetic.main.item_recently_read.view.* import java.text.DateFormat import java.text.DecimalFormat @@ -24,7 +24,7 @@ import java.util.* * @constructor creates a new recent chapter holder. */ class RecentlyReadHolder(view: View, private val adapter: RecentlyReadAdapter) -: RecyclerView.ViewHolder(view) { + : RecyclerView.ViewHolder(view) { /** * DecimalFormat used to display correct chapter number @@ -66,14 +66,19 @@ class RecentlyReadHolder(view: View, private val adapter: RecentlyReadAdapter) // Set remove clickListener itemView.remove.setOnClickListener { + // Create custom view + val dialogCheckboxView = DialogCheckboxView(itemView.context).apply { + setDescription(R.string.dialog_with_checkbox_remove_description) + setOptionDescription(R.string.dialog_with_checkbox_reset) + } MaterialDialog.Builder(itemView.context) .title(R.string.action_remove) - .customView(R.layout.dialog_remove_recently, true) + .customView(dialogCheckboxView, true) .positiveText(R.string.action_remove) .negativeText(android.R.string.cancel) .onPositive { materialDialog, dialogAction -> // Check if user wants all chapters reset - if (materialDialog.customView?.removeAll?.isChecked as Boolean) { + if (dialogCheckboxView.isChecked()) { adapter.fragment.removeAllFromHistory(manga.id!!) } else { adapter.fragment.removeFromHistory(history) @@ -81,8 +86,7 @@ class RecentlyReadHolder(view: View, private val adapter: RecentlyReadAdapter) } .onNegative { materialDialog, dialogAction -> materialDialog.dismiss() - } - .show() + }.show() } // Set continue reading clickListener diff --git a/app/src/main/java/eu/kanade/tachiyomi/util/ChapterRecognition.kt b/app/src/main/java/eu/kanade/tachiyomi/util/ChapterRecognition.kt index ff0a2c359..5382d7250 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/util/ChapterRecognition.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/util/ChapterRecognition.kt @@ -23,7 +23,7 @@ object ChapterRecognition { * Regex used when manga title removed * Example: Solanin 028 Vol. 2 -> 028 Vol.2 -> 028Vol.2 -R> 028 */ - private val withoutMange = Regex("""^([0-9]+)(\.[0-9]+)?(\.?[a-z]+)?""") + private val withoutManga = Regex("""^([0-9]+)(\.[0-9]+)?(\.?[a-z]+)?""") /** * Regex used to remove unwanted tags @@ -77,7 +77,7 @@ object ChapterRecognition { val nameWithoutManga = name.replace(manga.title.toLowerCase(), "").trim() // Check if first value is number after title remove. - if (updateChapter(withoutMange.find(nameWithoutManga), chapter)) + if (updateChapter(withoutManga.find(nameWithoutManga), chapter)) return // Take the first number encountered. @@ -123,7 +123,7 @@ object ChapterRecognition { if (alpha.contains("special")) return .97f - if (alpha[0].equals('.') ) { + if (alpha[0] == '.') { // Take value after (.) return parseAlphaPostFix(alpha[1]) } else { diff --git a/app/src/main/java/eu/kanade/tachiyomi/widget/DialogCheckboxView.kt b/app/src/main/java/eu/kanade/tachiyomi/widget/DialogCheckboxView.kt new file mode 100644 index 000000000..5a0d2a329 --- /dev/null +++ b/app/src/main/java/eu/kanade/tachiyomi/widget/DialogCheckboxView.kt @@ -0,0 +1,28 @@ +package eu.kanade.tachiyomi.widget + +import android.content.Context +import android.support.annotation.StringRes +import android.util.AttributeSet +import android.widget.LinearLayout +import android.widget.RelativeLayout +import eu.kanade.tachiyomi.R +import kotlinx.android.synthetic.main.dialog_with_checkbox.view.* + +class DialogCheckboxView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null) : + LinearLayout(context, attrs) { + init { + RelativeLayout.inflate(context, R.layout.dialog_with_checkbox, this) + } + + fun setDescription(@StringRes id: Int){ + description.text = context.getString(id) + } + + fun setOptionDescription(@StringRes id: Int){ + checkbox_option.text = context.getString(id) + } + + fun isChecked(): Boolean { + return checkbox_option.isChecked + } +} diff --git a/app/src/main/res/layout/dialog_remove_recently.xml b/app/src/main/res/layout/dialog_with_checkbox.xml similarity index 56% rename from app/src/main/res/layout/dialog_remove_recently.xml rename to app/src/main/res/layout/dialog_with_checkbox.xml index 9167c2944..748a488ee 100644 --- a/app/src/main/res/layout/dialog_remove_recently.xml +++ b/app/src/main/res/layout/dialog_with_checkbox.xml @@ -2,21 +2,23 @@ + android:orientation="vertical"> + android:layout_marginStart="-5dp" + android:layout_marginEnd="0dp" + android:layout_marginLeft="-5dp" + android:layout_marginRight="0dp" + android:layout_marginTop="@dimen/material_component_dialogs_padding_between_text_and_touch_target"/> diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml index efb13c44f..2f4cfe2fe 100644 --- a/app/src/main/res/values-es/strings.xml +++ b/app/src/main/res/values-es/strings.xml @@ -237,8 +237,8 @@ Capítulos - Esto eliminará la fecha de lectura de este capítulo. ¿Estás seguro? - Reiniciar todas los capítulos de este manga + Esto eliminará la fecha de lectura de este capítulo. ¿Estás seguro? + Reiniciar todas los capítulos de este manga diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml index a46f7c849..866b3f70c 100644 --- a/app/src/main/res/values-it/strings.xml +++ b/app/src/main/res/values-it/strings.xml @@ -285,8 +285,8 @@ Esiste già una categoria con questo nome - Questo rimuoverà la data lettura di questo capitolo. Sei sicuro? - Rimuovi per tutti i capitoli di questo manga + Questo rimuoverà la data lettura di questo capitolo. Sei sicuro? + Rimuovi per tutti i capitoli di questo manga Immagine salvata diff --git a/app/src/main/res/values-pt/strings.xml b/app/src/main/res/values-pt/strings.xml index cb2df4d23..add5e9df1 100644 --- a/app/src/main/res/values-pt/strings.xml +++ b/app/src/main/res/values-pt/strings.xml @@ -245,8 +245,8 @@ Capítulos - Esta ação irá remover a data de leitura deste capítulo. Continuar? - Repor todos os capítulos desta manga + Esta ação irá remover a data de leitura deste capítulo. Continuar? + Repor todos os capítulos desta manga A transferir… diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index aac63789c..1f77b50e4 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -38,6 +38,7 @@ Update Update library Edit + Add Add category Edit categories Rename category @@ -184,6 +185,7 @@ An error occurred while clearing cache Clear cookies Cookies cleared + Dialog choices reset Clear database Delete manga and chapters that are not in your library Are you sure? Read chapters and progress of non-library manga will be lost @@ -285,9 +287,12 @@ A category with this name already exists! - - This will remove the read date of this chapter. Are you sure? - Reset all chapters for this manga + + This will remove the read date of this chapter. Are you sure? + Reset all chapters for this manga + + + Add manga to library? Picture saved