Pārlūkot izejas kodu

Tweak flag classes for Library (#7829)

* Tweak flag classes for Library

- Add interface for Flag and Mask
- Merge Sort Type and Direction into one class
- Use custom serializers for preferences
  - Mainly to not break the old

* Review changes
Andreas 2 gadi atpakaļ
vecāks
revīzija
880407442c
25 mainītis faili ar 442 papildinājumiem un 250 dzēšanām
  1. 1 1
      app/build.gradle.kts
  2. 4 0
      app/src/main/java/eu/kanade/data/category/CategoryRepositoryImpl.kt
  3. 3 2
      app/src/main/java/eu/kanade/domain/category/interactor/CreateCategoryWithName.kt
  4. 2 10
      app/src/main/java/eu/kanade/domain/category/interactor/ResetCategoryFlags.kt
  5. 10 4
      app/src/main/java/eu/kanade/domain/category/interactor/SetDisplayModeForCategory.kt
  6. 10 7
      app/src/main/java/eu/kanade/domain/category/interactor/SetSortModeForCategory.kt
  7. 0 12
      app/src/main/java/eu/kanade/domain/category/model/Category.kt
  8. 2 0
      app/src/main/java/eu/kanade/domain/category/repository/CategoryRepository.kt
  9. 2 2
      app/src/main/java/eu/kanade/presentation/library/components/LibraryContent.kt
  10. 7 7
      app/src/main/java/eu/kanade/presentation/library/components/LibraryPager.kt
  11. 31 25
      app/src/main/java/eu/kanade/tachiyomi/Migrations.kt
  12. 0 1
      app/src/main/java/eu/kanade/tachiyomi/data/preference/PreferenceKeys.kt
  13. 5 7
      app/src/main/java/eu/kanade/tachiyomi/data/preference/PreferencesHelper.kt
  14. 8 8
      app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/BrowseSourceController.kt
  15. 8 8
      app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/SourceItem.kt
  16. 22 35
      app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryPresenter.kt
  17. 35 42
      app/src/main/java/eu/kanade/tachiyomi/ui/library/LibrarySettingsSheet.kt
  18. 0 17
      app/src/main/java/eu/kanade/tachiyomi/ui/library/setting/DisplayModeSetting.kt
  19. 35 0
      app/src/main/java/eu/kanade/tachiyomi/ui/library/setting/Flag.kt
  20. 60 0
      app/src/main/java/eu/kanade/tachiyomi/ui/library/setting/LibraryDisplayMode.kt
  21. 122 0
      app/src/main/java/eu/kanade/tachiyomi/ui/library/setting/LibrarySort.kt
  22. 0 20
      app/src/main/java/eu/kanade/tachiyomi/ui/library/setting/SortDirectionSetting.kt
  23. 0 42
      app/src/main/java/eu/kanade/tachiyomi/ui/library/setting/SortModeSetting.kt
  24. 6 0
      app/src/main/sqldelight/data/categories.sq
  25. 69 0
      app/src/test/java/eu/kanade/tachiyomi/util/chapter/LibraryFlagsTest.kt

+ 1 - 1
app/build.gradle.kts

@@ -27,7 +27,7 @@ android {
         applicationId = "eu.kanade.tachiyomi"
         minSdk = AndroidConfig.minSdk
         targetSdk = AndroidConfig.targetSdk
-        versionCode = 82
+        versionCode = 83
         versionName = "0.13.6"
 
         buildConfigField("String", "COMMIT_COUNT", "\"${getCommitCount()}\"")

+ 4 - 0
app/src/main/java/eu/kanade/data/category/CategoryRepositoryImpl.kt

@@ -11,6 +11,10 @@ class CategoryRepositoryImpl(
     private val handler: DatabaseHandler,
 ) : CategoryRepository {
 
+    override suspend fun get(id: Long): Category? {
+        return handler.awaitOneOrNull { categoriesQueries.getCategory(id, categoryMapper) }
+    }
+
     override suspend fun getAll(): List<Category> {
         return handler.awaitList { categoriesQueries.getCategories(categoryMapper) }
     }

+ 3 - 2
app/src/main/java/eu/kanade/domain/category/interactor/CreateCategoryWithName.kt

@@ -16,9 +16,10 @@ class CreateCategoryWithName(
 
     private val initialFlags: Long
         get() {
+            val sort = preferences.librarySortingMode().get()
             return preferences.libraryDisplayMode().get().flag or
-                preferences.librarySortingMode().get().flag or
-                preferences.librarySortingAscending().get().flag
+                sort.type.flag or
+                sort.direction.flag
         }
 
     suspend fun await(name: String): Result = withContext(NonCancellable) {

+ 2 - 10
app/src/main/java/eu/kanade/domain/category/interactor/ResetCategoryFlags.kt

@@ -2,9 +2,7 @@ package eu.kanade.domain.category.interactor
 
 import eu.kanade.domain.category.repository.CategoryRepository
 import eu.kanade.tachiyomi.data.preference.PreferencesHelper
-import eu.kanade.tachiyomi.ui.library.setting.DisplayModeSetting
-import eu.kanade.tachiyomi.ui.library.setting.SortDirectionSetting
-import eu.kanade.tachiyomi.ui.library.setting.SortModeSetting
+import eu.kanade.tachiyomi.ui.library.setting.plus
 
 class ResetCategoryFlags(
     private val preferences: PreferencesHelper,
@@ -14,12 +12,6 @@ class ResetCategoryFlags(
     suspend fun await() {
         val display = preferences.libraryDisplayMode().get()
         val sort = preferences.librarySortingMode().get()
-        val sortDirection = preferences.librarySortingAscending().get()
-
-        var flags = 0L
-        flags = flags and DisplayModeSetting.MASK.inv() or (display.flag and DisplayModeSetting.MASK)
-        flags = flags and SortModeSetting.MASK.inv() or (sort.flag and SortModeSetting.MASK)
-        flags = flags and SortDirectionSetting.MASK.inv() or (sortDirection.flag and SortDirectionSetting.MASK)
-        categoryRepository.updateAllFlags(flags)
+        categoryRepository.updateAllFlags(display + sort.type + sort.direction)
     }
 }

+ 10 - 4
app/src/main/java/eu/kanade/domain/category/interactor/SetDisplayModeForCategory.kt

@@ -4,15 +4,17 @@ import eu.kanade.domain.category.model.Category
 import eu.kanade.domain.category.model.CategoryUpdate
 import eu.kanade.domain.category.repository.CategoryRepository
 import eu.kanade.tachiyomi.data.preference.PreferencesHelper
-import eu.kanade.tachiyomi.ui.library.setting.DisplayModeSetting
+import eu.kanade.tachiyomi.ui.library.setting.LibraryDisplayMode
+import eu.kanade.tachiyomi.ui.library.setting.plus
 
 class SetDisplayModeForCategory(
     private val preferences: PreferencesHelper,
     private val categoryRepository: CategoryRepository,
 ) {
 
-    suspend fun await(category: Category, displayModeSetting: DisplayModeSetting) {
-        val flags = category.flags and DisplayModeSetting.MASK.inv() or (displayModeSetting.flag and DisplayModeSetting.MASK)
+    suspend fun await(categoryId: Long, display: LibraryDisplayMode) {
+        val category = categoryRepository.get(categoryId) ?: return
+        val flags = category.flags + display
         if (preferences.categorizedDisplaySettings().get()) {
             categoryRepository.updatePartial(
                 CategoryUpdate(
@@ -21,8 +23,12 @@ class SetDisplayModeForCategory(
                 ),
             )
         } else {
-            preferences.libraryDisplayMode().set(displayModeSetting)
+            preferences.libraryDisplayMode().set(display)
             categoryRepository.updateAllFlags(flags)
         }
     }
+
+    suspend fun await(category: Category, display: LibraryDisplayMode) {
+        await(category.id, display)
+    }
 }

+ 10 - 7
app/src/main/java/eu/kanade/domain/category/interactor/SetSortModeForCategory.kt

@@ -4,17 +4,17 @@ import eu.kanade.domain.category.model.Category
 import eu.kanade.domain.category.model.CategoryUpdate
 import eu.kanade.domain.category.repository.CategoryRepository
 import eu.kanade.tachiyomi.data.preference.PreferencesHelper
-import eu.kanade.tachiyomi.ui.library.setting.SortDirectionSetting
-import eu.kanade.tachiyomi.ui.library.setting.SortModeSetting
+import eu.kanade.tachiyomi.ui.library.setting.LibrarySort
+import eu.kanade.tachiyomi.ui.library.setting.plus
 
 class SetSortModeForCategory(
     private val preferences: PreferencesHelper,
     private val categoryRepository: CategoryRepository,
 ) {
 
-    suspend fun await(category: Category, sortModeSetting: SortModeSetting, sortDirectionSetting: SortDirectionSetting) {
-        var flags = category.flags and SortModeSetting.MASK.inv() or (sortModeSetting.flag and SortModeSetting.MASK)
-        flags = flags and SortDirectionSetting.MASK.inv() or (sortDirectionSetting.flag and SortDirectionSetting.MASK)
+    suspend fun await(categoryId: Long, type: LibrarySort.Type, direction: LibrarySort.Direction) {
+        val category = categoryRepository.get(categoryId) ?: return
+        val flags = category.flags + type + direction
         if (preferences.categorizedDisplaySettings().get()) {
             categoryRepository.updatePartial(
                 CategoryUpdate(
@@ -23,9 +23,12 @@ class SetSortModeForCategory(
                 ),
             )
         } else {
-            preferences.librarySortingMode().set(sortModeSetting)
-            preferences.librarySortingAscending().set(sortDirectionSetting)
+            preferences.librarySortingMode().set(LibrarySort(type, direction))
             categoryRepository.updateAllFlags(flags)
         }
     }
+
+    suspend fun await(category: Category, type: LibrarySort.Type, direction: LibrarySort.Direction) {
+        await(category.id, type, direction)
+    }
 }

+ 0 - 12
app/src/main/java/eu/kanade/domain/category/model/Category.kt

@@ -1,8 +1,5 @@
 package eu.kanade.domain.category.model
 
-import eu.kanade.tachiyomi.ui.library.setting.DisplayModeSetting
-import eu.kanade.tachiyomi.ui.library.setting.SortDirectionSetting
-import eu.kanade.tachiyomi.ui.library.setting.SortModeSetting
 import java.io.Serializable
 
 data class Category(
@@ -14,15 +11,6 @@ data class Category(
 
     val isSystemCategory: Boolean = id == UNCATEGORIZED_ID
 
-    val displayMode: Long
-        get() = flags and DisplayModeSetting.MASK
-
-    val sortMode: Long
-        get() = flags and SortModeSetting.MASK
-
-    val sortDirection: Long
-        get() = flags and SortDirectionSetting.MASK
-
     companion object {
 
         const val UNCATEGORIZED_ID = 0L

+ 2 - 0
app/src/main/java/eu/kanade/domain/category/repository/CategoryRepository.kt

@@ -6,6 +6,8 @@ import kotlinx.coroutines.flow.Flow
 
 interface CategoryRepository {
 
+    suspend fun get(id: Long): Category?
+
     suspend fun getAll(): List<Category>
 
     fun getAllAsFlow(): Flow<List<Category>>

+ 2 - 2
app/src/main/java/eu/kanade/presentation/library/components/LibraryContent.kt

@@ -24,7 +24,7 @@ import eu.kanade.presentation.library.LibraryState
 import eu.kanade.tachiyomi.R
 import eu.kanade.tachiyomi.data.database.models.LibraryManga
 import eu.kanade.tachiyomi.ui.library.LibraryItem
-import eu.kanade.tachiyomi.ui.library.setting.DisplayModeSetting
+import eu.kanade.tachiyomi.ui.library.setting.LibraryDisplayMode
 import eu.kanade.tachiyomi.widget.EmptyView
 import kotlinx.coroutines.delay
 import kotlinx.coroutines.launch
@@ -45,7 +45,7 @@ fun LibraryContent(
     onRefresh: (Category?) -> Boolean,
     onGlobalSearchClicked: () -> Unit,
     getNumberOfMangaForCategory: @Composable (Long) -> State<Int?>,
-    getDisplayModeForPage: @Composable (Int) -> State<DisplayModeSetting>,
+    getDisplayModeForPage: @Composable (Int) -> State<LibraryDisplayMode>,
     getColumnsForOrientation: (Boolean) -> PreferenceMutableState<Int>,
     getLibraryForPage: @Composable (Int) -> State<List<LibraryItem>>,
 ) {

+ 7 - 7
app/src/main/java/eu/kanade/presentation/library/components/LibraryPager.kt

@@ -15,7 +15,7 @@ import com.google.accompanist.pager.PagerState
 import eu.kanade.core.prefs.PreferenceMutableState
 import eu.kanade.tachiyomi.data.database.models.LibraryManga
 import eu.kanade.tachiyomi.ui.library.LibraryItem
-import eu.kanade.tachiyomi.ui.library.setting.DisplayModeSetting
+import eu.kanade.tachiyomi.ui.library.setting.LibraryDisplayMode
 
 @Composable
 fun LibraryPager(
@@ -24,7 +24,7 @@ fun LibraryPager(
     selectedManga: List<LibraryManga>,
     searchQuery: String?,
     onGlobalSearchClicked: () -> Unit,
-    getDisplayModeForPage: @Composable (Int) -> State<DisplayModeSetting>,
+    getDisplayModeForPage: @Composable (Int) -> State<LibraryDisplayMode>,
     getColumnsForOrientation: (Boolean) -> PreferenceMutableState<Int>,
     getLibraryForPage: @Composable (Int) -> State<List<LibraryItem>>,
     onClickManga: (LibraryManga) -> Unit,
@@ -42,7 +42,7 @@ fun LibraryPager(
         }
         val library by getLibraryForPage(page)
         val displayMode by getDisplayModeForPage(page)
-        val columns by if (displayMode != DisplayModeSetting.LIST) {
+        val columns by if (displayMode != LibraryDisplayMode.List) {
             val configuration = LocalConfiguration.current
             val isLandscape = configuration.orientation == Configuration.ORIENTATION_LANDSCAPE
 
@@ -52,7 +52,7 @@ fun LibraryPager(
         }
 
         when (displayMode) {
-            DisplayModeSetting.LIST -> {
+            LibraryDisplayMode.List -> {
                 LibraryList(
                     items = library,
                     selection = selectedManga,
@@ -62,7 +62,7 @@ fun LibraryPager(
                     onGlobalSearchClicked = onGlobalSearchClicked,
                 )
             }
-            DisplayModeSetting.COMPACT_GRID -> {
+            LibraryDisplayMode.CompactGrid -> {
                 LibraryCompactGrid(
                     items = library,
                     columns = columns,
@@ -73,7 +73,7 @@ fun LibraryPager(
                     onGlobalSearchClicked = onGlobalSearchClicked,
                 )
             }
-            DisplayModeSetting.COMFORTABLE_GRID -> {
+            LibraryDisplayMode.ComfortableGrid -> {
                 LibraryComfortableGrid(
                     items = library,
                     columns = columns,
@@ -84,7 +84,7 @@ fun LibraryPager(
                     onGlobalSearchClicked = onGlobalSearchClicked,
                 )
             }
-            DisplayModeSetting.COVER_ONLY_GRID -> {
+            LibraryDisplayMode.CoverOnlyGrid -> {
                 LibraryCoverOnlyGrid(
                     items = library,
                     columns = columns,

+ 31 - 25
app/src/main/java/eu/kanade/tachiyomi/Migrations.kt

@@ -13,8 +13,6 @@ import eu.kanade.tachiyomi.data.track.TrackManager
 import eu.kanade.tachiyomi.data.updater.AppUpdateJob
 import eu.kanade.tachiyomi.extension.ExtensionUpdateJob
 import eu.kanade.tachiyomi.network.PREF_DOH_CLOUDFLARE
-import eu.kanade.tachiyomi.ui.library.setting.SortDirectionSetting
-import eu.kanade.tachiyomi.ui.library.setting.SortModeSetting
 import eu.kanade.tachiyomi.ui.reader.setting.OrientationType
 import eu.kanade.tachiyomi.util.preference.minusAssign
 import eu.kanade.tachiyomi.util.preference.plusAssign
@@ -196,33 +194,33 @@ object Migrations {
             }
             if (oldVersion < 64) {
                 val oldSortingMode = prefs.getInt(PreferenceKeys.librarySortingMode, 0)
-                val oldSortingDirection = prefs.getBoolean(PreferenceKeys.librarySortingDirection, true)
+                val oldSortingDirection = prefs.getBoolean("library_sorting_ascending", true)
 
                 val newSortingMode = when (oldSortingMode) {
-                    0 -> SortModeSetting.ALPHABETICAL
-                    1 -> SortModeSetting.LAST_READ
-                    2 -> SortModeSetting.LAST_CHECKED
-                    3 -> SortModeSetting.UNREAD
-                    4 -> SortModeSetting.TOTAL_CHAPTERS
-                    6 -> SortModeSetting.LATEST_CHAPTER
-                    8 -> SortModeSetting.DATE_FETCHED
-                    7 -> SortModeSetting.DATE_ADDED
-                    else -> SortModeSetting.ALPHABETICAL
+                    0 -> "ALPHABETICAL"
+                    1 -> "LAST_READ"
+                    2 -> "LAST_CHECKED"
+                    3 -> "UNREAD"
+                    4 -> "TOTAL_CHAPTERS"
+                    6 -> "LATEST_CHAPTER"
+                    8 -> "DATE_FETCHED"
+                    7 -> "DATE_ADDED"
+                    else -> "ALPHABETICAL"
                 }
 
                 val newSortingDirection = when (oldSortingDirection) {
-                    true -> SortDirectionSetting.ASCENDING
-                    else -> SortDirectionSetting.DESCENDING
+                    true -> "ASCENDING"
+                    else -> "DESCENDING"
                 }
 
                 prefs.edit(commit = true) {
                     remove(PreferenceKeys.librarySortingMode)
-                    remove(PreferenceKeys.librarySortingDirection)
+                    remove("library_sorting_ascending")
                 }
 
                 prefs.edit {
-                    putString(PreferenceKeys.librarySortingMode, newSortingMode.name)
-                    putString(PreferenceKeys.librarySortingDirection, newSortingDirection.name)
+                    putString(PreferenceKeys.librarySortingMode, newSortingMode)
+                    putString("library_sorting_ascending", newSortingDirection)
                 }
             }
             if (oldVersion < 70) {
@@ -265,16 +263,24 @@ object Migrations {
             }
             if (oldVersion < 81) {
                 // Handle renamed enum values
-                @Suppress("DEPRECATION")
-                val newSortingMode = when (val oldSortingMode = preferences.librarySortingMode().get()) {
-                    SortModeSetting.LAST_CHECKED -> SortModeSetting.LAST_MANGA_UPDATE
-                    SortModeSetting.UNREAD -> SortModeSetting.UNREAD_COUNT
-                    SortModeSetting.DATE_FETCHED -> SortModeSetting.CHAPTER_FETCH_DATE
-                    else -> oldSortingMode
+                prefs.edit {
+                    val newSortingMode = when (val oldSortingMode = prefs.getString(PreferenceKeys.librarySortingMode, "ALPHABETICAL")) {
+                        "LAST_CHECKED" -> "LAST_MANGA_UPDATE"
+                        "UNREAD" -> "UNREAD_COUNT"
+                        "DATE_FETCHED" -> "CHAPTER_FETCH_DATE"
+                        else -> oldSortingMode
+                    }
+                    putString(PreferenceKeys.librarySortingMode, newSortingMode)
+                }
+            }
+            if (oldVersion < 82) {
+                prefs.edit {
+                    val sort = prefs.getString(PreferenceKeys.librarySortingMode, null) ?: return@edit
+                    val direction = prefs.getString("library_sorting_ascending", "ASCENDING")!!
+                    putString(PreferenceKeys.librarySortingMode, "$sort,$direction")
+                    remove("library_sorting_ascending")
                 }
-                preferences.librarySortingMode().set(newSortingMode)
             }
-
             return true
         }
 

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

@@ -36,7 +36,6 @@ object PreferenceKeys {
     const val filterTracked = "pref_filter_library_tracked"
 
     const val librarySortingMode = "library_sorting_mode"
-    const val librarySortingDirection = "library_sorting_ascending"
 
     const val migrationSortingMode = "pref_migration_sorting"
     const val migrationSortingDirection = "pref_migration_direction"

+ 5 - 7
app/src/main/java/eu/kanade/tachiyomi/data/preference/PreferencesHelper.kt

@@ -12,9 +12,8 @@ import eu.kanade.tachiyomi.R
 import eu.kanade.tachiyomi.data.database.models.Manga
 import eu.kanade.tachiyomi.data.track.TrackService
 import eu.kanade.tachiyomi.data.track.anilist.Anilist
-import eu.kanade.tachiyomi.ui.library.setting.DisplayModeSetting
-import eu.kanade.tachiyomi.ui.library.setting.SortDirectionSetting
-import eu.kanade.tachiyomi.ui.library.setting.SortModeSetting
+import eu.kanade.tachiyomi.ui.library.setting.LibraryDisplayMode
+import eu.kanade.tachiyomi.ui.library.setting.LibrarySort
 import eu.kanade.tachiyomi.ui.reader.setting.OrientationType
 import eu.kanade.tachiyomi.ui.reader.setting.ReadingModeType
 import eu.kanade.tachiyomi.util.system.DeviceUtil
@@ -169,7 +168,7 @@ class PreferencesHelper(val context: Context) {
 
     fun lastVersionCode() = flowPrefs.getInt("last_version_code", 0)
 
-    fun sourceDisplayMode() = flowPrefs.getEnum("pref_display_mode_catalogue", DisplayModeSetting.COMPACT_GRID)
+    fun sourceDisplayMode() = flowPrefs.getObject("pref_display_mode_catalogue", LibraryDisplayMode.Serializer, LibraryDisplayMode.default)
 
     fun enabledLanguages() = flowPrefs.getStringSet("source_languages", setOf("all", "en", Locale.getDefault().language))
 
@@ -230,7 +229,7 @@ class PreferencesHelper(val context: Context) {
     fun libraryUpdateCategories() = flowPrefs.getStringSet("library_update_categories", emptySet())
     fun libraryUpdateCategoriesExclude() = flowPrefs.getStringSet("library_update_categories_exclude", emptySet())
 
-    fun libraryDisplayMode() = flowPrefs.getEnum("pref_display_mode_library", DisplayModeSetting.COMPACT_GRID)
+    fun libraryDisplayMode() = flowPrefs.getObject("pref_display_mode_library", LibraryDisplayMode.Serializer, LibraryDisplayMode.default)
 
     fun downloadBadge() = flowPrefs.getBoolean("display_download_badge", false)
 
@@ -256,8 +255,7 @@ class PreferencesHelper(val context: Context) {
 
     fun filterTracking(name: Int) = flowPrefs.getInt("${Keys.filterTracked}_$name", ExtendedNavigationView.Item.TriStateGroup.State.IGNORE.value)
 
-    fun librarySortingMode() = flowPrefs.getEnum(Keys.librarySortingMode, SortModeSetting.ALPHABETICAL)
-    fun librarySortingAscending() = flowPrefs.getEnum(Keys.librarySortingDirection, SortDirectionSetting.ASCENDING)
+    fun librarySortingMode() = flowPrefs.getObject(Keys.librarySortingMode, LibrarySort.Serializer, LibrarySort.default)
 
     fun migrationSortingMode() = flowPrefs.getEnum(Keys.migrationSortingMode, SetMigrateSorting.Mode.ALPHABETICAL)
     fun migrationSortingDirection() = flowPrefs.getEnum(Keys.migrationSortingDirection, SetMigrateSorting.Direction.ASCENDING)

+ 8 - 8
app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/BrowseSourceController.kt

@@ -37,7 +37,7 @@ import eu.kanade.tachiyomi.ui.base.controller.SearchableNucleusController
 import eu.kanade.tachiyomi.ui.base.controller.pushController
 import eu.kanade.tachiyomi.ui.browse.source.globalsearch.GlobalSearchController
 import eu.kanade.tachiyomi.ui.library.ChangeMangaCategoriesDialog
-import eu.kanade.tachiyomi.ui.library.setting.DisplayModeSetting
+import eu.kanade.tachiyomi.ui.library.setting.LibraryDisplayMode
 import eu.kanade.tachiyomi.ui.main.MainActivity
 import eu.kanade.tachiyomi.ui.manga.AddDuplicateMangaDialog
 import eu.kanade.tachiyomi.ui.manga.MangaController
@@ -212,7 +212,7 @@ open class BrowseSourceController(bundle: Bundle) :
             binding.catalogueView.removeView(oldRecycler)
         }
 
-        val recycler = if (preferences.sourceDisplayMode().get() == DisplayModeSetting.LIST) {
+        val recycler = if (preferences.sourceDisplayMode().get() == LibraryDisplayMode.List) {
             RecyclerView(view.context).apply {
                 id = R.id.recycler
                 layoutManager = LinearLayoutManager(context)
@@ -280,8 +280,8 @@ open class BrowseSourceController(bundle: Bundle) :
         )
 
         val displayItem = when (preferences.sourceDisplayMode().get()) {
-            DisplayModeSetting.LIST -> R.id.action_list
-            DisplayModeSetting.COMFORTABLE_GRID -> R.id.action_comfortable_grid
+            LibraryDisplayMode.List -> R.id.action_list
+            LibraryDisplayMode.ComfortableGrid -> R.id.action_comfortable_grid
             else -> R.id.action_compact_grid
         }
         menu.findItem(displayItem).isChecked = true
@@ -304,9 +304,9 @@ open class BrowseSourceController(bundle: Bundle) :
     override fun onOptionsItemSelected(item: MenuItem): Boolean {
         when (item.itemId) {
             R.id.action_search -> expandActionViewFromInteraction = true
-            R.id.action_compact_grid -> setDisplayMode(DisplayModeSetting.COMPACT_GRID)
-            R.id.action_comfortable_grid -> setDisplayMode(DisplayModeSetting.COMFORTABLE_GRID)
-            R.id.action_list -> setDisplayMode(DisplayModeSetting.LIST)
+            R.id.action_compact_grid -> setDisplayMode(LibraryDisplayMode.CompactGrid)
+            R.id.action_comfortable_grid -> setDisplayMode(LibraryDisplayMode.ComfortableGrid)
+            R.id.action_list -> setDisplayMode(LibraryDisplayMode.List)
             R.id.action_open_in_web_view -> openInWebView()
             R.id.action_local_source_help -> openLocalSourceHelpGuide()
         }
@@ -503,7 +503,7 @@ open class BrowseSourceController(bundle: Bundle) :
      *
      * @param mode the mode to change to
      */
-    private fun setDisplayMode(mode: DisplayModeSetting) {
+    private fun setDisplayMode(mode: LibraryDisplayMode) {
         val view = view ?: return
         val adapter = adapter ?: return
 

+ 8 - 8
app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/SourceItem.kt

@@ -10,16 +10,16 @@ import eu.kanade.domain.manga.model.Manga
 import eu.kanade.tachiyomi.R
 import eu.kanade.tachiyomi.databinding.SourceComfortableGridItemBinding
 import eu.kanade.tachiyomi.databinding.SourceCompactGridItemBinding
-import eu.kanade.tachiyomi.ui.library.setting.DisplayModeSetting
+import eu.kanade.tachiyomi.ui.library.setting.LibraryDisplayMode
 
-class SourceItem(val manga: Manga, private val displayMode: Preference<DisplayModeSetting>) :
+class SourceItem(val manga: Manga, private val displayMode: Preference<LibraryDisplayMode>) :
     AbstractFlexibleItem<SourceHolder<*>>() {
 
     override fun getLayoutRes(): Int {
         return when (displayMode.get()) {
-            DisplayModeSetting.COMPACT_GRID, DisplayModeSetting.COVER_ONLY_GRID -> R.layout.source_compact_grid_item
-            DisplayModeSetting.COMFORTABLE_GRID -> R.layout.source_comfortable_grid_item
-            DisplayModeSetting.LIST -> R.layout.source_list_item
+            LibraryDisplayMode.CompactGrid, LibraryDisplayMode.CoverOnlyGrid -> R.layout.source_compact_grid_item
+            LibraryDisplayMode.ComfortableGrid -> R.layout.source_comfortable_grid_item
+            LibraryDisplayMode.List -> R.layout.source_list_item
         }
     }
 
@@ -28,13 +28,13 @@ class SourceItem(val manga: Manga, private val displayMode: Preference<DisplayMo
         adapter: FlexibleAdapter<IFlexible<RecyclerView.ViewHolder>>,
     ): SourceHolder<*> {
         return when (displayMode.get()) {
-            DisplayModeSetting.COMPACT_GRID, DisplayModeSetting.COVER_ONLY_GRID -> {
+            LibraryDisplayMode.CompactGrid, LibraryDisplayMode.CoverOnlyGrid -> {
                 SourceCompactGridHolder(SourceCompactGridItemBinding.bind(view), adapter)
             }
-            DisplayModeSetting.COMFORTABLE_GRID -> {
+            LibraryDisplayMode.ComfortableGrid -> {
                 SourceComfortableGridHolder(SourceComfortableGridItemBinding.bind(view), adapter)
             }
-            DisplayModeSetting.LIST -> {
+            LibraryDisplayMode.List -> {
                 SourceListHolder(view, adapter)
             }
         }

+ 22 - 35
app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryPresenter.kt

@@ -43,9 +43,10 @@ import eu.kanade.tachiyomi.source.SourceManager
 import eu.kanade.tachiyomi.source.model.SManga
 import eu.kanade.tachiyomi.source.online.HttpSource
 import eu.kanade.tachiyomi.ui.base.presenter.BasePresenter
-import eu.kanade.tachiyomi.ui.library.setting.DisplayModeSetting
-import eu.kanade.tachiyomi.ui.library.setting.SortDirectionSetting
-import eu.kanade.tachiyomi.ui.library.setting.SortModeSetting
+import eu.kanade.tachiyomi.ui.library.setting.LibraryDisplayMode
+import eu.kanade.tachiyomi.ui.library.setting.LibrarySort
+import eu.kanade.tachiyomi.ui.library.setting.display
+import eu.kanade.tachiyomi.ui.library.setting.sort
 import eu.kanade.tachiyomi.util.lang.combineLatest
 import eu.kanade.tachiyomi.util.lang.launchIO
 import eu.kanade.tachiyomi.util.removeCovers
@@ -334,12 +335,8 @@ class LibraryPresenter(
             }
         }
 
-        val sortingModes = categories.associate { category ->
-            category.id to SortModeSetting.get(preferences, category)
-        }
-
-        val sortDirections = categories.associate { category ->
-            category.id to SortDirectionSetting.get(category)
+        val sortModes = categories.associate { category ->
+            category.id to category.sort
         }
 
         val locale = Locale.getDefault()
@@ -347,53 +344,50 @@ class LibraryPresenter(
             strength = Collator.PRIMARY
         }
         val sortFn: (LibraryItem, LibraryItem) -> Int = { i1, i2 ->
-            val sortingMode = sortingModes[i1.manga.category.toLong()]!!
-            val sortAscending = sortDirections[i1.manga.category.toLong()]!! == SortDirectionSetting.ASCENDING
-            when (sortingMode) {
-                SortModeSetting.ALPHABETICAL -> {
+            val sort = sortModes[i1.manga.category.toLong()]!!
+            when (sort.type) {
+                LibrarySort.Type.Alphabetical -> {
                     collator.compare(i1.manga.title.lowercase(locale), i2.manga.title.lowercase(locale))
                 }
-                SortModeSetting.LAST_READ -> {
+                LibrarySort.Type.LastRead -> {
                     val manga1LastRead = lastReadManga[i1.manga.id!!] ?: 0
                     val manga2LastRead = lastReadManga[i2.manga.id!!] ?: 0
                     manga1LastRead.compareTo(manga2LastRead)
                 }
-                SortModeSetting.LAST_MANGA_UPDATE -> {
+                LibrarySort.Type.LastUpdate -> {
                     i1.manga.last_update.compareTo(i2.manga.last_update)
                 }
-                SortModeSetting.UNREAD_COUNT -> when {
+                LibrarySort.Type.UnreadCount -> when {
                     // Ensure unread content comes first
                     i1.manga.unreadCount == i2.manga.unreadCount -> 0
-                    i1.manga.unreadCount == 0 -> if (sortAscending) 1 else -1
-                    i2.manga.unreadCount == 0 -> if (sortAscending) -1 else 1
+                    i1.manga.unreadCount == 0 -> if (sort.isAscending) 1 else -1
+                    i2.manga.unreadCount == 0 -> if (sort.isAscending) -1 else 1
                     else -> i1.manga.unreadCount.compareTo(i2.manga.unreadCount)
                 }
-                SortModeSetting.TOTAL_CHAPTERS -> {
+                LibrarySort.Type.TotalChapters -> {
                     i1.manga.totalChapters.compareTo(i2.manga.totalChapters)
                 }
-                SortModeSetting.LATEST_CHAPTER -> {
+                LibrarySort.Type.LatestChapter -> {
                     val manga1latestChapter = latestChapterManga[i1.manga.id!!]
                         ?: latestChapterManga.size
                     val manga2latestChapter = latestChapterManga[i2.manga.id!!]
                         ?: latestChapterManga.size
                     manga1latestChapter.compareTo(manga2latestChapter)
                 }
-                SortModeSetting.CHAPTER_FETCH_DATE -> {
+                LibrarySort.Type.ChapterFetchDate -> {
                     val manga1chapterFetchDate = chapterFetchDateManga[i1.manga.id!!] ?: 0
                     val manga2chapterFetchDate = chapterFetchDateManga[i2.manga.id!!] ?: 0
                     manga1chapterFetchDate.compareTo(manga2chapterFetchDate)
                 }
-                SortModeSetting.DATE_ADDED -> {
+                LibrarySort.Type.DateAdded -> {
                     i1.manga.date_added.compareTo(i2.manga.date_added)
                 }
-                else -> throw IllegalStateException("Invalid SortModeSetting: $sortingMode")
+                else -> throw IllegalStateException("Invalid SortModeSetting: ${sort.type}")
             }
         }
 
         return map.mapValues { entry ->
-            val sortAscending = sortDirections[entry.key.toLong()]!! == SortDirectionSetting.ASCENDING
-
-            val comparator = if (sortAscending) {
+            val comparator = if (sortModes[entry.key]!!.isAscending) {
                 Comparator(sortFn)
             } else {
                 Collections.reverseOrder(sortFn)
@@ -416,13 +410,6 @@ class LibraryPresenter(
                 dbCategories
             }
 
-            libraryManga.forEach { (categoryId, libraryManga) ->
-                val category = categories.first { category -> category.id == categoryId }
-                libraryManga.forEach { libraryItem ->
-                    libraryItem.displayMode = category.displayMode
-                }
-            }
-
             state.categories = categories
             Library(categories, libraryManga)
         }.asObservable()
@@ -680,10 +667,10 @@ class LibraryPresenter(
     }
 
     @Composable
-    fun getDisplayMode(index: Int): androidx.compose.runtime.State<DisplayModeSetting> {
+    fun getDisplayMode(index: Int): androidx.compose.runtime.State<LibraryDisplayMode> {
         val category = categories[index]
         return derivedStateOf {
-            DisplayModeSetting.fromFlag(category.displayMode)
+            category.display
         }
     }
 

+ 35 - 42
app/src/main/java/eu/kanade/tachiyomi/ui/library/LibrarySettingsSheet.kt

@@ -11,9 +11,10 @@ import eu.kanade.tachiyomi.R
 import eu.kanade.tachiyomi.data.preference.PreferencesHelper
 import eu.kanade.tachiyomi.data.track.TrackManager
 import eu.kanade.tachiyomi.data.track.TrackService
-import eu.kanade.tachiyomi.ui.library.setting.DisplayModeSetting
-import eu.kanade.tachiyomi.ui.library.setting.SortDirectionSetting
-import eu.kanade.tachiyomi.ui.library.setting.SortModeSetting
+import eu.kanade.tachiyomi.ui.library.setting.LibraryDisplayMode
+import eu.kanade.tachiyomi.ui.library.setting.LibrarySort
+import eu.kanade.tachiyomi.ui.library.setting.display
+import eu.kanade.tachiyomi.ui.library.setting.sort
 import eu.kanade.tachiyomi.util.lang.launchIO
 import eu.kanade.tachiyomi.widget.ExtendedNavigationView
 import eu.kanade.tachiyomi.widget.ExtendedNavigationView.Item.TriStateGroup.State
@@ -202,29 +203,25 @@ class LibrarySettingsSheet(
             override val footer = null
 
             override fun initModels() {
-                val sorting = SortModeSetting.get(preferences, currentCategory)
-                val order = if (SortDirectionSetting.get(currentCategory) == SortDirectionSetting.ASCENDING) {
-                    Item.MultiSort.SORT_ASC
-                } else {
-                    Item.MultiSort.SORT_DESC
-                }
+                val sort = currentCategory?.sort ?: LibrarySort.default
+                val order = if (sort.isAscending) Item.MultiSort.SORT_ASC else Item.MultiSort.SORT_DESC
 
                 alphabetically.state =
-                    if (sorting == SortModeSetting.ALPHABETICAL) order else Item.MultiSort.SORT_NONE
+                    if (sort.type == LibrarySort.Type.Alphabetical) order else Item.MultiSort.SORT_NONE
                 lastRead.state =
-                    if (sorting == SortModeSetting.LAST_READ) order else Item.MultiSort.SORT_NONE
+                    if (sort.type == LibrarySort.Type.LastRead) order else Item.MultiSort.SORT_NONE
                 lastChecked.state =
-                    if (sorting == SortModeSetting.LAST_MANGA_UPDATE) order else Item.MultiSort.SORT_NONE
+                    if (sort.type == LibrarySort.Type.LastUpdate) order else Item.MultiSort.SORT_NONE
                 unread.state =
-                    if (sorting == SortModeSetting.UNREAD_COUNT) order else Item.MultiSort.SORT_NONE
+                    if (sort.type == LibrarySort.Type.UnreadCount) order else Item.MultiSort.SORT_NONE
                 total.state =
-                    if (sorting == SortModeSetting.TOTAL_CHAPTERS) order else Item.MultiSort.SORT_NONE
+                    if (sort.type == LibrarySort.Type.TotalChapters) order else Item.MultiSort.SORT_NONE
                 latestChapter.state =
-                    if (sorting == SortModeSetting.LATEST_CHAPTER) order else Item.MultiSort.SORT_NONE
+                    if (sort.type == LibrarySort.Type.LatestChapter) order else Item.MultiSort.SORT_NONE
                 chapterFetchDate.state =
-                    if (sorting == SortModeSetting.CHAPTER_FETCH_DATE) order else Item.MultiSort.SORT_NONE
+                    if (sort.type == LibrarySort.Type.ChapterFetchDate) order else Item.MultiSort.SORT_NONE
                 dateAdded.state =
-                    if (sorting == SortModeSetting.DATE_ADDED) order else Item.MultiSort.SORT_NONE
+                    if (sort.type == LibrarySort.Type.DateAdded) order else Item.MultiSort.SORT_NONE
             }
 
             override fun onItemClicked(item: Item) {
@@ -249,20 +246,20 @@ class LibrarySettingsSheet(
 
             private fun setSortPreference(item: Item.MultiStateGroup) {
                 val mode = when (item) {
-                    alphabetically -> SortModeSetting.ALPHABETICAL
-                    lastRead -> SortModeSetting.LAST_READ
-                    lastChecked -> SortModeSetting.LAST_MANGA_UPDATE
-                    unread -> SortModeSetting.UNREAD_COUNT
-                    total -> SortModeSetting.TOTAL_CHAPTERS
-                    latestChapter -> SortModeSetting.LATEST_CHAPTER
-                    chapterFetchDate -> SortModeSetting.CHAPTER_FETCH_DATE
-                    dateAdded -> SortModeSetting.DATE_ADDED
+                    alphabetically -> LibrarySort.Type.Alphabetical
+                    lastRead -> LibrarySort.Type.LastRead
+                    lastChecked -> LibrarySort.Type.LastUpdate
+                    unread -> LibrarySort.Type.UnreadCount
+                    total -> LibrarySort.Type.TotalChapters
+                    latestChapter -> LibrarySort.Type.LatestChapter
+                    chapterFetchDate -> LibrarySort.Type.ChapterFetchDate
+                    dateAdded -> LibrarySort.Type.DateAdded
                     else -> throw NotImplementedError("Unknown display mode")
                 }
                 val direction = if (item.state == Item.MultiSort.SORT_ASC) {
-                    SortDirectionSetting.ASCENDING
+                    LibrarySort.Direction.Ascending
                 } else {
-                    SortDirectionSetting.DESCENDING
+                    LibrarySort.Direction.Descending
                 }
 
                 sheetScope.launchIO {
@@ -297,12 +294,8 @@ class LibrarySettingsSheet(
         }
 
         // Gets user preference of currently selected display mode at current category
-        private fun getDisplayModePreference(): DisplayModeSetting {
-            return if (currentCategory != null && preferences.categorizedDisplaySettings().get()) {
-                DisplayModeSetting.fromFlag(currentCategory!!.displayMode)
-            } else {
-                preferences.libraryDisplayMode().get()
-            }
+        private fun getDisplayModePreference(): LibraryDisplayMode {
+            return currentCategory?.display ?: LibraryDisplayMode.default
         }
 
         inner class DisplayGroup : Group {
@@ -334,19 +327,19 @@ class LibrarySettingsSheet(
             }
 
             // Sets display group selections based on given mode
-            fun setGroupSelections(mode: DisplayModeSetting) {
-                compactGrid.checked = mode == DisplayModeSetting.COMPACT_GRID
-                comfortableGrid.checked = mode == DisplayModeSetting.COMFORTABLE_GRID
-                coverOnlyGrid.checked = mode == DisplayModeSetting.COVER_ONLY_GRID
-                list.checked = mode == DisplayModeSetting.LIST
+            fun setGroupSelections(mode: LibraryDisplayMode) {
+                compactGrid.checked = mode == LibraryDisplayMode.CompactGrid
+                comfortableGrid.checked = mode == LibraryDisplayMode.ComfortableGrid
+                coverOnlyGrid.checked = mode == LibraryDisplayMode.CoverOnlyGrid
+                list.checked = mode == LibraryDisplayMode.List
             }
 
             private fun setDisplayModePreference(item: Item) {
                 val flag = when (item) {
-                    compactGrid -> DisplayModeSetting.COMPACT_GRID
-                    comfortableGrid -> DisplayModeSetting.COMFORTABLE_GRID
-                    coverOnlyGrid -> DisplayModeSetting.COVER_ONLY_GRID
-                    list -> DisplayModeSetting.LIST
+                    compactGrid -> LibraryDisplayMode.CompactGrid
+                    comfortableGrid -> LibraryDisplayMode.ComfortableGrid
+                    coverOnlyGrid -> LibraryDisplayMode.CoverOnlyGrid
+                    list -> LibraryDisplayMode.List
                     else -> throw NotImplementedError("Unknown display mode")
                 }
 

+ 0 - 17
app/src/main/java/eu/kanade/tachiyomi/ui/library/setting/DisplayModeSetting.kt

@@ -1,17 +0,0 @@
-package eu.kanade.tachiyomi.ui.library.setting
-
-enum class DisplayModeSetting(val flag: Long) {
-    COMPACT_GRID(0b00000000),
-    COMFORTABLE_GRID(0b00000001),
-    LIST(0b00000010),
-    COVER_ONLY_GRID(0b00000011);
-
-    companion object {
-        const val MASK = 0b00000011L
-
-        fun fromFlag(flag: Long?): DisplayModeSetting {
-            return values()
-                .find { mode -> mode.flag == flag } ?: COMPACT_GRID
-        }
-    }
-}

+ 35 - 0
app/src/main/java/eu/kanade/tachiyomi/ui/library/setting/Flag.kt

@@ -0,0 +1,35 @@
+package eu.kanade.tachiyomi.ui.library.setting
+
+interface Flag {
+    val flag: Long
+}
+
+interface Mask {
+    val mask: Long
+}
+
+interface FlagWithMask : Flag, Mask
+
+operator fun Long.contains(other: Flag): Boolean {
+    return if (other is Mask) {
+        other.flag == this and other.mask
+    } else {
+        other.flag == this
+    }
+}
+
+operator fun Long.plus(other: Flag): Long {
+    return if (other is Mask) {
+        this and other.mask.inv() or (other.flag and other.mask)
+    } else {
+        this or other.flag
+    }
+}
+
+operator fun Flag.plus(other: Flag): Long {
+    return if (other is Mask) {
+        this.flag and other.mask.inv() or (other.flag and other.mask)
+    } else {
+        this.flag or other.flag
+    }
+}

+ 60 - 0
app/src/main/java/eu/kanade/tachiyomi/ui/library/setting/LibraryDisplayMode.kt

@@ -0,0 +1,60 @@
+package eu.kanade.tachiyomi.ui.library.setting
+
+import eu.kanade.domain.category.model.Category
+import com.fredporciuncula.flow.preferences.Serializer as PreferencesSerializer
+
+sealed class LibraryDisplayMode(
+    override val flag: Long,
+) : FlagWithMask {
+
+    override val mask: Long = 0b00000011L
+
+    object CompactGrid : LibraryDisplayMode(0b00000000)
+    object ComfortableGrid : LibraryDisplayMode(0b00000001)
+    object List : LibraryDisplayMode(0b00000010)
+    object CoverOnlyGrid : LibraryDisplayMode(0b00000011)
+
+    object Serializer : PreferencesSerializer<LibraryDisplayMode> {
+        override fun deserialize(serialized: String): LibraryDisplayMode {
+            return LibraryDisplayMode.deserialize(serialized)
+        }
+
+        override fun serialize(value: LibraryDisplayMode): String {
+            return value.serialize()
+        }
+    }
+
+    companion object {
+        val values = setOf(CompactGrid, ComfortableGrid, List, CoverOnlyGrid)
+        val default = CompactGrid
+
+        fun valueOf(flag: Long?): LibraryDisplayMode {
+            if (flag == null) return default
+            return values
+                .find { mode -> mode.flag == flag and mode.mask }
+                ?: default
+        }
+
+        fun deserialize(serialized: String): LibraryDisplayMode {
+            return when (serialized) {
+                "COMFORTABLE_GRID" -> ComfortableGrid
+                "COMPACT_GRID" -> CompactGrid
+                "COVER_ONLY_GRID" -> CoverOnlyGrid
+                "LIST" -> List
+                else -> default
+            }
+        }
+    }
+
+    fun serialize(): String {
+        return when (this) {
+            ComfortableGrid -> "COMFORTABLE_GRID"
+            CompactGrid -> "COMPACT_GRID"
+            CoverOnlyGrid -> "COVER_ONLY_GRID"
+            List -> "LIST"
+        }
+    }
+}
+
+val Category.display: LibraryDisplayMode
+    get() = LibraryDisplayMode.valueOf(flags)

+ 122 - 0
app/src/main/java/eu/kanade/tachiyomi/ui/library/setting/LibrarySort.kt

@@ -0,0 +1,122 @@
+package eu.kanade.tachiyomi.ui.library.setting
+
+import eu.kanade.domain.category.model.Category
+import com.fredporciuncula.flow.preferences.Serializer as PreferencesSerializer
+
+data class LibrarySort(
+    val type: Type,
+    val direction: Direction,
+) : FlagWithMask {
+
+    override val flag: Long
+        get() = type + direction
+
+    override val mask: Long
+        get() = type.mask or direction.mask
+
+    val isAscending: Boolean
+        get() = direction == Direction.Ascending
+
+    sealed class Type(
+        override val flag: Long,
+    ) : FlagWithMask {
+
+        override val mask: Long = 0b00111100L
+
+        object Alphabetical : Type(0b00000000)
+        object LastRead : Type(0b00000100)
+        object LastUpdate : Type(0b00001000)
+        object UnreadCount : Type(0b00001100)
+        object TotalChapters : Type(0b00010000)
+        object LatestChapter : Type(0b00010100)
+        object ChapterFetchDate : Type(0b00011000)
+        object DateAdded : Type(0b00011100)
+
+        companion object {
+
+            fun valueOf(flag: Long): Type {
+                return types.find { type -> type.flag == flag and type.mask } ?: default.type
+            }
+        }
+    }
+
+    sealed class Direction(
+        override val flag: Long,
+    ) : FlagWithMask {
+
+        override val mask: Long = 0b01000000L
+
+        object Ascending : Direction(0b01000000)
+        object Descending : Direction(0b00000000)
+
+        companion object {
+
+            fun valueOf(flag: Long): Direction {
+                return directions.find { direction -> direction.flag == flag and direction.mask } ?: default.direction
+            }
+        }
+    }
+
+    object Serializer : PreferencesSerializer<LibrarySort> {
+        override fun deserialize(serialized: String): LibrarySort {
+            return LibrarySort.deserialize(serialized)
+        }
+
+        override fun serialize(value: LibrarySort): String {
+            return value.serialize()
+        }
+    }
+
+    companion object {
+        val types = setOf(Type.Alphabetical, Type.LastRead, Type.LastUpdate, Type.UnreadCount, Type.TotalChapters, Type.LatestChapter, Type.ChapterFetchDate, Type.DateAdded)
+        val directions = setOf(Direction.Ascending, Direction.Descending)
+        val default = LibrarySort(Type.Alphabetical, Direction.Ascending)
+
+        fun valueOf(flag: Long): LibrarySort {
+            return LibrarySort(
+                Type.valueOf(flag),
+                Direction.valueOf(flag),
+            )
+        }
+
+        fun deserialize(serialized: String): LibrarySort {
+            if (serialized.isEmpty()) return default
+            return try {
+                val values = serialized.split(",")
+                val type = when (values[0]) {
+                    "ALPHABETICAL" -> Type.Alphabetical
+                    "LAST_READ" -> Type.LastRead
+                    "LAST_MANGA_UPDATE" -> Type.LastUpdate
+                    "UNREAD_COUNT" -> Type.UnreadCount
+                    "TOTAL_CHAPTERS" -> Type.TotalChapters
+                    "LATEST_CHAPTER" -> Type.LatestChapter
+                    "CHAPTER_FETCH_DATE" -> Type.ChapterFetchDate
+                    "DATE_ADDED" -> Type.DateAdded
+                    else -> Type.Alphabetical
+                }
+                val ascending = if (values[1] == "ASCENDING") Direction.Ascending else Direction.Descending
+                LibrarySort(type, ascending)
+            } catch (e: Exception) {
+                default
+            }
+        }
+    }
+
+    fun serialize(): String {
+        val type = when (type) {
+            Type.Alphabetical -> "ALPHABETICAL"
+            Type.LastRead -> "LAST_READ"
+            Type.LastUpdate -> "LAST_MANGA_UPDATE"
+            Type.UnreadCount -> "UNREAD_COUNT"
+            Type.TotalChapters -> "TOTAL_CHAPTERS"
+            Type.LatestChapter -> "LATEST_CHAPTER"
+            Type.ChapterFetchDate -> "CHAPTER_FETCH_DATE"
+            Type.DateAdded -> "DATE_ADDED"
+        }
+        val direction = if (direction == Direction.Ascending) "ASCENDING" else "DESCENDING"
+        return "$type,$direction"
+    }
+}
+
+val Category.sort: LibrarySort
+    get() = LibrarySort.valueOf(flags)

+ 0 - 20
app/src/main/java/eu/kanade/tachiyomi/ui/library/setting/SortDirectionSetting.kt

@@ -1,20 +0,0 @@
-package eu.kanade.tachiyomi.ui.library.setting
-
-import eu.kanade.domain.category.model.Category
-
-enum class SortDirectionSetting(val flag: Long) {
-    ASCENDING(0b01000000),
-    DESCENDING(0b00000000);
-
-    companion object {
-        const val MASK = 0b01000000L
-
-        private fun fromFlag(flag: Long?): SortDirectionSetting {
-            return values().find { mode -> mode.flag == flag } ?: ASCENDING
-        }
-
-        fun get(category: Category?): SortDirectionSetting {
-            return fromFlag(category?.sortDirection)
-        }
-    }
-}

+ 0 - 42
app/src/main/java/eu/kanade/tachiyomi/ui/library/setting/SortModeSetting.kt

@@ -1,42 +0,0 @@
-package eu.kanade.tachiyomi.ui.library.setting
-
-import eu.kanade.domain.category.model.Category
-import eu.kanade.tachiyomi.data.preference.PreferencesHelper
-
-enum class SortModeSetting(val flag: Long) {
-    ALPHABETICAL(0b00000000),
-    LAST_READ(0b00000100),
-    LAST_MANGA_UPDATE(0b00001000),
-    UNREAD_COUNT(0b00001100),
-    TOTAL_CHAPTERS(0b00010000),
-    LATEST_CHAPTER(0b00010100),
-    CHAPTER_FETCH_DATE(0b00011000),
-    DATE_ADDED(0b00011100),
-
-    @Deprecated("Use LAST_MANGA_UPDATE")
-    LAST_CHECKED(0b00001000),
-
-    @Deprecated("Use UNREAD_COUNT")
-    UNREAD(0b00001100),
-
-    @Deprecated("Use CHAPTER_FETCH_DATE")
-    DATE_FETCHED(0b00011000),
-    ;
-
-    companion object {
-        // Mask supports for more sorting flags if necessary
-        const val MASK = 0b00111100L
-
-        fun fromFlag(flag: Long?): SortModeSetting {
-            return values().find { mode -> mode.flag == flag } ?: ALPHABETICAL
-        }
-
-        fun get(preferences: PreferencesHelper, category: Category?): SortModeSetting {
-            return if (category != null && preferences.categorizedDisplaySettings().get()) {
-                fromFlag(category.sortMode)
-            } else {
-                preferences.librarySortingMode().get()
-            }
-        }
-    }
-}

+ 6 - 0
app/src/main/sqldelight/data/categories.sq

@@ -16,6 +16,12 @@ BEGIN SELECT CASE
     END;
 END;
 
+getCategory:
+SELECT *
+FROM categories
+WHERE _id = :id
+LIMIT 1;
+
 getCategories:
 SELECT
 _id AS id,

+ 69 - 0
app/src/test/java/eu/kanade/tachiyomi/util/chapter/LibraryFlagsTest.kt

@@ -0,0 +1,69 @@
+package eu.kanade.tachiyomi.util.chapter
+
+import eu.kanade.tachiyomi.ui.library.setting.LibraryDisplayMode
+import eu.kanade.tachiyomi.ui.library.setting.LibrarySort
+import eu.kanade.tachiyomi.ui.library.setting.plus
+import org.junit.jupiter.api.Assertions.assertEquals
+import org.junit.jupiter.api.Assertions.assertNotEquals
+import org.junit.jupiter.api.Test
+
+class LibraryFlagsTest {
+
+    @Test
+    fun `Check the amount of flags`() {
+        assertEquals(4, LibraryDisplayMode.values.size)
+        assertEquals(8, LibrarySort.types.size)
+        assertEquals(2, LibrarySort.directions.size)
+    }
+
+    @Test
+    fun `Test Flag plus operator (LibraryDisplayMode)`() {
+        val current = LibraryDisplayMode.List
+        val new = LibraryDisplayMode.CoverOnlyGrid
+        val flag = current + new
+
+        assertEquals(0b00000011, flag)
+    }
+
+    @Test
+    fun `Test Flag plus operator (LibrarySort)`() {
+        val current = LibrarySort(LibrarySort.Type.LastRead, LibrarySort.Direction.Ascending)
+        val new = LibrarySort(LibrarySort.Type.DateAdded, LibrarySort.Direction.Ascending)
+        val flag = current + new
+
+        assertEquals(0b01011100, flag)
+    }
+
+    @Test
+    fun `Test Flag plus operator`() {
+        val display = LibraryDisplayMode.CoverOnlyGrid
+        val sort = LibrarySort(LibrarySort.Type.DateAdded, LibrarySort.Direction.Ascending)
+        val flag = display + sort
+
+        assertEquals(0b01011111, flag)
+    }
+
+    @Test
+    fun `Test Flag plus operator with old flag as base`() {
+        val currentDisplay = LibraryDisplayMode.List
+        val currentSort = LibrarySort(LibrarySort.Type.UnreadCount, LibrarySort.Direction.Descending)
+        val currentFlag = currentDisplay + currentSort
+
+        val display = LibraryDisplayMode.CoverOnlyGrid
+        val sort = LibrarySort(LibrarySort.Type.DateAdded, LibrarySort.Direction.Ascending)
+        val flag = currentFlag + display + sort
+
+        assertEquals(0b00001110, currentFlag)
+        assertEquals(0b01011111, flag)
+        assertNotEquals(currentFlag, flag)
+    }
+
+    @Test
+    fun `Test default flags`() {
+        val sort = LibrarySort.default
+        val display = LibraryDisplayMode.default
+        val flag = display + sort.type + sort.direction
+
+        assertEquals(0b01000000, flag)
+    }
+}