mirror of
				https://github.com/mihonapp/mihon.git
				synced 2025-10-30 22:07:57 +01:00 
			
		
		
		
	Add manga straight into a category from catalogues (#737)
* Add feature mention in issue #625
This commit is contained in:
		
				
					committed by
					
						 Bram van de Kerkhof
						Bram van de Kerkhof
					
				
			
			
				
	
			
			
			
						parent
						
							bb9e230b35
						
					
				
				
					commit
					f6a79bde6f
				
			| @@ -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) | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -158,4 +158,6 @@ class PreferencesHelper(val context: Context) { | ||||
|  | ||||
|     fun lang() = prefs.getString(keys.lang, "") | ||||
|  | ||||
|     fun defaultCategory() = prefs.getInt(keys.defaultCategory, -1) | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -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 | ||||
|                     } | ||||
|                 }.show() | ||||
|                     .alwaysCallMultiChoiceCallback() | ||||
|                     .positiveText(android.R.string.ok) | ||||
|                     .negativeText(android.R.string.cancel) | ||||
|                     .onPositive { dialog, _ -> | ||||
|                         updateMangaCategories(manga, dialog, categories, position) | ||||
|                     } | ||||
|                     .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) | ||||
|     } | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -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) | ||||
|     } | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -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() | ||||
|     } | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -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) | ||||
|     } | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -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() | ||||
|   | ||||
		Reference in New Issue
	
	Block a user