From da2f3d4524a1088607a0ec741c4fc401a72ed19c Mon Sep 17 00:00:00 2001 From: Jay Date: Tue, 18 Feb 2020 21:27:24 -0800 Subject: [PATCH] More work on single list + fully restored pager Pager is now back to default preference Addedname for the setting in lbrary Single list: *Popup to when moving manga from one category to another (to switch to D&D or use current category sorting) *Made current spinner option reselectable *In filter bottom sheet, sort options are gone in single list, now always using D&D mode for sorting single list since categories have sort options * When hiding filters, list reverts back to library sorting and header modifies library sorting Pager: * Header item no longer shows * Restored tabs --- .../data/database/models/Category.kt | 40 ++- .../data/preference/PreferenceKeys.kt | 2 +- .../data/preference/PreferencesHelper.kt | 2 +- .../ui/library/LibraryCategoryAdapter.kt | 2 +- .../ui/library/LibraryCategoryView.kt | 2 +- .../tachiyomi/ui/library/LibraryController.kt | 255 ++++++++++-------- .../tachiyomi/ui/library/LibraryHeaderItem.kt | 32 ++- .../tachiyomi/ui/library/LibraryItem.kt | 4 +- .../tachiyomi/ui/library/LibraryPresenter.kt | 222 +++++++-------- .../library/filter/SortFilterBottomSheet.kt | 15 +- .../ui/setting/SettingsLibraryController.kt | 10 +- app/src/main/res/layout/library_category.xml | 1 + .../main/res/layout/library_controller.xml | 1 + app/src/main/res/values/strings.xml | 8 +- 14 files changed, 329 insertions(+), 267 deletions(-) diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/database/models/Category.kt b/app/src/main/java/eu/kanade/tachiyomi/data/database/models/Category.kt index 318cb79b55..2b71324e42 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/database/models/Category.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/database/models/Category.kt @@ -31,18 +31,31 @@ interface Category : Serializable { UPDATED_ASC, UPDATED_DSC -> LibrarySort.LAST_UPDATED UNREAD_ASC, UNREAD_DSC -> LibrarySort.UNREAD LAST_READ_ASC, LAST_READ_DSC -> LibrarySort.LAST_READ + TOTAL_ASC, TOTAL_DSC -> LibrarySort.TOTAL + DRAG_AND_DROP -> LibrarySort.DRAG_AND_DROP else -> null } + fun sortRes(): Int = when (mangaSort) { + ALPHA_ASC, ALPHA_DSC -> R.string.title + UPDATED_ASC, UPDATED_DSC -> R.string.action_sort_last_updated + UNREAD_ASC, UNREAD_DSC -> R.string.action_filter_unread + LAST_READ_ASC, LAST_READ_DSC -> R.string.action_sort_last_read + TOTAL_ASC, TOTAL_DSC -> R.string.action_sort_total + else -> R.string.action_sort_drag_and_drop + } + fun catSortingMode(): Int? = when (mangaSort) { ALPHA_ASC, ALPHA_DSC -> 0 UPDATED_ASC, UPDATED_DSC -> 1 UNREAD_ASC, UNREAD_DSC -> 2 LAST_READ_ASC, LAST_READ_DSC -> 3 + TOTAL_ASC, TOTAL_DSC -> 4 else -> null } companion object { + private const val DRAG_AND_DROP = 'D' private const val ALPHA_ASC = 'a' private const val ALPHA_DSC = 'b' private const val UPDATED_ASC = 'c' @@ -51,15 +64,34 @@ interface Category : Serializable { private const val UNREAD_DSC = 'f' private const val LAST_READ_ASC = 'g' private const val LAST_READ_DSC = 'h' + private const val TOTAL_ASC = 'i' + private const val TOTAL_DSC = 'j' fun create(name: String): Category = CategoryImpl().apply { this.name = name } - fun createDefault(context: Context): Category = create(context.getString(R.string.default_columns)) - .apply { - id = - 0 } + fun createDefault(context: Context): Category = + create(context.getString(R.string.default_columns)).apply { + id = 0 + } + + fun createAll(context: Context, libSort: Int, ascending: Boolean): Category = + create(context.getString(R.string.all)).apply { + id = -1 + mangaSort = when (libSort) { + LibrarySort.ALPHA -> ALPHA_ASC + LibrarySort.LAST_UPDATED -> UPDATED_ASC + LibrarySort.UNREAD -> UNREAD_ASC + LibrarySort.LAST_READ -> LAST_READ_ASC + LibrarySort.TOTAL -> TOTAL_ASC + else -> 'D' + } + if (mangaSort != 'D' && !ascending) { + mangaSort?.plus(1) + } + order = -1 + } } } \ No newline at end of file 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 9ae7a1221b..65eeea5d4a 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 @@ -113,7 +113,7 @@ object PreferenceKeys { const val libraryLayout = "pref_display_library_layout" - const val libraryUsingPager = "library_using_pager" + const val libraryAsSingleList = "library_as_single_list" const val lang = "app_language" 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 81195bd611..a8fcadff69 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 @@ -173,7 +173,7 @@ class PreferencesHelper(val context: Context) { fun libraryLayout() = rxPrefs.getInteger(Keys.libraryLayout, 1) - fun libraryUsingPager() = rxPrefs.getBoolean(Keys.libraryUsingPager, false) + fun libraryAsSingleList() = rxPrefs.getBoolean(Keys.libraryAsSingleList, false) fun downloadBadge() = rxPrefs.getBoolean(Keys.downloadBadge, false) diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryCategoryAdapter.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryCategoryAdapter.kt index 90c1fa4b41..998238883e 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryCategoryAdapter.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryCategoryAdapter.kt @@ -25,7 +25,7 @@ class LibraryCategoryAdapter(val libraryListener: LibraryListener) : FlexibleAdapter>(null, libraryListener, true) { init { - setDisplayHeadersAtStartUp(!Injekt.get().libraryUsingPager() + setDisplayHeadersAtStartUp(Injekt.get().libraryAsSingleList() .getOrDefault()) } /** diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryCategoryView.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryCategoryView.kt index ece03226e3..031016c3c2 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryCategoryView.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryCategoryView.kt @@ -184,6 +184,7 @@ class LibraryCategoryView @JvmOverloads constructor(context: Context, attrs: Att val filterOff = preferences.filterCompleted().getOrDefault() + preferences.filterTracked().getOrDefault() + preferences.filterUnread().getOrDefault() + + preferences.filterMangaType().getOrDefault() + preferences.filterCompleted().getOrDefault() == 0 && !preferences.hideCategories().getOrDefault() return sortingMode == LibrarySort.DRAG_AND_DROP && filterOff && @@ -212,7 +213,6 @@ class LibraryCategoryView @JvmOverloads constructor(context: Context, attrs: Att val mangaForCategory = event.getMangaForCategory(category).orEmpty() adapter.setItems(mangaForCategory) - adapter.hideAllHeaders() swipe_refresh.isEnabled = !preferences.hideCategories().getOrDefault() diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryController.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryController.kt index f53f5847aa..215ecdc5a7 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryController.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryController.kt @@ -5,6 +5,7 @@ import android.content.Context import android.content.res.Configuration import android.graphics.Color import android.os.Bundle +import android.util.AttributeSet import android.view.LayoutInflater import android.view.Menu import android.view.MenuInflater @@ -15,10 +16,8 @@ import android.view.WindowInsets import android.view.inputmethod.InputMethodManager import android.widget.ArrayAdapter import android.widget.Spinner -import android.widget.TextView import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.view.ActionMode -import androidx.appcompat.widget.AppCompatSpinner import androidx.appcompat.widget.SearchView import androidx.core.graphics.drawable.DrawableCompat import androidx.core.math.MathUtils.clamp @@ -33,6 +32,7 @@ import com.f2prateek.rx.preferences.Preference import com.google.android.material.bottomsheet.BottomSheetBehavior import com.google.android.material.snackbar.BaseTransientBottomBar import com.google.android.material.snackbar.Snackbar +import com.google.android.material.tabs.TabLayout import com.jakewharton.rxrelay.BehaviorRelay import com.jakewharton.rxrelay.PublishRelay import eu.davidea.flexibleadapter.FlexibleAdapter @@ -48,6 +48,7 @@ import eu.kanade.tachiyomi.data.library.LibraryUpdateService import eu.kanade.tachiyomi.data.preference.PreferencesHelper import eu.kanade.tachiyomi.data.preference.getOrDefault import eu.kanade.tachiyomi.ui.base.controller.BaseController +import eu.kanade.tachiyomi.ui.base.controller.TabbedController import eu.kanade.tachiyomi.ui.base.controller.withFadeTransaction import eu.kanade.tachiyomi.ui.category.CategoryController import eu.kanade.tachiyomi.ui.download.DownloadController @@ -73,16 +74,18 @@ import eu.kanade.tachiyomi.widget.AutofitRecyclerView import eu.kanade.tachiyomi.widget.IgnoreFirstSpinnerListener import kotlinx.android.synthetic.main.filter_bottom_sheet.* import kotlinx.android.synthetic.main.library_controller.* +import kotlinx.android.synthetic.main.main_activity.* import kotlinx.coroutines.delay -import timber.log.Timber +import rx.Subscription import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.api.get +import java.util.Locale +import kotlin.math.min class LibraryController( bundle: Bundle? = null, private val preferences: PreferencesHelper = Injekt.get() -) : BaseController(bundle), - //TabbedController, +) : BaseController(bundle), TabbedController, ActionMode.Callback, ChangeMangaCategoriesDialog.Listener, MigrationInterface, @@ -163,9 +166,9 @@ class LibraryController( /** * Drawer listener to allow swipe only for closing the drawer. */ - // private var tabsVisibilityRelay: BehaviorRelay = BehaviorRelay.create(false) + private var tabsVisibilityRelay: BehaviorRelay = BehaviorRelay.create(false) - // private var tabsVisibilitySubscription: Subscription? = null + private var tabsVisibilitySubscription: Subscription? = null private var observeLater:Boolean = false @@ -196,12 +199,11 @@ class LibraryController( val category = presenter.categories.find { it.order == order } bottom_sheet.lastCategory = category - if (preferences.librarySortingMode().getOrDefault() == LibrarySort.DRAG_AND_DROP) - bottom_sheet.updateTitle() - updateScroll = true - spinner.setSelection(order + 1) - spinnerAdapter?.setCustomText(category?.name) - + bottom_sheet.updateTitle() + if (spinner.selectedItemPosition != order + 1) { + updateScroll = true + spinner.setSelection(order + 1, true) + } } } } @@ -211,7 +213,7 @@ class LibraryController( */ private lateinit var recycler: RecyclerView - var usePager = preferences.libraryUsingPager().getOrDefault() + private var usePager: Boolean = !preferences.libraryAsSingleList().getOrDefault() init { setHasOptionsMenu(true) @@ -219,15 +221,9 @@ class LibraryController( } override fun getTitle(): String? { - return null//if (title != null) null else resources?.getString(R.string.label_library) + return if (usePager) resources?.getString(R.string.label_library) else null } - private var title: String? = null - set(value) { - field = value - setTitle() - } - override fun inflateView(inflater: LayoutInflater, container: ViewGroup): View { return inflater.inflate(R.layout.library_controller, container, false) } @@ -283,14 +279,15 @@ class LibraryController( adapter.fastScroller = fast_scroller recycler.addOnScrollListener(scrollListener) - spinner = recycler_layout.inflate(R.layout.library_spinner) as AppCompatSpinner + spinner = ReSpinner(view.context) (activity as MainActivity).supportActionBar?.setDisplayShowCustomEnabled(true) (activity as MainActivity).supportActionBar?.customView = spinner spinnerAdapter = SpinnerAdapter(view.context, R.layout.library_spinner_textview, arrayOf(resources!!.getString(R.string.label_library))) + spinnerAdapter?.setDropDownViewResource(R.layout.library_spinner_entry_text) spinner.adapter = spinnerAdapter - spinnerAdapter?.setCustomText(resources?.getString(R.string.label_library)) + //spinnerAdapter?.setCustomText(resources?.getString(R.string.label_library)) } @@ -318,22 +315,14 @@ class LibraryController( router.pushController(DownloadController().withFadeTransaction()) } - - // spinner.onItemSelectedListener = listener - /*spinner.onItemSelectedListener = IgnoreFirstSpinnerListener { position -> - if (!updateScroll) return@IgnoreFirstSpinnerListener - val headerPosition = adapter.indexOf(position) + 1 - if (headerPosition > -1) (recycler.layoutManager as LinearLayoutManager) - .scrollToPositionWithOffset(position, 0) - }*/ - val config = resources?.configuration val phoneLandscape = (config?.orientation == Configuration.ORIENTATION_LANDSCAPE && (config.screenLayout.and(Configuration.SCREENLAYOUT_SIZE_MASK)) < Configuration.SCREENLAYOUT_SIZE_LARGE) + // pad the recycler if the filter bottom sheet is visible if (!usePager && !phoneLandscape) { - val height = view.context.resources.getDimensionPixelSize(R.dimen.rounder_radius) + 5.dpToPx + val height = view.context.resources.getDimensionPixelSize(R.dimen.rounder_radius) + 4.dpToPx recycler.updatePaddingRelative(bottom = height) } @@ -358,7 +347,8 @@ class LibraryController( if (type.isEnter) { if (!usePager) (activity as MainActivity).supportActionBar?.setDisplayShowCustomEnabled(true) - //activity?.tabs?.setupWithViewPager(library_pager) + else + activity?.tabs?.setupWithViewPager(library_pager) presenter.getLibrary() DownloadService.addListener(this) DownloadService.callListeners() @@ -389,13 +379,13 @@ class LibraryController( } override fun onDestroyView(view: View) { - //adapter.onDestroy() + pagerAdapter?.onDestroy() DownloadService.removeListener(this) LibraryUpdateService.removeListener() - //adapter = null + pagerAdapter = null actionMode = null - //tabsVisibilitySubscription?.unsubscribe() - //tabsVisibilitySubscription = null + tabsVisibilitySubscription?.unsubscribe() + tabsVisibilitySubscription = null super.onDestroyView(view) } @@ -421,7 +411,7 @@ class LibraryController( super.onDetach(view) } - /*override fun configureTabs(tabs: TabLayout) { + override fun configureTabs(tabs: TabLayout) { with(tabs) { tabGravity = TabLayout.GRAVITY_CENTER tabMode = TabLayout.MODE_SCROLLABLE @@ -440,7 +430,7 @@ class LibraryController( override fun cleanupTabs(tabs: TabLayout) { tabsVisibilitySubscription?.unsubscribe() tabsVisibilitySubscription = null - }*/ + } fun onNextLibraryUpdate(mangaMap: List, freshStart: Boolean = false) { if (mangaMap.isNotEmpty()) { @@ -455,14 +445,14 @@ class LibraryController( spinner.onItemSelectedListener = null spinnerAdapter = SpinnerAdapter(view!!.context, R.layout.library_spinner_textview, presenter.categories.map { it.name }.toTypedArray()) + spinnerAdapter?.setDropDownViewResource(R.layout.library_spinner_entry_text) spinner.adapter = spinnerAdapter - spinnerAdapter?.setCustomText(presenter.categories.find { it.order == activeCategory - }?.name ?: resources?.getString(R.string.label_library)) + spinner.setSelection(min(presenter.categories.size - 1, activeCategory + 1)) + /* spinnerAdapter?.setCustomText(presenter.categories.find { it.order == activeCategory + }?.name ?: resources?.getString(R.string.label_library))*/ if (!freshStart) { - spinnerAdapter?.setCustomText(presenter.categories.find { it.order == activeCategory - }?.name ?: resources?.getString(R.string.label_library)) justStarted = false if (recycler_layout.alpha == 0f) recycler_layout.animate().alpha(1f).setDuration(500).start() @@ -474,6 +464,7 @@ class LibraryController( .scrollToPositionWithOffset(position, 0) } adapter.isLongPressDragEnabled = canDrag() + tabsVisibilityRelay.call(false) bottom_sheet.lastCategory = presenter.categories[clamp(activeCategory, 0, @@ -527,13 +518,13 @@ class LibraryController( bottom_sheet.lastCategory = adapter.categories.getOrNull(activeCat) bottom_sheet.updateTitle() - //tabsVisibilityRelay.call(categories.size > 1) + tabsVisibilityRelay.call(categories.size > 1) if (freshStart || !justStarted) { // Delay the scroll position to allow the view to be properly measured. view.post { if (isAttached) { - //activity?.tabs?.setScrollPosition(library_pager.currentItem, 0f, true) + activity?.tabs?.setScrollPosition(library_pager.currentItem, 0f, true) } } @@ -583,57 +574,72 @@ class LibraryController( } fun onCatSortChanged(id: Int? = null) { - val catId = id ?: presenter.categories.find { it.order == activeCategory }?.id ?: return + val catId = + (if (usePager)(id ?: pagerAdapter?.categories?.getOrNull(library_pager.currentItem)?.id) + else (id ?: presenter.categories.find { it.order == activeCategory }?.id)) + ?: return presenter.requestCatSortUpdate(catId) - //val catId = id ?: adapter?.categories?.getOrNull(library_pager.currentItem)?.id ?: return - // presenter.requestCatSortUpdate(catId) } /** * Reattaches the adapter to the view pager to recreate fragments */ private fun reattachAdapter() { - val position = (recycler.layoutManager as LinearLayoutManager).findFirstVisibleItemPosition() - if (recycler is AutofitRecyclerView && preferences.libraryLayout().getOrDefault() == 0 || - recycler !is AutofitRecyclerView && preferences.libraryLayout().getOrDefault() > 0) { - destroyActionModeIfNeeded() - recycler_layout.removeView(recycler) - recycler = if (preferences.libraryLayout().getOrDefault() == 0) { - (recycler_layout.inflate(R.layout.library_list_recycler) as RecyclerView).apply { - layoutManager = LinearLayoutManager(context) + if (usePager) { + val adapter = pagerAdapter ?: return + + val position = library_pager.currentItem + + adapter.recycle = false + library_pager.adapter = adapter + library_pager.currentItem = position + adapter.recycle = true + } + else { + val position = + (recycler.layoutManager as LinearLayoutManager).findFirstVisibleItemPosition() + // if switching from list to grid + if (recycler is AutofitRecyclerView && preferences.libraryLayout().getOrDefault() == 0 + || recycler !is AutofitRecyclerView && preferences.libraryLayout().getOrDefault() > 0) { + destroyActionModeIfNeeded() + recycler_layout.removeView(recycler) + recycler = if (preferences.libraryLayout().getOrDefault() == 0) { + (recycler_layout.inflate(R.layout.library_list_recycler) as RecyclerView).apply { + layoutManager = LinearLayoutManager(context) + } + } else { + (recycler_layout.inflate(R.layout.library_grid_recycler) as AutofitRecyclerView).apply { + spanCount = mangaPerRow + manager.spanSizeLookup = (object : GridLayoutManager.SpanSizeLookup() { + override fun getSpanSize(position: Int): Int { + val item = this@LibraryController.adapter.getItem(position) + return if (item is LibraryHeaderItem) manager.spanCount else 1 + } + }) + } + } + recycler.setHasFixedSize(true) + adapter = LibraryCategoryAdapter(this) + recycler.adapter = adapter + recycler.addOnScrollListener(scrollListener) + adapter.isLongPressDragEnabled = canDrag() + recycler_layout.addView(recycler) + adapter.setItems(presenter.getList()) + val config = resources?.configuration + val phoneLandscape = (config?.orientation == Configuration.ORIENTATION_LANDSCAPE && + (config.screenLayout.and(Configuration.SCREENLAYOUT_SIZE_MASK)) < + Configuration.SCREENLAYOUT_SIZE_LARGE) + if (!usePager && !phoneLandscape) { + val height = recycler.resources.getDimensionPixelSize(R.dimen + .rounder_radius) + 4.dpToPx + recycler.updatePaddingRelative(bottom = height) } } else { - (recycler_layout.inflate(R.layout.library_grid_recycler) as AutofitRecyclerView).apply { - spanCount = mangaPerRow - manager.spanSizeLookup = (object : GridLayoutManager.SpanSizeLookup() { - override fun getSpanSize(position: Int): Int { - val item = this@LibraryController.adapter.getItem(position) - return if (item is LibraryHeaderItem) - manager.spanCount else 1 - } - }) - } + recycler.adapter = adapter } - recycler.setHasFixedSize(true) - adapter = LibraryCategoryAdapter(this) - recycler.adapter = adapter - recycler.addOnScrollListener(scrollListener) - adapter.isLongPressDragEnabled = canDrag() - recycler_layout.addView(recycler) - adapter.setItems(presenter.getList()) - } else { - recycler.adapter = adapter + + (recycler.layoutManager as LinearLayoutManager).scrollToPositionWithOffset(position, 0) } - - (recycler.layoutManager as LinearLayoutManager).scrollToPositionWithOffset(position, 0) - //val adapter = adapter ?: return - - /*val position = library_pager.currentItem - - adapter.recycle = false - library_pager.adapter = adapter - library_pager.currentItem = position - adapter.recycle = true*/ } /** @@ -921,7 +927,10 @@ class LibraryController( override fun startReading(position: Int) { val activity = activity ?: return val manga = (adapter.getItem(position) as? LibraryItem)?.manga ?: return - if (adapter.mode == SelectableAdapter.Mode.MULTI) toggleSelection(position) + if (adapter.mode == SelectableAdapter.Mode.MULTI) { + toggleSelection(position) + return + } val chapter = presenter.getFirstUnread(manga) ?: return val intent = ReaderActivity.newIntent(activity, manga, chapter) destroyActionModeIfNeeded() @@ -937,14 +946,13 @@ class LibraryController( } override fun canDrag(): Boolean { - val sortingMode = preferences.librarySortingMode().getOrDefault() val filterOff = preferences.filterCompleted().getOrDefault() + preferences.filterTracked().getOrDefault() + preferences.filterUnread().getOrDefault() + + preferences.filterMangaType().getOrDefault() + preferences.filterCompleted().getOrDefault() == 0 && !preferences.hideCategories().getOrDefault() - return sortingMode == LibrarySort.DRAG_AND_DROP && filterOff && - adapter.mode != SelectableAdapter.Mode.MULTI + return filterOff && adapter.mode != SelectableAdapter.Mode.MULTI } /** @@ -1020,13 +1028,32 @@ class LibraryController( if (adapter.selectedItemCount > 0) return val item = adapter.getItem(position) as? LibraryItem ?: return val newHeader = adapter.getSectionHeader(position) as? LibraryHeaderItem - val libraryItems = adapter.getSectionItems(adapter.getSectionHeader(position)).filterIsInstance() + val libraryItems = adapter.getSectionItems(adapter.getSectionHeader(position)) + .filterIsInstance() val mangaIds = libraryItems.mapNotNull { (it as? LibraryItem)?.manga?.id } if (newHeader?.category?.id == item.manga.category) { presenter.rearrangeCategory(item.manga.category, mangaIds) } else { - presenter.moveMangaToCategory(item, newHeader?.category?.id, mangaIds) - Timber.d("Manga has Moved") + if (newHeader?.category?.mangaSort == null) { + presenter.moveMangaToCategory(item, newHeader?.category?.id, mangaIds, true) + } else { + MaterialDialog(activity!!).message(R.string.switch_to_dnd) + .positiveButton(R.string.action_switch) { + presenter.moveMangaToCategory(item, newHeader.category.id, mangaIds, true) + }.negativeButton( + text = resources?.getString( + R.string.keep_current_sort, + resources!!.getString(newHeader.category.sortRes()).toLowerCase + (Locale.getDefault()) + ) + ) { + presenter.moveMangaToCategory( + item, newHeader.category.id, mangaIds, false + ) + } + .cancelOnTouchOutside(false) + .show() + } } } @@ -1077,26 +1104,32 @@ object HeightTopWindowInsetsListener : View.OnApplyWindowInsetsListener { class SpinnerAdapter(context: Context, layoutId: Int, val array: Array) : ArrayAdapter - (context, layoutId, array) { - private var mCustomText = "" + (context, layoutId, array) - override fun getView(position: Int, convertView: View?, parent: ViewGroup): View { - val view = super.getView(position, convertView, parent) - val tv: TextView = view as TextView - tv.text = mCustomText - return view +class ReSpinner : Spinner { + constructor(context: Context?) : super(context) {} + constructor(context: Context?, attrs: AttributeSet?) : super(context, attrs) {} + constructor(context: Context?, attrs: AttributeSet?, defStyle: Int) : super( + context, attrs, defStyle + ) + + override fun setSelection(position: Int, animate: Boolean) { + val sameSelected = position == selectedItemPosition + super.setSelection(position, animate) + if (sameSelected) { // Spinner does not call the OnItemSelectedListener if the same item is selected, so do it manually now + onItemSelectedListener?.onItemSelected( + this, selectedView, position, selectedItemId + ) + } } - fun setCustomText(customText: String?) { - // Call to set the text that must be shown in the spinner for the custom option. - val text = customText ?: return - mCustomText = text - notifyDataSetChanged() - } - - override fun getDropDownView(position: Int, convertView: View?, parent: ViewGroup): View { - val view = parent.inflate(R.layout.library_spinner_entry_text) as TextView - view.text = array[position] - return view + override fun setSelection(position: Int) { + val sameSelected = position == selectedItemPosition + super.setSelection(position) + if (sameSelected) { // Spinner does not call the OnItemSelectedListener if the same item is selected, so do it manually now + onItemSelectedListener?.onItemSelected( + this, selectedView, position, selectedItemId + ) + } } } \ No newline at end of file diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryHeaderItem.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryHeaderItem.kt index 542a0b6792..52941b6dee 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryHeaderItem.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryHeaderItem.kt @@ -90,12 +90,20 @@ class LibraryHeaderItem(val category: Category) : AbstractHeaderItem { + updateButton.invisible() + catProgress.gone() + } + LibraryUpdateService.categoryInQueue(item.category.id) -> { + catProgress.visible() + updateButton.invisible() + } + else -> { + catProgress.gone() + updateButton.visible() + } } } @@ -113,7 +121,9 @@ class LibraryHeaderItem(val category: Category) : AbstractHeaderItem @@ -140,8 +150,11 @@ class LibraryHeaderItem(val category: Category) : AbstractHeaderItem R.drawable.ic_check_white_24dp + category.isAscending() -> R.drawable.ic_arrow_up_white_24dp + else -> R.drawable.ic_arrow_down_white_24dp + } ) // Finally show the PopupMenu @@ -162,6 +175,7 @@ class LibraryHeaderItem(val category: Category) : AbstractHeaderItem 4 R.id.action_last_read -> 3 R.id.action_unread -> 2 R.id.action_update -> 1 diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryItem.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryItem.kt index c87e4c8fc8..4feb81fcfd 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryItem.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryItem.kt @@ -23,8 +23,8 @@ import uy.kohesive.injekt.injectLazy class LibraryItem(val manga: LibraryManga, private val libraryLayout: Preference, - header: LibraryHeaderItem) : - AbstractSectionableItem(header), IFilterable { + header: LibraryHeaderItem?) : + AbstractSectionableItem(header), IFilterable { var downloadCount = -1 var unreadType = 1 diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryPresenter.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryPresenter.kt index 2666257aeb..3843af8e74 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryPresenter.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryPresenter.kt @@ -94,6 +94,8 @@ class LibraryPresenter( fun getLibrary() { launchUI { + val freshStart = !preferences.libraryAsSingleList().getOrDefault() + && (currentMangaMap?.values?.firstOrNull()?.firstOrNull()?.header != null) val mangaMap = withContext(Dispatchers.IO) { val library = getLibraryFromDB() library.apply { setDownloadCount(library.mangaMap) } @@ -104,7 +106,7 @@ class LibraryPresenter( mangaMap } currentMangaMap = mangaMap - updateView(categories, mangaMap) + updateView(categories, mangaMap, freshStart) } } @@ -252,41 +254,7 @@ class LibraryPresenter( } val sortFn: (LibraryItem, LibraryItem) -> Int = { i1, i2 -> - val compare = when { - category.mangaSort != null -> { - var sort = when (category.sortingMode()) { - LibrarySort.ALPHA -> sortAlphabetical(i1, i2) - LibrarySort.LAST_UPDATED -> i2.manga.last_update.compareTo(i1.manga.last_update) - LibrarySort.UNREAD -> when { - i1.manga.unread == i2.manga.unread -> 0 - i1.manga.unread == 0 -> if (category.isAscending()) 1 else -1 - i2.manga.unread == 0 -> if (category.isAscending()) -1 else 1 - else -> i1.manga.unread.compareTo(i2.manga.unread) - } - LibrarySort.LAST_READ -> { - val manga1LastRead = lastReadManga[i1.manga.id!!] ?: lastReadManga.size - val manga2LastRead = lastReadManga[i2.manga.id!!] ?: lastReadManga.size - manga1LastRead.compareTo(manga2LastRead) - } - else -> sortAlphabetical(i1, i2) - } - if (!category.isAscending()) sort *= -1 - sort - } - category.mangaOrder.isNotEmpty() -> { - val order = category.mangaOrder - val index1 = order.indexOf(i1.manga.id!!) - val index2 = order.indexOf(i2.manga.id!!) - when { - index1 == index2 -> 0 - index1 == -1 -> -1 - index2 == -1 -> 1 - else -> index1.compareTo(index2) - } - } - else -> 0 - } - compare + sortCategory(i1, i2, lastReadManga, category) } val comparator = Comparator(sortFn) @@ -309,80 +277,36 @@ class LibraryPresenter( var counter = 0 db.getTotalChapterManga().executeAsBlocking().associate { it.id!! to counter++ } } - val catListing by lazy { - val default = createDefaultCategory() - listOf(default) + db.getCategories().executeAsBlocking() - } val ascending = preferences.librarySortingAscending().getOrDefault() + val useDnD = preferences.libraryAsSingleList().getOrDefault() && !preferences + .hideCategories().getOrDefault() val sortFn: (LibraryItem, LibraryItem) -> Int = { i1, i2 -> - val compare = when (sortingMode) { - LibrarySort.ALPHA -> sortAlphabetical(i1, i2) - LibrarySort.LAST_READ -> { + val compare = when { + sortingMode == LibrarySort.DRAG_AND_DROP || useDnD -> + sortCategory(i1, i2, lastReadManga) + sortingMode == LibrarySort.ALPHA -> sortAlphabetical(i1, i2) + sortingMode == LibrarySort.LAST_READ -> { // Get index of manga, set equal to list if size unknown. val manga1LastRead = lastReadManga[i1.manga.id!!] ?: lastReadManga.size val manga2LastRead = lastReadManga[i2.manga.id!!] ?: lastReadManga.size manga1LastRead.compareTo(manga2LastRead) } - LibrarySort.LAST_UPDATED -> i2.manga.last_update.compareTo(i1.manga.last_update) - LibrarySort.UNREAD -> + sortingMode == LibrarySort.LAST_UPDATED -> i2.manga.last_update.compareTo(i1 + .manga.last_update) + sortingMode == LibrarySort.UNREAD -> when { i1.manga.unread == i2.manga.unread -> 0 i1.manga.unread == 0 -> if (ascending) 1 else -1 i2.manga.unread == 0 -> if (ascending) -1 else 1 else -> i1.manga.unread.compareTo(i2.manga.unread) } - LibrarySort.TOTAL -> { + sortingMode == LibrarySort.TOTAL -> { val manga1TotalChapter = totalChapterManga[i1.manga.id!!] ?: 0 val mange2TotalChapter = totalChapterManga[i2.manga.id!!] ?: 0 manga1TotalChapter.compareTo(mange2TotalChapter) } - LibrarySort.DRAG_AND_DROP -> { - if (i1.manga.category == i2.manga.category) { - val category = catListing.find { it.id == i1.manga.category } - when { - category?.mangaSort != null -> { - var sort = when (category.sortingMode()) { - LibrarySort.ALPHA -> sortAlphabetical(i1, i2) - LibrarySort.LAST_UPDATED -> i2.manga.last_update.compareTo(i1.manga.last_update) - LibrarySort.UNREAD -> when { - i1.manga.unread == i2.manga.unread -> 0 - i1.manga.unread == 0 -> if (category.isAscending()) 1 else -1 - i2.manga.unread == 0 -> if (category.isAscending()) -1 else 1 - else -> i1.manga.unread.compareTo(i2.manga.unread) - } - LibrarySort.LAST_READ -> { - val manga1LastRead = lastReadManga[i1.manga.id!!] ?: lastReadManga.size - val manga2LastRead = lastReadManga[i2.manga.id!!] ?: lastReadManga.size - manga1LastRead.compareTo(manga2LastRead) - } - else -> sortAlphabetical(i1, i2) - } - if (!category.isAscending()) - sort *= -1 - sort - } - category?.mangaOrder?.isEmpty() == false -> { - val order = category.mangaOrder - val index1 = order.indexOf(i1.manga.id!!) - val index2 = order.indexOf(i2.manga.id!!) - when { - index1 == index2 -> 0 - index1 == -1 -> -1 - index2 == -1 -> 1 - else -> index1.compareTo(index2) - } - } - else -> 0 - } - } - else { - val category = catListing.find { it.id == i1.manga.category }?.order ?: -1 - val category2 = catListing.find { it.id == i2.manga.category }?.order ?: -1 - category.compareTo(category2) - } - } else -> 0 } if (compare == 0) { @@ -400,6 +324,54 @@ class LibraryPresenter( return map.mapValues { entry -> entry.value.sortedWith(comparator) } } + private fun sortCategory(i1: LibraryItem, i2: LibraryItem, + lastReadManga: Map, initCat: Category? = null): Int { + return if (initCat != null || i1.manga.category == i2.manga.category) { + val category = initCat ?: allCategories.find { it.id == i1.manga.category } + when { + category?.mangaSort != null -> { + var sort = when (category.sortingMode()) { + LibrarySort.ALPHA -> sortAlphabetical(i1, i2) + LibrarySort.LAST_UPDATED -> i2.manga.last_update.compareTo(i1.manga.last_update) + LibrarySort.UNREAD -> when { + i1.manga.unread == i2.manga.unread -> 0 + i1.manga.unread == 0 -> if (category.isAscending()) 1 else -1 + i2.manga.unread == 0 -> if (category.isAscending()) -1 else 1 + else -> i1.manga.unread.compareTo(i2.manga.unread) + } + LibrarySort.LAST_READ -> { + val manga1LastRead = lastReadManga[i1.manga.id!!] ?: lastReadManga.size + val manga2LastRead = lastReadManga[i2.manga.id!!] ?: lastReadManga.size + manga1LastRead.compareTo(manga2LastRead) + } + else -> sortAlphabetical(i1, i2) + } + if (!category.isAscending()) + sort *= -1 + sort + } + category?.mangaOrder?.isEmpty() == false -> { + val order = category.mangaOrder + val index1 = order.indexOf(i1.manga.id!!) + val index2 = order.indexOf(i2.manga.id!!) + when { + index1 == index2 -> 0 + index1 == -1 -> -1 + index2 == -1 -> 1 + else -> index1.compareTo(index2) + } + } + else -> 0 + } + } + else { + val category = allCategories.find { it.id == i1.manga.category }?.order ?: -1 + val category2 = allCategories.find { it.id == i2.manga.category }?.order ?: -1 + category.compareTo(category2) + } + } + + private fun sortAlphabetical(i1: LibraryItem, i2: LibraryItem): Int { return if (preferences.removeArticles().getOrDefault()) i1.manga.currentTitle().removeArticles().compareTo(i2.manga.currentTitle().removeArticles(), true) @@ -424,11 +396,13 @@ class LibraryPresenter( }.groupBy { if (showCategories) it.manga.category else 0 }*/ - val catItemMain = LibraryHeaderItem(categories.firstOrNull() ?: createDefaultCategory()) + val catItemAll = LibraryHeaderItem(Category.createAll(context, + preferences.librarySortingMode().getOrDefault(), + preferences.librarySortingAscending().getOrDefault())) val libraryMap = - if (preferences.libraryUsingPager().getOrDefault()) { + if (!preferences.libraryAsSingleList().getOrDefault()) { libraryManga.map { manga -> - LibraryItem(manga, libraryLayout, catItemMain).apply { unreadType = unreadBadgeType } + LibraryItem(manga, libraryLayout, null).apply { unreadType = unreadBadgeType } }.groupBy { if (showCategories) it.manga.category else 0 } @@ -438,8 +412,10 @@ class LibraryPresenter( if (showCategories) manga.category else 0 //LibraryItem(manga, libraryLayout).apply { unreadType = unreadBadgeType } }.map { entry -> - val categoryItem = LibraryHeaderItem(categories.find { entry.key == it.id } - ?: createDefaultCategory()) + val categoryItem = + if (!showCategories) catItemAll else + (LibraryHeaderItem(categories.find { entry.key == it.id } + ?: createDefaultCategory())) entry.value.map { LibraryItem( it, libraryLayout, categoryItem @@ -455,8 +431,7 @@ class LibraryPresenter( categories.add(0, createDefaultCategory()) this.allCategories = categories - this.categories = if (preferences.hideCategories().getOrDefault()) - arrayListOf(createDefaultCategory()) + this.categories = if (!showCategories) arrayListOf(catItemAll.category) else categories return Library(this.categories, libraryMap) @@ -486,8 +461,8 @@ class LibraryPresenter( suspend fun updateView(categories: List, mangaMap: LibraryMap, freshStart:Boolean = false) { - if (preferences.libraryUsingPager().getOrDefault()) { - view.onNextLibraryUpdate(categories, mangaMap, true) + if (!preferences.libraryAsSingleList().getOrDefault()) { + view.onNextLibraryUpdate(categories, mangaMap, freshStart) } else { val mangaList = withContext(Dispatchers.IO) { @@ -514,20 +489,15 @@ class LibraryPresenter( } fun updateViewBlocking() { - /* val list = withContext(Dispatchers.IO) { - val showCategories = !preferences.hideCategories().getOrDefault() - val current = mangaMap.values.first() - current.groupBy { - if (showCategories) it.manga.category else 0 - }.flatMap { it.value } - }*/ val mangaMap = currentMangaMap ?: return - if (preferences.libraryUsingPager().getOrDefault()) { + if (!preferences.libraryAsSingleList().getOrDefault()) { + if (mangaMap.values.firstOrNull()?.firstOrNull()?.header != null) + return view.onNextLibraryUpdate(categories, mangaMap, true) } else { val list = mutableListOf() - for (element in mangaMap?.toSortedMap(compareBy { entry -> + for (element in mangaMap.toSortedMap(compareBy { entry -> categories.find { it.id == entry }?.order ?: -1 })) { list.addAll(element.value) @@ -776,11 +746,17 @@ class LibraryPresenter( fun sortCategory(catId: Int, order: Int) { val category = categories.find { catId == it.id } ?: return category.mangaSort = ('a' + (order - 1)) - if (category.id == 0) - preferences.defaultMangaOrder().set(category.mangaSort.toString()) - else - Injekt.get().insertCategory(category).asRxObservable().subscribe() - requestCatSortUpdate(category.id!!) + if (catId == -1) { + val sort = category.sortingMode() ?: LibrarySort.ALPHA + preferences.librarySortingMode().set(sort) + preferences.librarySortingAscending().set(category.isAscending()) + requestSortUpdate() + } + else { + if (category.id == 0) preferences.defaultMangaOrder().set(category.mangaSort.toString()) + else Injekt.get().insertCategory(category).asRxObservable().subscribe() + requestCatSortUpdate(category.id!!) + } } fun rearrangeCategory(catId: Int?, mangaIds: List) { @@ -794,7 +770,7 @@ class LibraryPresenter( } } - fun moveMangaToCategory(item: LibraryItem, catId: Int?, mangaIds: List) { + fun moveMangaToCategory(item: LibraryItem, catId: Int?, mangaIds: List, useDND: Boolean) { GlobalScope.launch(Dispatchers.IO) { val categoryId = catId ?: return@launch val category = categories.find { catId == it.id } ?: return@launch @@ -812,6 +788,7 @@ class LibraryPresenter( item.manga.category = categoryId + val mc = ArrayList() val categories = db.getCategoriesForManga(manga).executeAsBlocking().filter { it.id != oldCatId } + listOf(category) @@ -822,13 +799,14 @@ class LibraryPresenter( db.setMangaCategories(mc, listOf(manga)) - category.mangaSort = null - val ids = mangaIds.toMutableList() - if (!ids.contains(manga.id!!)) - ids.add(manga.id!!) - category.mangaOrder = ids - if (category.id == 0) preferences.defaultMangaOrder().set(mangaIds.joinToString("/")) - else db.insertCategory(category).executeAsBlocking() + if (useDND) { + category.mangaSort = null + val ids = mangaIds.toMutableList() + if (!ids.contains(manga.id!!)) ids.add(manga.id!!) + category.mangaOrder = ids + if (category.id == 0) preferences.defaultMangaOrder().set(mangaIds.joinToString("/")) + else db.insertCategory(category).executeAsBlocking() + } getLibrary() } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/library/filter/SortFilterBottomSheet.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/library/filter/SortFilterBottomSheet.kt index db6dd8db32..ba7ebee7b7 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/library/filter/SortFilterBottomSheet.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/library/filter/SortFilterBottomSheet.kt @@ -179,6 +179,9 @@ class SortFilterBottomSheet @JvmOverloads constructor(context: Context, attrs: A } } createTags() + sorting_layout.visibility = + if (preferences.libraryAsSingleList().getOrDefault()) View.GONE + else View.VISIBLE library_sort_text.setOnClickListener { showMainSortOptions() } category_sort_text.setOnClickListener { showCatSortOptions() } @@ -290,16 +293,12 @@ class SortFilterBottomSheet @JvmOverloads constructor(context: Context, attrs: A fun sorting(trueSort:Boolean = false): Int { val sortingMode = preferences.librarySortingMode().getOrDefault() - return if (!trueSort && sortingMode == LibrarySort.DRAG_AND_DROP && + val singleList = preferences.libraryAsSingleList().getOrDefault() + return if (!trueSort && + (sortingMode == LibrarySort.DRAG_AND_DROP || singleList) && lastCategory != null && !preferences.hideCategories().getOrDefault()) { - when (lastCategory?.mangaSort) { - 'a', 'b' -> LibrarySort.ALPHA - 'c', 'd' -> LibrarySort.LAST_UPDATED - 'e', 'f' -> LibrarySort.UNREAD - 'g', 'h' -> LibrarySort.LAST_READ - else -> LibrarySort.DRAG_AND_DROP - } + lastCategory?.sortingMode() ?: LibrarySort.DRAG_AND_DROP } else { sortingMode diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsLibraryController.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsLibraryController.kt index ca49dddcc0..b96608326b 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsLibraryController.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsLibraryController.kt @@ -40,8 +40,8 @@ class SettingsLibraryController : SettingsController() { } Observable.combineLatest(preferences.portraitColumns().asObservable(), - preferences.landscapeColumns().asObservable(), - { portraitCols, landscapeCols -> Pair(portraitCols, landscapeCols) }) + preferences.landscapeColumns().asObservable() + ) { portraitCols, landscapeCols -> Pair(portraitCols, landscapeCols) } .subscribeUntilDestroy { (portraitCols, landscapeCols) -> val portrait = getColumnValue(portraitCols) val landscape = getColumnValue(landscapeCols) @@ -53,9 +53,9 @@ class SettingsLibraryController : SettingsController() { } switchPreference { - key = Keys.libraryUsingPager - titleRes = R.string.pref_remove_articles - summaryRes = R.string.pref_remove_articles_summary + key = Keys.libraryAsSingleList + titleRes = R.string.pref_library_single_list + summaryRes = R.string.pref_library_single_list_summary defaultValue = false } diff --git a/app/src/main/res/layout/library_category.xml b/app/src/main/res/layout/library_category.xml index 40c954d8a8..e9b50b1f9c 100644 --- a/app/src/main/res/layout/library_category.xml +++ b/app/src/main/res/layout/library_category.xml @@ -19,6 +19,7 @@ android:layout_width="wrap_content" android:layout_height="match_parent" android:layout_gravity="end" + app:fastScrollerIgnoreTouchesOutsideHandle="true" app:fastScrollerBubbleEnabled="true" tools:visibility="visible" /> diff --git a/app/src/main/res/layout/library_controller.xml b/app/src/main/res/layout/library_controller.xml index e2a9b5b6c9..ada64bc2eb 100644 --- a/app/src/main/res/layout/library_controller.xml +++ b/app/src/main/res/layout/library_controller.xml @@ -30,6 +30,7 @@ android:layout_width="wrap_content" android:layout_height="match_parent" android:layout_gravity="end" + app:fastScrollerIgnoreTouchesOutsideHandle="true" app:fastScrollerBubbleEnabled="false" tools:visibility="visible" /> diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 51e3dc911f..de71627375 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -129,6 +129,7 @@ Move to bottom Track Sort category by… + Switch Loading… @@ -198,8 +199,9 @@ Only update ongoing manga Sync chapters after reading Sort by ignoring articles - Fixed grid size in library - Show all covers as the same height by cropping + Show library as a single list + Show all categories under a single + sectioned list When sorting alphabetically, sort ignoring articles (a, an, the) at the start of manga titles Skip pre-migration @@ -413,6 +415,8 @@ %1$s is already in queue Local Remove from library? + Switch to Drag & Drop mode? + Keep sorting by %1$s Delete category? Manga in this category will moved into the default category.