mirror of
https://github.com/tachiyomiorg/tachiyomi.git
synced 2024-11-20 07:19:18 +01:00
Set default for chapter sorting method + ui updates + sort by upload date
* New Class: ChapterSort, to handle sorting chapter list with the defaults + filtering if wanted * Said class is used now to find the next unread chapter, in case sort method is not source order * Remove close button in chapter filter sheet * Added "reset" button to sort in filter sheet, to go back to default sort * New view: SortTextView, like tristatebox but with arrows instead
This commit is contained in:
parent
074321865e
commit
469db068e3
@ -32,12 +32,13 @@ interface Manga : SManga {
|
|||||||
fun isBlank() = id == Long.MIN_VALUE
|
fun isBlank() = id == Long.MIN_VALUE
|
||||||
fun isHidden() = status == -1
|
fun isHidden() = status == -1
|
||||||
|
|
||||||
fun setChapterOrder(order: Int) {
|
fun setChapterOrder(sorting: Int, order: Int) {
|
||||||
|
setChapterFlags(sorting, CHAPTER_SORTING_MASK)
|
||||||
setChapterFlags(order, CHAPTER_SORT_MASK)
|
setChapterFlags(order, CHAPTER_SORT_MASK)
|
||||||
setChapterFlags(CHAPTER_SORT_LOCAL, CHAPTER_SORT_SELF_MASK)
|
setChapterFlags(CHAPTER_SORT_LOCAL, CHAPTER_SORT_LOCAL_MASK)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun setSortToGlobal() = setChapterFlags(CHAPTER_SORT_GLOBAL, CHAPTER_SORT_SELF_MASK)
|
fun setSortToGlobal() = setChapterFlags(CHAPTER_SORT_FILTER_GLOBAL, CHAPTER_SORT_LOCAL_MASK)
|
||||||
|
|
||||||
private fun setChapterFlags(flag: Int, mask: Int) {
|
private fun setChapterFlags(flag: Int, mask: Int) {
|
||||||
chapter_flags = chapter_flags and mask.inv() or (flag and mask)
|
chapter_flags = chapter_flags and mask.inv() or (flag and mask)
|
||||||
@ -49,11 +50,14 @@ interface Manga : SManga {
|
|||||||
|
|
||||||
fun sortDescending(): Boolean = chapter_flags and CHAPTER_SORT_MASK == CHAPTER_SORT_DESC
|
fun sortDescending(): Boolean = chapter_flags and CHAPTER_SORT_MASK == CHAPTER_SORT_DESC
|
||||||
|
|
||||||
fun usesLocalSort(): Boolean = chapter_flags and CHAPTER_SORT_SELF_MASK == CHAPTER_SORT_LOCAL
|
fun usesLocalSort(): Boolean = chapter_flags and CHAPTER_SORT_LOCAL_MASK == CHAPTER_SORT_LOCAL
|
||||||
|
|
||||||
fun sortDescending(defaultDesc: Boolean): Boolean {
|
fun sortDescending(defaultDesc: Boolean): Boolean {
|
||||||
return if (chapter_flags and CHAPTER_SORT_SELF_MASK == CHAPTER_SORT_GLOBAL) defaultDesc
|
return if (usesLocalSort()) sortDescending() else defaultDesc
|
||||||
else sortDescending()
|
}
|
||||||
|
|
||||||
|
fun chapterOrder(defaultOrder: Int): Int {
|
||||||
|
return if (usesLocalSort()) sorting else defaultOrder
|
||||||
}
|
}
|
||||||
|
|
||||||
fun showChapterTitle(defaultShow: Boolean): Boolean = chapter_flags and CHAPTER_DISPLAY_MASK == CHAPTER_DISPLAY_NUMBER
|
fun showChapterTitle(defaultShow: Boolean): Boolean = chapter_flags and CHAPTER_DISPLAY_MASK == CHAPTER_DISPLAY_NUMBER
|
||||||
@ -228,16 +232,16 @@ interface Manga : SManga {
|
|||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|
||||||
|
// Generic filter that does not filter anything
|
||||||
|
const val SHOW_ALL = 0x00000000
|
||||||
|
|
||||||
const val CHAPTER_SORT_DESC = 0x00000000
|
const val CHAPTER_SORT_DESC = 0x00000000
|
||||||
const val CHAPTER_SORT_ASC = 0x00000001
|
const val CHAPTER_SORT_ASC = 0x00000001
|
||||||
const val CHAPTER_SORT_MASK = 0x00000001
|
const val CHAPTER_SORT_MASK = 0x00000001
|
||||||
|
|
||||||
const val CHAPTER_SORT_GLOBAL = 0x00000000
|
const val CHAPTER_SORT_FILTER_GLOBAL = 0x00000000
|
||||||
const val CHAPTER_SORT_LOCAL = 0x00001000
|
const val CHAPTER_SORT_LOCAL = 0x00001000
|
||||||
const val CHAPTER_SORT_SELF_MASK = 0x00001000
|
const val CHAPTER_SORT_LOCAL_MASK = 0x00001000
|
||||||
|
|
||||||
// Generic filter that does not filter anything
|
|
||||||
const val SHOW_ALL = 0x00000000
|
|
||||||
|
|
||||||
const val CHAPTER_SHOW_UNREAD = 0x00000002
|
const val CHAPTER_SHOW_UNREAD = 0x00000002
|
||||||
const val CHAPTER_SHOW_READ = 0x00000004
|
const val CHAPTER_SHOW_READ = 0x00000004
|
||||||
@ -253,7 +257,8 @@ interface Manga : SManga {
|
|||||||
|
|
||||||
const val CHAPTER_SORTING_SOURCE = 0x00000000
|
const val CHAPTER_SORTING_SOURCE = 0x00000000
|
||||||
const val CHAPTER_SORTING_NUMBER = 0x00000100
|
const val CHAPTER_SORTING_NUMBER = 0x00000100
|
||||||
const val CHAPTER_SORTING_MASK = 0x00000100
|
const val CHAPTER_SORTING_UPLOAD_DATE = 0x00000200
|
||||||
|
const val CHAPTER_SORTING_MASK = 0x00000300
|
||||||
|
|
||||||
const val CHAPTER_DISPLAY_NAME = 0x00000000
|
const val CHAPTER_DISPLAY_NAME = 0x00000000
|
||||||
const val CHAPTER_DISPLAY_NUMBER = 0x00100000
|
const val CHAPTER_DISPLAY_NUMBER = 0x00100000
|
||||||
|
@ -275,8 +275,6 @@ class PreferencesHelper(val context: Context) {
|
|||||||
|
|
||||||
fun uniformGrid() = flowPrefs.getBoolean(Keys.uniformGrid, true)
|
fun uniformGrid() = flowPrefs.getBoolean(Keys.uniformGrid, true)
|
||||||
|
|
||||||
fun chaptersDescAsDefault() = rxPrefs.getBoolean("chapters_desc_as_default", true)
|
|
||||||
|
|
||||||
fun downloadBadge() = rxPrefs.getBoolean(Keys.downloadBadge, false)
|
fun downloadBadge() = rxPrefs.getBoolean(Keys.downloadBadge, false)
|
||||||
|
|
||||||
fun filterDownloaded() = rxPrefs.getInteger(Keys.filterDownloaded, 0)
|
fun filterDownloaded() = rxPrefs.getInteger(Keys.filterDownloaded, 0)
|
||||||
@ -432,10 +430,12 @@ class PreferencesHelper(val context: Context) {
|
|||||||
|
|
||||||
fun filterChapterByBookmarked() = prefs.getInt(Keys.defaultChapterFilterByBookmarked, Manga.SHOW_ALL)
|
fun filterChapterByBookmarked() = prefs.getInt(Keys.defaultChapterFilterByBookmarked, Manga.SHOW_ALL)
|
||||||
|
|
||||||
fun sortChapterBySourceOrNumber() = prefs.getInt(Keys.defaultChapterSortBySourceOrNumber, Manga.CHAPTER_SORTING_SOURCE)
|
fun sortChapterOrder() = flowPrefs.getInt(Keys.defaultChapterSortBySourceOrNumber, Manga.CHAPTER_SORTING_SOURCE)
|
||||||
|
|
||||||
fun displayChapterByNameOrNumber() = prefs.getInt(Keys.defaultChapterDisplayByNameOrNumber, Manga.CHAPTER_DISPLAY_NAME)
|
fun displayChapterByNameOrNumber() = prefs.getInt(Keys.defaultChapterDisplayByNameOrNumber, Manga.CHAPTER_DISPLAY_NAME)
|
||||||
|
|
||||||
|
fun chaptersDescAsDefault() = rxPrefs.getBoolean("chapters_desc_as_default", true)
|
||||||
|
|
||||||
fun sortChapterByAscendingOrDescending() = prefs.getInt(Keys.defaultChapterSortByAscendingOrDescending, Manga.CHAPTER_SORT_DESC)
|
fun sortChapterByAscendingOrDescending() = prefs.getInt(Keys.defaultChapterSortByAscendingOrDescending, Manga.CHAPTER_SORT_DESC)
|
||||||
|
|
||||||
fun setChapterSettingsDefault(manga: Manga) {
|
fun setChapterSettingsDefault(manga: Manga) {
|
||||||
|
@ -30,6 +30,8 @@ import eu.kanade.tachiyomi.ui.library.filter.FilterBottomSheet.Companion.STATE_E
|
|||||||
import eu.kanade.tachiyomi.ui.library.filter.FilterBottomSheet.Companion.STATE_IGNORE
|
import eu.kanade.tachiyomi.ui.library.filter.FilterBottomSheet.Companion.STATE_IGNORE
|
||||||
import eu.kanade.tachiyomi.ui.library.filter.FilterBottomSheet.Companion.STATE_INCLUDE
|
import eu.kanade.tachiyomi.ui.library.filter.FilterBottomSheet.Companion.STATE_INCLUDE
|
||||||
import eu.kanade.tachiyomi.ui.recents.RecentsPresenter
|
import eu.kanade.tachiyomi.ui.recents.RecentsPresenter
|
||||||
|
import eu.kanade.tachiyomi.util.chapter.ChapterFilter
|
||||||
|
import eu.kanade.tachiyomi.util.chapter.ChapterSort
|
||||||
import eu.kanade.tachiyomi.util.lang.capitalizeWords
|
import eu.kanade.tachiyomi.util.lang.capitalizeWords
|
||||||
import eu.kanade.tachiyomi.util.lang.chopByWords
|
import eu.kanade.tachiyomi.util.lang.chopByWords
|
||||||
import eu.kanade.tachiyomi.util.lang.removeArticles
|
import eu.kanade.tachiyomi.util.lang.removeArticles
|
||||||
@ -59,7 +61,8 @@ class LibraryPresenter(
|
|||||||
private val preferences: PreferencesHelper = Injekt.get(),
|
private val preferences: PreferencesHelper = Injekt.get(),
|
||||||
private val coverCache: CoverCache = Injekt.get(),
|
private val coverCache: CoverCache = Injekt.get(),
|
||||||
private val sourceManager: SourceManager = Injekt.get(),
|
private val sourceManager: SourceManager = Injekt.get(),
|
||||||
private val downloadManager: DownloadManager = Injekt.get()
|
private val downloadManager: DownloadManager = Injekt.get(),
|
||||||
|
private val chapterFilter: ChapterFilter = Injekt.get()
|
||||||
) : BaseCoroutinePresenter() {
|
) : BaseCoroutinePresenter() {
|
||||||
|
|
||||||
private val context = preferences.context
|
private val context = preferences.context
|
||||||
@ -847,7 +850,7 @@ class LibraryPresenter(
|
|||||||
/** Returns first unread chapter of a manga */
|
/** Returns first unread chapter of a manga */
|
||||||
fun getFirstUnread(manga: Manga): Chapter? {
|
fun getFirstUnread(manga: Manga): Chapter? {
|
||||||
val chapters = db.getChapters(manga).executeAsBlocking()
|
val chapters = db.getChapters(manga).executeAsBlocking()
|
||||||
return chapters.sortedByDescending { it.source_order }.find { !it.read }
|
return ChapterSort(manga, chapterFilter, preferences).getNextUnreadChapter(chapters, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Update a category's sorting */
|
/** Update a category's sorting */
|
||||||
|
@ -20,6 +20,7 @@ import eu.kanade.tachiyomi.ui.setting.SettingsController
|
|||||||
import eu.kanade.tachiyomi.ui.setting.SettingsReaderController
|
import eu.kanade.tachiyomi.ui.setting.SettingsReaderController
|
||||||
import eu.kanade.tachiyomi.ui.source.browse.BrowseSourceController
|
import eu.kanade.tachiyomi.ui.source.browse.BrowseSourceController
|
||||||
import eu.kanade.tachiyomi.ui.source.global_search.GlobalSearchController
|
import eu.kanade.tachiyomi.ui.source.global_search.GlobalSearchController
|
||||||
|
import eu.kanade.tachiyomi.util.chapter.ChapterSort
|
||||||
import eu.kanade.tachiyomi.util.view.withFadeTransaction
|
import eu.kanade.tachiyomi.util.view.withFadeTransaction
|
||||||
import uy.kohesive.injekt.Injekt
|
import uy.kohesive.injekt.Injekt
|
||||||
import uy.kohesive.injekt.api.get
|
import uy.kohesive.injekt.api.get
|
||||||
@ -118,13 +119,15 @@ class SearchActivity : MainActivity() {
|
|||||||
if (mangaId != 0L) {
|
if (mangaId != 0L) {
|
||||||
val db = Injekt.get<DatabaseHelper>()
|
val db = Injekt.get<DatabaseHelper>()
|
||||||
val chapters = db.getChapters(mangaId).executeAsBlocking()
|
val chapters = db.getChapters(mangaId).executeAsBlocking()
|
||||||
val nextUnreadChapter = chapters.sortedByDescending { it.source_order }.find { !it.read }
|
db.getManga(mangaId).executeAsBlocking()?.let { manga ->
|
||||||
val manga = db.getManga(mangaId).executeAsBlocking()
|
val nextUnreadChapter = ChapterSort(manga).getNextUnreadChapter(chapters, false)
|
||||||
if (nextUnreadChapter != null && manga != null) {
|
if (nextUnreadChapter != null) {
|
||||||
val activity = ReaderActivity.newIntent(this, manga, nextUnreadChapter)
|
val activity =
|
||||||
startActivity(activity)
|
ReaderActivity.newIntent(this, manga, nextUnreadChapter)
|
||||||
finish()
|
startActivity(activity)
|
||||||
return true
|
finish()
|
||||||
|
return true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -41,6 +41,7 @@ import eu.kanade.tachiyomi.ui.manga.track.SetTrackReadingDatesDialog
|
|||||||
import eu.kanade.tachiyomi.ui.manga.track.TrackItem
|
import eu.kanade.tachiyomi.ui.manga.track.TrackItem
|
||||||
import eu.kanade.tachiyomi.ui.security.SecureActivityDelegate
|
import eu.kanade.tachiyomi.ui.security.SecureActivityDelegate
|
||||||
import eu.kanade.tachiyomi.util.chapter.ChapterFilter
|
import eu.kanade.tachiyomi.util.chapter.ChapterFilter
|
||||||
|
import eu.kanade.tachiyomi.util.chapter.ChapterSort
|
||||||
import eu.kanade.tachiyomi.util.chapter.ChapterUtil
|
import eu.kanade.tachiyomi.util.chapter.ChapterUtil
|
||||||
import eu.kanade.tachiyomi.util.chapter.syncChaptersWithSource
|
import eu.kanade.tachiyomi.util.chapter.syncChaptersWithSource
|
||||||
import eu.kanade.tachiyomi.util.chapter.syncChaptersWithTrackServiceTwoWay
|
import eu.kanade.tachiyomi.util.chapter.syncChaptersWithTrackServiceTwoWay
|
||||||
@ -85,6 +86,8 @@ class MangaDetailsPresenter(
|
|||||||
private val customMangaManager: CustomMangaManager by injectLazy()
|
private val customMangaManager: CustomMangaManager by injectLazy()
|
||||||
private val mangaShortcutManager: MangaShortcutManager by injectLazy()
|
private val mangaShortcutManager: MangaShortcutManager by injectLazy()
|
||||||
|
|
||||||
|
private val chapterSort by lazy { ChapterSort(manga, chapterFilter, preferences) }
|
||||||
|
|
||||||
var isLockedFromSearch = false
|
var isLockedFromSearch = false
|
||||||
var hasRequested = false
|
var hasRequested = false
|
||||||
var isLoading = false
|
var isLoading = false
|
||||||
@ -223,6 +226,8 @@ class MangaDetailsPresenter(
|
|||||||
*/
|
*/
|
||||||
fun sortDescending() = manga.sortDescending(globalSort())
|
fun sortDescending() = manga.sortDescending(globalSort())
|
||||||
|
|
||||||
|
fun sortingOrder() = manga.chapterOrder(globalSorting())
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Applies the view filters to the list of chapters obtained from the database.
|
* Applies the view filters to the list of chapters obtained from the database.
|
||||||
* @param chapterList the list of chapters from the database
|
* @param chapterList the list of chapters from the database
|
||||||
@ -232,22 +237,8 @@ class MangaDetailsPresenter(
|
|||||||
if (isLockedFromSearch) {
|
if (isLockedFromSearch) {
|
||||||
return chapterList
|
return chapterList
|
||||||
}
|
}
|
||||||
|
getScrollType(chapterList)
|
||||||
val chapters = chapterFilter.filterChapters(chapterList, manga)
|
return chapterSort.getChaptersSorted(chapterList)
|
||||||
|
|
||||||
val sortFunction: (Chapter, Chapter) -> Int = when (manga.sorting) {
|
|
||||||
Manga.CHAPTER_SORTING_SOURCE -> when (sortDescending()) {
|
|
||||||
true -> { c1, c2 -> c1.source_order.compareTo(c2.source_order) }
|
|
||||||
false -> { c1, c2 -> c2.source_order.compareTo(c1.source_order) }
|
|
||||||
}
|
|
||||||
Manga.CHAPTER_SORTING_NUMBER -> when (sortDescending()) {
|
|
||||||
true -> { c1, c2 -> c2.chapter_number.compareTo(c1.chapter_number) }
|
|
||||||
false -> { c1, c2 -> c1.chapter_number.compareTo(c2.chapter_number) }
|
|
||||||
}
|
|
||||||
else -> { c1, c2 -> c1.source_order.compareTo(c2.source_order) }
|
|
||||||
}
|
|
||||||
getScrollType(chapters)
|
|
||||||
return chapters.sortedWith(Comparator(sortFunction))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getScrollType(chapters: List<ChapterItem>) {
|
private fun getScrollType(chapters: List<ChapterItem>) {
|
||||||
@ -263,7 +254,7 @@ class MangaDetailsPresenter(
|
|||||||
* Returns the next unread chapter or null if everything is read.
|
* Returns the next unread chapter or null if everything is read.
|
||||||
*/
|
*/
|
||||||
fun getNextUnreadChapter(): ChapterItem? {
|
fun getNextUnreadChapter(): ChapterItem? {
|
||||||
return chapters.sortedByDescending { it.source_order }.find { !it.read }
|
return chapterSort.getNextUnreadChapter(chapters)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun anyRead(): Boolean = allChapters.any { it.read }
|
fun anyRead(): Boolean = allChapters.any { it.read }
|
||||||
@ -272,7 +263,7 @@ class MangaDetailsPresenter(
|
|||||||
|
|
||||||
fun getUnreadChaptersSorted() =
|
fun getUnreadChaptersSorted() =
|
||||||
allChapters.filter { !it.read && it.status == Download.State.NOT_DOWNLOADED }.distinctBy { it.name }
|
allChapters.filter { !it.read && it.status == Download.State.NOT_DOWNLOADED }.distinctBy { it.name }
|
||||||
.sortedByDescending { it.source_order }
|
.sortedWith(chapterSort.sortComparator(true))
|
||||||
|
|
||||||
fun startDownloadingNow(chapter: Chapter) {
|
fun startDownloadingNow(chapter: Chapter) {
|
||||||
downloadManager.startDownloadNow(chapter)
|
downloadManager.startDownloadNow(chapter)
|
||||||
@ -508,24 +499,31 @@ class MangaDetailsPresenter(
|
|||||||
/**
|
/**
|
||||||
* Sets the sorting order and requests an UI update.
|
* Sets the sorting order and requests an UI update.
|
||||||
*/
|
*/
|
||||||
fun setSortOrder(descend: Boolean) {
|
fun setSortOrder(sort: Int, descend: Boolean) {
|
||||||
manga.setChapterOrder(if (descend) Manga.CHAPTER_SORT_DESC else Manga.CHAPTER_SORT_ASC)
|
manga.setChapterOrder(sort, if (descend) Manga.CHAPTER_SORT_DESC else Manga.CHAPTER_SORT_ASC)
|
||||||
|
if (mangaSortMatchesDefault()) {
|
||||||
|
manga.setSortToGlobal()
|
||||||
|
}
|
||||||
asyncUpdateMangaAndChapters()
|
asyncUpdateMangaAndChapters()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun globalSort(): Boolean = preferences.chaptersDescAsDefault().getOrDefault()
|
private fun globalSort(): Boolean = preferences.chaptersDescAsDefault().getOrDefault()
|
||||||
|
|
||||||
fun setGlobalChapterSort(descend: Boolean) {
|
private fun globalSorting(): Int = preferences.sortChapterOrder().get()
|
||||||
|
|
||||||
|
fun mangaSortMatchesDefault(): Boolean {
|
||||||
|
return (manga.sortDescending() == globalSort() && manga.sorting == globalSorting()) || !manga.usesLocalSort()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setGlobalChapterSort(sort: Int, descend: Boolean) {
|
||||||
|
preferences.sortChapterOrder().set(sort)
|
||||||
preferences.chaptersDescAsDefault().set(descend)
|
preferences.chaptersDescAsDefault().set(descend)
|
||||||
manga.setSortToGlobal()
|
manga.setSortToGlobal()
|
||||||
asyncUpdateMangaAndChapters()
|
asyncUpdateMangaAndChapters()
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
fun resetSortingToDefault() {
|
||||||
* Sets the sorting method and requests an UI update.
|
manga.setSortToGlobal()
|
||||||
*/
|
|
||||||
fun setSortMethod(bySource: Boolean) {
|
|
||||||
manga.sorting = if (bySource) Manga.CHAPTER_SORTING_SOURCE else Manga.CHAPTER_SORTING_NUMBER
|
|
||||||
asyncUpdateMangaAndChapters()
|
asyncUpdateMangaAndChapters()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,15 +4,14 @@ import android.os.Bundle
|
|||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import androidx.core.view.isInvisible
|
import androidx.core.view.isInvisible
|
||||||
import androidx.core.view.isVisible
|
|
||||||
import com.google.android.material.bottomsheet.BottomSheetBehavior
|
import com.google.android.material.bottomsheet.BottomSheetBehavior
|
||||||
import eu.kanade.tachiyomi.R
|
|
||||||
import eu.kanade.tachiyomi.data.database.models.Manga
|
import eu.kanade.tachiyomi.data.database.models.Manga
|
||||||
import eu.kanade.tachiyomi.databinding.ChapterSortBottomSheetBinding
|
import eu.kanade.tachiyomi.databinding.ChapterSortBottomSheetBinding
|
||||||
import eu.kanade.tachiyomi.ui.manga.MangaDetailsController
|
import eu.kanade.tachiyomi.ui.manga.MangaDetailsController
|
||||||
import eu.kanade.tachiyomi.util.system.dpToPx
|
import eu.kanade.tachiyomi.util.system.dpToPx
|
||||||
import eu.kanade.tachiyomi.util.view.setBottomEdge
|
import eu.kanade.tachiyomi.util.view.setBottomEdge
|
||||||
import eu.kanade.tachiyomi.widget.E2EBottomSheetDialog
|
import eu.kanade.tachiyomi.widget.E2EBottomSheetDialog
|
||||||
|
import eu.kanade.tachiyomi.widget.SortTextView
|
||||||
import kotlin.math.max
|
import kotlin.math.max
|
||||||
|
|
||||||
class ChaptersSortBottomSheet(controller: MangaDetailsController) :
|
class ChaptersSortBottomSheet(controller: MangaDetailsController) :
|
||||||
@ -58,12 +57,10 @@ class ChaptersSortBottomSheet(controller: MangaDetailsController) :
|
|||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
initGeneralPreferences()
|
initGeneralPreferences()
|
||||||
setBottomEdge(binding.hideTitles, activity)
|
setBottomEdge(binding.hideTitles, activity)
|
||||||
binding.closeButton.setOnClickListener { dismiss() }
|
|
||||||
binding.settingsScrollView.viewTreeObserver.addOnGlobalLayoutListener {
|
binding.settingsScrollView.viewTreeObserver.addOnGlobalLayoutListener {
|
||||||
val isScrollable =
|
val isScrollable =
|
||||||
binding.settingsScrollView.height < binding.sortLayout.height +
|
binding.settingsScrollView.height < binding.sortLayout.height +
|
||||||
binding.settingsScrollView.paddingTop + binding.settingsScrollView.paddingBottom
|
binding.settingsScrollView.paddingTop + binding.settingsScrollView.paddingBottom
|
||||||
binding.closeButton.isVisible = isScrollable
|
|
||||||
// making the view gone somehow breaks the layout so lets make it invisible
|
// making the view gone somehow breaks the layout so lets make it invisible
|
||||||
binding.pill.isInvisible = isScrollable
|
binding.pill.isInvisible = isScrollable
|
||||||
}
|
}
|
||||||
@ -80,43 +77,89 @@ class ChaptersSortBottomSheet(controller: MangaDetailsController) :
|
|||||||
private fun initGeneralPreferences() {
|
private fun initGeneralPreferences() {
|
||||||
binding.chapterFilterLayout.root.setCheckboxes(presenter.manga)
|
binding.chapterFilterLayout.root.setCheckboxes(presenter.manga)
|
||||||
|
|
||||||
var defPref = presenter.globalSort()
|
binding.byChapterNumber.state = SortTextView.State.NONE
|
||||||
binding.sortGroup.check(
|
binding.byUploadDate.state = SortTextView.State.NONE
|
||||||
if (presenter.manga.sortDescending(defPref)) R.id.sort_newest else {
|
binding.bySource.state = SortTextView.State.NONE
|
||||||
R.id.sort_oldest
|
|
||||||
}
|
val sortItem = when (presenter.sortingOrder()) {
|
||||||
)
|
Manga.CHAPTER_SORTING_NUMBER -> binding.byChapterNumber
|
||||||
|
Manga.CHAPTER_SORTING_UPLOAD_DATE -> binding.byUploadDate
|
||||||
|
else -> binding.bySource
|
||||||
|
}
|
||||||
|
|
||||||
|
sortItem.state = if (presenter.sortDescending()) {
|
||||||
|
SortTextView.State.DESCENDING
|
||||||
|
} else {
|
||||||
|
SortTextView.State.ASCENDING
|
||||||
|
}
|
||||||
|
|
||||||
|
checkIfSortMatchesDefault()
|
||||||
|
binding.byChapterNumber.setOnSortChangeListener(::sortChanged)
|
||||||
|
binding.byUploadDate.setOnSortChangeListener(::sortChanged)
|
||||||
|
binding.bySource.setOnSortChangeListener(::sortChanged)
|
||||||
|
|
||||||
binding.hideTitles.isChecked = presenter.manga.displayMode != Manga.CHAPTER_DISPLAY_NAME
|
binding.hideTitles.isChecked = presenter.manga.displayMode != Manga.CHAPTER_DISPLAY_NAME
|
||||||
binding.sortMethodGroup.check(
|
|
||||||
if (presenter.manga.sorting == Manga.CHAPTER_SORTING_SOURCE) R.id.sort_by_source else {
|
|
||||||
R.id.sort_by_number
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
binding.setAsDefaultSort.isInvisible = defPref == presenter.manga.sortDescending() ||
|
|
||||||
!presenter.manga.usesLocalSort()
|
|
||||||
binding.sortGroup.setOnCheckedChangeListener { _, checkedId ->
|
|
||||||
presenter.setSortOrder(checkedId == R.id.sort_newest)
|
|
||||||
binding.setAsDefaultSort.isInvisible = (
|
|
||||||
defPref == presenter.manga.sortDescending() ||
|
|
||||||
!presenter.manga.usesLocalSort()
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
binding.setAsDefaultSort.setOnClickListener {
|
binding.setAsDefaultSort.setOnClickListener {
|
||||||
val desc = binding.sortGroup.checkedRadioButtonId == R.id.sort_newest
|
presenter.setGlobalChapterSort(
|
||||||
presenter.setGlobalChapterSort(desc)
|
presenter.manga.sorting,
|
||||||
defPref = desc
|
presenter.manga.sortDescending()
|
||||||
|
)
|
||||||
binding.setAsDefaultSort.isInvisible = true
|
binding.setAsDefaultSort.isInvisible = true
|
||||||
|
binding.resetAsDefaultSort.isInvisible = true
|
||||||
}
|
}
|
||||||
|
|
||||||
binding.sortMethodGroup.setOnCheckedChangeListener { _, checkedId ->
|
binding.resetAsDefaultSort.setOnClickListener {
|
||||||
presenter.setSortMethod(checkedId == R.id.sort_by_source)
|
presenter.resetSortingToDefault()
|
||||||
|
|
||||||
|
binding.byChapterNumber.state = SortTextView.State.NONE
|
||||||
|
binding.byUploadDate.state = SortTextView.State.NONE
|
||||||
|
binding.bySource.state = SortTextView.State.NONE
|
||||||
|
|
||||||
|
val sortItemNew = when (presenter.sortingOrder()) {
|
||||||
|
Manga.CHAPTER_SORTING_NUMBER -> binding.byChapterNumber
|
||||||
|
Manga.CHAPTER_SORTING_UPLOAD_DATE -> binding.byUploadDate
|
||||||
|
else -> binding.bySource
|
||||||
|
}
|
||||||
|
|
||||||
|
sortItemNew.state = if (presenter.sortDescending()) {
|
||||||
|
SortTextView.State.DESCENDING
|
||||||
|
} else {
|
||||||
|
SortTextView.State.ASCENDING
|
||||||
|
}
|
||||||
|
binding.setAsDefaultSort.isInvisible = true
|
||||||
|
binding.resetAsDefaultSort.isInvisible = true
|
||||||
}
|
}
|
||||||
|
|
||||||
binding.hideTitles.setOnCheckedChangeListener { _, isChecked ->
|
binding.hideTitles.setOnCheckedChangeListener { _, isChecked ->
|
||||||
presenter.hideTitle(isChecked)
|
presenter.hideTitle(isChecked)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun checkIfSortMatchesDefault() {
|
||||||
|
val matches = presenter.mangaSortMatchesDefault()
|
||||||
|
binding.setAsDefaultSort.isInvisible = matches
|
||||||
|
binding.resetAsDefaultSort.isInvisible = matches
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun sortChanged(sortTextView: SortTextView, state: SortTextView.State) {
|
||||||
|
if (sortTextView != binding.byChapterNumber) {
|
||||||
|
binding.byChapterNumber.state = SortTextView.State.NONE
|
||||||
|
}
|
||||||
|
if (sortTextView != binding.byUploadDate) {
|
||||||
|
binding.byUploadDate.state = SortTextView.State.NONE
|
||||||
|
}
|
||||||
|
if (sortTextView != binding.bySource) {
|
||||||
|
binding.bySource.state = SortTextView.State.NONE
|
||||||
|
}
|
||||||
|
presenter.setSortOrder(
|
||||||
|
when (sortTextView) {
|
||||||
|
binding.byChapterNumber -> Manga.CHAPTER_SORTING_NUMBER
|
||||||
|
binding.byUploadDate -> Manga.CHAPTER_SORTING_UPLOAD_DATE
|
||||||
|
else -> Manga.CHAPTER_SORTING_SOURCE
|
||||||
|
},
|
||||||
|
state == SortTextView.State.DESCENDING
|
||||||
|
)
|
||||||
|
checkIfSortMatchesDefault()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package eu.kanade.tachiyomi.ui.reader
|
package eu.kanade.tachiyomi.ui.reader
|
||||||
|
|
||||||
import eu.kanade.tachiyomi.data.database.models.Chapter
|
import eu.kanade.tachiyomi.data.database.models.Chapter
|
||||||
|
import uy.kohesive.injekt.api.get
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Load strategy using the source order. This is the default ordering.
|
* Load strategy using the source order. This is the default ordering.
|
||||||
@ -35,3 +36,12 @@ class ChapterLoadByNumber {
|
|||||||
return chapters.sortedBy { it.chapter_number }
|
return chapters.sortedBy { it.chapter_number }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Load strategy using the source order. This is the default ordering.
|
||||||
|
*/
|
||||||
|
class ChapterLoadByDate {
|
||||||
|
fun get(allChapters: List<Chapter>): List<Chapter> {
|
||||||
|
return allChapters.sortedBy { it.date_upload }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -17,7 +17,6 @@ import eu.kanade.tachiyomi.data.download.DownloadManager
|
|||||||
import eu.kanade.tachiyomi.data.notification.NotificationReceiver
|
import eu.kanade.tachiyomi.data.notification.NotificationReceiver
|
||||||
import eu.kanade.tachiyomi.data.notification.Notifications
|
import eu.kanade.tachiyomi.data.notification.Notifications
|
||||||
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
||||||
import eu.kanade.tachiyomi.data.preference.getOrDefault
|
|
||||||
import eu.kanade.tachiyomi.data.track.DelayedTrackingUpdateJob
|
import eu.kanade.tachiyomi.data.track.DelayedTrackingUpdateJob
|
||||||
import eu.kanade.tachiyomi.data.track.TrackManager
|
import eu.kanade.tachiyomi.data.track.TrackManager
|
||||||
import eu.kanade.tachiyomi.source.LocalSource
|
import eu.kanade.tachiyomi.source.LocalSource
|
||||||
@ -33,6 +32,7 @@ import eu.kanade.tachiyomi.ui.reader.model.ViewerChapters
|
|||||||
import eu.kanade.tachiyomi.ui.reader.settings.OrientationType
|
import eu.kanade.tachiyomi.ui.reader.settings.OrientationType
|
||||||
import eu.kanade.tachiyomi.ui.reader.settings.ReadingModeType
|
import eu.kanade.tachiyomi.ui.reader.settings.ReadingModeType
|
||||||
import eu.kanade.tachiyomi.util.chapter.ChapterFilter
|
import eu.kanade.tachiyomi.util.chapter.ChapterFilter
|
||||||
|
import eu.kanade.tachiyomi.util.chapter.ChapterSort
|
||||||
import eu.kanade.tachiyomi.util.chapter.syncChaptersWithSource
|
import eu.kanade.tachiyomi.util.chapter.syncChaptersWithSource
|
||||||
import eu.kanade.tachiyomi.util.storage.DiskUtil
|
import eu.kanade.tachiyomi.util.storage.DiskUtil
|
||||||
import eu.kanade.tachiyomi.util.system.ImageUtil
|
import eu.kanade.tachiyomi.util.system.ImageUtil
|
||||||
@ -118,9 +118,10 @@ class ReaderPresenter(
|
|||||||
val chaptersForReader =
|
val chaptersForReader =
|
||||||
chapterFilter.filterChaptersForReader(dbChapters, manga, selectedChapter)
|
chapterFilter.filterChaptersForReader(dbChapters, manga, selectedChapter)
|
||||||
|
|
||||||
when (manga.sorting) {
|
when (manga.chapterOrder(preferences.sortChapterOrder().get())) {
|
||||||
Manga.CHAPTER_SORTING_SOURCE -> ChapterLoadBySource().get(chaptersForReader)
|
Manga.CHAPTER_SORTING_SOURCE -> ChapterLoadBySource().get(chaptersForReader)
|
||||||
Manga.CHAPTER_SORTING_NUMBER -> ChapterLoadByNumber().get(chaptersForReader, selectedChapter)
|
Manga.CHAPTER_SORTING_NUMBER -> ChapterLoadByNumber().get(chaptersForReader, selectedChapter)
|
||||||
|
Manga.CHAPTER_SORTING_UPLOAD_DATE -> ChapterLoadByDate().get(chaptersForReader)
|
||||||
else -> error("Unknown sorting method")
|
else -> error("Unknown sorting method")
|
||||||
}.map(::ReaderChapter)
|
}.map(::ReaderChapter)
|
||||||
}
|
}
|
||||||
@ -212,25 +213,14 @@ class ReaderPresenter(
|
|||||||
suspend fun getChapters(): List<ReaderChapterItem> {
|
suspend fun getChapters(): List<ReaderChapterItem> {
|
||||||
val manga = manga ?: return emptyList()
|
val manga = manga ?: return emptyList()
|
||||||
chapterItems = withContext(Dispatchers.IO) {
|
chapterItems = withContext(Dispatchers.IO) {
|
||||||
|
val chapterSort = ChapterSort(manga, chapterFilter, preferences)
|
||||||
val dbChapters = db.getChapters(manga).executeAsBlocking()
|
val dbChapters = db.getChapters(manga).executeAsBlocking()
|
||||||
val list =
|
chapterSort.getChaptersSorted(dbChapters, filterForReader = true, currentChapter = getCurrentChapter()?.chapter).map {
|
||||||
chapterFilter.filterChaptersForReader(dbChapters, manga, getCurrentChapter()?.chapter)
|
ReaderChapterItem(
|
||||||
.sortedBy {
|
it,
|
||||||
when (manga.sorting) {
|
manga,
|
||||||
Manga.CHAPTER_SORTING_NUMBER -> it.chapter_number
|
it.id == getCurrentChapter()?.chapter?.id ?: chapterId
|
||||||
else -> it.source_order.toFloat()
|
)
|
||||||
}
|
|
||||||
}.map {
|
|
||||||
ReaderChapterItem(
|
|
||||||
it,
|
|
||||||
manga,
|
|
||||||
it.id == getCurrentChapter()?.chapter?.id ?: chapterId
|
|
||||||
)
|
|
||||||
}
|
|
||||||
if (!manga.sortDescending(preferences.chaptersDescAsDefault().getOrDefault())) {
|
|
||||||
list.reversed()
|
|
||||||
} else {
|
|
||||||
list
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -16,6 +16,8 @@ import eu.kanade.tachiyomi.data.library.LibraryUpdateService
|
|||||||
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
||||||
import eu.kanade.tachiyomi.source.SourceManager
|
import eu.kanade.tachiyomi.source.SourceManager
|
||||||
import eu.kanade.tachiyomi.ui.base.presenter.BaseCoroutinePresenter
|
import eu.kanade.tachiyomi.ui.base.presenter.BaseCoroutinePresenter
|
||||||
|
import eu.kanade.tachiyomi.util.chapter.ChapterFilter
|
||||||
|
import eu.kanade.tachiyomi.util.chapter.ChapterSort
|
||||||
import eu.kanade.tachiyomi.util.system.executeOnIO
|
import eu.kanade.tachiyomi.util.system.executeOnIO
|
||||||
import eu.kanade.tachiyomi.util.system.launchIO
|
import eu.kanade.tachiyomi.util.system.launchIO
|
||||||
import eu.kanade.tachiyomi.util.system.launchUI
|
import eu.kanade.tachiyomi.util.system.launchUI
|
||||||
@ -39,7 +41,8 @@ class RecentsPresenter(
|
|||||||
val controller: RecentsController?,
|
val controller: RecentsController?,
|
||||||
val preferences: PreferencesHelper = Injekt.get(),
|
val preferences: PreferencesHelper = Injekt.get(),
|
||||||
val downloadManager: DownloadManager = Injekt.get(),
|
val downloadManager: DownloadManager = Injekt.get(),
|
||||||
private val db: DatabaseHelper = Injekt.get()
|
private val db: DatabaseHelper = Injekt.get(),
|
||||||
|
private val chapterFilter: ChapterFilter = Injekt.get()
|
||||||
) : BaseCoroutinePresenter(), DownloadQueue.DownloadListener, LibraryServiceListener, DownloadServiceListener {
|
) : BaseCoroutinePresenter(), DownloadQueue.DownloadListener, LibraryServiceListener, DownloadServiceListener {
|
||||||
|
|
||||||
private var recentsJob: Job? = null
|
private var recentsJob: Job? = null
|
||||||
@ -319,12 +322,12 @@ class RecentsPresenter(
|
|||||||
|
|
||||||
private fun getNextChapter(manga: Manga): Chapter? {
|
private fun getNextChapter(manga: Manga): Chapter? {
|
||||||
val chapters = db.getChapters(manga).executeAsBlocking()
|
val chapters = db.getChapters(manga).executeAsBlocking()
|
||||||
return chapters.sortedByDescending { it.source_order }.find { !it.read }
|
return ChapterSort(manga, chapterFilter, preferences).getNextUnreadChapter(chapters, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getFirstUpdatedChapter(manga: Manga, chapter: Chapter): Chapter? {
|
private fun getFirstUpdatedChapter(manga: Manga, chapter: Chapter): Chapter? {
|
||||||
val chapters = db.getChapters(manga).executeAsBlocking()
|
val chapters = db.getChapters(manga).executeAsBlocking()
|
||||||
return chapters.sortedByDescending { it.source_order }.find {
|
return chapters.sortedWith(ChapterSort(manga, chapterFilter, preferences).sortComparator(true)).find {
|
||||||
!it.read && abs(it.date_fetch - chapter.date_fetch) <= TimeUnit.HOURS.toMillis(12)
|
!it.read && abs(it.date_fetch - chapter.date_fetch) <= TimeUnit.HOURS.toMillis(12)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -38,7 +38,7 @@ class ChapterFilter(val preferences: PreferencesHelper = Injekt.get(), val downl
|
|||||||
}
|
}
|
||||||
|
|
||||||
// filter chapters for the reader
|
// filter chapters for the reader
|
||||||
fun filterChaptersForReader(chapters: List<Chapter>, manga: Manga, selectedChapter: Chapter? = null): List<Chapter> {
|
fun <T : Chapter> filterChaptersForReader(chapters: List<T>, manga: Manga, selectedChapter: T? = null): List<T> {
|
||||||
// if neither preference is enabled don't even filter
|
// if neither preference is enabled don't even filter
|
||||||
if (!preferences.skipRead() && !preferences.skipFiltered()) {
|
if (!preferences.skipRead() && !preferences.skipFiltered()) {
|
||||||
return chapters
|
return chapters
|
||||||
|
@ -0,0 +1,62 @@
|
|||||||
|
package eu.kanade.tachiyomi.util.chapter
|
||||||
|
|
||||||
|
import eu.kanade.tachiyomi.data.database.models.Chapter
|
||||||
|
import eu.kanade.tachiyomi.data.database.models.Manga
|
||||||
|
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
||||||
|
import eu.kanade.tachiyomi.data.preference.getOrDefault
|
||||||
|
import uy.kohesive.injekt.Injekt
|
||||||
|
import uy.kohesive.injekt.api.get
|
||||||
|
|
||||||
|
class ChapterSort(val manga: Manga, val chapterFilter: ChapterFilter = Injekt.get(), val preferences: PreferencesHelper = Injekt.get()) {
|
||||||
|
|
||||||
|
fun <T : Chapter> getChaptersSorted(
|
||||||
|
rawChapters: List<T>,
|
||||||
|
andFiltered: Boolean = true,
|
||||||
|
filterForReader: Boolean = false,
|
||||||
|
currentChapter: T? = null
|
||||||
|
): List<T> {
|
||||||
|
val chapters = when {
|
||||||
|
filterForReader -> chapterFilter.filterChaptersForReader(
|
||||||
|
rawChapters,
|
||||||
|
manga,
|
||||||
|
currentChapter
|
||||||
|
)
|
||||||
|
andFiltered -> chapterFilter.filterChapters(rawChapters, manga)
|
||||||
|
else -> rawChapters
|
||||||
|
}
|
||||||
|
|
||||||
|
val sortDescending =
|
||||||
|
manga.sortDescending(preferences.chaptersDescAsDefault().getOrDefault())
|
||||||
|
return chapters.sortedWith(sortComparator())
|
||||||
|
}
|
||||||
|
|
||||||
|
fun <T : Chapter> getNextUnreadChapter(rawChapters: List<T>, andFiltered: Boolean = true,): T? {
|
||||||
|
val chapters = when {
|
||||||
|
andFiltered -> chapterFilter.filterChapters(rawChapters, manga)
|
||||||
|
else -> rawChapters
|
||||||
|
}
|
||||||
|
return chapters.sortedWith(sortComparator(true)).find { !it.read }
|
||||||
|
}
|
||||||
|
|
||||||
|
fun <T : Chapter> sortComparator(ignoreAsc: Boolean = false): Comparator<T> {
|
||||||
|
val sortDescending = !ignoreAsc &&
|
||||||
|
manga.sortDescending(preferences.chaptersDescAsDefault().getOrDefault())
|
||||||
|
val sortFunction: (T, T) -> Int =
|
||||||
|
when (manga.chapterOrder(preferences.sortChapterOrder().get())) {
|
||||||
|
Manga.CHAPTER_SORTING_SOURCE -> when (sortDescending) {
|
||||||
|
true -> { c1, c2 -> c1.source_order.compareTo(c2.source_order) }
|
||||||
|
false -> { c1, c2 -> c2.source_order.compareTo(c1.source_order) }
|
||||||
|
}
|
||||||
|
Manga.CHAPTER_SORTING_NUMBER -> when (sortDescending) {
|
||||||
|
true -> { c1, c2 -> c2.chapter_number.compareTo(c1.chapter_number) }
|
||||||
|
false -> { c1, c2 -> c1.chapter_number.compareTo(c2.chapter_number) }
|
||||||
|
}
|
||||||
|
Manga.CHAPTER_SORTING_UPLOAD_DATE -> when (sortDescending) {
|
||||||
|
true -> { c1, c2 -> c2.date_upload.compareTo(c1.date_upload) }
|
||||||
|
false -> { c1, c2 -> c1.date_upload.compareTo(c2.date_upload) }
|
||||||
|
}
|
||||||
|
else -> { c1, c2 -> c1.source_order.compareTo(c2.source_order) }
|
||||||
|
}
|
||||||
|
return Comparator(sortFunction)
|
||||||
|
}
|
||||||
|
}
|
106
app/src/main/java/eu/kanade/tachiyomi/widget/SortTextView.kt
Normal file
106
app/src/main/java/eu/kanade/tachiyomi/widget/SortTextView.kt
Normal file
@ -0,0 +1,106 @@
|
|||||||
|
package eu.kanade.tachiyomi.widget
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.util.AttributeSet
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.widget.FrameLayout
|
||||||
|
import eu.kanade.tachiyomi.R
|
||||||
|
import eu.kanade.tachiyomi.databinding.SortTextViewBinding
|
||||||
|
import eu.kanade.tachiyomi.util.view.setVectorCompat
|
||||||
|
|
||||||
|
class SortTextView constructor(context: Context, attrs: AttributeSet?) :
|
||||||
|
FrameLayout(context, attrs) {
|
||||||
|
|
||||||
|
var text: CharSequence
|
||||||
|
get() {
|
||||||
|
return binding.textView.text
|
||||||
|
}
|
||||||
|
set(value) {
|
||||||
|
binding.textView.text = value
|
||||||
|
}
|
||||||
|
|
||||||
|
var state: State = State.NONE
|
||||||
|
set(value) {
|
||||||
|
field = value
|
||||||
|
updateDrawable()
|
||||||
|
}
|
||||||
|
|
||||||
|
val isSorting: Boolean
|
||||||
|
get() = state != State.NONE
|
||||||
|
|
||||||
|
private val binding = SortTextViewBinding.inflate(
|
||||||
|
LayoutInflater.from(context),
|
||||||
|
this,
|
||||||
|
false
|
||||||
|
)
|
||||||
|
private var mOnSortChangeListener: OnSortChangeListener? = null
|
||||||
|
|
||||||
|
init {
|
||||||
|
addView(binding.root)
|
||||||
|
val a = context.obtainStyledAttributes(attrs, R.styleable.SortTextView, 0, 0)
|
||||||
|
|
||||||
|
val str = a.getString(R.styleable.SortTextView_android_text) ?: ""
|
||||||
|
text = str
|
||||||
|
|
||||||
|
val maxLines = a.getInt(R.styleable.SortTextView_android_maxLines, Int.MAX_VALUE)
|
||||||
|
binding.textView.maxLines = maxLines
|
||||||
|
|
||||||
|
a.recycle()
|
||||||
|
|
||||||
|
setOnClickListener {
|
||||||
|
state =
|
||||||
|
when (state) {
|
||||||
|
State.DESCENDING -> State.ASCENDING
|
||||||
|
else -> State.DESCENDING
|
||||||
|
}
|
||||||
|
mOnSortChangeListener?.onSortChanged(this, state)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register a callback to be invoked when the checked state of this button
|
||||||
|
* changes.
|
||||||
|
*
|
||||||
|
* @param listener the callback to call on checked state change
|
||||||
|
*/
|
||||||
|
fun setOnSortChangeListener(listener: OnSortChangeListener?) {
|
||||||
|
mOnSortChangeListener = listener
|
||||||
|
}
|
||||||
|
|
||||||
|
fun updateDrawable() {
|
||||||
|
with(binding.sortImageView) {
|
||||||
|
when (state) {
|
||||||
|
State.ASCENDING -> {
|
||||||
|
setVectorCompat(R.drawable.ic_arrow_upward_24dp, R.attr.colorAccent)
|
||||||
|
}
|
||||||
|
State.DESCENDING -> {
|
||||||
|
setVectorCompat(R.drawable.ic_arrow_downward_24dp, R.attr.colorAccent)
|
||||||
|
}
|
||||||
|
State.NONE -> {
|
||||||
|
setVectorCompat(R.drawable.ic_blank_24dp, R.attr.colorAccentText)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum class State {
|
||||||
|
ASCENDING,
|
||||||
|
DESCENDING,
|
||||||
|
NONE,
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface definition for a callback to be invoked when the checked state
|
||||||
|
* of a compound button changed.
|
||||||
|
*/
|
||||||
|
fun interface OnSortChangeListener {
|
||||||
|
/**
|
||||||
|
* Called when the checked state of a compound button has changed.
|
||||||
|
*
|
||||||
|
* @param buttonView The compound button view whose state has changed.
|
||||||
|
* @param state The new checked state of buttonView.
|
||||||
|
*/
|
||||||
|
fun onSortChanged(buttonView: SortTextView, state: State)
|
||||||
|
}
|
||||||
|
}
|
@ -16,20 +16,24 @@
|
|||||||
style="@style/BottomSheetDialogTheme"
|
style="@style/BottomSheetDialogTheme"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:background="@android:color/transparent"
|
|
||||||
android:orientation="vertical"
|
android:orientation="vertical"
|
||||||
android:paddingTop="12dp"
|
android:paddingTop="12dp"
|
||||||
app:layout_behavior="com.google.android.material.bottomsheet.BottomSheetBehavior">
|
app:layout_behavior="com.google.android.material.bottomsheet.BottomSheetBehavior">
|
||||||
|
|
||||||
<LinearLayout
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
|
android:id="@+id/sort_title_bar"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content">
|
||||||
android:orientation="horizontal">
|
|
||||||
|
|
||||||
<com.google.android.material.textview.MaterialTextView
|
<com.google.android.material.textview.MaterialTextView
|
||||||
|
android:id="@+id/sort_title"
|
||||||
style="@style/TextAppearance.MaterialComponents.Headline6"
|
style="@style/TextAppearance.MaterialComponents.Headline6"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="center_vertical"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
android:paddingStart="16dp"
|
android:paddingStart="16dp"
|
||||||
android:paddingEnd="12dp"
|
android:paddingEnd="12dp"
|
||||||
android:text="@string/sort" />
|
android:text="@string/sort" />
|
||||||
@ -38,33 +42,44 @@
|
|||||||
android:id="@+id/set_as_default_sort"
|
android:id="@+id/set_as_default_sort"
|
||||||
style="@style/Theme.Widget.Button.TextButton"
|
style="@style/Theme.Widget.Button.TextButton"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="38sp"
|
||||||
android:text="@string/set_as_default_for_all" />
|
android:padding="4dp"
|
||||||
</LinearLayout>
|
android:layout_marginStart="6dp"
|
||||||
|
android:layout_gravity="center_vertical"
|
||||||
|
app:layout_constraintBaseline_toBaselineOf="@id/sort_title"
|
||||||
|
app:layout_constraintStart_toEndOf="@id/sort_title"
|
||||||
|
android:text="@string/set_as_default" />
|
||||||
|
|
||||||
<RadioGroup
|
<com.google.android.material.button.MaterialButton
|
||||||
android:id="@+id/sort_group"
|
android:id="@+id/reset_as_default_sort"
|
||||||
android:layout_width="wrap_content"
|
style="@style/Theme.Widget.Button.TextButton"
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:orientation="horizontal"
|
|
||||||
android:paddingStart="12dp"
|
|
||||||
android:paddingEnd="12dp">
|
|
||||||
|
|
||||||
<com.google.android.material.radiobutton.MaterialRadioButton
|
|
||||||
android:id="@+id/sort_newest"
|
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="38sp"
|
||||||
android:layout_weight="1"
|
android:padding="4dp"
|
||||||
android:text="@string/newest_first" />
|
android:layout_marginEnd="6dp"
|
||||||
|
android:layout_gravity="center_vertical"
|
||||||
|
app:layout_constraintBaseline_toBaselineOf="@id/sort_title"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
android:text="@string/reset" />
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
|
||||||
<com.google.android.material.radiobutton.MaterialRadioButton
|
<eu.kanade.tachiyomi.widget.SortTextView
|
||||||
android:id="@+id/sort_oldest"
|
android:id="@+id/by_source"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:text="@string/by_source_order"
|
||||||
android:layout_marginStart="12dp"
|
android:layout_height="wrap_content"/>
|
||||||
android:layout_weight="1"
|
|
||||||
android:text="@string/oldest_first" />
|
<eu.kanade.tachiyomi.widget.SortTextView
|
||||||
</RadioGroup>
|
android:id="@+id/by_chapter_number"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:text="@string/by_chapter_number"
|
||||||
|
android:layout_height="wrap_content"/>
|
||||||
|
|
||||||
|
<eu.kanade.tachiyomi.widget.SortTextView
|
||||||
|
android:id="@+id/by_upload_date"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:text="@string/by_update_date"
|
||||||
|
android:layout_height="wrap_content"/>
|
||||||
|
|
||||||
<include layout="@layout/chapter_filter_layout"
|
<include layout="@layout/chapter_filter_layout"
|
||||||
android:id="@+id/chapter_filter_layout"
|
android:id="@+id/chapter_filter_layout"
|
||||||
@ -80,27 +95,6 @@
|
|||||||
android:paddingEnd="12dp"
|
android:paddingEnd="12dp"
|
||||||
android:text="@string/more" />
|
android:text="@string/more" />
|
||||||
|
|
||||||
<RadioGroup
|
|
||||||
android:id="@+id/sort_method_group"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:orientation="horizontal"
|
|
||||||
android:paddingStart="12dp"
|
|
||||||
android:paddingEnd="12dp">
|
|
||||||
|
|
||||||
<com.google.android.material.radiobutton.MaterialRadioButton
|
|
||||||
android:id="@+id/sort_by_source"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="@string/sort_by_source_s_order" />
|
|
||||||
|
|
||||||
<com.google.android.material.radiobutton.MaterialRadioButton
|
|
||||||
android:id="@+id/sort_by_number"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="@string/sort_by_chapter_number" />
|
|
||||||
</RadioGroup>
|
|
||||||
|
|
||||||
<com.google.android.material.checkbox.MaterialCheckBox
|
<com.google.android.material.checkbox.MaterialCheckBox
|
||||||
android:id="@+id/hide_titles"
|
android:id="@+id/hide_titles"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
@ -122,18 +116,4 @@
|
|||||||
android:contentDescription="@string/drag_handle"
|
android:contentDescription="@string/drag_handle"
|
||||||
android:src="@drawable/draggable_pill"
|
android:src="@drawable/draggable_pill"
|
||||||
app:tint="?android:attr/textColorPrimary" />
|
app:tint="?android:attr/textColorPrimary" />
|
||||||
|
|
||||||
<ImageView
|
|
||||||
android:id="@+id/close_button"
|
|
||||||
android:layout_width="32dp"
|
|
||||||
android:layout_height="32dp"
|
|
||||||
android:layout_gravity="end"
|
|
||||||
android:layout_marginTop="12dp"
|
|
||||||
android:layout_marginEnd="12dp"
|
|
||||||
android:background="@drawable/round_ripple"
|
|
||||||
android:clickable="true"
|
|
||||||
android:contentDescription="@string/close"
|
|
||||||
android:focusable="true"
|
|
||||||
android:src="@drawable/ic_close_24dp"
|
|
||||||
app:tint="@color/gray_button" />
|
|
||||||
</FrameLayout>
|
</FrameLayout>
|
33
app/src/main/res/layout/sort_text_view.xml
Normal file
33
app/src/main/res/layout/sort_text_view.xml
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:paddingTop="8dp"
|
||||||
|
android:background="?selectableItemBackground"
|
||||||
|
android:paddingBottom="8dp"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/sort_image_view"
|
||||||
|
android:contentDescription="@string/sort"
|
||||||
|
android:layout_marginStart="12dp"
|
||||||
|
android:padding="4dp"
|
||||||
|
tools:srcCompat="@drawable/ic_arrow_upward_24dp"
|
||||||
|
app:srcCompat="@drawable/ic_blank_24dp"
|
||||||
|
app:tint="?colorAccent"
|
||||||
|
style="@style/MD_ListItem_Control" />
|
||||||
|
|
||||||
|
<com.google.android.material.textview.MaterialTextView
|
||||||
|
android:id="@+id/text_view"
|
||||||
|
android:layout_marginStart="6dp"
|
||||||
|
android:layout_marginEnd="4dp"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="center_vertical"
|
||||||
|
android:textSize="14sp"
|
||||||
|
style="@style/TextAppearance.Regular"
|
||||||
|
tools:text="Item" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
@ -31,6 +31,12 @@
|
|||||||
<attr name="android:maxLines"/>
|
<attr name="android:maxLines"/>
|
||||||
</declare-styleable>
|
</declare-styleable>
|
||||||
|
|
||||||
|
|
||||||
|
<declare-styleable name="SortTextView">
|
||||||
|
<attr name="android:text" format="reference|string"/>
|
||||||
|
<attr name="android:maxLines"/>
|
||||||
|
</declare-styleable>
|
||||||
|
|
||||||
<declare-styleable name="MenuSheetItemView">
|
<declare-styleable name="MenuSheetItemView">
|
||||||
<attr name="android:text"/>
|
<attr name="android:text"/>
|
||||||
<attr name="android:maxLines"/>
|
<attr name="android:maxLines"/>
|
||||||
|
@ -59,6 +59,9 @@
|
|||||||
<string name="no_pages_found">No pages found</string>
|
<string name="no_pages_found">No pages found</string>
|
||||||
<string name="remove_all_downloads">Remove all downloads?</string>
|
<string name="remove_all_downloads">Remove all downloads?</string>
|
||||||
<string name="no_chapters_to_delete">No chapters to delete</string>
|
<string name="no_chapters_to_delete">No chapters to delete</string>
|
||||||
|
<string name="by_source_order">By source\'s order</string>
|
||||||
|
<string name="by_chapter_number">By chapter number</string>
|
||||||
|
<string name="by_update_date">By upload date</string>
|
||||||
|
|
||||||
<plurals name="remove_n_chapters">
|
<plurals name="remove_n_chapters">
|
||||||
<item quantity="one">Remove %1$d downloaded chapter?</item>
|
<item quantity="one">Remove %1$d downloaded chapter?</item>
|
||||||
@ -508,6 +511,7 @@
|
|||||||
<string name="error_saving_cover">Error saving cover</string>
|
<string name="error_saving_cover">Error saving cover</string>
|
||||||
<string name="error_sharing_cover">Error sharing cover</string>
|
<string name="error_sharing_cover">Error sharing cover</string>
|
||||||
<string name="custom_manga_info">Custom manga info</string>
|
<string name="custom_manga_info">Custom manga info</string>
|
||||||
|
<string name="set_as_default">Set as default</string>
|
||||||
<plurals name="deleted_chapters">
|
<plurals name="deleted_chapters">
|
||||||
<item quantity="one">A chapter has been removed from the source:\n%2$s\nDelete
|
<item quantity="one">A chapter has been removed from the source:\n%2$s\nDelete
|
||||||
its download?</item>
|
its download?</item>
|
||||||
|
Loading…
Reference in New Issue
Block a user