mirror of
https://github.com/tachiyomiorg/tachiyomi.git
synced 2024-11-12 21:35:18 +01:00
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:
parent
2d10680fd9
commit
0c66d3b8b6
@ -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)
|
||||||
|
|
||||||
|
@ -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 -> {
|
||||||
|
@ -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) {
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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!!)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -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,35 +158,33 @@ 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)
|
) {
|
||||||
) {
|
f2.second.date_upload.compareTo(f1.second.date_upload)
|
||||||
f2.second.date_upload.compareTo(f1.second.date_upload)
|
} else {
|
||||||
} else {
|
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,36 +203,43 @@ 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
|
.compareTo(d1)
|
||||||
.compareTo(d1)
|
}
|
||||||
|
val byDay =
|
||||||
|
pairs.groupByTo(map, { getMapKey(it.first.history.last_read) })
|
||||||
|
byDay.flatMap {
|
||||||
|
val dateItem = DateItem(it.key, true)
|
||||||
|
it.value.map { item ->
|
||||||
|
RecentMangaItem(item.first, item.second, dateItem)
|
||||||
}
|
}
|
||||||
val byDay =
|
}
|
||||||
pairs.groupByTo(map, { getMapKey(it.first.history.last_read) })
|
} else pairs.map { RecentMangaItem(it.first, it.second, null) }
|
||||||
byDay.flatMap {
|
}
|
||||||
val dateItem = DateItem(it.key, true)
|
recentItems = if (page == 0) {
|
||||||
it.value.map { item ->
|
newItems
|
||||||
RecentMangaItem(item.first, item.second, dateItem)
|
} else {
|
||||||
}
|
recentItems + newItems
|
||||||
}
|
}
|
||||||
} else pairs.map { RecentMangaItem(it.first, it.second, null) }
|
val newCount = itemCount + newItems.size
|
||||||
if (isUngrouped && recentItems.isEmpty()) {
|
val hasNewItems = newItems.isNotEmpty()
|
||||||
recentItems = listOf(
|
if (newCount < 25 && viewType != VIEW_TYPE_GROUP_ALL && query.isEmpty()) {
|
||||||
RecentMangaItem(header = newChaptersHeader),
|
page++
|
||||||
RecentMangaItem(header = continueReadingHeader)
|
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) {
|
||||||
preferences.recentsViewType().set(pref)
|
if (updatePref) {
|
||||||
|
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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
4
app/src/main/res/color/accent_alpha.xml
Normal file
4
app/src/main/res/color/accent_alpha.xml
Normal 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>
|
10
app/src/main/res/color/tabs_selector_alt.xml
Normal file
10
app/src/main/res/color/tabs_selector_alt.xml
Normal 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>
|
12
app/src/main/res/drawable/tab_highlight_indicator.xml
Normal file
12
app/src/main/res/drawable/tab_highlight_indicator.xml
Normal 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>
|
@ -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>
|
@ -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>
|
||||||
|
@ -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-->
|
||||||
<!--==============-->
|
<!--==============-->
|
||||||
|
Loading…
Reference in New Issue
Block a user