mirror of
https://github.com/tachiyomiorg/tachiyomi.git
synced 2025-01-11 11:19:09 +01:00
Cleanup History/Updates Controller
This commit is contained in:
parent
f264a69626
commit
53c04c7084
@ -1,110 +0,0 @@
|
|||||||
package eu.kanade.tachiyomi.ui.recent_updates
|
|
||||||
|
|
||||||
import android.app.Activity
|
|
||||||
import android.view.View
|
|
||||||
import androidx.core.content.ContextCompat
|
|
||||||
import coil.api.clear
|
|
||||||
import coil.transform.CircleCropTransformation
|
|
||||||
import eu.kanade.tachiyomi.R
|
|
||||||
import eu.kanade.tachiyomi.data.image.coil.loadLibraryManga
|
|
||||||
import eu.kanade.tachiyomi.databinding.RecentChaptersItemBinding
|
|
||||||
import eu.kanade.tachiyomi.ui.manga.chapter.BaseChapterHolder
|
|
||||||
import eu.kanade.tachiyomi.util.chapter.ChapterUtil
|
|
||||||
import eu.kanade.tachiyomi.util.system.getResourceColor
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Holder that contains chapter item
|
|
||||||
* Uses R.layout.item_recent_chapters.
|
|
||||||
* UI related actions should be called from here.
|
|
||||||
*
|
|
||||||
* @param view the inflated view for this holder.
|
|
||||||
* @param adapter the adapter handling this holder.
|
|
||||||
* @param listener a listener to react to single tap and long tap events.
|
|
||||||
* @constructor creates a new recent chapter holder.
|
|
||||||
*/
|
|
||||||
class RecentChapterHolder(private val view: View, private val adapter: RecentChaptersAdapter) :
|
|
||||||
BaseChapterHolder(view, adapter) {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Color of read chapter
|
|
||||||
*/
|
|
||||||
private var readColor = view.context.getResourceColor(android.R.attr.textColorHint)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Color of unread chapter
|
|
||||||
*/
|
|
||||||
private var unreadColor = view.context.getResourceColor(android.R.attr.textColorPrimary)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Currently bound item.
|
|
||||||
*/
|
|
||||||
private var item: RecentChapterItem? = null
|
|
||||||
|
|
||||||
private val binding = RecentChaptersItemBinding.bind(view)
|
|
||||||
init {
|
|
||||||
binding.mangaCover.setOnClickListener {
|
|
||||||
adapter.coverClickListener.onCoverClick(flexibleAdapterPosition)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set values of view
|
|
||||||
*
|
|
||||||
* @param item item containing chapter information
|
|
||||||
*/
|
|
||||||
fun bind(item: RecentChapterItem) {
|
|
||||||
this.item = item
|
|
||||||
|
|
||||||
// Set chapter binding.title
|
|
||||||
binding.chapterTitle.text = item.chapter.name
|
|
||||||
|
|
||||||
// Set manga binding.title
|
|
||||||
binding.title.text = item.manga.title
|
|
||||||
|
|
||||||
if (binding.frontView.translationX == 0f) {
|
|
||||||
binding.read.setImageDrawable(
|
|
||||||
ContextCompat.getDrawable(
|
|
||||||
binding.root.context,
|
|
||||||
if (item.read) R.drawable.ic_eye_off_24dp
|
|
||||||
else R.drawable.ic_eye_24dp
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set cover
|
|
||||||
if ((view.context as? Activity)?.isDestroyed != true) {
|
|
||||||
binding.mangaCover.clear()
|
|
||||||
binding.mangaCover.loadLibraryManga(item.manga) {
|
|
||||||
transformations(CircleCropTransformation())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
val chapterColor = ChapterUtil.chapterColor(itemView.context, item)
|
|
||||||
binding.chapterTitle.setTextColor(chapterColor)
|
|
||||||
binding.title.setTextColor(chapterColor)
|
|
||||||
|
|
||||||
// Set chapter status
|
|
||||||
notifyStatus(item.status, item.progress)
|
|
||||||
resetFrontView()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun resetFrontView() {
|
|
||||||
if (binding.frontView.translationX != 0f) itemView.post { adapter.notifyItemChanged(flexibleAdapterPosition) }
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun getFrontView(): View {
|
|
||||||
return binding.frontView
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun getRearRightView(): View {
|
|
||||||
return binding.rightView
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Updates chapter status in view.
|
|
||||||
*
|
|
||||||
* @param status download status
|
|
||||||
*/
|
|
||||||
fun notifyStatus(status: Int, progress: Int) =
|
|
||||||
binding.downloadButton.root.setDownloadStatus(status, progress)
|
|
||||||
}
|
|
@ -1,36 +0,0 @@
|
|||||||
package eu.kanade.tachiyomi.ui.recent_updates
|
|
||||||
|
|
||||||
import android.view.View
|
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
|
||||||
import eu.davidea.flexibleadapter.FlexibleAdapter
|
|
||||||
import eu.davidea.flexibleadapter.items.IFlexible
|
|
||||||
import eu.kanade.tachiyomi.R
|
|
||||||
import eu.kanade.tachiyomi.data.database.models.Chapter
|
|
||||||
import eu.kanade.tachiyomi.data.database.models.Manga
|
|
||||||
import eu.kanade.tachiyomi.ui.manga.chapter.BaseChapterItem
|
|
||||||
|
|
||||||
class RecentChapterItem(chapter: Chapter, val manga: Manga, header: DateItem) :
|
|
||||||
BaseChapterItem<RecentChapterHolder, DateItem>(chapter, header) {
|
|
||||||
|
|
||||||
override fun getLayoutRes(): Int {
|
|
||||||
return R.layout.recent_chapters_item
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun createViewHolder(view: View, adapter: FlexibleAdapter<IFlexible<RecyclerView.ViewHolder>>): RecentChapterHolder {
|
|
||||||
return RecentChapterHolder(view, adapter as RecentChaptersAdapter)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun bindViewHolder(
|
|
||||||
adapter: FlexibleAdapter<IFlexible<RecyclerView.ViewHolder>>,
|
|
||||||
holder: RecentChapterHolder,
|
|
||||||
position: Int,
|
|
||||||
payloads: MutableList<Any?>?
|
|
||||||
) {
|
|
||||||
holder.bind(this)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun filter(text: String): Boolean {
|
|
||||||
return chapter.name.contains(text, false) ||
|
|
||||||
manga.title.contains(text, false)
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,42 +0,0 @@
|
|||||||
package eu.kanade.tachiyomi.ui.recent_updates
|
|
||||||
|
|
||||||
import androidx.recyclerview.widget.ItemTouchHelper
|
|
||||||
import eu.davidea.flexibleadapter.items.IFlexible
|
|
||||||
import eu.kanade.tachiyomi.ui.manga.chapter.BaseChapterAdapter
|
|
||||||
|
|
||||||
class RecentChaptersAdapter(val controller: RecentChaptersController) :
|
|
||||||
BaseChapterAdapter<IFlexible<*>>(controller) {
|
|
||||||
|
|
||||||
val coverClickListener: OnCoverClickListener = controller
|
|
||||||
var recents = emptyList<RecentChapterItem>()
|
|
||||||
|
|
||||||
init {
|
|
||||||
setDisplayHeadersAtStartUp(true)
|
|
||||||
// setStickyHeaders(true)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun setItems(recents: List<RecentChapterItem>) {
|
|
||||||
this.recents = recents
|
|
||||||
performFilter()
|
|
||||||
}
|
|
||||||
|
|
||||||
fun performFilter() {
|
|
||||||
val s = getFilter(String::class.java)
|
|
||||||
if (s.isNullOrBlank()) {
|
|
||||||
updateDataSet(recents)
|
|
||||||
} else {
|
|
||||||
updateDataSet(recents.filter { it.filter(s) })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
interface OnCoverClickListener {
|
|
||||||
fun onCoverClick(position: Int)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onItemSwiped(position: Int, direction: Int) {
|
|
||||||
super.onItemSwiped(position, direction)
|
|
||||||
when (direction) {
|
|
||||||
ItemTouchHelper.LEFT -> controller.toggleMarkAsRead(position)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,265 +0,0 @@
|
|||||||
package eu.kanade.tachiyomi.ui.recent_updates
|
|
||||||
|
|
||||||
import android.app.Activity
|
|
||||||
import android.os.Bundle
|
|
||||||
import android.view.LayoutInflater
|
|
||||||
import android.view.View
|
|
||||||
import androidx.recyclerview.widget.DividerItemDecoration
|
|
||||||
import androidx.recyclerview.widget.ItemTouchHelper
|
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager
|
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
|
||||||
import com.google.android.material.snackbar.BaseTransientBottomBar
|
|
||||||
import com.google.android.material.snackbar.Snackbar
|
|
||||||
import eu.davidea.flexibleadapter.FlexibleAdapter
|
|
||||||
import eu.kanade.tachiyomi.R
|
|
||||||
import eu.kanade.tachiyomi.data.download.DownloadService
|
|
||||||
import eu.kanade.tachiyomi.data.download.model.Download
|
|
||||||
import eu.kanade.tachiyomi.data.library.LibraryUpdateService
|
|
||||||
import eu.kanade.tachiyomi.data.notification.Notifications
|
|
||||||
import eu.kanade.tachiyomi.databinding.RecentChaptersControllerBinding
|
|
||||||
import eu.kanade.tachiyomi.ui.base.controller.BaseController
|
|
||||||
import eu.kanade.tachiyomi.ui.main.MainActivity
|
|
||||||
import eu.kanade.tachiyomi.ui.manga.MangaDetailsController
|
|
||||||
import eu.kanade.tachiyomi.ui.manga.chapter.BaseChapterAdapter
|
|
||||||
import eu.kanade.tachiyomi.ui.reader.ReaderActivity
|
|
||||||
import eu.kanade.tachiyomi.util.system.notificationManager
|
|
||||||
import eu.kanade.tachiyomi.util.view.scrollViewWith
|
|
||||||
import eu.kanade.tachiyomi.util.view.setStyle
|
|
||||||
import eu.kanade.tachiyomi.util.view.snack
|
|
||||||
import eu.kanade.tachiyomi.util.view.withFadeTransaction
|
|
||||||
import timber.log.Timber
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Fragment that shows recent chapters.
|
|
||||||
* Uses [R.layout.recent_chapters_controller].
|
|
||||||
* UI related actions should be called from here.
|
|
||||||
*/
|
|
||||||
class RecentChaptersController(bundle: Bundle? = null) :
|
|
||||||
BaseController<RecentChaptersControllerBinding>(bundle),
|
|
||||||
FlexibleAdapter.OnItemClickListener,
|
|
||||||
FlexibleAdapter.OnUpdateListener,
|
|
||||||
FlexibleAdapter.OnItemMoveListener,
|
|
||||||
RecentChaptersAdapter.OnCoverClickListener,
|
|
||||||
BaseChapterAdapter.DownloadInterface {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Adapter containing the recent chapters.
|
|
||||||
*/
|
|
||||||
var adapter: RecentChaptersAdapter? = null
|
|
||||||
private set
|
|
||||||
|
|
||||||
private var presenter = RecentChaptersPresenter(this)
|
|
||||||
private var snack: Snackbar? = null
|
|
||||||
private var lastChapterId: Long? = null
|
|
||||||
|
|
||||||
override fun getTitle(): String? {
|
|
||||||
return resources?.getString(R.string.recent_updates)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun createBinding(inflater: LayoutInflater) = RecentChaptersControllerBinding.inflate(inflater)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called when view is created
|
|
||||||
* @param view created view
|
|
||||||
*/
|
|
||||||
override fun onViewCreated(view: View) {
|
|
||||||
super.onViewCreated(view)
|
|
||||||
// view.applyWindowInsetsForController()
|
|
||||||
|
|
||||||
view.context.notificationManager.cancel(Notifications.ID_NEW_CHAPTERS)
|
|
||||||
// Init RecyclerView and adapter
|
|
||||||
val layoutManager = LinearLayoutManager(view.context)
|
|
||||||
binding.recycler.layoutManager = layoutManager
|
|
||||||
binding.recycler.addItemDecoration(DividerItemDecoration(view.context, DividerItemDecoration.VERTICAL))
|
|
||||||
binding.recycler.setHasFixedSize(true)
|
|
||||||
adapter = RecentChaptersAdapter(this@RecentChaptersController)
|
|
||||||
binding.recycler.adapter = adapter
|
|
||||||
|
|
||||||
adapter?.isSwipeEnabled = true
|
|
||||||
adapter?.itemTouchHelperCallback?.setSwipeFlags(
|
|
||||||
ItemTouchHelper.LEFT
|
|
||||||
)
|
|
||||||
if (presenter.chapters.isNotEmpty()) adapter?.updateDataSet(presenter.chapters.toList())
|
|
||||||
binding.swipeRefresh.setStyle()
|
|
||||||
binding.swipeRefresh.setDistanceToTriggerSync((2 * 64 * view.resources.displayMetrics.density).toInt())
|
|
||||||
binding.swipeRefresh.setOnRefreshListener {
|
|
||||||
if (!LibraryUpdateService.isRunning()) {
|
|
||||||
LibraryUpdateService.start(view.context)
|
|
||||||
snack = view.snack(R.string.updating_library)
|
|
||||||
}
|
|
||||||
// It can be a very long operation, so we disable swipe refresh and show a snackbar.
|
|
||||||
binding.swipeRefresh.isRefreshing = false
|
|
||||||
}
|
|
||||||
|
|
||||||
scrollViewWith(binding.recycler, swipeRefreshLayout = binding.swipeRefresh, padBottom = true)
|
|
||||||
|
|
||||||
presenter.onCreate()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onDestroy() {
|
|
||||||
super.onDestroy()
|
|
||||||
presenter.onDestroy()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onDestroyView(view: View) {
|
|
||||||
adapter = null
|
|
||||||
snack = null
|
|
||||||
super.onDestroyView(view)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onActivityResumed(activity: Activity) {
|
|
||||||
super.onActivityResumed(activity)
|
|
||||||
if (view != null) {
|
|
||||||
refresh()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun refresh() = presenter.getUpdates()
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called when item in list is clicked
|
|
||||||
* @param position position of clicked item
|
|
||||||
*/
|
|
||||||
override fun onItemClick(view: View?, position: Int): Boolean {
|
|
||||||
val adapter = adapter ?: return false
|
|
||||||
|
|
||||||
// Get item from position
|
|
||||||
val item = adapter.getItem(position) as? RecentChapterItem ?: return false
|
|
||||||
openChapter(item)
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Open chapter in reader
|
|
||||||
* @param chapter selected chapter
|
|
||||||
*/
|
|
||||||
private fun openChapter(item: RecentChapterItem) {
|
|
||||||
val activity = activity ?: return
|
|
||||||
val intent = ReaderActivity.newIntent(activity, item.manga, item.chapter)
|
|
||||||
startActivity(intent)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Populate adapter with chapters
|
|
||||||
* @param chapters list of [Any]
|
|
||||||
*/
|
|
||||||
fun onNextRecentChapters(chapters: List<RecentChapterItem>) {
|
|
||||||
adapter?.setItems(chapters)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun updateChapterDownload(download: Download) {
|
|
||||||
if (view == null) return
|
|
||||||
val id = download.chapter.id ?: return
|
|
||||||
val holder = binding.recycler.findViewHolderForItemId(id) as? RecentChapterHolder ?: return
|
|
||||||
holder.notifyStatus(download.status, download.progress)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onUpdateEmptyView(size: Int) {
|
|
||||||
if (size > 0) {
|
|
||||||
binding.emptyView.hide()
|
|
||||||
} else {
|
|
||||||
binding.emptyView.show(R.drawable.ic_update_24dp, R.string.no_recent_chapters)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onItemMove(fromPosition: Int, toPosition: Int) { }
|
|
||||||
override fun shouldMoveItem(fromPosition: Int, toPosition: Int) = true
|
|
||||||
|
|
||||||
override fun onActionStateChanged(viewHolder: RecyclerView.ViewHolder?, actionState: Int) {
|
|
||||||
binding.swipeRefresh.isEnabled = actionState != ItemTouchHelper.ACTION_STATE_SWIPE
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Update download status of chapter
|
|
||||||
* @param download [Download] object containing download progress.
|
|
||||||
*/
|
|
||||||
fun onChapterStatusChange(download: Download) {
|
|
||||||
getHolder(download)?.notifyStatus(download.status, download.progress)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns holder belonging to chapter
|
|
||||||
* @param download [Download] object containing download progress.
|
|
||||||
*/
|
|
||||||
private fun getHolder(download: Download): RecentChapterHolder? {
|
|
||||||
return binding.recycler.findViewHolderForItemId(download.chapter.id!!) as? RecentChapterHolder
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Mark chapter as read
|
|
||||||
* @param position position of chapter item
|
|
||||||
*/
|
|
||||||
fun toggleMarkAsRead(position: Int) {
|
|
||||||
val item = adapter?.getItem(position) as? RecentChapterItem ?: return
|
|
||||||
val chapter = item.chapter
|
|
||||||
val lastRead = chapter.last_page_read
|
|
||||||
val pagesLeft = chapter.pages_left
|
|
||||||
val read = item.chapter.read
|
|
||||||
lastChapterId = chapter.id
|
|
||||||
presenter.markChapterRead(item, !read)
|
|
||||||
if (!read) {
|
|
||||||
snack = view?.snack(R.string.marked_as_read, Snackbar.LENGTH_INDEFINITE) {
|
|
||||||
var undoing = false
|
|
||||||
setAction(R.string.undo) {
|
|
||||||
presenter.markChapterRead(item, read, lastRead, pagesLeft)
|
|
||||||
undoing = true
|
|
||||||
}
|
|
||||||
addCallback(
|
|
||||||
object : BaseTransientBottomBar.BaseCallback<Snackbar>() {
|
|
||||||
override fun onDismissed(transientBottomBar: Snackbar?, event: Int) {
|
|
||||||
super.onDismissed(transientBottomBar, event)
|
|
||||||
if (!undoing && presenter.preferences.removeAfterMarkedAsRead()) {
|
|
||||||
lastChapterId = chapter.id
|
|
||||||
presenter.deleteChapter(chapter, item.manga)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
(activity as? MainActivity)?.setUndoSnackBar(snack)
|
|
||||||
}
|
|
||||||
// presenter.markChapterRead(item, !item.chapter.read)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun downloadChapter(position: Int) {
|
|
||||||
val view = view ?: return
|
|
||||||
val item = adapter?.getItem(position) as? RecentChapterItem ?: return
|
|
||||||
val chapter = item.chapter
|
|
||||||
val manga = item.manga
|
|
||||||
if (item.status != Download.NOT_DOWNLOADED && item.status != Download.ERROR) {
|
|
||||||
presenter.deleteChapter(chapter, manga)
|
|
||||||
} else {
|
|
||||||
if (item.status == Download.ERROR) DownloadService.start(view.context)
|
|
||||||
else presenter.downloadChapters(listOf(item))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun startDownloadNow(position: Int) {
|
|
||||||
val chapter = (adapter?.getItem(position) as? RecentChapterItem)?.chapter ?: return
|
|
||||||
presenter.startDownloadChapterNow(chapter)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onCoverClick(position: Int) {
|
|
||||||
val chapterClicked = adapter?.getItem(position) as? RecentChapterItem ?: return
|
|
||||||
openManga(chapterClicked)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun openManga(chapter: RecentChapterItem) {
|
|
||||||
router.pushController(MangaDetailsController(chapter.manga).withFadeTransaction())
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called when chapters are deleted
|
|
||||||
*/
|
|
||||||
fun onChaptersDeleted() {
|
|
||||||
adapter?.notifyDataSetChanged()
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called when error while deleting
|
|
||||||
* @param error error message
|
|
||||||
*/
|
|
||||||
fun onChaptersDeletedError(error: Throwable) {
|
|
||||||
Timber.e(error)
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,183 +0,0 @@
|
|||||||
package eu.kanade.tachiyomi.ui.recent_updates
|
|
||||||
|
|
||||||
import eu.kanade.tachiyomi.data.database.DatabaseHelper
|
|
||||||
import eu.kanade.tachiyomi.data.database.models.Chapter
|
|
||||||
import eu.kanade.tachiyomi.data.database.models.Manga
|
|
||||||
import eu.kanade.tachiyomi.data.database.models.MangaChapter
|
|
||||||
import eu.kanade.tachiyomi.data.download.DownloadManager
|
|
||||||
import eu.kanade.tachiyomi.data.download.model.Download
|
|
||||||
import eu.kanade.tachiyomi.data.download.model.DownloadQueue
|
|
||||||
import eu.kanade.tachiyomi.data.library.LibraryServiceListener
|
|
||||||
import eu.kanade.tachiyomi.data.library.LibraryUpdateService
|
|
||||||
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
|
||||||
import eu.kanade.tachiyomi.source.SourceManager
|
|
||||||
import eu.kanade.tachiyomi.util.system.executeOnIO
|
|
||||||
import kotlinx.coroutines.CoroutineScope
|
|
||||||
import kotlinx.coroutines.Dispatchers
|
|
||||||
import kotlinx.coroutines.Job
|
|
||||||
import kotlinx.coroutines.cancel
|
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
import kotlinx.coroutines.withContext
|
|
||||||
import uy.kohesive.injekt.Injekt
|
|
||||||
import uy.kohesive.injekt.api.get
|
|
||||||
import java.util.Calendar
|
|
||||||
import java.util.Date
|
|
||||||
import java.util.TreeMap
|
|
||||||
|
|
||||||
class RecentChaptersPresenter(
|
|
||||||
private val controller: RecentChaptersController,
|
|
||||||
val preferences: PreferencesHelper = Injekt.get(),
|
|
||||||
private val db: DatabaseHelper = Injekt.get(),
|
|
||||||
private val downloadManager: DownloadManager = Injekt.get(),
|
|
||||||
private val sourceManager: SourceManager = Injekt.get()
|
|
||||||
) : DownloadQueue.DownloadListener, LibraryServiceListener {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* List containing chapter and manga information
|
|
||||||
*/
|
|
||||||
var chapters: List<RecentChapterItem> = emptyList()
|
|
||||||
|
|
||||||
private var scope = CoroutineScope(Job() + Dispatchers.Default)
|
|
||||||
|
|
||||||
fun onCreate() {
|
|
||||||
downloadManager.addListener(this)
|
|
||||||
LibraryUpdateService.setListener(this)
|
|
||||||
getUpdates()
|
|
||||||
}
|
|
||||||
|
|
||||||
fun getUpdates() {
|
|
||||||
scope.launch {
|
|
||||||
val cal = Calendar.getInstance().apply {
|
|
||||||
time = Date()
|
|
||||||
add(Calendar.MONTH, -1)
|
|
||||||
}
|
|
||||||
val mangaChapters = db.getRecentChapters(cal.time).executeOnIO()
|
|
||||||
val map = TreeMap<Date, MutableList<MangaChapter>> { d1, d2 -> d2.compareTo(d1) }
|
|
||||||
val byDay = mangaChapters.groupByTo(map, { getMapKey(it.chapter.date_fetch) })
|
|
||||||
val items = byDay.flatMap {
|
|
||||||
val dateItem = DateItem(it.key)
|
|
||||||
it.value.map { mc ->
|
|
||||||
RecentChapterItem(mc.chapter, mc.manga, dateItem)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
setDownloadedChapters(items)
|
|
||||||
chapters = items
|
|
||||||
withContext(Dispatchers.Main) { controller.onNextRecentChapters(chapters) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun onDestroy() {
|
|
||||||
downloadManager.removeListener(this)
|
|
||||||
LibraryUpdateService.removeListener(this)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun cancelScope() {
|
|
||||||
scope.cancel()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun updateDownload(download: Download) {
|
|
||||||
chapters.find { it.chapter.id == download.chapter.id }?.download = download
|
|
||||||
scope.launch(Dispatchers.Main) {
|
|
||||||
controller.updateChapterDownload(download)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun updateDownloads() {
|
|
||||||
scope.launch {
|
|
||||||
setDownloadedChapters(chapters)
|
|
||||||
withContext(Dispatchers.Main) {
|
|
||||||
controller.onNextRecentChapters(chapters)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onUpdateManga(manga: Manga?) {
|
|
||||||
getUpdates()
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get date as time key
|
|
||||||
*
|
|
||||||
* @param date desired date
|
|
||||||
* @return date as time key
|
|
||||||
*/
|
|
||||||
private fun getMapKey(date: Long): Date {
|
|
||||||
val cal = Calendar.getInstance()
|
|
||||||
cal.time = Date(date)
|
|
||||||
cal[Calendar.HOUR_OF_DAY] = 0
|
|
||||||
cal[Calendar.MINUTE] = 0
|
|
||||||
cal[Calendar.SECOND] = 0
|
|
||||||
cal[Calendar.MILLISECOND] = 0
|
|
||||||
return cal.time
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Finds and assigns the list of downloaded chapters.
|
|
||||||
*
|
|
||||||
* @param chapters the list of chapter from the database.
|
|
||||||
*/
|
|
||||||
private fun setDownloadedChapters(chapters: List<RecentChapterItem>) {
|
|
||||||
for (item in chapters) {
|
|
||||||
if (downloadManager.isChapterDownloaded(item.chapter, item.manga)) {
|
|
||||||
item.status = Download.DOWNLOADED
|
|
||||||
} else if (downloadManager.hasQueue()) {
|
|
||||||
item.status = downloadManager.queue.find { it.chapter.id == item.chapter.id }
|
|
||||||
?.status ?: 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Mark selected chapter as read
|
|
||||||
*
|
|
||||||
* @param items list of selected chapters
|
|
||||||
* @param read read status
|
|
||||||
*/
|
|
||||||
fun markChapterRead(
|
|
||||||
item: RecentChapterItem,
|
|
||||||
read: Boolean,
|
|
||||||
lastRead: Int? = null,
|
|
||||||
pagesLeft: Int? = null
|
|
||||||
) {
|
|
||||||
item.chapter.apply {
|
|
||||||
this.read = read
|
|
||||||
if (!read) {
|
|
||||||
last_page_read = lastRead ?: 0
|
|
||||||
pages_left = pagesLeft ?: 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
db.updateChapterProgress(item.chapter).executeAsBlocking()
|
|
||||||
controller.onNextRecentChapters(this.chapters)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun startDownloadChapterNow(chapter: Chapter) {
|
|
||||||
downloadManager.startDownloadNow(chapter)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Deletes the given list of chapter.
|
|
||||||
* @param chapter the chapter to delete.
|
|
||||||
*/
|
|
||||||
fun deleteChapter(chapter: Chapter, manga: Manga, update: Boolean = true) {
|
|
||||||
val source = Injekt.get<SourceManager>().getOrStub(manga.source)
|
|
||||||
downloadManager.deleteChapters(listOf(chapter), manga, source)
|
|
||||||
|
|
||||||
if (update) {
|
|
||||||
val item = chapters.find { it.chapter.id == chapter.id } ?: return
|
|
||||||
item.apply {
|
|
||||||
status = Download.NOT_DOWNLOADED
|
|
||||||
download = null
|
|
||||||
}
|
|
||||||
|
|
||||||
controller.onNextRecentChapters(chapters)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Download selected chapters
|
|
||||||
* @param items list of recent chapters seleted.
|
|
||||||
*/
|
|
||||||
fun downloadChapters(items: List<RecentChapterItem>) {
|
|
||||||
items.forEach { downloadManager.downloadChapters(it.manga, listOf(it.chapter)) }
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,57 +0,0 @@
|
|||||||
package eu.kanade.tachiyomi.ui.recently_read
|
|
||||||
|
|
||||||
import eu.davidea.flexibleadapter.FlexibleAdapter
|
|
||||||
import eu.davidea.flexibleadapter.items.IFlexible
|
|
||||||
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
|
||||||
import eu.kanade.tachiyomi.source.SourceManager
|
|
||||||
import uy.kohesive.injekt.injectLazy
|
|
||||||
import java.text.DateFormat
|
|
||||||
import java.text.DecimalFormat
|
|
||||||
import java.text.DecimalFormatSymbols
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Adapter of RecentlyReadHolder.
|
|
||||||
* Connection between Fragment and Holder
|
|
||||||
* Holder updates should be called from here.
|
|
||||||
*
|
|
||||||
* @param controller a RecentlyReadController object
|
|
||||||
* @constructor creates an instance of the adapter.
|
|
||||||
*/
|
|
||||||
class RecentlyReadAdapter(controller: RecentlyReadController) :
|
|
||||||
FlexibleAdapter<IFlexible<*>>(null, controller, true) {
|
|
||||||
|
|
||||||
val sourceManager by injectLazy<SourceManager>()
|
|
||||||
|
|
||||||
val resumeClickListener: OnResumeClickListener = controller
|
|
||||||
|
|
||||||
val removeClickListener: OnRemoveClickListener = controller
|
|
||||||
|
|
||||||
val coverClickListener: OnCoverClickListener = controller
|
|
||||||
|
|
||||||
/**
|
|
||||||
* DecimalFormat used to display correct chapter number
|
|
||||||
*/
|
|
||||||
val decimalFormat = DecimalFormat(
|
|
||||||
"#.###",
|
|
||||||
DecimalFormatSymbols()
|
|
||||||
.apply { decimalSeparator = '.' }
|
|
||||||
)
|
|
||||||
|
|
||||||
private val preferences: PreferencesHelper by injectLazy()
|
|
||||||
|
|
||||||
val dateFormat: DateFormat by lazy {
|
|
||||||
preferences.dateFormat()
|
|
||||||
}
|
|
||||||
|
|
||||||
interface OnResumeClickListener {
|
|
||||||
fun onResumeClick(position: Int)
|
|
||||||
}
|
|
||||||
|
|
||||||
interface OnRemoveClickListener {
|
|
||||||
fun onRemoveClick(position: Int)
|
|
||||||
}
|
|
||||||
|
|
||||||
interface OnCoverClickListener {
|
|
||||||
fun onCoverClick(position: Int)
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,244 +0,0 @@
|
|||||||
package eu.kanade.tachiyomi.ui.recently_read
|
|
||||||
|
|
||||||
import android.app.Activity
|
|
||||||
import android.os.Bundle
|
|
||||||
import android.view.LayoutInflater
|
|
||||||
import android.view.Menu
|
|
||||||
import android.view.MenuInflater
|
|
||||||
import android.view.MenuItem
|
|
||||||
import android.view.View
|
|
||||||
import androidx.appcompat.widget.SearchView
|
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager
|
|
||||||
import eu.davidea.flexibleadapter.FlexibleAdapter
|
|
||||||
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.Manga
|
|
||||||
import eu.kanade.tachiyomi.databinding.RecentlyReadControllerBinding
|
|
||||||
import eu.kanade.tachiyomi.ui.base.controller.BaseController
|
|
||||||
import eu.kanade.tachiyomi.ui.manga.MangaDetailsController
|
|
||||||
import eu.kanade.tachiyomi.ui.reader.ReaderActivity
|
|
||||||
import eu.kanade.tachiyomi.ui.source.browse.ProgressItem
|
|
||||||
import eu.kanade.tachiyomi.util.system.launchUI
|
|
||||||
import eu.kanade.tachiyomi.util.system.toast
|
|
||||||
import eu.kanade.tachiyomi.util.view.RecyclerWindowInsetsListener
|
|
||||||
import eu.kanade.tachiyomi.util.view.scrollViewWith
|
|
||||||
import eu.kanade.tachiyomi.util.view.setOnQueryTextChangeListener
|
|
||||||
import eu.kanade.tachiyomi.util.view.withFadeTransaction
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Fragment that shows recently read manga.
|
|
||||||
* Uses R.layout.fragment_recently_read.
|
|
||||||
* UI related actions should be called from here.
|
|
||||||
*/
|
|
||||||
class RecentlyReadController(bundle: Bundle? = null) :
|
|
||||||
BaseController<RecentlyReadControllerBinding>(bundle),
|
|
||||||
FlexibleAdapter.OnUpdateListener,
|
|
||||||
FlexibleAdapter.EndlessScrollListener,
|
|
||||||
RecentlyReadAdapter.OnRemoveClickListener,
|
|
||||||
RecentlyReadAdapter.OnResumeClickListener,
|
|
||||||
RecentlyReadAdapter.OnCoverClickListener,
|
|
||||||
RemoveHistoryDialog.Listener {
|
|
||||||
|
|
||||||
init {
|
|
||||||
setHasOptionsMenu(true)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Adapter containing the recent manga.
|
|
||||||
*/
|
|
||||||
var adapter: RecentlyReadAdapter? = null
|
|
||||||
private set
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Endless loading item.
|
|
||||||
*/
|
|
||||||
private var progressItem: ProgressItem? = null
|
|
||||||
private var observeLater: Boolean = false
|
|
||||||
private var query = ""
|
|
||||||
|
|
||||||
private var presenter = RecentlyReadPresenter(this)
|
|
||||||
private var recentItems: MutableList<RecentlyReadItem>? = null
|
|
||||||
|
|
||||||
override fun getTitle(): String? {
|
|
||||||
return resources?.getString(R.string.history)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun createBinding(inflater: LayoutInflater) = RecentlyReadControllerBinding.inflate(inflater)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called when view is created
|
|
||||||
*
|
|
||||||
* @param view created view
|
|
||||||
*/
|
|
||||||
override fun onViewCreated(view: View) {
|
|
||||||
super.onViewCreated(view)
|
|
||||||
// view.applyWindowInsetsForController()
|
|
||||||
// Initialize adapter
|
|
||||||
adapter = RecentlyReadAdapter(this)
|
|
||||||
binding.recycler.adapter = adapter
|
|
||||||
binding.recycler.layoutManager = LinearLayoutManager(view.context)
|
|
||||||
binding.recycler.setHasFixedSize(true)
|
|
||||||
binding.recycler.setOnApplyWindowInsetsListener(RecyclerWindowInsetsListener)
|
|
||||||
resetProgressItem()
|
|
||||||
scrollViewWith(binding.recycler, padBottom = true)
|
|
||||||
|
|
||||||
if (recentItems != null) {
|
|
||||||
adapter?.updateDataSet(recentItems!!.toList())
|
|
||||||
}
|
|
||||||
|
|
||||||
launchUI {
|
|
||||||
val manga = presenter.refresh(query)
|
|
||||||
recentItems = manga.toMutableList()
|
|
||||||
adapter?.updateDataSet(manga)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onActivityResumed(activity: Activity) {
|
|
||||||
super.onActivityResumed(activity)
|
|
||||||
if (observeLater) {
|
|
||||||
launchUI {
|
|
||||||
val manga = presenter.refresh(query)
|
|
||||||
recentItems = manga.toMutableList()
|
|
||||||
adapter?.updateDataSet(manga)
|
|
||||||
}
|
|
||||||
observeLater = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Populate adapter with chapters
|
|
||||||
*
|
|
||||||
* @param mangaHistory list of manga history
|
|
||||||
*/
|
|
||||||
fun onNextManga(mangaHistory: List<RecentlyReadItem>) {
|
|
||||||
val adapter = adapter ?: return
|
|
||||||
adapter.updateDataSet(mangaHistory)
|
|
||||||
adapter.onLoadMoreComplete(null)
|
|
||||||
if (recentItems == null) {
|
|
||||||
resetProgressItem()
|
|
||||||
}
|
|
||||||
recentItems = mangaHistory.toMutableList()
|
|
||||||
}
|
|
||||||
|
|
||||||
fun onAddPageError() {
|
|
||||||
adapter?.onLoadMoreComplete(null)
|
|
||||||
adapter?.endlessTargetCount = 1
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onUpdateEmptyView(size: Int) {
|
|
||||||
if (size > 0) {
|
|
||||||
binding.emptyView.hide()
|
|
||||||
} else {
|
|
||||||
binding.emptyView.show(
|
|
||||||
R.drawable.ic_history_24dp,
|
|
||||||
R.string
|
|
||||||
.no_recently_read_manga
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets a new progress item and reenables the scroll listener.
|
|
||||||
*/
|
|
||||||
private fun resetProgressItem() {
|
|
||||||
progressItem = ProgressItem()
|
|
||||||
adapter?.endlessTargetCount = 0
|
|
||||||
adapter?.setEndlessScrollListener(this, progressItem!!)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onLoadMore(lastPosition: Int, currentPage: Int) {
|
|
||||||
val view = view ?: return
|
|
||||||
if (BackupRestoreService.isRunning(view.context.applicationContext)) {
|
|
||||||
onAddPageError()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
presenter.requestNext(query)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun noMoreLoad(newItemsSize: Int) { }
|
|
||||||
|
|
||||||
override fun onResumeClick(position: Int) {
|
|
||||||
val activity = activity ?: return
|
|
||||||
observeLater = true
|
|
||||||
val (manga, chapter, _) = (adapter?.getItem(position) as? RecentlyReadItem)?.mch ?: return
|
|
||||||
|
|
||||||
val nextChapter = presenter.getNextChapter(chapter, manga)
|
|
||||||
if (nextChapter != null) {
|
|
||||||
val intent = ReaderActivity.newIntent(activity, manga, nextChapter)
|
|
||||||
startActivity(intent)
|
|
||||||
} else {
|
|
||||||
activity.toast(R.string.next_chapter_not_found)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onRemoveClick(position: Int) {
|
|
||||||
val (manga, _, history) = (adapter?.getItem(position) as? RecentlyReadItem)?.mch ?: return
|
|
||||||
RemoveHistoryDialog(this, manga, history).showDialog(router)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onCoverClick(position: Int) {
|
|
||||||
val manga = (adapter?.getItem(position) as? RecentlyReadItem)?.mch?.manga ?: return
|
|
||||||
router.pushController(MangaDetailsController(manga).withFadeTransaction())
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun removeHistory(manga: Manga, history: History, all: Boolean) {
|
|
||||||
presenter.lastCount = adapter?.itemCount ?: 25
|
|
||||||
if (all) {
|
|
||||||
// Reset last read of chapter to 0L
|
|
||||||
presenter.removeAllFromHistory(manga.id!!)
|
|
||||||
} else {
|
|
||||||
// Remove all chapters belonging to manga from library
|
|
||||||
presenter.removeFromHistory(history)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
|
|
||||||
inflater.inflate(R.menu.recently_read, menu)
|
|
||||||
val searchItem = menu.findItem(R.id.action_search)
|
|
||||||
val searchView = searchItem.actionView as SearchView
|
|
||||||
if (query.isNotEmpty()) {
|
|
||||||
searchItem.expandActionView()
|
|
||||||
searchView.setQuery(query, true)
|
|
||||||
searchView.clearFocus()
|
|
||||||
}
|
|
||||||
setOnQueryTextChangeListener(searchView) {
|
|
||||||
if (query != it) {
|
|
||||||
query = it ?: return@setOnQueryTextChangeListener false
|
|
||||||
launchUI {
|
|
||||||
resetProgressItem()
|
|
||||||
presenter.lastCount = 25
|
|
||||||
val manga = presenter.refresh(query)
|
|
||||||
recentItems = manga.toMutableList()
|
|
||||||
adapter?.updateDataSet(manga)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
true
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fixes problem with the overflow icon showing up in lieu of search
|
|
||||||
searchItem.setOnActionExpandListener(
|
|
||||||
object : MenuItem.OnActionExpandListener {
|
|
||||||
override fun onMenuItemActionExpand(item: MenuItem): Boolean {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onMenuItemActionCollapse(item: MenuItem): Boolean {
|
|
||||||
activity?.invalidateOptionsMenu()
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/*override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
|
||||||
when (item.itemId) {
|
|
||||||
R.id.action_recents -> {
|
|
||||||
router.setRoot(
|
|
||||||
RecentChaptersController().withFadeTransaction().tag(R.id.nav_recents.toString()))
|
|
||||||
Injekt.get<PreferencesHelper>().showRecentUpdates().set(true)
|
|
||||||
(activity as? MainActivity)?.updateRecentsIcon()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return super.onOptionsItemSelected(item)
|
|
||||||
}*/
|
|
||||||
}
|
|
@ -1,66 +0,0 @@
|
|||||||
package eu.kanade.tachiyomi.ui.recently_read
|
|
||||||
|
|
||||||
import android.view.View
|
|
||||||
import coil.api.clear
|
|
||||||
import eu.kanade.tachiyomi.R
|
|
||||||
import eu.kanade.tachiyomi.data.database.models.MangaChapterHistory
|
|
||||||
import eu.kanade.tachiyomi.data.image.coil.loadLibraryManga
|
|
||||||
import eu.kanade.tachiyomi.databinding.RecentlyReadItemBinding
|
|
||||||
import eu.kanade.tachiyomi.ui.base.holder.BaseFlexibleViewHolder
|
|
||||||
import eu.kanade.tachiyomi.util.lang.toTimestampString
|
|
||||||
import java.util.Date
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Holder that contains recent manga item
|
|
||||||
* Uses R.layout.item_recently_read.
|
|
||||||
* UI related actions should be called from here.
|
|
||||||
*
|
|
||||||
* @param view the inflated view for this holder.
|
|
||||||
* @param adapter the adapter handling this holder.
|
|
||||||
* @constructor creates a new recent chapter holder.
|
|
||||||
*/
|
|
||||||
class RecentlyReadHolder(
|
|
||||||
view: View,
|
|
||||||
val adapter: RecentlyReadAdapter
|
|
||||||
) : BaseFlexibleViewHolder(view, adapter) {
|
|
||||||
|
|
||||||
private val binding = RecentlyReadItemBinding.bind(view)
|
|
||||||
init {
|
|
||||||
binding.remove.setOnClickListener {
|
|
||||||
adapter.removeClickListener.onRemoveClick(flexibleAdapterPosition)
|
|
||||||
}
|
|
||||||
|
|
||||||
binding.resume.setOnClickListener {
|
|
||||||
adapter.resumeClickListener.onResumeClick(flexibleAdapterPosition)
|
|
||||||
}
|
|
||||||
|
|
||||||
binding.cover.setOnClickListener {
|
|
||||||
adapter.coverClickListener.onCoverClick(flexibleAdapterPosition)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set values of view
|
|
||||||
*
|
|
||||||
* @param item item containing history information
|
|
||||||
*/
|
|
||||||
fun bind(item: MangaChapterHistory) {
|
|
||||||
// Retrieve objects
|
|
||||||
val (manga, chapter, history) = item
|
|
||||||
|
|
||||||
// Set manga title
|
|
||||||
binding.title.text = manga.title
|
|
||||||
|
|
||||||
// Set source + chapter title
|
|
||||||
val formattedNumber = adapter.decimalFormat.format(chapter.chapter_number.toDouble())
|
|
||||||
binding.mangaSource.text = itemView.context.getString(R.string.source_dash_chapter_)
|
|
||||||
.format(adapter.sourceManager.getOrStub(manga.source).toString(), formattedNumber)
|
|
||||||
|
|
||||||
// Set last read timestamp title
|
|
||||||
binding.lastRead.text = Date(history.last_read).toTimestampString(adapter.dateFormat)
|
|
||||||
|
|
||||||
// Set binding.cover
|
|
||||||
binding.cover.clear()
|
|
||||||
binding.cover.loadLibraryManga(manga)
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,40 +0,0 @@
|
|||||||
package eu.kanade.tachiyomi.ui.recently_read
|
|
||||||
|
|
||||||
import android.view.View
|
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
|
||||||
import eu.davidea.flexibleadapter.FlexibleAdapter
|
|
||||||
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
|
|
||||||
import eu.davidea.flexibleadapter.items.IFlexible
|
|
||||||
import eu.kanade.tachiyomi.R
|
|
||||||
import eu.kanade.tachiyomi.data.database.models.MangaChapterHistory
|
|
||||||
|
|
||||||
class RecentlyReadItem(val mch: MangaChapterHistory) : AbstractFlexibleItem<RecentlyReadHolder>() {
|
|
||||||
|
|
||||||
override fun getLayoutRes(): Int {
|
|
||||||
return R.layout.recently_read_item
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun createViewHolder(view: View, adapter: FlexibleAdapter<IFlexible<RecyclerView.ViewHolder>>): RecentlyReadHolder {
|
|
||||||
return RecentlyReadHolder(view, adapter as RecentlyReadAdapter)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun bindViewHolder(
|
|
||||||
adapter: FlexibleAdapter<IFlexible<RecyclerView.ViewHolder>>,
|
|
||||||
holder: RecentlyReadHolder,
|
|
||||||
position: Int,
|
|
||||||
payloads: MutableList<Any?>?
|
|
||||||
) {
|
|
||||||
holder.bind(mch)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun equals(other: Any?): Boolean {
|
|
||||||
if (other is RecentlyReadItem) {
|
|
||||||
return mch.manga.id == other.mch.manga.id
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun hashCode(): Int {
|
|
||||||
return mch.manga.id!!.hashCode()
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,131 +0,0 @@
|
|||||||
package eu.kanade.tachiyomi.ui.recently_read
|
|
||||||
|
|
||||||
import eu.kanade.tachiyomi.data.database.DatabaseHelper
|
|
||||||
import eu.kanade.tachiyomi.data.database.models.Chapter
|
|
||||||
import eu.kanade.tachiyomi.data.database.models.History
|
|
||||||
import eu.kanade.tachiyomi.data.database.models.Manga
|
|
||||||
import eu.kanade.tachiyomi.util.system.executeOnIO
|
|
||||||
import eu.kanade.tachiyomi.util.system.launchUI
|
|
||||||
import kotlinx.coroutines.Dispatchers
|
|
||||||
import kotlinx.coroutines.withContext
|
|
||||||
import uy.kohesive.injekt.injectLazy
|
|
||||||
import java.util.Calendar
|
|
||||||
import java.util.Comparator
|
|
||||||
import java.util.Date
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Presenter of RecentlyReadFragment.
|
|
||||||
* Contains information and data for fragment.
|
|
||||||
* Observable updates should be called from here.
|
|
||||||
*/
|
|
||||||
class RecentlyReadPresenter(private val view: RecentlyReadController) {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Used to connect to database
|
|
||||||
*/
|
|
||||||
val db: DatabaseHelper by injectLazy()
|
|
||||||
var lastCount = 25
|
|
||||||
var lastSearch = ""
|
|
||||||
|
|
||||||
fun requestNext(search: String = "") {
|
|
||||||
lastCount += 25
|
|
||||||
lastSearch = search
|
|
||||||
updateList(search)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get all recent manga up to a point
|
|
||||||
* @return list of history
|
|
||||||
*/
|
|
||||||
private suspend fun getRecentMangaLimit(search: String = ""): List<RecentlyReadItem> {
|
|
||||||
// Set date for recent manga
|
|
||||||
val cal = Calendar.getInstance()
|
|
||||||
cal.time = Date()
|
|
||||||
cal.add(Calendar.YEAR, -50)
|
|
||||||
|
|
||||||
return db.getRecentMangaLimit(cal.time, lastCount, search).executeOnIO()
|
|
||||||
.map(::RecentlyReadItem)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reset last read of chapter to 0L
|
|
||||||
* @param history history belonging to chapter
|
|
||||||
*/
|
|
||||||
fun removeFromHistory(history: History) {
|
|
||||||
history.last_read = 0L
|
|
||||||
db.updateHistoryLastRead(history).executeAsBlocking()
|
|
||||||
updateList()
|
|
||||||
}
|
|
||||||
|
|
||||||
suspend fun refresh(search: String? = null): List<RecentlyReadItem> {
|
|
||||||
val manga = getRecentMangaLimit(search ?: "")
|
|
||||||
checkIfNew(manga.size, search)
|
|
||||||
lastSearch = search ?: lastSearch
|
|
||||||
lastCount = manga.size
|
|
||||||
return manga
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun updateList(search: String? = null) {
|
|
||||||
launchUI {
|
|
||||||
val manga = withContext(Dispatchers.IO) { getRecentMangaLimit(search ?: "") }
|
|
||||||
checkIfNew(manga.size, search)
|
|
||||||
lastSearch = search ?: lastSearch
|
|
||||||
lastCount = manga.size
|
|
||||||
view.onNextManga(manga)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun checkIfNew(newCount: Int, newSearch: String?) {
|
|
||||||
if (lastCount > newCount && newSearch == lastSearch) {
|
|
||||||
view.onAddPageError()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Removes all chapters belonging to manga from history.
|
|
||||||
* @param mangaId id of manga
|
|
||||||
*/
|
|
||||||
fun removeAllFromHistory(mangaId: Long) {
|
|
||||||
val history = db.getHistoryByMangaId(mangaId).executeAsBlocking()
|
|
||||||
history.forEach { it.last_read = 0L }
|
|
||||||
db.updateHistoryLastRead(history).executeAsBlocking()
|
|
||||||
updateList()
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieves the next chapter of the given one.
|
|
||||||
*
|
|
||||||
* @param chapter the chapter of the history object.
|
|
||||||
* @param manga the manga of the chapter.
|
|
||||||
*/
|
|
||||||
fun getNextChapter(chapter: Chapter, manga: Manga): Chapter? {
|
|
||||||
if (!chapter.read) {
|
|
||||||
return chapter
|
|
||||||
}
|
|
||||||
|
|
||||||
val sortFunction: (Chapter, Chapter) -> Int = when (manga.sorting) {
|
|
||||||
Manga.SORTING_SOURCE -> { c1, c2 -> c2.source_order.compareTo(c1.source_order) }
|
|
||||||
Manga.SORTING_NUMBER -> { c1, c2 -> c1.chapter_number.compareTo(c2.chapter_number) }
|
|
||||||
else -> throw NotImplementedError("Unknown sorting method")
|
|
||||||
}
|
|
||||||
|
|
||||||
val chapters = db.getChapters(manga).executeAsBlocking()
|
|
||||||
.sortedWith(Comparator<Chapter> { c1, c2 -> sortFunction(c1, c2) })
|
|
||||||
|
|
||||||
val currChapterIndex = chapters.indexOfFirst { chapter.id == it.id }
|
|
||||||
return when (manga.sorting) {
|
|
||||||
Manga.SORTING_SOURCE -> chapters.getOrNull(currChapterIndex + 1)
|
|
||||||
Manga.SORTING_NUMBER -> {
|
|
||||||
val chapterNumber = chapter.chapter_number
|
|
||||||
|
|
||||||
((currChapterIndex + 1) until chapters.size)
|
|
||||||
.map { chapters[it] }
|
|
||||||
.firstOrNull {
|
|
||||||
it.chapter_number > chapterNumber &&
|
|
||||||
it.chapter_number <= chapterNumber + 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else -> throw NotImplementedError("Unknown sorting method")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,4 +1,4 @@
|
|||||||
package eu.kanade.tachiyomi.ui.recent_updates
|
package eu.kanade.tachiyomi.ui.recents
|
||||||
|
|
||||||
import android.text.format.DateUtils
|
import android.text.format.DateUtils
|
||||||
import android.view.View
|
import android.view.View
|
||||||
@ -11,8 +11,7 @@ import eu.davidea.viewholders.FlexibleViewHolder
|
|||||||
import eu.kanade.tachiyomi.R
|
import eu.kanade.tachiyomi.R
|
||||||
import java.util.Date
|
import java.util.Date
|
||||||
|
|
||||||
class DateItem(val date: Date, val addedString: Boolean = false) : AbstractHeaderItem<DateItem
|
class DateItem(val date: Date, val addedString: Boolean = false) : AbstractHeaderItem<DateItem.Holder>() {
|
||||||
.Holder>() {
|
|
||||||
|
|
||||||
override fun getLayoutRes(): Int {
|
override fun getLayoutRes(): Int {
|
||||||
return R.layout.recent_chapters_section_item
|
return R.layout.recent_chapters_section_item
|
@ -33,7 +33,6 @@ 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.recently_read.RemoveHistoryDialog
|
|
||||||
import eu.kanade.tachiyomi.ui.source.browse.ProgressItem
|
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
|
||||||
|
@ -13,7 +13,6 @@ 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.source.SourceManager
|
import eu.kanade.tachiyomi.source.SourceManager
|
||||||
import eu.kanade.tachiyomi.ui.recent_updates.DateItem
|
|
||||||
import eu.kanade.tachiyomi.util.system.executeOnIO
|
import eu.kanade.tachiyomi.util.system.executeOnIO
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
package eu.kanade.tachiyomi.ui.recently_read
|
package eu.kanade.tachiyomi.ui.recents
|
||||||
|
|
||||||
import android.app.Dialog
|
import android.app.Dialog
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
Loading…
x
Reference in New Issue
Block a user