UI Revamp of Recents

*Now Endless scrolling in ungrouped/history/updates
*Removed going to history/updates controller in grouped (will later be removed entirely)
*Always showing a header to switch modes, instead of using a filter button (which will later be replaced
This commit is contained in:
Jays2Kings 2021-04-06 17:06:22 -04:00
parent 2d10680fd9
commit 0c66d3b8b6
12 changed files with 260 additions and 109 deletions

View File

@ -283,7 +283,7 @@ class PreferencesHelper(val context: Context) {
fun extensionUpdatesCount() = rxPrefs.getInteger("ext_updates_count", 0) fun extensionUpdatesCount() = rxPrefs.getInteger("ext_updates_count", 0)
fun recentsViewType() = rxPrefs.getInteger("recents_view_type", 0) fun recentsViewType() = flowPrefs.getInt("recents_view_type", 0)
fun lastExtCheck() = rxPrefs.getLong("last_ext_check", 0) fun lastExtCheck() = rxPrefs.getLong("last_ext_check", 0)

View File

@ -52,9 +52,8 @@ import eu.kanade.tachiyomi.ui.base.controller.BaseController
import eu.kanade.tachiyomi.ui.base.controller.DialogController import eu.kanade.tachiyomi.ui.base.controller.DialogController
import eu.kanade.tachiyomi.ui.library.LibraryController import eu.kanade.tachiyomi.ui.library.LibraryController
import eu.kanade.tachiyomi.ui.manga.MangaDetailsController import eu.kanade.tachiyomi.ui.manga.MangaDetailsController
import eu.kanade.tachiyomi.ui.recent_updates.RecentChaptersController
import eu.kanade.tachiyomi.ui.recently_read.RecentlyReadController
import eu.kanade.tachiyomi.ui.recents.RecentsController import eu.kanade.tachiyomi.ui.recents.RecentsController
import eu.kanade.tachiyomi.ui.recents.RecentsPresenter
import eu.kanade.tachiyomi.ui.security.SecureActivityDelegate import eu.kanade.tachiyomi.ui.security.SecureActivityDelegate
import eu.kanade.tachiyomi.ui.setting.AboutController import eu.kanade.tachiyomi.ui.setting.AboutController
import eu.kanade.tachiyomi.ui.setting.SettingsController import eu.kanade.tachiyomi.ui.setting.SettingsController
@ -473,11 +472,11 @@ open class MainActivity : BaseActivity<MainActivityBinding>(), DownloadServiceLi
SHORTCUT_LIBRARY -> binding.bottomNav.selectedItemId = R.id.nav_library SHORTCUT_LIBRARY -> binding.bottomNav.selectedItemId = R.id.nav_library
SHORTCUT_RECENTLY_UPDATED, SHORTCUT_RECENTLY_READ -> { SHORTCUT_RECENTLY_UPDATED, SHORTCUT_RECENTLY_READ -> {
binding.bottomNav.selectedItemId = R.id.nav_recents binding.bottomNav.selectedItemId = R.id.nav_recents
val controller: Controller = when (intent.action) { val viewType = when (intent.action) {
SHORTCUT_RECENTLY_UPDATED -> RecentChaptersController() SHORTCUT_RECENTLY_UPDATED -> RecentsPresenter.VIEW_TYPE_ONLY_UPDATES
else -> RecentlyReadController() else -> RecentsPresenter.VIEW_TYPE_ONLY_HISTORY
} }
router.pushController(controller.withFadeTransaction()) router.pushController(RecentsController(viewType).withFadeTransaction())
} }
SHORTCUT_BROWSE -> binding.bottomNav.selectedItemId = R.id.nav_browse SHORTCUT_BROWSE -> binding.bottomNav.selectedItemId = R.id.nav_browse
SHORTCUT_EXTENSIONS -> { SHORTCUT_EXTENSIONS -> {

View File

@ -29,8 +29,8 @@ class RecentMangaAdapter(val delegate: RecentsInterface) :
fun onCoverClick(position: Int) fun onCoverClick(position: Int)
fun markAsRead(position: Int) fun markAsRead(position: Int)
fun isSearching(): Boolean fun isSearching(): Boolean
fun showHistory() fun setViewType(viewType: Int)
fun showUpdates() fun getViewType(): Int
} }
override fun onItemSwiped(position: Int, direction: Int) { override fun onItemSwiped(position: Int, direction: Int) {

View File

@ -2,6 +2,7 @@ package eu.kanade.tachiyomi.ui.recents
import android.view.View import android.view.View
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import com.google.android.material.tabs.TabLayout
import eu.davidea.flexibleadapter.FlexibleAdapter import eu.davidea.flexibleadapter.FlexibleAdapter
import eu.davidea.flexibleadapter.items.AbstractHeaderItem import eu.davidea.flexibleadapter.items.AbstractHeaderItem
import eu.davidea.flexibleadapter.items.IFlexible import eu.davidea.flexibleadapter.items.IFlexible
@ -62,8 +63,19 @@ class RecentMangaHeaderItem(val recentsType: Int) :
private val binding = RecentsHeaderItemBinding.bind(view) private val binding = RecentsHeaderItemBinding.bind(view)
init { init {
binding.actionHistory.setOnClickListener { adapter.delegate.showHistory() } listOf(R.string.grouped, R.string.all, R.string.history, R.string.updates).forEach { resId ->
binding.actionUpdate.setOnClickListener { adapter.delegate.showUpdates() } binding.recentsTabs.addTab(binding.recentsTabs.newTab().setText(resId))
}
val selectedTab = (this@Holder.bindingAdapter as? RecentMangaAdapter)?.delegate?.getViewType() ?: 0
binding.recentsTabs.selectTab(binding.recentsTabs.getTabAt(selectedTab))
binding.recentsTabs.addOnTabSelectedListener(object : TabLayout.OnTabSelectedListener {
override fun onTabSelected(tab: TabLayout.Tab?) {
(this@Holder.bindingAdapter as? RecentMangaAdapter)?.delegate?.setViewType(tab?.position ?: 0)
}
override fun onTabUnselected(tab: TabLayout.Tab?) { }
override fun onTabReselected(tab: TabLayout.Tab?) { }
})
} }
fun bind(recentsType: Int) { fun bind(recentsType: Int) {
@ -75,8 +87,9 @@ class RecentMangaHeaderItem(val recentsType: Int) :
else -> R.string.continue_reading else -> R.string.continue_reading
} }
) )
binding.actionHistory.visibleIf(recentsType == -1) binding.recentsTabs.visibleIf(recentsType == -1)
binding.actionUpdate.visibleIf(recentsType == -1) val selectedTab = (this@Holder.bindingAdapter as? RecentMangaAdapter)?.delegate?.getViewType() ?: 0
binding.recentsTabs.selectTab(binding.recentsTabs.getTabAt(selectedTab))
binding.title.visibleIf(recentsType != -1) binding.title.visibleIf(recentsType != -1)
} }
} }

View File

@ -14,7 +14,6 @@ import androidx.appcompat.widget.SearchView
import androidx.recyclerview.widget.ItemTouchHelper import androidx.recyclerview.widget.ItemTouchHelper
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import com.bluelinelabs.conductor.Controller
import com.bluelinelabs.conductor.ControllerChangeHandler import com.bluelinelabs.conductor.ControllerChangeHandler
import com.bluelinelabs.conductor.ControllerChangeType import com.bluelinelabs.conductor.ControllerChangeType
import com.google.android.material.bottomsheet.BottomSheetBehavior import com.google.android.material.bottomsheet.BottomSheetBehavior
@ -22,6 +21,7 @@ import com.google.android.material.snackbar.BaseTransientBottomBar
import com.google.android.material.snackbar.Snackbar import com.google.android.material.snackbar.Snackbar
import eu.davidea.flexibleadapter.FlexibleAdapter import eu.davidea.flexibleadapter.FlexibleAdapter
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.backup.BackupRestoreService
import eu.kanade.tachiyomi.data.database.models.History import eu.kanade.tachiyomi.data.database.models.History
import eu.kanade.tachiyomi.data.database.models.Manga import eu.kanade.tachiyomi.data.database.models.Manga
import eu.kanade.tachiyomi.data.download.DownloadService import eu.kanade.tachiyomi.data.download.DownloadService
@ -34,9 +34,8 @@ import eu.kanade.tachiyomi.ui.main.MainActivity
import eu.kanade.tachiyomi.ui.main.RootSearchInterface import eu.kanade.tachiyomi.ui.main.RootSearchInterface
import eu.kanade.tachiyomi.ui.manga.MangaDetailsController import eu.kanade.tachiyomi.ui.manga.MangaDetailsController
import eu.kanade.tachiyomi.ui.reader.ReaderActivity import eu.kanade.tachiyomi.ui.reader.ReaderActivity
import eu.kanade.tachiyomi.ui.recent_updates.RecentChaptersController
import eu.kanade.tachiyomi.ui.recently_read.RecentlyReadController
import eu.kanade.tachiyomi.ui.recently_read.RemoveHistoryDialog import eu.kanade.tachiyomi.ui.recently_read.RemoveHistoryDialog
import eu.kanade.tachiyomi.ui.source.browse.ProgressItem
import eu.kanade.tachiyomi.util.system.dpToPx import eu.kanade.tachiyomi.util.system.dpToPx
import eu.kanade.tachiyomi.util.system.spToPx import eu.kanade.tachiyomi.util.system.spToPx
import eu.kanade.tachiyomi.util.system.toast import eu.kanade.tachiyomi.util.system.toast
@ -65,6 +64,7 @@ class RecentsController(bundle: Bundle? = null) :
FlexibleAdapter.OnItemClickListener, FlexibleAdapter.OnItemClickListener,
FlexibleAdapter.OnItemLongClickListener, FlexibleAdapter.OnItemLongClickListener,
FlexibleAdapter.OnItemMoveListener, FlexibleAdapter.OnItemMoveListener,
FlexibleAdapter.EndlessScrollListener,
RootSearchInterface, RootSearchInterface,
BottomSheetController, BottomSheetController,
RemoveHistoryDialog.Listener { RemoveHistoryDialog.Listener {
@ -74,11 +74,16 @@ class RecentsController(bundle: Bundle? = null) :
retainViewMode = RetainViewMode.RETAIN_DETACH retainViewMode = RetainViewMode.RETAIN_DETACH
} }
constructor(viewType: Int) : this() {
presenter.toggleGroupRecents(viewType, false)
}
/** /**
* Adapter containing the recent manga. * Adapter containing the recent manga.
*/ */
private var adapter = RecentMangaAdapter(this) private var adapter = RecentMangaAdapter(this)
private var progressItem: ProgressItem? = null
private var presenter = RecentsPresenter(this) private var presenter = RecentsPresenter(this)
private var snack: Snackbar? = null private var snack: Snackbar? = null
private var lastChapterId: Long? = null private var lastChapterId: Long? = null
@ -113,6 +118,7 @@ class RecentsController(bundle: Bundle? = null) :
adapter.itemTouchHelperCallback.setSwipeFlags( adapter.itemTouchHelperCallback.setSwipeFlags(
ItemTouchHelper.LEFT ItemTouchHelper.LEFT
) )
resetProgressItem()
val attrsArray = intArrayOf(android.R.attr.actionBarSize) val attrsArray = intArrayOf(android.R.attr.actionBarSize)
val array = view.context.obtainStyledAttributes(attrsArray) val array = view.context.obtainStyledAttributes(attrsArray)
val appBarHeight = array.getDimensionPixelSize(0, 0) val appBarHeight = array.getDimensionPixelSize(0, 0)
@ -257,6 +263,10 @@ class RecentsController(bundle: Bundle? = null) :
binding.downloadBottomSheet.dlBottomSheet.dismiss() binding.downloadBottomSheet.dlBottomSheet.dismiss()
return true return true
} }
if (presenter.preferences.recentsViewType().get() != presenter.viewType) {
presenter.toggleGroupRecents(RecentsPresenter.VIEW_TYPE_GROUP_ALL, false)
return true
}
return false return false
} }
@ -284,14 +294,29 @@ class RecentsController(bundle: Bundle? = null) :
fun refresh() = presenter.getRecents() fun refresh() = presenter.getRecents()
fun showLists(recents: List<RecentMangaItem>) { fun showLists(recents: List<RecentMangaItem>, hasNewItems: Boolean, shouldMoveToTop: Boolean = false) {
if (view == null) return if (view == null) return
binding.swipeRefresh.isRefreshing = LibraryUpdateService.isRunning() binding.swipeRefresh.isRefreshing = LibraryUpdateService.isRunning()
adapter.updateItems(recents) adapter.updateItems(recents)
adapter.removeAllScrollableHeaders() adapter.headerItems.forEach {
if (presenter.viewType > 0) { if (it != presenter.generalHeader) {
adapter.removeScrollableHeader(it)
}
}
if (!adapter.headerItems.any { it === presenter.generalHeader }) {
adapter.addScrollableHeader(presenter.generalHeader) adapter.addScrollableHeader(presenter.generalHeader)
} }
adapter.onLoadMoreComplete(null)
if (!hasNewItems || presenter.viewType == RecentsPresenter.VIEW_TYPE_GROUP_ALL || presenter.query.isNotEmpty() ||
recents.isEmpty()
) {
onAddPageError()
} else if (hasNewItems && presenter.viewType != RecentsPresenter.VIEW_TYPE_GROUP_ALL && presenter.query.isEmpty()) {
resetProgressItem()
}
if (shouldMoveToTop) {
binding.recycler.scrollToPosition(0)
}
if (lastChapterId != null) { if (lastChapterId != null) {
refreshItem(lastChapterId ?: 0L) refreshItem(lastChapterId ?: 0L)
lastChapterId = null lastChapterId = null
@ -339,20 +364,32 @@ class RecentsController(bundle: Bundle? = null) :
router.pushController(MangaDetailsController(manga).withFadeTransaction()) router.pushController(MangaDetailsController(manga).withFadeTransaction())
} }
override fun showHistory() = router.pushController(RecentlyReadController().withFadeTransaction()) fun showHistory() {
override fun showUpdates() = router.pushController(RecentChaptersController().withFadeTransaction()) presenter.toggleGroupRecents(RecentsPresenter.VIEW_TYPE_ONLY_HISTORY, false)
}
fun showUpdates() {
presenter.toggleGroupRecents(RecentsPresenter.VIEW_TYPE_ONLY_UPDATES, false)
}
override fun setViewType(viewType: Int) {
if (viewType != presenter.viewType) {
presenter.toggleGroupRecents(viewType)
}
}
override fun getViewType() = presenter.viewType
override fun onItemClick(view: View?, position: Int): Boolean { override fun onItemClick(view: View?, position: Int): Boolean {
val item = adapter.getItem(position) ?: return false val item = adapter.getItem(position) ?: return false
if (item is RecentMangaItem) { if (item is RecentMangaItem) {
if (item.mch.manga.id == null) { if (item.mch.manga.id == null) {
val headerItem = adapter.getHeaderOf(item) as? RecentMangaHeaderItem val headerItem = adapter.getHeaderOf(item) as? RecentMangaHeaderItem
val controller: Controller = when (headerItem?.recentsType) { when (headerItem?.recentsType) {
RecentMangaHeaderItem.NEW_CHAPTERS -> RecentChaptersController() RecentMangaHeaderItem.NEW_CHAPTERS -> showUpdates()
RecentMangaHeaderItem.CONTINUE_READING -> RecentlyReadController() RecentMangaHeaderItem.CONTINUE_READING -> showHistory()
else -> return false else -> return false
} }
router.pushController(controller.withFadeTransaction())
} else { } else {
val activity = activity ?: return false val activity = activity ?: return false
val intent = ReaderActivity.newIntent(activity, item.mch.manga, item.chapter) val intent = ReaderActivity.newIntent(activity, item.mch.manga, item.chapter)
@ -440,6 +477,7 @@ class RecentsController(bundle: Bundle? = null) :
setOnQueryTextChangeListener(searchView) { setOnQueryTextChangeListener(searchView) {
if (presenter.query != it) { if (presenter.query != it) {
presenter.query = it ?: return@setOnQueryTextChangeListener false presenter.query = it ?: return@setOnQueryTextChangeListener false
resetProgressItem()
refresh() refresh()
} }
true true
@ -515,4 +553,31 @@ class RecentsController(bundle: Bundle? = null) :
} }
return super.onOptionsItemSelected(item) return super.onOptionsItemSelected(item)
} }
override fun noMoreLoad(newItemsSize: Int) {}
override fun onLoadMore(lastPosition: Int, currentPage: Int) {
val view = view ?: return
if (presenter.finished || BackupRestoreService.isRunning(view.context.applicationContext)) {
onAddPageError()
return
}
presenter.requestNext()
}
private fun onAddPageError() {
adapter.onLoadMoreComplete(null)
adapter.endlessTargetCount = 0
adapter.setEndlessScrollListener(null, progressItem!!)
adapter.setEndlessProgressItem(null)
}
/**
* Sets a new progress item and reenables the scroll listener.
*/
private fun resetProgressItem() {
progressItem = ProgressItem()
adapter.endlessTargetCount = 0
adapter.setEndlessScrollListener(this, progressItem!!)
}
} }

View File

@ -12,7 +12,6 @@ import eu.kanade.tachiyomi.data.download.model.DownloadQueue
import eu.kanade.tachiyomi.data.library.LibraryServiceListener import eu.kanade.tachiyomi.data.library.LibraryServiceListener
import eu.kanade.tachiyomi.data.library.LibraryUpdateService 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.data.preference.getOrDefault
import eu.kanade.tachiyomi.source.SourceManager import eu.kanade.tachiyomi.source.SourceManager
import eu.kanade.tachiyomi.ui.recent_updates.DateItem import eu.kanade.tachiyomi.ui.recent_updates.DateItem
import eu.kanade.tachiyomi.util.system.executeOnIO import eu.kanade.tachiyomi.util.system.executeOnIO
@ -42,6 +41,10 @@ class RecentsPresenter(
var recentItems = listOf<RecentMangaItem>() var recentItems = listOf<RecentMangaItem>()
private set private set
var query = "" var query = ""
set(value) {
field = value
page = 0
}
private val newAdditionsHeader = RecentMangaHeaderItem(RecentMangaHeaderItem.NEWLY_ADDED) private val newAdditionsHeader = RecentMangaHeaderItem(RecentMangaHeaderItem.NEWLY_ADDED)
private val newChaptersHeader = RecentMangaHeaderItem(RecentMangaHeaderItem.NEW_CHAPTERS) private val newChaptersHeader = RecentMangaHeaderItem(RecentMangaHeaderItem.NEW_CHAPTERS)
val generalHeader = RecentMangaHeaderItem(-1) val generalHeader = RecentMangaHeaderItem(-1)
@ -49,7 +52,17 @@ class RecentsPresenter(
RecentMangaHeaderItem RecentMangaHeaderItem
.CONTINUE_READING .CONTINUE_READING
) )
var viewType: Int = preferences.recentsViewType().getOrDefault() var finished = false
var shouldMoveToTop = false
var viewType: Int = preferences.recentsViewType().get()
private var page = 0
set(value) {
field = value
if (value == 0) {
finished = false
shouldMoveToTop = true
}
}
fun onCreate() { fun onCreate() {
downloadManager.addListener(this) downloadManager.addListener(this)
@ -63,25 +76,44 @@ class RecentsPresenter(
getRecents() getRecents()
} }
fun getRecents() { fun getRecents(updatePageCount: Boolean = false, retryCount: Int = 0, itemCount: Int = 0) {
val oldQuery = query val oldQuery = query
scope.launch { scope.launch {
val isUngrouped = viewType > 0 && query.isEmpty() if (retryCount > 20) {
// groupRecents && query.isEmpty() finished = true
setDownloadedChapters(recentItems)
withContext(Dispatchers.Main) { controller.showLists(recentItems, false) }
}
if (updatePageCount) {
page++
}
val isUngrouped = viewType > VIEW_TYPE_GROUP_ALL && query.isEmpty()
val cal = Calendar.getInstance().apply { val cal = Calendar.getInstance().apply {
time = Date() time = Date()
when { when {
query.isNotEmpty() -> add(Calendar.YEAR, -50) query.isNotEmpty() -> add(Calendar.YEAR, -50)
isUngrouped -> add(Calendar.MONTH, -1) isUngrouped -> add(Calendar.MONTH, -(page + 1))
else -> add(Calendar.MONTH, -1) else -> add(Calendar.MONTH, -1)
} }
} }
val startCal = Calendar.getInstance().apply {
time = Date()
when {
query.isNotEmpty() -> {}
isUngrouped && !updatePageCount -> {}
isUngrouped -> add(Calendar.MONTH, -page)
else -> {}
}
}
val calWeek = Calendar.getInstance().apply { val calWeek = Calendar.getInstance().apply {
time = Date() time = Date()
when { when {
query.isNotEmpty() -> add(Calendar.YEAR, -50) query.isNotEmpty() -> add(Calendar.YEAR, -50)
isUngrouped -> add(Calendar.MONTH, -1) isUngrouped -> add(Calendar.MONTH, -(page + 1))
else -> add(Calendar.WEEK_OF_YEAR, -1) else -> add(Calendar.WEEK_OF_YEAR, -1)
} }
} }
@ -95,27 +127,29 @@ class RecentsPresenter(
} }
} }
val cReading = if (viewType != 3) { val cReading = if (viewType != VIEW_TYPE_ONLY_UPDATES) {
if (query.isEmpty() && viewType != 2) { if (query.isEmpty() && viewType != VIEW_TYPE_ONLY_HISTORY) {
db.getRecentsWithUnread(cal.time, query, isUngrouped).executeOnIO() db.getRecentsWithUnread(startCal.time, cal.time, query, isUngrouped)
.executeOnIO()
} else db.getRecentMangaLimit( } else db.getRecentMangaLimit(
startCal.time,
cal.time, cal.time,
if (viewType == 2) 200 else 8, if (viewType == VIEW_TYPE_ONLY_HISTORY) 200 else 8,
query query
).executeOnIO() ).executeOnIO()
} else emptyList() } else emptyList()
val rUpdates = when { val rUpdates = when {
viewType == 3 -> db.getRecentChapters(calWeek.time).executeOnIO().map { viewType == VIEW_TYPE_ONLY_UPDATES -> db.getRecentChapters(startCal.time, calWeek.time).executeOnIO().map {
MangaChapterHistory(it.manga, it.chapter, HistoryImpl()) MangaChapterHistory(it.manga, it.chapter, HistoryImpl())
} }
viewType != 2 -> db.getUpdatedManga(calWeek.time, query, isUngrouped).executeOnIO() viewType != VIEW_TYPE_ONLY_HISTORY -> db.getUpdatedManga(startCal.time, calWeek.time, query, isUngrouped).executeOnIO()
else -> emptyList() else -> emptyList()
} }
rUpdates.forEach { rUpdates.forEach {
it.history.last_read = it.chapter.date_fetch it.history.last_read = it.chapter.date_fetch
} }
val nAdditions = if (viewType < 2) { val nAdditions = if (viewType < VIEW_TYPE_ONLY_HISTORY) {
db.getRecentlyAdded(calDay.time, query, isUngrouped).executeOnIO() db.getRecentlyAdded(startCal.time, calDay.time, query, isUngrouped).executeOnIO()
} else emptyList() } else emptyList()
nAdditions.forEach { nAdditions.forEach {
it.history.last_read = it.manga.date_added it.history.last_read = it.manga.date_added
@ -124,26 +158,25 @@ class RecentsPresenter(
val mangaList = (cReading + rUpdates + nAdditions).sortedByDescending { val mangaList = (cReading + rUpdates + nAdditions).sortedByDescending {
it.history.last_read it.history.last_read
}.distinctBy { }.distinctBy {
if (query.isEmpty() && viewType != 3) it.manga.id else it.chapter.id if (query.isEmpty() && viewType != VIEW_TYPE_ONLY_HISTORY) it.manga.id else it.chapter.id
} }
val pairs = mangaList.mapNotNull { val pairs = mangaList.mapNotNull {
val chapter = when { val chapter = when {
viewType == 3 -> it.chapter viewType == VIEW_TYPE_ONLY_HISTORY -> it.chapter
it.chapter.read || it.chapter.id == null -> getNextChapter(it.manga) it.chapter.read || it.chapter.id == null -> getNextChapter(it.manga)
it.history.id == null -> getFirstUpdatedChapter(it.manga, it.chapter) it.history.id == null -> getFirstUpdatedChapter(it.manga, it.chapter)
else -> it.chapter else -> it.chapter
} }
if (chapter == null) if ((query.isNotEmpty() || viewType > 1) && if (chapter == null) if ((query.isNotEmpty() || viewType > VIEW_TYPE_UNGROUP_ALL) &&
it.chapter.id != null it.chapter.id != null
) Pair(it, it.chapter) ) Pair(it, it.chapter)
else null else null
else Pair(it, chapter) else Pair(it, chapter)
} }
if (query.isEmpty() && !isUngrouped) { val newItems = if (query.isEmpty() && !isUngrouped) {
val nChaptersItems = val nChaptersItems =
pairs.filter { it.first.history.id == null && it.first.chapter.id != null } pairs.filter { it.first.history.id == null && it.first.chapter.id != null }
.sortedWith( .sortedWith { f1, f2 ->
Comparator<Pair<MangaChapterHistory, Chapter>> { f1, f2 ->
if (abs(f1.second.date_fetch - f2.second.date_fetch) <= if (abs(f1.second.date_fetch - f2.second.date_fetch) <=
TimeUnit.HOURS.toMillis(12) TimeUnit.HOURS.toMillis(12)
) { ) {
@ -152,7 +185,6 @@ class RecentsPresenter(
f2.second.date_fetch.compareTo(f1.second.date_fetch) f2.second.date_fetch.compareTo(f1.second.date_fetch)
} }
} }
)
.take(4).map { .take(4).map {
RecentMangaItem( RecentMangaItem(
it.first, it.first,
@ -171,13 +203,11 @@ class RecentsPresenter(
} + RecentMangaItem(header = continueReadingHeader) } + RecentMangaItem(header = continueReadingHeader)
val nAdditionsItems = pairs.filter { it.first.chapter.id == null }.take(4) val nAdditionsItems = pairs.filter { it.first.chapter.id == null }.take(4)
.map { RecentMangaItem(it.first, it.second, newAdditionsHeader) } .map { RecentMangaItem(it.first, it.second, newAdditionsHeader) }
recentItems =
listOf(nChaptersItems, cReadingItems, nAdditionsItems).sortedByDescending { listOf(nChaptersItems, cReadingItems, nAdditionsItems).sortedByDescending {
it.firstOrNull()?.mch?.history?.last_read ?: 0L it.firstOrNull()?.mch?.history?.last_read ?: 0L
}.flatten() }.flatten()
} else { } else {
recentItems = if (viewType == VIEW_TYPE_ONLY_UPDATES) {
if (viewType == 3) {
val map = TreeMap<Date, MutableList<Pair<MangaChapterHistory, Chapter>>> { val map = TreeMap<Date, MutableList<Pair<MangaChapterHistory, Chapter>>> {
d1, d2 -> d1, d2 ->
d2 d2
@ -192,15 +222,24 @@ class RecentsPresenter(
} }
} }
} else pairs.map { RecentMangaItem(it.first, it.second, null) } } else pairs.map { RecentMangaItem(it.first, it.second, null) }
if (isUngrouped && recentItems.isEmpty()) {
recentItems = listOf(
RecentMangaItem(header = newChaptersHeader),
RecentMangaItem(header = continueReadingHeader)
)
} }
recentItems = if (page == 0) {
newItems
} else {
recentItems + newItems
}
val newCount = itemCount + newItems.size
val hasNewItems = newItems.isNotEmpty()
if (newCount < 25 && viewType != VIEW_TYPE_GROUP_ALL && query.isEmpty()) {
page++
getRecents(true, retryCount + (if (hasNewItems) 0 else 1), newCount)
return@launch
} }
setDownloadedChapters(recentItems) setDownloadedChapters(recentItems)
withContext(Dispatchers.Main) { controller.showLists(recentItems) } withContext(Dispatchers.Main) {
controller.showLists(recentItems, hasNewItems, shouldMoveToTop)
shouldMoveToTop = false
}
} }
} }
@ -226,9 +265,12 @@ class RecentsPresenter(
scope.cancel() scope.cancel()
} }
fun toggleGroupRecents(pref: Int) { fun toggleGroupRecents(pref: Int, updatePref: Boolean = true) {
if (updatePref) {
preferences.recentsViewType().set(pref) preferences.recentsViewType().set(pref)
}
viewType = pref viewType = pref
page = 0
getRecents() getRecents()
} }
@ -259,7 +301,7 @@ class RecentsPresenter(
scope.launch { scope.launch {
setDownloadedChapters(recentItems) setDownloadedChapters(recentItems)
withContext(Dispatchers.Main) { withContext(Dispatchers.Main) {
controller.showLists(recentItems) controller.showLists(recentItems, true)
} }
} }
} }
@ -289,7 +331,7 @@ class RecentsPresenter(
download = null download = null
} }
controller.showLists(recentItems) controller.showLists(recentItems, true)
} }
} }
@ -367,7 +409,16 @@ class RecentsPresenter(
getRecents() getRecents()
} }
private companion object { fun requestNext() {
var lastRecents: List<RecentMangaItem>? = null getRecents(true)
}
companion object {
private var lastRecents: List<RecentMangaItem>? = null
const val VIEW_TYPE_GROUP_ALL = 0
const val VIEW_TYPE_UNGROUP_ALL = 1
const val VIEW_TYPE_ONLY_HISTORY = 2
const val VIEW_TYPE_ONLY_UPDATES = 3
} }
} }

View File

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:alpha="0.80" android:color="?attr/colorAccent" />
</selector>

View File

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Based on mtrl_tabs_icon_color_selector_colored.
Ensures visibility on top of the background color.
-->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:color="@color/md_white_1000" android:state_selected="true"/>
<item android:alpha="0.60" android:color="?attr/colorOnBackground"/>
</selector>

View File

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:end="2dp"
android:start="2dp" >
<shape>
<corners android:radius="4dp" />
<solid android:color="@color/accent_alpha" />
<size android:height="32sp"/>
</shape>
</item>
</layer-list>

View File

@ -21,30 +21,17 @@
app:layout_constraintHorizontal_bias="0.0" app:layout_constraintHorizontal_bias="0.0"
tools:text="@string/recent_updates" /> tools:text="@string/recent_updates" />
<com.google.android.material.button.MaterialButton <com.google.android.material.tabs.TabLayout
android:id="@+id/action_history" android:id="@+id/recents_tabs"
android:visibility="gone" style="@style/Theme.Widget.Tabs.Highlight"
android:layout_width="wrap_content" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
app:icon="@drawable/ic_history_24dp" android:layout_marginStart="6dp"
android:layout_marginStart="12dp" android:layout_marginEnd="6dp"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
android:text="@string/history"
style="@style/Theme.Widget.Button.RoundedOutline"/>
<com.google.android.material.button.MaterialButton
android:id="@+id/action_update"
android:layout_width="wrap_content"
android:visibility="gone"
android:layout_height="wrap_content"
app:icon="@drawable/ic_update_24dp"
android:layout_marginEnd="12dp"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
android:text="@string/updates" app:tabIndicatorColor="?attr/colorAccent"
style="@style/Theme.Widget.Button.RoundedOutline"/> app:tabGravity="fill"/>
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -224,6 +224,7 @@
<string name="no_recent_chapters">No recent chapters</string> <string name="no_recent_chapters">No recent chapters</string>
<string name="no_recently_read_manga">No recently read manga</string> <string name="no_recently_read_manga">No recently read manga</string>
<string name="group_all">Group all</string> <string name="group_all">Group all</string>
<string name="grouped">Grouped</string>
<string name="ungroup_all">Ungroup all</string> <string name="ungroup_all">Ungroup all</string>
<string name="only_history">Only history</string> <string name="only_history">Only history</string>
<string name="only_updates">Only updates</string> <string name="only_updates">Only updates</string>

View File

@ -214,13 +214,22 @@
<item name="tabTextColor">@color/tabs_selector</item> <item name="tabTextColor">@color/tabs_selector</item>
<item name="tabRippleColor">@color/rippleColor</item> <item name="tabRippleColor">@color/rippleColor</item>
<item name="tabIndicatorFullWidth">false</item> <item name="tabIndicatorFullWidth">false</item>
<item name="tabIndicatorHeight">3dp</item>
<item name="tabInlineLabel">true</item> <item name="tabInlineLabel">true</item>
<item name="tabMinWidth">75dp</item> <item name="tabMinWidth">75dp</item>
<item name="tabMode">scrollable</item> <item name="tabMode">scrollable</item>
<item name="tabTextAppearance">@style/TextAppearance.Widget.Tab</item> <item name="tabTextAppearance">@style/TextAppearance.Widget.Tab</item>
</style> </style>
<style name="Theme.Widget.Tabs.Highlight" parent="Theme.Widget.Tabs">
<item name="android:background">@android:color/transparent</item>
<item name="tabIndicatorGravity">center</item>
<item name="tabTextColor">@color/tabs_selector_alt</item>
<item name="tabIndicator">@drawable/tab_highlight_indicator</item>
<item name="tabMaxWidth">0dp</item>
<item name="tabMode">fixed</item>
<item name="tabIndicatorFullWidth">true</item>
</style>
<!--==============--> <!--==============-->
<!--Widgets.Button--> <!--Widgets.Button-->
<!--==============--> <!--==============-->