Explorar el Código

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
Bram van de Kerkhof hace 8 años
padre
commit
72f8c4d5e2

+ 3 - 0
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<MangaActivity>() {
 
         // 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) {

+ 9 - 0
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<ChaptersPresenter>(), ActionMode.Callbac
     fun downloadChapters(chapters: List<ChapterModel>) {
         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<ChapterModel>, bookmarked: Boolean) {

+ 28 - 19
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<ChaptersFragment>() {
     /**
      * Subject of list of chapters to allow updating the view without going to DB.
      */
-    val chaptersSubject by lazy { PublishSubject.create<List<ChapterModel>>() }
+    val chaptersSubject: PublishSubject<List<ChapterModel>>
+            by lazy { PublishSubject.create<List<ChapterModel>>() }
 
     /**
      * Whether the chapter list has been requested to the source.
@@ -100,23 +102,23 @@ class ChaptersPresenter : BasePresenter<ChaptersFragment>() {
                 // 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<ChaptersFragment>() {
     /**
      * 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<Pair<List<Chapter>, List<Chapter>>> =
+            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<Download> =
+            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<ChaptersFragment>() {
         }
         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<ChaptersFragment>() {
                 .observeOn(AndroidSchedulers.mainThread())
                 .subscribeFirst({ view, result ->
                     view.onChaptersDeleted()
-                }, { view, error ->
-                    view.onChaptersDeletedError(error)
-                })
+                }, ChaptersFragment::onChaptersDeletedError)
     }
 
     /**
@@ -401,6 +403,13 @@ class ChaptersPresenter : BasePresenter<ChaptersFragment>() {
         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.

+ 1 - 1
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<Int>()
 

+ 16 - 0
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<Boolean>()
+
+    val observable: Observable<Boolean>
+        get() = subject
+
+    fun call(favorite: Boolean) {
+        subject.call(favorite)
+    }
+}

+ 14 - 5
app/src/main/java/eu/kanade/tachiyomi/ui/manga/info/MangaInfoPresenter.kt

@@ -77,11 +77,14 @@ class MangaInfoPresenter : BasePresenter<MangaInfoFragment>() {
         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<MangaInfoFragment>() {
         refreshManga()
     }
 
+    private fun setFavorite(favorite:Boolean){
+        if (manga.favorite == favorite)
+            return
+        toggleFavorite()
+    }
+
     /**
      * Refresh MangaInfo view.
      */

+ 10 - 6
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

+ 3 - 3
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 {

+ 28 - 0
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
+    }
+}

+ 8 - 6
app/src/main/res/layout/dialog_remove_recently.xml → app/src/main/res/layout/dialog_with_checkbox.xml

@@ -2,21 +2,23 @@
 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
               android:layout_width="match_parent"
               android:layout_height="wrap_content"
-              android:orientation="vertical"
-              android:padding="@dimen/activity_vertical_margin">
+              android:orientation="vertical">
 
     <TextView
+        android:id="@+id/description"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
-        android:text="@string/dialog_remove_recently_description"
         android:textAppearance="@style/TextAppearance.Regular.Body1"/>
 
 
     <CheckBox
-        android:id="@+id/removeAll"
+        android:id="@+id/checkbox_option"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
-        android:layout_marginTop="18dp"
-        android:text="@string/dialog_remove_recently_reset"/>
+        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"/>
 
 </LinearLayout>

+ 2 - 2
app/src/main/res/values-es/strings.xml

@@ -237,8 +237,8 @@
     <string name="chapters">Capítulos</string>
 
     <!-- Dialog remove recently view -->
-    <string name="dialog_remove_recently_description">Esto eliminará la fecha de lectura de este capítulo. ¿Estás seguro?</string>
-    <string name="dialog_remove_recently_reset">Reiniciar todas los capítulos de este manga</string>
+    <string name="dialog_with_checkbox_remove_description">Esto eliminará la fecha de lectura de este capítulo. ¿Estás seguro?</string>
+    <string name="dialog_with_checkbox_reset">Reiniciar todas los capítulos de este manga</string>
 
 
     <!-- Reader activity -->

+ 2 - 2
app/src/main/res/values-it/strings.xml

@@ -285,8 +285,8 @@
     <string name="error_category_exists">Esiste già una categoria con questo nome</string>
 
     <!-- Dialog remove recently view -->
-    <string name="dialog_remove_recently_description">Questo rimuoverà la data lettura di questo capitolo. Sei sicuro?</string>
-    <string name="dialog_remove_recently_reset">Rimuovi per tutti i capitoli di questo manga</string>
+    <string name="dialog_with_checkbox_remove_description">Questo rimuoverà la data lettura di questo capitolo. Sei sicuro?</string>
+    <string name="dialog_with_checkbox_reset">Rimuovi per tutti i capitoli di questo manga</string>
 
     <!-- Image notifier -->
     <string name="picture_saved">Immagine salvata</string>

+ 2 - 2
app/src/main/res/values-pt/strings.xml

@@ -245,8 +245,8 @@
     <string name="chapters">Capítulos</string>
 
     <!-- Dialog remove recently view -->
-    <string name="dialog_remove_recently_description">Esta ação irá remover a data de leitura deste capítulo. Continuar?</string>
-    <string name="dialog_remove_recently_reset">Repor todos os capítulos desta manga</string>
+    <string name="dialog_with_checkbox_remove_description">Esta ação irá remover a data de leitura deste capítulo. Continuar?</string>
+    <string name="dialog_with_checkbox_reset">Repor todos os capítulos desta manga</string>
 
     <!-- Reader activity -->
     <string name="downloading">A transferir…</string>

+ 8 - 3
app/src/main/res/values/strings.xml

@@ -38,6 +38,7 @@
     <string name="action_update">Update</string>
     <string name="action_update_library">Update library</string>
     <string name="action_edit">Edit</string>
+    <string name="action_add">Add</string>
     <string name="action_add_category">Add category</string>
     <string name="action_edit_categories">Edit categories</string>
     <string name="action_rename_category">Rename category</string>
@@ -184,6 +185,7 @@
     <string name="cache_delete_error">An error occurred while clearing cache</string>
     <string name="pref_clear_cookies">Clear cookies</string>
     <string name="cookies_cleared">Cookies cleared</string>
+    <string name="choices_reset">Dialog choices reset</string>
     <string name="pref_clear_database">Clear database</string>
     <string name="pref_clear_database_summary">Delete manga and chapters that are not in your library</string>
     <string name="clear_database_confirmation">Are you sure? Read chapters and progress of non-library manga will be lost</string>
@@ -285,9 +287,12 @@
     <!-- Category activity -->
     <string name="error_category_exists">A category with this name already exists!</string>
 
-    <!-- Dialog remove recently view -->
-    <string name="dialog_remove_recently_description">This will remove the read date of this chapter. Are you sure?</string>
-    <string name="dialog_remove_recently_reset">Reset all chapters for this manga</string>
+    <!-- Dialog option with checkbox view -->
+    <string name="dialog_with_checkbox_remove_description">This will remove the read date of this chapter. Are you sure?</string>
+    <string name="dialog_with_checkbox_reset">Reset all chapters for this manga</string>
+
+    <!-- SnackBar -->
+    <string name="snack_add_to_library">Add manga to library?</string>
 
     <!-- Image notifier -->
     <string name="picture_saved">Picture saved</string>