Sfoglia il codice sorgente

Add manga straight into a category from catalogues (#737)

* Add feature mention in issue #625
lifeweaver 8 anni fa
parent
commit
f6a79bde6f

+ 2 - 0
app/src/main/java/eu/kanade/tachiyomi/data/preference/PreferenceKeys.kt

@@ -115,4 +115,6 @@ class PreferenceKeys(context: Context) {
 
     val lang = context.getString(R.string.pref_language_key)
 
+    val defaultCategory = context.getString(R.string.default_category_key)
+
 }

+ 2 - 0
app/src/main/java/eu/kanade/tachiyomi/data/preference/PreferencesHelper.kt

@@ -158,4 +158,6 @@ class PreferencesHelper(val context: Context) {
 
     fun lang() = prefs.getString(keys.lang, "")
 
+    fun defaultCategory() = prefs.getInt(keys.defaultCategory, -1)
+
 }

+ 58 - 10
app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/CatalogueFragment.kt

@@ -34,6 +34,10 @@ import rx.Subscription
 import rx.android.schedulers.AndroidSchedulers
 import rx.subjects.PublishSubject
 import java.util.concurrent.TimeUnit.MILLISECONDS
+import android.widget.Toast
+import eu.kanade.tachiyomi.data.database.models.Category
+import eu.kanade.tachiyomi.data.preference.PreferencesHelper
+import uy.kohesive.injekt.injectLazy
 
 /**
  * Fragment that shows the manga from the catalogue.
@@ -45,6 +49,11 @@ open class CatalogueFragment : BaseRxFragment<CataloguePresenter>(),
         FlexibleAdapter.OnItemLongClickListener,
         FlexibleAdapter.EndlessScrollListener<ProgressItem> {
 
+    /**
+     * Preferences helper.
+     */
+    private val preferences: PreferencesHelper by injectLazy()
+
     /**
      * Spinner shown in the toolbar to change the selected source.
      */
@@ -530,23 +539,62 @@ open class CatalogueFragment : BaseRxFragment<CataloguePresenter>(),
     /**
      * Called when a manga is long clicked.
      *
+     * Adds the manga to the default category if none is set it shows a list of categories for the user to put the manga
+     * in, the list consists of the default category plus the user's categories. The default category is preselected on
+     * new manga, and on already favorited manga the manga's categories are preselected.
+     *
      * @param position the position of the element clicked.
      */
     override fun onItemLongClick(position: Int) {
         val manga = (adapter.getItem(position) as? CatalogueItem?)?.manga ?: return
+        val categories = presenter.getCategories()
 
-        val textRes = if (manga.favorite) R.string.remove_from_library else R.string.add_to_library
-
-        MaterialDialog.Builder(activity)
-                .items(getString(textRes))
-                .itemsCallback { dialog, itemView, which, text ->
-                    when (which) {
-                        0 -> {
-                            presenter.changeMangaFavorite(manga)
-                            adapter.notifyItemChanged(position)
+        val defaultCategory = categories.find { it.id == preferences.defaultCategory()}
+        if(defaultCategory != null) {
+            if(!manga.favorite) {
+                presenter.changeMangaFavorite(manga)
+            }
+            presenter.moveMangaToCategory(defaultCategory, manga)
+        } else {
+            MaterialDialog.Builder(activity)
+                    .title(R.string.action_move_category)
+                    .items(categories.map { it.name })
+                    .itemsCallbackMultiChoice(presenter.getMangaCategoryIds(manga)) { dialog, position, _ ->
+                        if (defaultSelectedWithOtherCategory(position)) {
+                            // Deselect default category
+                            dialog.setSelectedIndices(position.filter {it > 0}.toTypedArray())
+                            Toast.makeText(dialog.context, R.string.invalid_combination, Toast.LENGTH_SHORT).show()
                         }
+
+                        true
+                    }
+                    .alwaysCallMultiChoiceCallback()
+                    .positiveText(android.R.string.ok)
+                    .negativeText(android.R.string.cancel)
+                    .onPositive { dialog, _ ->
+                        updateMangaCategories(manga, dialog, categories, position)
                     }
-                }.show()
+                    .build()
+                    .show()
+        }
+    }
+
+    private fun defaultSelectedWithOtherCategory(position: Array<Int>): Boolean {
+        return position.contains(0) && position.count() > 1
+    }
+
+    private fun updateMangaCategories(manga: Manga, dialog: MaterialDialog, categories: List<Category>, position: Int) {
+        val selectedCategories = dialog.selectedIndices?.map { categories[it] } ?: emptyList()
+
+        if(!selectedCategories.isEmpty()) {
+            if(!manga.favorite) {
+                presenter.changeMangaFavorite(manga)
+            }
+            presenter.moveMangaToCategories(selectedCategories.filter { it.id != 0}, manga)
+        } else {
+            presenter.changeMangaFavorite(manga)
+        }
+        adapter.notifyItemChanged(position)
     }
 
 }

+ 48 - 0
app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/CataloguePresenter.kt

@@ -5,7 +5,9 @@ import eu.davidea.flexibleadapter.items.IFlexible
 import eu.davidea.flexibleadapter.items.ISectionable
 import eu.kanade.tachiyomi.data.cache.CoverCache
 import eu.kanade.tachiyomi.data.database.DatabaseHelper
+import eu.kanade.tachiyomi.data.database.models.Category
 import eu.kanade.tachiyomi.data.database.models.Manga
+import eu.kanade.tachiyomi.data.database.models.MangaCategory
 import eu.kanade.tachiyomi.data.preference.PreferencesHelper
 import eu.kanade.tachiyomi.data.preference.getOrDefault
 import eu.kanade.tachiyomi.source.CatalogueSource
@@ -24,6 +26,7 @@ import rx.schedulers.Schedulers
 import rx.subjects.PublishSubject
 import timber.log.Timber
 import uy.kohesive.injekt.injectLazy
+import java.util.ArrayList
 
 /**
  * Presenter of [CatalogueFragment].
@@ -396,4 +399,49 @@ open class CataloguePresenter : BasePresenter<CatalogueFragment>() {
         }
     }
 
+    /**
+     * Get the default, and user categories.
+     *
+     * @return List of categories, default plus user categories
+     */
+    fun getCategories(): List<Category> {
+        return arrayListOf(Category.createDefault()) + db.getCategories().executeAsBlocking()
+    }
+
+    /**
+     * Gets the category id's the manga is in, if the manga is not in a category, returns the default id.
+     *
+     * @param manga the manga to get categories from.
+     * @return Array of category ids the manga is in, if none returns default id
+     */
+    fun getMangaCategoryIds(manga: Manga): Array<Int?> {
+        val categories = db.getCategoriesForManga(manga).executeAsBlocking()
+        if(categories.isEmpty()) {
+            return arrayListOf(Category.createDefault().id).toTypedArray()
+        }
+        return categories.map { it.id }.toTypedArray()
+    }
+
+    /**
+     * Move the given manga to categories.
+     *
+     * @param categories the selected categories.
+     * @param manga the manga to move.
+     */
+    fun moveMangaToCategories(categories: List<Category>, manga: Manga) {
+        val mc = categories.map { MangaCategory.create(manga, it) }
+
+        db.setMangaCategories(mc, arrayListOf(manga))
+    }
+
+    /**
+     * Move the given manga to the category.
+     *
+     * @param category the selected category.
+     * @param manga the manga to move.
+     */
+    fun moveMangaToCategory(category: Category, manga: Manga) {
+        moveMangaToCategories(arrayListOf(category), manga)
+    }
+
 }

+ 57 - 1
app/src/main/java/eu/kanade/tachiyomi/ui/manga/info/MangaInfoFragment.kt

@@ -6,6 +6,7 @@ import android.net.Uri
 import android.os.Bundle
 import android.support.customtabs.CustomTabsIntent
 import android.view.*
+import android.widget.Toast
 import com.afollestad.materialdialogs.MaterialDialog
 import com.bumptech.glide.BitmapRequestBuilder
 import com.bumptech.glide.BitmapTypeRequest
@@ -14,6 +15,7 @@ import com.bumptech.glide.load.engine.DiskCacheStrategy
 import com.bumptech.glide.load.resource.bitmap.CenterCrop
 import eu.kanade.tachiyomi.R
 import eu.kanade.tachiyomi.data.database.models.Manga
+import eu.kanade.tachiyomi.data.preference.PreferencesHelper
 import eu.kanade.tachiyomi.source.Source
 import eu.kanade.tachiyomi.source.model.SManga
 import eu.kanade.tachiyomi.source.online.HttpSource
@@ -31,6 +33,7 @@ import nucleus.factory.RequiresPresenter
 import rx.Observable
 import rx.android.schedulers.AndroidSchedulers
 import rx.schedulers.Schedulers
+import uy.kohesive.injekt.injectLazy
 
 /**
  * Fragment that shows manga information.
@@ -52,6 +55,11 @@ class MangaInfoFragment : BaseRxFragment<MangaInfoPresenter>() {
 
     }
 
+    /**
+     * Preferences helper.
+     */
+    private val preferences: PreferencesHelper by injectLazy()
+
     override fun onCreate(savedState: Bundle?) {
         super.onCreate(savedState)
         setHasOptionsMenu(true)
@@ -63,7 +71,19 @@ class MangaInfoFragment : BaseRxFragment<MangaInfoPresenter>() {
 
     override fun onViewCreated(view: View?, savedState: Bundle?) {
         // Set onclickListener to toggle favorite when FAB clicked.
-        fab_favorite.setOnClickListener { toggleFavorite() }
+        fab_favorite.setOnClickListener {
+            if(!presenter.manga.favorite) {
+                val defaultCategory = presenter.getCategories().find { it.id == preferences.defaultCategory()}
+                if(defaultCategory == null) {
+                    onFabClick()
+                } else {
+                    toggleFavorite()
+                    presenter.moveMangaToCategory(defaultCategory, presenter.manga)
+                }
+            } else {
+                toggleFavorite()
+            }
+        }
 
         // Set SwipeRefresh to refresh manga data.
         swipe_refresh.setOnRefreshListener { fetchMangaFromSource() }
@@ -334,4 +354,40 @@ class MangaInfoFragment : BaseRxFragment<MangaInfoPresenter>() {
         swipe_refresh.isRefreshing = value
     }
 
+    /**
+     * Called when the fab is clicked.
+     */
+    private fun onFabClick() {
+        val categories = presenter.getCategories()
+
+        MaterialDialog.Builder(activity)
+                .title(R.string.action_move_category)
+                .items(categories.map { it.name })
+                .itemsCallbackMultiChoice(presenter.getMangaCategoryIds(presenter.manga)) { dialog, position, text ->
+                    if (position.contains(0) && position.count() > 1) {
+                        dialog.setSelectedIndices(position.filter {it > 0}.toTypedArray())
+                        Toast.makeText(dialog.context, R.string.invalid_combination, Toast.LENGTH_SHORT).show()
+                    }
+
+                    true
+                }
+                .alwaysCallMultiChoiceCallback()
+                .positiveText(android.R.string.ok)
+                .negativeText(android.R.string.cancel)
+                .onPositive { dialog, _ ->
+                    val selectedCategories = dialog.selectedIndices?.map { categories[it] } ?: emptyList()
+
+                    if(!selectedCategories.isEmpty()) {
+                        if(!presenter.manga.favorite) {
+                            toggleFavorite()
+                        }
+                        presenter.moveMangaToCategories(selectedCategories.filter { it.id != 0}, presenter.manga)
+                    } else {
+                        toggleFavorite()
+                    }
+                }
+                .build()
+                .show()
+    }
+
 }

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

@@ -3,7 +3,9 @@ package eu.kanade.tachiyomi.ui.manga.info
 import android.os.Bundle
 import eu.kanade.tachiyomi.data.cache.CoverCache
 import eu.kanade.tachiyomi.data.database.DatabaseHelper
+import eu.kanade.tachiyomi.data.database.models.Category
 import eu.kanade.tachiyomi.data.database.models.Manga
+import eu.kanade.tachiyomi.data.database.models.MangaCategory
 import eu.kanade.tachiyomi.data.download.DownloadManager
 import eu.kanade.tachiyomi.source.Source
 import eu.kanade.tachiyomi.source.SourceManager
@@ -16,6 +18,7 @@ import rx.Subscription
 import rx.android.schedulers.AndroidSchedulers
 import rx.schedulers.Schedulers
 import uy.kohesive.injekt.injectLazy
+import java.util.ArrayList
 
 /**
  * Presenter of MangaInfoFragment.
@@ -148,4 +151,49 @@ class MangaInfoPresenter : BasePresenter<MangaInfoFragment>() {
         downloadManager.findMangaDir(source, manga)?.delete()
     }
 
+    /**
+     * Get the default, and user categories.
+     *
+     * @return List of categories, default plus user categories
+     */
+    fun getCategories(): List<Category> {
+        return arrayListOf(Category.createDefault()) + db.getCategories().executeAsBlocking()
+    }
+
+    /**
+     * Gets the category id's the manga is in, if the manga is not in a category, returns the default id.
+     *
+     * @param manga the manga to get categories from.
+     * @return Array of category ids the manga is in, if none returns default id
+     */
+    fun getMangaCategoryIds(manga: Manga): Array<Int?> {
+        val categories = db.getCategoriesForManga(manga).executeAsBlocking()
+        if(categories.isEmpty()) {
+            return arrayListOf(Category.createDefault().id).toTypedArray()
+        }
+        return categories.map { it.id }.toTypedArray()
+    }
+
+    /**
+     * Move the given manga to categories.
+     *
+     * @param categories the selected categories.
+     * @param manga the manga to move.
+     */
+    fun moveMangaToCategories(categories: List<Category>, manga: Manga) {
+        val mc = categories.map { MangaCategory.create(manga, it) }
+
+        db.setMangaCategories(mc, arrayListOf(manga))
+    }
+
+    /**
+     * Move the given manga to the category.
+     *
+     * @param category the selected category.
+     * @param manga the manga to move.
+     */
+    fun moveMangaToCategory(category: Category, manga: Manga) {
+        moveMangaToCategories(arrayListOf(category), manga)
+    }
+
 }

+ 19 - 0
app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsGeneralFragment.kt

@@ -7,6 +7,7 @@ import android.support.v7.preference.XpPreferenceFragment
 import android.view.View
 import eu.kanade.tachiyomi.R
 import eu.kanade.tachiyomi.data.database.DatabaseHelper
+import eu.kanade.tachiyomi.data.database.models.Category
 import eu.kanade.tachiyomi.data.library.LibraryUpdateJob
 import eu.kanade.tachiyomi.data.preference.PreferencesHelper
 import eu.kanade.tachiyomi.util.LocaleHelper
@@ -46,6 +47,8 @@ class SettingsGeneralFragment : SettingsFragment(),
 
     val categoryUpdate: MultiSelectListPreference by bindPref(R.string.pref_library_update_categories_key)
 
+    val defaultCategory: IntListPreference by bindPref(R.string.default_category_key)
+
     val langPreference: ListPreference by bindPref(R.string.pref_language_key)
 
     override fun onViewCreated(view: View, savedState: Bundle?) {
@@ -100,6 +103,22 @@ class SettingsGeneralFragment : SettingsFragment(),
                     categoryUpdate.summary = summary
                 }
 
+        defaultCategory.apply {
+            val selectedCategory = dbCategories.find { it.id == preferences.defaultCategory()}
+            value = selectedCategory?.id?.toString() ?: value
+            entries += dbCategories.map { it.name }.toTypedArray()
+            entryValues += dbCategories.map { it.id.toString() }.toTypedArray()
+            summary = selectedCategory?.name ?: summary
+        }
+
+        defaultCategory.setOnPreferenceChangeListener { _, newValue ->
+            defaultCategory.summary = dbCategories.find {
+                it.id == (newValue as String).toInt()
+            }?.name ?: getString(R.string.default_category_summary)
+
+            true
+        }
+
         themePreference.setOnPreferenceChangeListener { preference, newValue ->
             (activity as SettingsActivity).parentFlags = SettingsActivity.FLAG_THEME_CHANGED
             activity.recreate()

+ 8 - 0
app/src/main/res/values/arrays.xml

@@ -242,4 +242,12 @@
         <item>vi</item>
     </string-array>
 
+    <string-array name="default_category_entry">
+        <item>@string/default_category_summary</item>
+    </string-array>
+
+    <string-array name="default_category_entry_value">
+        <item>-1</item>
+    </string-array>
+
 </resources>

+ 1 - 0
app/src/main/res/values/keys.xml

@@ -21,6 +21,7 @@
     <string name="pref_library_update_restriction_key" translatable="false">library_update_restriction</string>
     <string name="pref_start_screen_key" translatable="false">start_screen</string>
     <string name="pref_language_key" translatable="false">app_language</string>
+    <string name="default_category_key" translatable="false">default_category</string>
 
     <string name="pref_default_viewer_key" translatable="false">pref_default_viewer_key</string>
     <string name="pref_image_scale_type_key" translatable="false">pref_image_scale_type_key</string>

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

@@ -134,6 +134,8 @@
     <string name="pref_start_screen">Start screen</string>
     <string name="pref_language">Language</string>
     <string name="system_default">System default</string>
+    <string name="default_category">Default category</string>
+    <string name="default_category_summary">Always ask</string>
 
       <!-- Reader section -->
     <string name="pref_fullscreen">Fullscreen</string>
@@ -269,6 +271,7 @@
     <string name="no_valid_sources">Please enable at least one valid source</string>
     <string name="no_more_results">No more results</string>
     <string name="local_source">Local manga</string>
+    <string name="invalid_combination">Default can\'t be selected with other categories</string>
 
     <!-- Manga activity -->
     <string name="manga_not_in_db">This manga was removed from the database!</string>

+ 8 - 0
app/src/main/res/xml/pref_general.xml

@@ -63,6 +63,14 @@
             android:key="@string/pref_update_only_non_completed_key"
             android:title="@string/pref_update_only_non_completed" />
 
+        <eu.kanade.tachiyomi.widget.preference.IntListPreference
+            android:defaultValue="-1"
+            android:entries="@array/default_category_entry"
+            android:entryValues="@array/default_category_entry_value"
+            android:key="@string/default_category_key"
+            android:title="@string/default_category"
+            android:summary="@string/default_category_summary"/>
+
     </PreferenceScreen>
 
 </PreferenceScreen>