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 e99c4d182a..c28c20ea7d 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 @@ -165,6 +165,8 @@ class PreferencesHelper(val context: Context) { fun filterTracked() = rxPrefs.getInteger(Keys.filterTrcaked, 0) + fun showCategories() = rxPrefs.getBoolean("show_categories", true) + fun librarySortingMode() = rxPrefs.getInteger(Keys.librarySortingMode, 0) fun librarySortingAscending() = rxPrefs.getBoolean("library_sorting_ascending", true) 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 21e2970eb3..ab115c74de 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 @@ -7,6 +7,7 @@ import eu.kanade.tachiyomi.data.database.models.Manga import eu.kanade.tachiyomi.data.preference.PreferencesHelper import eu.kanade.tachiyomi.data.preference.getOrDefault import eu.kanade.tachiyomi.ui.category.CategoryAdapter +import eu.kanade.tachiyomi.util.chop import eu.kanade.tachiyomi.util.removeArticles import uy.kohesive.injekt.injectLazy import java.text.SimpleDateFormat @@ -69,14 +70,36 @@ class LibraryCategoryAdapter(val view: LibraryCategoryView) : val iFlexible: IFlexible<*>? = getItem(position) val preferences:PreferencesHelper by injectLazy() when (preferences.librarySortingMode().getOrDefault()) { + LibrarySort.DRAG_AND_DROP -> { + if (preferences.showCategories().getOrDefault()) { + val title = (iFlexible as LibraryItem).manga.currentTitle() + if (preferences.removeArticles().getOrDefault()) + title.removeArticles().substring(0, 1).toUpperCase(Locale.US) + else title.substring(0, 1).toUpperCase(Locale.US) + } + else { + val db:DatabaseHelper by injectLazy() + val category = db.getCategoriesForManga((iFlexible as LibraryItem).manga) + .executeAsBlocking().firstOrNull()?.name + category?.chop(10) ?: "Default" + } + } LibrarySort.LAST_READ -> { val db:DatabaseHelper by injectLazy() val id = (iFlexible as LibraryItem).manga.id ?: return "" val history = db.getHistoryByMangaId(id).executeAsBlocking() - if (history.firstOrNull() != null) - getShortDate(Date(history.first().last_read)) + val last = history.maxBy { it.last_read } + if (last != null) + getShortDate(Date(last.last_read)) else - "Never Read" + "N/A" + } + LibrarySort.UNREAD -> { + val unread = (iFlexible as LibraryItem).manga.unread + if (unread > 0) + unread.toString() + else + "Read" } LibrarySort.LAST_UPDATED -> { val lastUpdate = (iFlexible as LibraryItem).manga.last_update @@ -105,9 +128,9 @@ class LibraryCategoryAdapter(val view: LibraryCategoryView) : val yearThen = cal2.get(Calendar.YEAR) return if (yearNow == yearThen) - SimpleDateFormat("MMM").format(date) + SimpleDateFormat("MMM", Locale.getDefault()).format(date) else - SimpleDateFormat("yyyy").format(date) + SimpleDateFormat("yyyy", Locale.getDefault()).format(date) } } 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 511a4fda4b..83d6ac7719 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 @@ -13,15 +13,19 @@ import eu.davidea.flexibleadapter.SelectableAdapter 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.database.models.LibraryManga import eu.kanade.tachiyomi.data.database.models.Manga 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.category.CategoryAdapter -import eu.kanade.tachiyomi.util.* +import eu.kanade.tachiyomi.util.doOnApplyWindowInsets +import eu.kanade.tachiyomi.util.inflate +import eu.kanade.tachiyomi.util.launchUI +import eu.kanade.tachiyomi.util.plusAssign +import eu.kanade.tachiyomi.util.snack +import eu.kanade.tachiyomi.util.updateLayoutParams +import eu.kanade.tachiyomi.util.updatePaddingRelative import eu.kanade.tachiyomi.widget.AutofitRecyclerView -import kotlinx.android.synthetic.main.chapters_controller.* import kotlinx.android.synthetic.main.library_category.view.* import kotlinx.coroutines.delay import rx.subscriptions.CompositeSubscription @@ -98,10 +102,12 @@ class LibraryCategoryView @JvmOverloads constructor(context: Context, attrs: Att // Disable swipe refresh when view is not at the top val firstPos = (recycler.layoutManager as LinearLayoutManager) .findFirstCompletelyVisibleItemPosition() - swipe_refresh.isEnabled = firstPos <= 0 + swipe_refresh.isEnabled = firstPos <= 0 && preferences.showCategories().getOrDefault() } }) - fast_scroller?.gone() + fast_scroller.addOnScrollStateChangeListener { + controller.lockFilterBar(it) + } recycler.doOnApplyWindowInsets { v, insets, padding -> v.updatePaddingRelative(bottom = padding.bottom + insets.systemWindowInsetBottom) @@ -185,7 +191,8 @@ class LibraryCategoryView @JvmOverloads constructor(context: Context, attrs: Att val filterOff = preferences.filterCompleted().getOrDefault() + preferences.filterTracked().getOrDefault() + preferences.filterUnread().getOrDefault() + - preferences.filterCompleted().getOrDefault() == 0 + preferences.filterCompleted().getOrDefault() == 0 && + preferences.showCategories().getOrDefault() return sortingMode == LibrarySort.DRAG_AND_DROP && filterOff && adapter.mode != SelectableAdapter.Mode.MULTI } @@ -214,6 +221,8 @@ class LibraryCategoryView @JvmOverloads constructor(context: Context, attrs: Att // Update the category with its manga. adapter.setItems(mangaForCategory) + swipe_refresh.isEnabled = preferences.showCategories().getOrDefault() + if (adapter.mode == SelectableAdapter.Mode.MULTI) { controller.selectedMangas.forEach { manga -> val position = adapter.indexOf(manga) 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 5c7b646ace..4e1cc8bcac 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 @@ -54,6 +54,8 @@ import eu.kanade.tachiyomi.util.marginBottom import eu.kanade.tachiyomi.util.marginTop import eu.kanade.tachiyomi.util.snack import eu.kanade.tachiyomi.util.updatePaddingRelative +import eu.kanade.tachiyomi.util.visible +import eu.kanade.tachiyomi.widget.ExtendedNavigationView import kotlinx.android.synthetic.main.library_controller.* import kotlinx.android.synthetic.main.main_activity.* import rx.Subscription @@ -259,9 +261,9 @@ class LibraryController( navView = view drawer.setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED, GravityCompat.END) - navView?.onGroupClicked = { group -> + navView?.onGroupClicked = { group, item -> when (group) { - is LibraryNavigationView.FilterGroup -> onFilterChanged() + is LibraryNavigationView.FilterGroup -> onFilterChanged(item) is LibraryNavigationView.SortGroup -> onSortChanged() is LibraryNavigationView.DisplayGroup -> reattachAdapter() is LibraryNavigationView.BadgeGroup -> onDownloadBadgeChanged() @@ -361,7 +363,12 @@ class LibraryController( /** * Called when a filter is changed. */ - private fun onFilterChanged() { + private fun onFilterChanged(item: ExtendedNavigationView.Item) { + if (item is ExtendedNavigationView.Item.MultiStateGroup && item.resTitle == R.string.categories) { + activity?.invalidateOptionsMenu() + presenter.requestFullUpdate() + return + } presenter.requestFilterUpdate() destroyActionModeIfNeeded() activity?.invalidateOptionsMenu() @@ -417,7 +424,9 @@ class LibraryController( inflater.inflate(R.menu.library, menu) val reorganizeItem = menu.findItem(R.id.action_reorganize) - reorganizeItem.isVisible = preferences.librarySortingMode().getOrDefault() == LibrarySort.DRAG_AND_DROP + reorganizeItem.isVisible = + preferences.librarySortingMode().getOrDefault() == LibrarySort.DRAG_AND_DROP && + preferences.showCategories().getOrDefault() reorderMenuItem = reorganizeItem enableReorderItems() @@ -629,7 +638,7 @@ class LibraryController( val mangas = selectedMangas.toList() // Hide the default category because it has a different behavior than the ones from db. - val categories = presenter.categories.filter { it.id != 0 } + val categories = presenter.allCategories.filter { it.id != 0 } // Get indexes of the common categories to preselect. val commonCategoriesIndexes = presenter.getCommonCategories(mangas) @@ -640,6 +649,17 @@ class LibraryController( .showDialog(router) } + fun lockFilterBar(lock: Boolean) { + val drawer = (navView?.parent as? DrawerLayout) ?: return + if (lock) { + drawer.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED) + drawer.closeDrawers() + } else { + drawer.setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED) + drawer.visible() + } + } + private fun deleteMangasFromLibrary() { val mangas = selectedMangas.toList() presenter.removeMangaFromLibrary(mangas) diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryNavigationView.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryNavigationView.kt index 8decc9b23e..20069022f0 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryNavigationView.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryNavigationView.kt @@ -3,17 +3,17 @@ package eu.kanade.tachiyomi.ui.library import android.content.Context import android.util.AttributeSet import eu.kanade.tachiyomi.R +import eu.kanade.tachiyomi.data.database.DatabaseHelper import eu.kanade.tachiyomi.data.preference.PreferencesHelper import eu.kanade.tachiyomi.data.preference.getOrDefault import eu.kanade.tachiyomi.data.track.TrackManager -import eu.kanade.tachiyomi.ui.catalogue.filter.TriStateItem import eu.kanade.tachiyomi.widget.ExtendedNavigationView import eu.kanade.tachiyomi.widget.ExtendedNavigationView.Item.MultiSort.Companion.SORT_ASC import eu.kanade.tachiyomi.widget.ExtendedNavigationView.Item.MultiSort.Companion.SORT_DESC import eu.kanade.tachiyomi.widget.ExtendedNavigationView.Item.MultiSort.Companion.SORT_NONE +import eu.kanade.tachiyomi.widget.ExtendedNavigationView.Item.TriStateGroup.Companion.STATE_EXCLUDE import eu.kanade.tachiyomi.widget.ExtendedNavigationView.Item.TriStateGroup.Companion.STATE_IGNORE import eu.kanade.tachiyomi.widget.ExtendedNavigationView.Item.TriStateGroup.Companion.STATE_INCLUDE -import eu.kanade.tachiyomi.widget.ExtendedNavigationView.Item.TriStateGroup.Companion.STATE_EXCLUDE import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.api.get import uy.kohesive.injekt.injectLazy @@ -42,7 +42,7 @@ class LibraryNavigationView @JvmOverloads constructor(context: Context, attrs: A /** * Click listener to notify the parent fragment when an item from a group is clicked. */ - var onGroupClicked: (Group) -> Unit = {} + var onGroupClicked: (Group, Item) -> Unit = { _, _ -> } init { recycler.adapter = adapter @@ -55,7 +55,15 @@ class LibraryNavigationView @JvmOverloads constructor(context: Context, attrs: A * Returns true if there's at least one filter from [FilterGroup] active. */ fun hasActiveFilters(): Boolean { - return (groups[0] as FilterGroup).items.any { it.state != STATE_IGNORE } + return (groups[0] as FilterGroup).items.any { + when (it) { + is Item.TriStateGroup -> + if (it.resTitle == R.string.categories) it.state == STATE_IGNORE + else it.state != STATE_IGNORE + is Item.CheckboxGroup -> it.checked + else -> false + } + } } /** @@ -66,7 +74,7 @@ class LibraryNavigationView @JvmOverloads constructor(context: Context, attrs: A override fun onItemClicked(item: Item) { if (item is GroupedItem) { item.group.onItemClicked(item) - onGroupClicked(item.group) + onGroupClicked(item.group, item) } } } @@ -84,9 +92,19 @@ class LibraryNavigationView @JvmOverloads constructor(context: Context, attrs: A private val tracked = Item.TriStateGroup(R.string.tracked, this) - override val items = if (Injekt.get().hasLoggedServices()) - listOf(downloaded, unread, completed, tracked) else listOf(downloaded, unread, - completed) + private val categories = Item.TriStateGroup(R.string.categories, this) + + override val items:List = { + val list = mutableListOf() + if (Injekt.get().getCategories().executeAsBlocking().isNotEmpty()) + list.add(categories) + list.add(downloaded) + list.add(unread) + list.add(completed) + if (Injekt.get().hasLoggedServices()) + list.add(tracked) + list + }() override val header = Item.Header(R.string.action_filter) @@ -94,6 +112,8 @@ class LibraryNavigationView @JvmOverloads constructor(context: Context, attrs: A override fun initModels() { try { + categories.state = if (preferences.showCategories().getOrDefault()) STATE_INCLUDE + else STATE_IGNORE downloaded.state = preferences.filterDownloaded().getOrDefault() unread.state = preferences.filterUnread().getOrDefault() completed.state = preferences.filterCompleted().getOrDefault() @@ -105,20 +125,31 @@ class LibraryNavigationView @JvmOverloads constructor(context: Context, attrs: A } override fun onItemClicked(item: Item) { - item as Item.TriStateGroup - val newState = when (item.state) { - STATE_IGNORE -> STATE_INCLUDE - STATE_INCLUDE -> STATE_EXCLUDE - else -> STATE_IGNORE + if (item == categories) { + item as Item.TriStateGroup + val newState = when (item.state) { + STATE_IGNORE -> STATE_INCLUDE + else -> STATE_IGNORE + } + item.state = newState + when (item) { + categories -> preferences.showCategories().set(item.state == STATE_INCLUDE) + } } - item.state = newState - when (item) { - downloaded -> preferences.filterDownloaded().set(item.state) - unread -> preferences.filterUnread().set(item.state) - completed -> preferences.filterCompleted().set(item.state) - tracked -> preferences.filterTracked().set(item.state) + else if (item is Item.TriStateGroup) { + val newState = when (item.state) { + STATE_IGNORE -> STATE_INCLUDE + STATE_INCLUDE -> STATE_EXCLUDE + else -> STATE_IGNORE + } + item.state = newState + when (item) { + downloaded -> preferences.filterDownloaded().set(item.state) + unread -> preferences.filterUnread().set(item.state) + completed -> preferences.filterCompleted().set(item.state) + tracked -> preferences.filterTracked().set(item.state) + } } - adapter.notifyItemChanged(item) } } 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 fd257777b9..764f550775 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 @@ -70,6 +70,8 @@ class LibraryPresenter( var categories: List = emptyList() private set + var allCategories: List = emptyList() + private set /** * Relay used to apply the UI filters to the last emission of the library. */ @@ -264,7 +266,11 @@ class LibraryPresenter( else -> 0 } } - else 0 + else { + val category = catListing.find { it.id == i1.manga.category } + val category2 = catListing.find { it.id == i2.manga.category } + category?.order?.compareTo(category2?.order ?: 0) ?: 0 + } } else -> 0 } @@ -296,12 +302,15 @@ class LibraryPresenter( */ private fun getLibraryObservable(): Observable { return Observable.combineLatest(getCategoriesObservable(), getLibraryMangasObservable()) { dbCategories, libraryManga -> - val categories = if (libraryManga.containsKey(0)) arrayListOf(Category.createDefault - (context)) + dbCategories + val categories = if (libraryManga.containsKey(0)) + arrayListOf(Category.createDefault(context)) + dbCategories else dbCategories - this.categories = categories - Library(categories, libraryManga) + this.allCategories = categories + this.categories = if (!preferences.showCategories().getOrDefault()) + arrayListOf(Category.createDefault(context)) + else categories + Library(this.categories, libraryManga) } } @@ -324,7 +333,13 @@ class LibraryPresenter( val libraryAsList = preferences.libraryAsList() return db.getLibraryMangas().asRxObservable() .map { list -> - list.map { LibraryItem(it, libraryAsList) }.groupBy { it.manga.category } + if (preferences.showCategories().getOrDefault()) { + list.map { LibraryItem(it, libraryAsList) }.groupBy { it.manga.category } + } + else { + list.distinctBy { it.id }.map { LibraryItem(it, libraryAsList)}.groupBy { + 0 } + } } } @@ -349,6 +364,11 @@ class LibraryPresenter( sortTriggerRelay.call(Unit) } + fun requestFullUpdate() { + librarySubscription?.unsubscribe() + subscribeLibrary() + } + /** * Called when a manga is opened. */ diff --git a/app/src/main/java/eu/kanade/tachiyomi/widget/SimpleNavigationView.kt b/app/src/main/java/eu/kanade/tachiyomi/widget/SimpleNavigationView.kt index 0c95c17eae..05928c6fbb 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/widget/SimpleNavigationView.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/widget/SimpleNavigationView.kt @@ -5,11 +5,14 @@ import android.content.Context import android.util.AttributeSet import android.view.View import android.view.ViewGroup -import android.widget.* +import android.widget.CheckBox +import android.widget.CheckedTextView +import android.widget.EditText +import android.widget.RadioButton +import android.widget.Spinner +import android.widget.TextView import androidx.appcompat.widget.TintTypedArray import androidx.core.view.ViewCompat -import androidx.recyclerview.widget.LinearLayoutManager -import androidx.recyclerview.widget.RecyclerView import com.google.android.material.R import com.google.android.material.internal.ScrimInsetsFrameLayout import com.google.android.material.textfield.TextInputLayout diff --git a/app/src/main/res/layout/library_category.xml b/app/src/main/res/layout/library_category.xml index 83355885a0..658fc28807 100644 --- a/app/src/main/res/layout/library_category.xml +++ b/app/src/main/res/layout/library_category.xml @@ -18,5 +18,6 @@ android:layout_height="match_parent" android:layout_gravity="end" app:fastScrollerBubbleEnabled="true" + app:fastScrollerIgnoreTouchesOutsideHandle="true" tools:visibility="visible" />