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 changespull/5658/merge
parent
3b34a878a7
commit
880407442c
@ -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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
@ -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)
|
@ -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)
|
@ -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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -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()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in new issue