From f6a79bde6fd4b2da5eeaaf3b83cb865f0564fc69 Mon Sep 17 00:00:00 2001 From: lifeweaver Date: Fri, 7 Apr 2017 14:39:09 -0400 Subject: [PATCH] Add manga straight into a category from catalogues (#737) * Add feature mention in issue #625 --- .../data/preference/PreferenceKeys.kt | 2 + .../data/preference/PreferencesHelper.kt | 2 + .../ui/catalogue/CatalogueFragment.kt | 68 ++++++++++++++++--- .../ui/catalogue/CataloguePresenter.kt | 48 +++++++++++++ .../ui/manga/info/MangaInfoFragment.kt | 58 +++++++++++++++- .../ui/manga/info/MangaInfoPresenter.kt | 48 +++++++++++++ .../ui/setting/SettingsGeneralFragment.kt | 19 ++++++ app/src/main/res/values/arrays.xml | 8 +++ app/src/main/res/values/keys.xml | 1 + app/src/main/res/values/strings.xml | 3 + app/src/main/res/xml/pref_general.xml | 8 +++ 11 files changed, 254 insertions(+), 11 deletions(-) diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/preference/PreferenceKeys.kt b/app/src/main/java/eu/kanade/tachiyomi/data/preference/PreferenceKeys.kt index d0f391332b..b687b8d3d2 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/preference/PreferenceKeys.kt +++ b/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) + } diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/preference/PreferencesHelper.kt b/app/src/main/java/eu/kanade/tachiyomi/data/preference/PreferencesHelper.kt index 930f561efe..de56700f42 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/preference/PreferencesHelper.kt +++ b/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) + } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/CatalogueFragment.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/CatalogueFragment.kt index 8c96390981..3e50386028 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/CatalogueFragment.kt +++ b/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(), FlexibleAdapter.OnItemLongClickListener, FlexibleAdapter.EndlessScrollListener { + /** + * 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(), /** * 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): Boolean { + return position.contains(0) && position.count() > 1 + } + + private fun updateMangaCategories(manga: Manga, dialog: MaterialDialog, categories: List, 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) } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/CataloguePresenter.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/CataloguePresenter.kt index 25ac8abb84..cd9b889324 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/CataloguePresenter.kt +++ b/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() { } } + /** + * Get the default, and user categories. + * + * @return List of categories, default plus user categories + */ + fun getCategories(): List { + 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 { + 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, 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) + } + } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/info/MangaInfoFragment.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/info/MangaInfoFragment.kt index e874f034c4..5492e2456f 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/info/MangaInfoFragment.kt +++ b/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() { } + /** + * 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() { 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() { 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() + } + } 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 3db68e5fb4..52f8be360b 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 @@ -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() { downloadManager.findMangaDir(source, manga)?.delete() } + /** + * Get the default, and user categories. + * + * @return List of categories, default plus user categories + */ + fun getCategories(): List { + 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 { + 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, 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) + } + } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsGeneralFragment.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsGeneralFragment.kt index 7dbb3eb446..186bf112bb 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsGeneralFragment.kt +++ b/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() diff --git a/app/src/main/res/values/arrays.xml b/app/src/main/res/values/arrays.xml index 72139c01e0..e01f29ba06 100644 --- a/app/src/main/res/values/arrays.xml +++ b/app/src/main/res/values/arrays.xml @@ -242,4 +242,12 @@ vi + + @string/default_category_summary + + + + -1 + + diff --git a/app/src/main/res/values/keys.xml b/app/src/main/res/values/keys.xml index 2dbbb39f67..bee25b223b 100644 --- a/app/src/main/res/values/keys.xml +++ b/app/src/main/res/values/keys.xml @@ -21,6 +21,7 @@ library_update_restriction start_screen app_language + default_category pref_default_viewer_key pref_image_scale_type_key diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index c85030d1dc..4d5f1bcceb 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -134,6 +134,8 @@ Start screen Language System default + Default category + Always ask Fullscreen @@ -269,6 +271,7 @@ Please enable at least one valid source No more results Local manga + Default can\'t be selected with other categories This manga was removed from the database! diff --git a/app/src/main/res/xml/pref_general.xml b/app/src/main/res/xml/pref_general.xml index 23285b8adc..0c68c19bef 100644 --- a/app/src/main/res/xml/pref_general.xml +++ b/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" /> + + \ No newline at end of file