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
This commit is contained in:
Andreas 2022-08-29 19:10:30 +02:00 committed by GitHub
parent 3b34a878a7
commit 880407442c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
25 changed files with 442 additions and 250 deletions

View File

@ -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()}\"")

View File

@ -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) }
}

View File

@ -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) {

View File

@ -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)
}
}

View File

@ -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)
}
}

View File

@ -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)
}
}

View File

@ -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

View File

@ -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>>

View File

@ -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>>,
) {

View File

@ -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,

View File

@ -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
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
}
preferences.librarySortingMode().set(newSortingMode)
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")
}
}
return true
}

View File

@ -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"

View File

@ -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)

View File

@ -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

View File

@ -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)
}
}

View File

@ -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
}
}

View File

@ -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")
}

View File

@ -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
}
}
}

View File

@ -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
}
}

View File

@ -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)

View File

@ -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)

View File

@ -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)
}
}
}

View File

@ -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()
}
}
}
}

View File

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

View File

@ -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)
}
}