diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/preference/PreferenceKeys.kt b/app/src/main/java/eu/kanade/tachiyomi/data/preference/PreferenceKeys.kt index 37d9fcc3e..b85e41801 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/preference/PreferenceKeys.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/preference/PreferenceKeys.kt @@ -13,7 +13,9 @@ object PreferenceKeys { const val confirmExit = "pref_confirm_exit" - const val hideBottomBar = "pref_hide_bottom_bar_on_scroll" + const val hideBottomBarOnScroll = "pref_hide_bottom_bar_on_scroll" + + const val showSideNavOnBottom = "pref_show_side_nav_on_bottom" const val enableTransitions = "pref_enable_transitions_key" diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/preference/PreferencesHelper.kt b/app/src/main/java/eu/kanade/tachiyomi/data/preference/PreferencesHelper.kt index d27b8d7d1..aa014cbc6 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/preference/PreferencesHelper.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/preference/PreferencesHelper.kt @@ -64,7 +64,9 @@ class PreferencesHelper(val context: Context) { fun confirmExit() = prefs.getBoolean(Keys.confirmExit, false) - fun hideBottomBar() = flowPrefs.getBoolean(Keys.hideBottomBar, true) + fun hideBottomBarOnScroll() = flowPrefs.getBoolean(Keys.hideBottomBarOnScroll, true) + + fun showSideNavOnBottom() = flowPrefs.getBoolean(Keys.showSideNavOnBottom, false) fun useAuthenticator() = flowPrefs.getBoolean(Keys.useAuthenticator, false) diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/main/MainActivity.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/main/MainActivity.kt index 173007c9f..341d21f2e 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/main/MainActivity.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/main/MainActivity.kt @@ -5,6 +5,7 @@ import android.content.Intent import android.graphics.Color import android.os.Build import android.os.Bundle +import android.view.Gravity import android.view.View import android.view.ViewGroup import android.widget.Toast @@ -149,11 +150,19 @@ class MainActivity : BaseViewBindingActivity() { ) // Set behavior of bottom nav - preferences.hideBottomBar() + preferences.hideBottomBarOnScroll() .asImmediateFlow { setBottomNavBehaviorOnScroll() } .launchIn(lifecycleScope) } + if (binding.sideNav != null) { + preferences.showSideNavOnBottom() + .asImmediateFlow { + binding.sideNav?.menuGravity = if (!it) Gravity.TOP else Gravity.BOTTOM + } + .launchIn(lifecycleScope) + } + nav.setOnItemSelectedListener { item -> val id = item.itemId @@ -532,7 +541,7 @@ class MainActivity : BaseViewBindingActivity() { binding.bottomNav?.updateLayoutParams { behavior = when { - preferences.hideBottomBar().get() -> HideBottomViewOnScrollBehavior() + preferences.hideBottomBarOnScroll().get() -> HideBottomViewOnScrollBehavior() else -> null } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaController.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaController.kt index c0209f352..ccdc4fa85 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaController.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaController.kt @@ -206,11 +206,17 @@ class MangaController : override fun onViewCreated(view: View) { super.onViewCreated(view) - binding.recycler.applyInsetter { - type(navigationBars = true) { - padding() + listOfNotNull(binding.fullRecycler, binding.infoRecycler, binding.chaptersRecycler) + .forEach { + it.applyInsetter { + type(navigationBars = true) { + padding() + } + } + + it.layoutManager = LinearLayoutManager(view.context) + it.setHasFixedSize(true) } - } binding.actionToolbar.applyInsetter { type(navigationBars = true) { margin(bottom = true) @@ -220,32 +226,41 @@ class MangaController : if (manga == null || source == null) return // Init RecyclerView and adapter - mangaInfoAdapter = MangaInfoHeaderAdapter(this, fromSource) + mangaInfoAdapter = MangaInfoHeaderAdapter(this, fromSource, binding.infoRecycler != null) chaptersHeaderAdapter = MangaChaptersHeaderAdapter(this) chaptersAdapter = ChaptersAdapter(this, view.context) - binding.recycler.adapter = ConcatAdapter(mangaInfoAdapter, chaptersHeaderAdapter, chaptersAdapter) - binding.recycler.layoutManager = LinearLayoutManager(view.context) - binding.recycler.setHasFixedSize(true) - chaptersAdapter?.fastScroller = binding.fastScroller + // Phone layout + binding.fullRecycler?.let { + it.adapter = ConcatAdapter(mangaInfoAdapter, chaptersHeaderAdapter, chaptersAdapter) - actionFabScrollListener = actionFab?.shrinkOnScroll(binding.recycler) + it.scrollEvents() + .onEach { updateToolbarTitleAlpha() } + .launchIn(viewScope) - // Skips directly to chapters list if navigated to from the library - binding.recycler.post { - if (!fromSource && preferences.jumpToChapters()) { - (binding.recycler.layoutManager as LinearLayoutManager).scrollToPositionWithOffset(1, 0) - } + // Skips directly to chapters list if navigated to from the library + it.post { + if (!fromSource && preferences.jumpToChapters()) { + (it.layoutManager as LinearLayoutManager).scrollToPositionWithOffset(1, 0) + } - // Delayed in case we need to jump to chapters - binding.recycler.post { - updateToolbarTitleAlpha() + // Delayed in case we need to jump to chapters + it.post { + updateToolbarTitleAlpha() + } } } + // Tablet layout + binding.infoRecycler?.let { + it.adapter = mangaInfoAdapter + } + binding.chaptersRecycler?.let { + it.adapter = ConcatAdapter(chaptersHeaderAdapter, chaptersAdapter) + } - binding.recycler.scrollEvents() - .onEach { updateToolbarTitleAlpha() } - .launchIn(viewScope) + chaptersAdapter?.fastScroller = binding.fastScroller + + actionFabScrollListener = actionFab?.shrinkOnScroll(chapterRecycler) binding.swipeRefresh.refreshes() .onEach { @@ -269,15 +284,19 @@ class MangaController : } private fun updateToolbarTitleAlpha(alpha: Int? = null) { + if (binding.fullRecycler == null) { + return + } + val calculatedAlpha = when { // Specific alpha provided alpha != null -> alpha // First item isn't in view, full opacity - ((binding.recycler.layoutManager as LinearLayoutManager).findFirstVisibleItemPosition() > 0) -> 255 + ((binding.fullRecycler!!.layoutManager as LinearLayoutManager).findFirstVisibleItemPosition() > 0) -> 255 // Based on scroll amount when first item is in view - else -> min(binding.recycler.computeVerticalScrollOffset(), 255) + else -> min(binding.fullRecycler!!.computeVerticalScrollOffset(), 255) } (activity as? MainActivity)?.binding?.toolbar?.setTitleTextColor( @@ -321,7 +340,7 @@ class MangaController : override fun cleanupFab(fab: ExtendedFloatingActionButton) { fab.setOnClickListener(null) - actionFabScrollListener?.let { binding.recycler.removeOnScrollListener(it) } + actionFabScrollListener?.let { chapterRecycler.removeOnScrollListener(it) } actionFab = null } @@ -690,7 +709,7 @@ class MangaController : fun onNextChapters(chapters: List) { // If the list is empty and it hasn't requested previously, fetch chapters from source // We use presenter chapters instead because they are always unfiltered - if (!presenter.hasRequested && presenter.chapters.isEmpty()) { + if (!presenter.hasRequested && presenter.allChapters.isEmpty()) { fetchChaptersFromSource() } @@ -1020,29 +1039,17 @@ class MangaController : // OVERFLOW MENU DIALOGS - private fun getUnreadChaptersSorted(): List { - val chapters = presenter.chapters - .sortedWith(presenter.getChapterSort()) - .filter { !it.read && it.status == Download.State.NOT_DOWNLOADED } - .distinctBy { it.name } - return if (presenter.sortDescending()) { - chapters.reversed() - } else { - chapters - } - } - private fun downloadChapters(choice: Int) { val chaptersToDownload = when (choice) { - R.id.download_next -> getUnreadChaptersSorted().take(1) - R.id.download_next_5 -> getUnreadChaptersSorted().take(5) - R.id.download_next_10 -> getUnreadChaptersSorted().take(10) + R.id.download_next -> presenter.getUnreadChaptersSorted().take(1) + R.id.download_next_5 -> presenter.getUnreadChaptersSorted().take(5) + R.id.download_next_10 -> presenter.getUnreadChaptersSorted().take(10) R.id.download_custom -> { showCustomDownloadDialog() return } - R.id.download_unread -> presenter.chapters.filter { !it.read } - R.id.download_all -> presenter.chapters + R.id.download_unread -> presenter.allChapters.filter { !it.read } + R.id.download_all -> presenter.allChapters else -> emptyList() } if (chaptersToDownload.isNotEmpty()) { @@ -1054,12 +1061,12 @@ class MangaController : private fun showCustomDownloadDialog() { DownloadCustomChaptersDialog( this, - presenter.chapters.size + presenter.allChapters.size ).showDialog(router) } override fun downloadCustomChapters(amount: Int) { - val chaptersToDownload = getUnreadChaptersSorted().take(amount) + val chaptersToDownload = presenter.getUnreadChaptersSorted().take(amount) if (chaptersToDownload.isNotEmpty()) { downloadChapters(chaptersToDownload) } @@ -1096,6 +1103,9 @@ class MangaController : // Tracker sheet - end + private val chapterRecycler: RecyclerView + get() = binding.fullRecycler ?: binding.chaptersRecycler!! + companion object { const val FROM_SOURCE_EXTRA = "from_source" const val MANGA_EXTRA = "manga" diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaPresenter.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaPresenter.kt index 87c6452d7..cffd365f2 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaPresenter.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaPresenter.kt @@ -26,6 +26,7 @@ import eu.kanade.tachiyomi.ui.base.presenter.BasePresenter import eu.kanade.tachiyomi.ui.manga.chapter.ChapterItem import eu.kanade.tachiyomi.ui.manga.track.TrackItem import eu.kanade.tachiyomi.util.chapter.ChapterSettingsHelper +import eu.kanade.tachiyomi.util.chapter.getChapterSort import eu.kanade.tachiyomi.util.chapter.syncChaptersWithSource import eu.kanade.tachiyomi.util.chapter.syncChaptersWithTrackServiceTwoWay import eu.kanade.tachiyomi.util.isLocal @@ -65,10 +66,9 @@ class MangaPresenter( */ private var fetchMangaJob: Job? = null - /** - * List of chapters of the manga. It's always unfiltered and unsorted. - */ - var chapters: List = emptyList() + var allChapters: List = emptyList() + private set + var filteredAndSortedChapters: List = emptyList() private set /** @@ -124,7 +124,13 @@ class MangaPresenter( // Prepare the relay. chaptersRelay.flatMap { applyChapterFilters(it) } .observeOn(AndroidSchedulers.mainThread()) - .subscribeLatestCache(MangaController::onNextChapters) { _, error -> Timber.e(error) } + .subscribeLatestCache( + { _, chapters -> + filteredAndSortedChapters = chapters + view?.onNextChapters(chapters) + }, + { _, error -> Timber.e(error) } + ) // Manga info - end @@ -143,7 +149,7 @@ class MangaPresenter( setDownloadedChapters(chapters) // Store the last emission - this.chapters = chapters + this.allChapters = chapters // Listen for download status changes observeDownloads() @@ -401,7 +407,7 @@ class MangaPresenter( * Updates the UI after applying the filters. */ private fun refreshChapters() { - chaptersRelay.call(chapters) + chaptersRelay.call(allChapters) } /** @@ -433,25 +439,7 @@ class MangaPresenter( observable = observable.filter { !it.bookmark } } - return observable.toSortedList(getChapterSort()) - } - - fun getChapterSort(): (Chapter, Chapter) -> Int { - return 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) } - } - 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 -> throw NotImplementedError("Unimplemented sorting method") - } + return observable.toSortedList(getChapterSort(manga)) } /** @@ -461,7 +449,7 @@ class MangaPresenter( private fun onDownloadStatusChange(download: Download) { // Assign the download to the model object. if (download.status == Download.State.QUEUE) { - chapters.find { it.id == download.chapter.id }?.let { + allChapters.find { it.id == download.chapter.id }?.let { if (it.download == null) { it.download = download } @@ -478,11 +466,22 @@ class MangaPresenter( * Returns the next unread chapter or null if everything is read. */ fun getNextUnreadChapter(): ChapterItem? { - val chapters = chapters.sortedWith(getChapterSort()) return if (sortDescending()) { - return chapters.findLast { !it.read } + return filteredAndSortedChapters.findLast { !it.read } } else { - chapters.find { !it.read } + filteredAndSortedChapters.find { !it.read } + } + } + + fun getUnreadChaptersSorted(): List { + val chapters = allChapters + .sortedWith(getChapterSort(manga)) + .filter { !it.read && it.status == Download.State.NOT_DOWNLOADED } + .distinctBy { it.name } + return if (sortDescending()) { + chapters.reversed() + } else { + chapters } } @@ -713,7 +712,7 @@ class MangaPresenter( db.insertTrack(track).executeAsBlocking() if (it.service is UnattendedTrackService) { - syncChaptersWithTrackServiceTwoWay(db, chapters, track, it.service) + syncChaptersWithTrackServiceTwoWay(db, allChapters, track, it.service) } } } @@ -748,7 +747,7 @@ class MangaPresenter( db.insertTrack(item).executeAsBlocking() if (service is UnattendedTrackService) { - syncChaptersWithTrackServiceTwoWay(db, chapters, item, service) + syncChaptersWithTrackServiceTwoWay(db, allChapters, item, service) } } catch (e: Throwable) { withUIContext { view?.applicationContext?.toast(e.message) } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/info/MangaInfoHeaderAdapter.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/info/MangaInfoHeaderAdapter.kt index dc5f4c37b..8a156b4e2 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/info/MangaInfoHeaderAdapter.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/info/MangaInfoHeaderAdapter.kt @@ -28,7 +28,8 @@ import uy.kohesive.injekt.injectLazy class MangaInfoHeaderAdapter( private val controller: MangaController, - private val fromSource: Boolean + private val fromSource: Boolean, + private val isTablet: Boolean, ) : RecyclerView.Adapter() { @@ -194,10 +195,16 @@ class MangaInfoHeaderAdapter( */ private fun setMangaInfo(manga: Manga, source: Source?) { // Update full title TextView. - binding.mangaFullTitle.text = if (manga.title.isBlank()) { - view.context.getString(R.string.unknown) - } else { - manga.title + with(binding.mangaFullTitle) { + if (isTablet) { + isVisible = false + } else { + text = if (manga.title.isBlank()) { + view.context.getString(R.string.unknown) + } else { + manga.title + } + } } // Update author TextView. @@ -282,8 +289,9 @@ class MangaInfoHeaderAdapter( .onEach { toggleMangaInfo() } .launchIn(controller.viewScope) - // Expand manga info if navigated from source listing - if (initialLoad && fromSource) { + // Expand manga info if navigated from source listing or explicitly set to + // (e.g. on tablets) + if (initialLoad && (fromSource || isTablet)) { toggleMangaInfo() initialLoad = false } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ChapterLoadStrategy.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ChapterLoadStrategy.kt deleted file mode 100644 index 0d5d18d60..000000000 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ChapterLoadStrategy.kt +++ /dev/null @@ -1,46 +0,0 @@ -package eu.kanade.tachiyomi.ui.reader - -import eu.kanade.tachiyomi.data.database.models.Chapter - -/** - * Load strategy using the source order. This is the default ordering. - */ -class ChapterLoadBySource { - fun get(allChapters: List): List { - return allChapters.sortedByDescending { it.source_order } - } -} - -/** - * Load strategy using unique chapter numbers with same scanlator preference. - */ -class ChapterLoadByNumber { - fun get(allChapters: List, selectedChapter: Chapter): List { - val chapters = mutableListOf() - val chaptersByNumber = allChapters.groupBy { it.chapter_number } - - for ((number, chaptersForNumber) in chaptersByNumber) { - val preferredChapter = when { - // Make sure the selected chapter is always present - number == selectedChapter.chapter_number -> selectedChapter - // If there is only one chapter for this number, use it - chaptersForNumber.size == 1 -> chaptersForNumber.first() - // Prefer a chapter of the same scanlator as the selected - else -> - chaptersForNumber.find { it.scanlator == selectedChapter.scanlator } - ?: chaptersForNumber.first() - } - chapters.add(preferredChapter) - } - return chapters.sortedBy { it.chapter_number } - } -} - -/** - * Load strategy using the chapter upload date. This ordering ignores scanlators - */ -class ChapterLoadByUploadDate { - fun get(allChapters: List): List { - return allChapters.sortedBy { it.date_upload } - } -} diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderPresenter.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderPresenter.kt index b2a308e9b..fa471c314 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderPresenter.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderPresenter.kt @@ -23,6 +23,7 @@ import eu.kanade.tachiyomi.ui.reader.model.ReaderPage import eu.kanade.tachiyomi.ui.reader.model.ViewerChapters import eu.kanade.tachiyomi.ui.reader.setting.OrientationType import eu.kanade.tachiyomi.ui.reader.setting.ReadingModeType +import eu.kanade.tachiyomi.util.chapter.getChapterSort import eu.kanade.tachiyomi.util.isLocal import eu.kanade.tachiyomi.util.lang.byteSize import eu.kanade.tachiyomi.util.lang.launchIO @@ -96,28 +97,22 @@ class ReaderPresenter( val selectedChapter = dbChapters.find { it.id == chapterId } ?: error("Requested chapter of id $chapterId not found in chapter list") - val chaptersForReader = - if (preferences.skipRead() || preferences.skipFiltered()) { - val list = dbChapters - .filter { - if (preferences.skipRead() && it.read) { - return@filter false - } else if (preferences.skipFiltered()) { - if ( - (manga.readFilter == Manga.CHAPTER_SHOW_READ && !it.read) || + val chaptersForReader = when { + (preferences.skipRead() || preferences.skipFiltered()) -> { + val list = dbChapters.filterNot { + when { + preferences.skipRead() && it.read -> true + preferences.skipFiltered() -> { + (manga.readFilter == Manga.CHAPTER_SHOW_READ && !it.read) || (manga.readFilter == Manga.CHAPTER_SHOW_UNREAD && it.read) || - ( - manga.downloadedFilter == Manga.CHAPTER_SHOW_DOWNLOADED && - !downloadManager.isChapterDownloaded(it, manga) - ) || - (manga.bookmarkedFilter == Manga.CHAPTER_SHOW_BOOKMARKED && !it.bookmark) - ) { - return@filter false - } + (manga.downloadedFilter == Manga.CHAPTER_SHOW_DOWNLOADED && !downloadManager.isChapterDownloaded(it, manga)) || + (manga.downloadedFilter == Manga.CHAPTER_SHOW_NOT_DOWNLOADED && downloadManager.isChapterDownloaded(it, manga)) || + (manga.bookmarkedFilter == Manga.CHAPTER_SHOW_BOOKMARKED && !it.bookmark) || + (manga.bookmarkedFilter == Manga.CHAPTER_SHOW_NOT_BOOKMARKED && it.bookmark) } - - true + else -> false } + } .toMutableList() val find = list.find { it.id == chapterId } @@ -125,16 +120,13 @@ class ReaderPresenter( list.add(selectedChapter) } list - } else { - dbChapters } + else -> dbChapters + } - when (manga.sorting) { - Manga.CHAPTER_SORTING_SOURCE -> ChapterLoadBySource().get(chaptersForReader) - Manga.CHAPTER_SORTING_NUMBER -> ChapterLoadByNumber().get(chaptersForReader, selectedChapter) - Manga.CHAPTER_SORTING_UPLOAD_DATE -> ChapterLoadByUploadDate().get(chaptersForReader) - else -> error("Unknown sorting method") - }.map(::ReaderChapter) + chaptersForReader + .sortedWith(getChapterSort(manga, sortDescending = false)) + .map(::ReaderChapter) } private var hasTrackers: Boolean = false diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsGeneralController.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsGeneralController.kt index 62e21c6d0..5c53a1e35 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsGeneralController.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsGeneralController.kt @@ -17,6 +17,7 @@ import eu.kanade.tachiyomi.util.preference.preferenceCategory import eu.kanade.tachiyomi.util.preference.switchPreference import eu.kanade.tachiyomi.util.preference.titleRes import eu.kanade.tachiyomi.util.system.LocaleHelper +import eu.kanade.tachiyomi.util.system.isTablet import kotlinx.coroutines.flow.launchIn import java.util.Date import eu.kanade.tachiyomi.data.preference.PreferenceKeys as Keys @@ -45,10 +46,18 @@ class SettingsGeneralController : SettingsController() { titleRes = R.string.pref_confirm_exit defaultValue = false } - switchPreference { - key = Keys.hideBottomBar - titleRes = R.string.pref_hide_bottom_bar_on_scroll - defaultValue = true + if (context.isTablet()) { + switchPreference { + key = Keys.showSideNavOnBottom + titleRes = R.string.pref_move_side_nav_to_bottom + defaultValue = false + } + } else { + switchPreference { + key = Keys.hideBottomBarOnScroll + titleRes = R.string.pref_hide_bottom_bar_on_scroll + defaultValue = true + } } if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsLibraryController.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsLibraryController.kt index 3f8baf1bc..70915f8e9 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsLibraryController.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsLibraryController.kt @@ -28,6 +28,7 @@ import eu.kanade.tachiyomi.util.preference.preferenceCategory import eu.kanade.tachiyomi.util.preference.summaryRes import eu.kanade.tachiyomi.util.preference.switchPreference import eu.kanade.tachiyomi.util.preference.titleRes +import eu.kanade.tachiyomi.util.system.isTablet import eu.kanade.tachiyomi.widget.MinMaxNumberPicker import eu.kanade.tachiyomi.widget.materialdialogs.QuadStateCheckBox import eu.kanade.tachiyomi.widget.materialdialogs.listItemsQuadStateMultiChoice @@ -75,10 +76,12 @@ class SettingsLibraryController : SettingsController() { } .launchIn(viewScope) } - switchPreference { - key = Keys.jumpToChapters - titleRes = R.string.pref_jump_to_chapters - defaultValue = false + if (!context.isTablet()) { + switchPreference { + key = Keys.jumpToChapters + titleRes = R.string.pref_jump_to_chapters + defaultValue = false + } } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/util/chapter/ChapterSorter.kt b/app/src/main/java/eu/kanade/tachiyomi/util/chapter/ChapterSorter.kt new file mode 100644 index 000000000..bce8fc6cb --- /dev/null +++ b/app/src/main/java/eu/kanade/tachiyomi/util/chapter/ChapterSorter.kt @@ -0,0 +1,22 @@ +package eu.kanade.tachiyomi.util.chapter + +import eu.kanade.tachiyomi.data.database.models.Chapter +import eu.kanade.tachiyomi.data.database.models.Manga + +fun getChapterSort(manga: Manga, sortDescending: Boolean = manga.sortDescending()): (Chapter, Chapter) -> Int { + return 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) } + } + 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 -> throw NotImplementedError("Unimplemented sorting method") + } +} diff --git a/app/src/main/java/eu/kanade/tachiyomi/util/system/ContextExtensions.kt b/app/src/main/java/eu/kanade/tachiyomi/util/system/ContextExtensions.kt index 3344e9817..0cb74bb9a 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/util/system/ContextExtensions.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/util/system/ContextExtensions.kt @@ -258,3 +258,10 @@ fun Context.createFileInCacheDir(name: String): File { file.createNewFile() return file } + +/** + * We consider anything with a width of >= 600dp as a tablet, i.e. with layouts in layout-sw600dp. + */ +fun Context.isTablet(): Boolean { + return resources.configuration.screenWidthDp >= 600 +} diff --git a/app/src/main/res/drawable/ic_discord_24dp.xml b/app/src/main/res/drawable/ic_discord_24dp.xml index 2b1a1354d..4524f21c1 100644 --- a/app/src/main/res/drawable/ic_discord_24dp.xml +++ b/app/src/main/res/drawable/ic_discord_24dp.xml @@ -1,9 +1,9 @@ + android:viewportWidth="24" + android:viewportHeight="24"> + android:pathData="M20.349,4.389a0.06,0.06 0,0 0,-0.032 -0.029,19.77 19.77,0 0,0 -4.885,-1.515 0.075,0.075 0,0 0,-0.079 0.037c-0.209,0.375 -0.444,0.865 -0.607,1.249a18.35,18.35 0,0 0,-5.487 0,12.583 12.583,0 0,0 -0.617,-1.249 0.078,0.078 0,0 0,-0.079 -0.037,19.746 19.746,0 0,0 -4.886,1.515 0.06,0.06 0,0 0,-0.031 0.028c-3.111,4.648 -3.964,9.183 -3.545,13.66a0.082,0.082 0,0 0,0.031 0.057,19.912 19.912,0 0,0 5.993,3.03 0.075,0.075 0,0 0,0.084 -0.028c0.461,-0.631 0.873,-1.296 1.226,-1.994a0.076,0.076 0,0 0,-0.042 -0.106,13.172 13.172,0 0,1 -1.872,-0.892 0.077,0.077 0,0 1,-0.007 -0.128c0.125,-0.094 0.251,-0.192 0.371,-0.292a0.078,0.078 0,0 1,0.078 -0.011c3.928,1.794 8.179,1.794 12.062,0a0.073,0.073 0,0 1,0.077 0.01c0.122,0.099 0.247,0.199 0.373,0.293a0.078,0.078 0,0 1,-0.007 0.128c-0.597,0.349 -1.22,0.645 -1.873,0.892a0.075,0.075 0,0 0,-0.04 0.106c0.359,0.697 0.771,1.362 1.225,1.993a0.077,0.077 0,0 0,0.085 0.029,19.842 19.842,0 0,0 6.003,-3.03 0.077,0.077 0,0 0,0.03 -0.056c0.499,-5.177 -0.839,-9.674 -3.549,-13.66zM8.02,15.322c-1.183,0 -2.157,-1.087 -2.157,-2.419 0,-1.334 0.956,-2.42 2.157,-2.42 1.21,0 2.176,1.095 2.157,2.42 0,1.332 -0.955,2.419 -2.157,2.419zM15.995,15.322c-1.184,0 -2.157,-1.087 -2.157,-2.419 0,-1.334 0.955,-2.42 2.157,-2.42 1.211,0 2.175,1.095 2.156,2.42 0,1.332 -0.945,2.419 -2.156,2.419z" /> diff --git a/app/src/main/res/layout-sw600dp/main_activity.xml b/app/src/main/res/layout-sw600dp/main_activity.xml index ce8f96392..f0a4a89c7 100644 --- a/app/src/main/res/layout-sw600dp/main_activity.xml +++ b/app/src/main/res/layout-sw600dp/main_activity.xml @@ -76,7 +76,7 @@ android:layout_width="wrap_content" android:layout_height="0dp" app:itemIconTint="@color/nav_selector" - app:itemRippleColor="?attr/rippleNavColor" + app:itemRippleColor="?attr/rippleSecondaryColor" app:itemTextColor="@color/nav_selector" app:labelVisibilityMode="labeled" app:layout_constraintBottom_toBottomOf="parent" diff --git a/app/src/main/res/layout-sw600dp/manga_controller.xml b/app/src/main/res/layout-sw600dp/manga_controller.xml new file mode 100644 index 000000000..8582e0c05 --- /dev/null +++ b/app/src/main/res/layout-sw600dp/manga_controller.xml @@ -0,0 +1,78 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/extension_controller.xml b/app/src/main/res/layout/extension_controller.xml index c6dd1c376..0db6a3024 100644 --- a/app/src/main/res/layout/extension_controller.xml +++ b/app/src/main/res/layout/extension_controller.xml @@ -13,7 +13,7 @@ + android:layout_height="match_parent" /> diff --git a/app/src/main/res/layout/source_main_controller.xml b/app/src/main/res/layout/source_main_controller.xml index fee14a4c9..64af2528c 100644 --- a/app/src/main/res/layout/source_main_controller.xml +++ b/app/src/main/res/layout/source_main_controller.xml @@ -8,7 +8,7 @@ - + + diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml index 1b768fa1f..4c43322e9 100644 --- a/app/src/main/res/values/colors.xml +++ b/app/src/main/res/values/colors.xml @@ -19,7 +19,8 @@ @color/md_black_1000_38 @color/md_black_1000_12 @color/md_black_1000_6 - @color/md_blue_A400_4 + @color/md_blue_A400_4 + @color/rippleColorLight @color/md_grey_50 @color/md_white_1000 @color/md_blue_A400_38 @@ -31,7 +32,8 @@ @color/md_white_1000_50 @android:color/transparent @color/md_white_1000_6 - #0A3399FF + #0A3399FF + @color/rippleColorDark @color/colorDarkPrimaryDark @color/colorDarkPrimary @color/md_blue_A200_50 diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index bc68d7a39..bd879ac96 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -175,6 +175,7 @@ Date format Confirm exit Hide bottom bar on scroll + Move side navigation buttons to bottom Manage notifications Security diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index 07351f3ee..2f7d92a3e 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -6,12 +6,16 @@ - + + @@ -348,7 +352,7 @@ 75dp scrollable @style/TextAppearance.Widget.Tab - ?attr/rippleNavColor + ?attr/rippleSecondaryColor diff --git a/app/src/main/res/values/themes.xml b/app/src/main/res/values/themes.xml index bd47b387a..a4586996e 100644 --- a/app/src/main/res/values/themes.xml +++ b/app/src/main/res/values/themes.xml @@ -36,13 +36,16 @@ @color/textColorPrimaryDark @color/textColorSecondaryDark @color/textColorHintDark - @color/rippleColorLight - @color/rippleNavColorLight ?attr/colorAccent @color/dividerLight @drawable/line_divider + + @color/rippleColorLight + @color/rippleSecondaryColorLight + @color/rippleToolbarColorLight + gravity locale @@ -57,6 +60,7 @@ @style/Theme.ActionMode @style/Theme.ActionMode.CloseButton @drawable/ic_close_24dp + @style/Theme.Toolbar.Custom @style/ThemeOverlay.MaterialComponents @style/Theme.Toolbar.Navigation @style/PreferenceThemeCustom @@ -104,7 +108,8 @@ @color/textColorPrimaryDark @color/colorPrimaryDark @color/filterColorDark - @color/md_white_1000_6 + @color/md_white_1000_6 + @color/md_white_1000_12 @style/Theme.Toolbar.Light false false @@ -138,13 +143,16 @@ @color/textColorPrimaryLight @color/textColorSecondaryLight @color/textColorHintLight - @color/rippleNavColorDark - @color/rippleColorDark ?attr/colorAccent @color/dividerDark @drawable/line_divider + + @color/rippleColorDark + @color/rippleSecondaryColorDark + @color/rippleToolbarColorDark + ?attr/colorPrimary ?attr/colorPrimary @@ -155,7 +163,7 @@ @style/Theme.ActionMode @style/Theme.ActionMode.CloseButton @drawable/ic_close_24dp - @style/Theme.Toolbar.Custom + @style/Theme.Toolbar.Custom.Dark @style/ThemeOverlay.MaterialComponents @style/Theme.Toolbar.Navigation @style/PreferenceThemeCustom @@ -194,8 +202,8 @@ @color/colorPrimary @color/textColorPrimaryDark @color/colorPrimary - - @color/md_white_1000_6 + @color/md_black_1000_6 + @color/md_black_1000_12