mirror of
https://github.com/tachiyomiorg/tachiyomi.git
synced 2025-01-11 13:09:08 +01:00
Updates to Date Added
On upgrade, assign date added to the earliest chapter fetch Added "Newly Added" section to recents Reformated Recents to use a single recyclerview
This commit is contained in:
parent
0154653a2f
commit
5963c09691
@ -65,7 +65,7 @@ object Migrations {
|
||||
if (oldVersion < 54)
|
||||
DownloadProvider(context).renameChaapters()
|
||||
if (oldVersion < 62)
|
||||
LibraryPresenter.resetCustomManga()
|
||||
LibraryPresenter.updateDB()
|
||||
return true
|
||||
}
|
||||
return false
|
||||
|
@ -7,4 +7,8 @@ package eu.kanade.tachiyomi.data.database.models
|
||||
* @param chapter object containing chater
|
||||
* @param history object containing history
|
||||
*/
|
||||
data class MangaChapterHistory(val manga: Manga, val chapter: Chapter, val history: History)
|
||||
data class MangaChapterHistory(val manga: Manga, val chapter: Chapter, val history: History) {
|
||||
companion object {
|
||||
fun createBlank() = MangaChapterHistory(MangaImpl(), ChapterImpl(), HistoryImpl())
|
||||
}
|
||||
}
|
||||
|
@ -8,6 +8,7 @@ import eu.kanade.tachiyomi.data.database.models.MangaChapterHistory
|
||||
import eu.kanade.tachiyomi.data.database.resolvers.HistoryLastReadPutResolver
|
||||
import eu.kanade.tachiyomi.data.database.resolvers.MangaChapterHistoryGetResolver
|
||||
import eu.kanade.tachiyomi.data.database.tables.HistoryTable
|
||||
import eu.kanade.tachiyomi.data.database.tables.MangaTable
|
||||
import java.util.Date
|
||||
|
||||
interface HistoryQueries : DbProvider {
|
||||
@ -33,6 +34,21 @@ interface HistoryQueries : DbProvider {
|
||||
.withGetResolver(MangaChapterHistoryGetResolver.INSTANCE)
|
||||
.prepare()
|
||||
|
||||
/**
|
||||
* Returns history of recent manga containing last read chapter in 25s
|
||||
* @param date recent date range
|
||||
* @offset offset the db by
|
||||
*/
|
||||
fun getRecentlyAdded(date: Date, search: String = "") = db.get()
|
||||
.listOfObjects(MangaChapterHistory::class.java)
|
||||
.withQuery(RawQuery.builder()
|
||||
.query(getRecentAdditionsQuery(search))
|
||||
.args(date.time)
|
||||
.observesTables(MangaTable.TABLE)
|
||||
.build())
|
||||
.withGetResolver(MangaChapterHistoryGetResolver.INSTANCE)
|
||||
.prepare()
|
||||
|
||||
/**
|
||||
* Returns history of recent manga containing last read chapter in 25s
|
||||
* @param date recent date range
|
||||
|
@ -61,6 +61,18 @@ fun getRecentsQuery() = """
|
||||
ORDER BY ${Chapter.COL_DATE_UPLOAD} DESC
|
||||
"""
|
||||
|
||||
/**
|
||||
* Query to get the recent chapters of manga from the library up to a date.
|
||||
*/
|
||||
fun getRecentAdditionsQuery(search: String) = """
|
||||
SELECT ${Manga.TABLE}.${Manga.COL_URL} as mangaUrl, * FROM ${Manga.TABLE}
|
||||
WHERE ${Manga.COL_FAVORITE} = 1
|
||||
AND ${Manga.COL_DATE_ADDED} > ?
|
||||
AND lower(${Manga.COL_TITLE}) LIKE '%$search%'
|
||||
ORDER BY ${Manga.COL_DATE_ADDED} DESC
|
||||
LIMIT 8
|
||||
"""
|
||||
|
||||
/**
|
||||
* Query to get the recent chapters of manga from the library up to a date.
|
||||
*/
|
||||
@ -73,7 +85,7 @@ fun getRecentsQueryDistinct(search: String) = """
|
||||
SELECT ${Chapter.TABLE}.${Chapter.COL_MANGA_ID},${Chapter.TABLE}.${Chapter.COL_ID} as ${History.COL_CHAPTER_ID},MAX(${Chapter.TABLE}.${Chapter.COL_DATE_UPLOAD})
|
||||
FROM ${Chapter.TABLE} JOIN ${Manga.TABLE}
|
||||
ON ${Manga.TABLE}.${Manga.COL_ID} = ${Chapter.TABLE}.${Chapter.COL_MANGA_ID}
|
||||
WHERE ${Chapter.COL_DATE_FETCH} > ?
|
||||
WHERE ${Chapter.COL_DATE_UPLOAD} > ?
|
||||
AND ${Chapter.COL_READ} = 0
|
||||
GROUP BY ${Chapter.TABLE}.${Chapter.COL_MANGA_ID}) AS newest_chapter
|
||||
ON ${Chapter.TABLE}.${Chapter.COL_MANGA_ID} = newest_chapter.${Chapter.COL_MANGA_ID}
|
||||
|
@ -5,6 +5,7 @@ import com.pushtorefresh.storio.sqlite.operations.get.DefaultGetResolver
|
||||
import eu.kanade.tachiyomi.data.database.mappers.ChapterGetResolver
|
||||
import eu.kanade.tachiyomi.data.database.mappers.HistoryGetResolver
|
||||
import eu.kanade.tachiyomi.data.database.mappers.MangaGetResolver
|
||||
import eu.kanade.tachiyomi.data.database.models.ChapterImpl
|
||||
import eu.kanade.tachiyomi.data.database.models.HistoryImpl
|
||||
import eu.kanade.tachiyomi.data.database.models.MangaChapterHistory
|
||||
|
||||
@ -36,14 +37,17 @@ class MangaChapterHistoryGetResolver : DefaultGetResolver<MangaChapterHistory>()
|
||||
val manga = mangaGetResolver.mapFromCursor(cursor)
|
||||
|
||||
// Get chapter object
|
||||
val chapter = chapterResolver.mapFromCursor(cursor)
|
||||
val chapter = try { chapterResolver.mapFromCursor(cursor) } catch (e: Exception) {
|
||||
ChapterImpl() }
|
||||
|
||||
// Get history object
|
||||
val history = try { historyGetResolver.mapFromCursor(cursor) } catch (e: Exception) { HistoryImpl() }
|
||||
|
||||
// Make certain column conflicts are dealt with
|
||||
manga.id = chapter.manga_id
|
||||
manga.url = cursor.getString(cursor.getColumnIndex("mangaUrl"))
|
||||
if (chapter.id != null) {
|
||||
manga.id = chapter.manga_id
|
||||
manga.url = cursor.getString(cursor.getColumnIndex("mangaUrl"))
|
||||
}
|
||||
if (history.id != null)
|
||||
chapter.id = history.chapter_id
|
||||
|
||||
|
@ -55,7 +55,8 @@ import kotlin.math.pow
|
||||
import kotlin.math.roundToInt
|
||||
import kotlin.math.sign
|
||||
|
||||
class LibraryListController(bundle: Bundle? = null) : LibraryController(bundle),
|
||||
class
|
||||
LibraryListController(bundle: Bundle? = null) : LibraryController(bundle),
|
||||
FlexibleAdapter.OnItemClickListener, FlexibleAdapter.OnItemLongClickListener,
|
||||
FlexibleAdapter.OnItemMoveListener, LibraryCategoryAdapter.LibraryListener,
|
||||
SpinnerTitleInterface, OnTouchEventInterface, SwipeGestureInterface {
|
||||
|
@ -927,11 +927,16 @@ class LibraryPresenter(
|
||||
companion object {
|
||||
private var currentLibrary: Library? = null
|
||||
|
||||
fun resetCustomManga() {
|
||||
fun updateDB() {
|
||||
val db: DatabaseHelper = Injekt.get()
|
||||
db.inTransaction {
|
||||
val libraryManga = db.getLibraryMangas().executeAsBlocking()
|
||||
libraryManga.forEach { manga ->
|
||||
if (manga.date_added == 0L) {
|
||||
val chapters = db.getChapters(manga).executeAsBlocking()
|
||||
manga.date_added = chapters.minBy { it.date_fetch }?.date_fetch ?: 0L
|
||||
db.insertManga(manga).executeAsBlocking()
|
||||
}
|
||||
db.resetMangaInfo(manga).executeAsBlocking()
|
||||
}
|
||||
}
|
||||
|
@ -7,6 +7,6 @@ object LibrarySort {
|
||||
const val LATEST_CHAPTER = 2
|
||||
const val UNREAD = 3
|
||||
const val TOTAL = 4
|
||||
const val DATE_ADDED = 5
|
||||
const val DRAG_AND_DROP = 6
|
||||
const val DATE_ADDED = 7
|
||||
}
|
||||
|
@ -13,11 +13,11 @@ open class BaseChapterHolder(
|
||||
) : BaseFlexibleViewHolder(view, adapter) {
|
||||
|
||||
init {
|
||||
download_button.setOnClickListener { downloadOrRemoveMenu() }
|
||||
download_button?.setOnClickListener { downloadOrRemoveMenu() }
|
||||
}
|
||||
|
||||
private fun downloadOrRemoveMenu() {
|
||||
val chapter = adapter.getItem(adapterPosition) as? BaseChapterItem ?: return
|
||||
val chapter = adapter.getItem(adapterPosition) as? BaseChapterItem<*, *> ?: return
|
||||
if (chapter.status == Download.NOT_DOWNLOADED || chapter.status == Download.ERROR) {
|
||||
adapter.baseDelegate.downloadChapter(adapterPosition)
|
||||
} else {
|
||||
|
@ -1,12 +1,17 @@
|
||||
package eu.kanade.tachiyomi.ui.manga.chapter
|
||||
|
||||
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
|
||||
import eu.davidea.flexibleadapter.items.AbstractHeaderItem
|
||||
import eu.davidea.flexibleadapter.items.AbstractSectionableItem
|
||||
import eu.kanade.tachiyomi.data.database.models.Chapter
|
||||
import eu.kanade.tachiyomi.data.download.model.Download
|
||||
import eu.kanade.tachiyomi.source.model.Page
|
||||
|
||||
abstract class BaseChapterItem<T : BaseChapterHolder>(val chapter: Chapter) :
|
||||
AbstractFlexibleItem<T>(),
|
||||
abstract class BaseChapterItem<T : BaseChapterHolder, H : AbstractHeaderItem<*>>(
|
||||
val chapter:
|
||||
Chapter,
|
||||
header: H? = null
|
||||
) :
|
||||
AbstractSectionableItem<T, H?>(header),
|
||||
Chapter by chapter {
|
||||
|
||||
private var _status: Int = 0
|
||||
@ -28,13 +33,13 @@ abstract class BaseChapterItem<T : BaseChapterHolder>(val chapter: Chapter) :
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (other is BaseChapterItem<*>) {
|
||||
return chapter.id!! == other.chapter.id!!
|
||||
if (other is BaseChapterItem<*, *>) {
|
||||
return chapter.id == other.chapter.id
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
return chapter.id!!.hashCode()
|
||||
return (chapter.id ?: 0L).hashCode()
|
||||
}
|
||||
}
|
||||
|
@ -3,14 +3,16 @@ package eu.kanade.tachiyomi.ui.manga.chapter
|
||||
import android.view.View
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import eu.davidea.flexibleadapter.FlexibleAdapter
|
||||
import eu.davidea.flexibleadapter.items.AbstractHeaderItem
|
||||
import eu.davidea.flexibleadapter.items.IFlexible
|
||||
import eu.davidea.viewholders.FlexibleViewHolder
|
||||
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.MangaDetailsAdapter
|
||||
|
||||
class ChapterItem(chapter: Chapter, val manga: Manga) :
|
||||
BaseChapterItem<ChapterHolder>(chapter) {
|
||||
BaseChapterItem<ChapterHolder, AbstractHeaderItem<FlexibleViewHolder>>(chapter) {
|
||||
|
||||
var isLocked = false
|
||||
|
||||
|
@ -9,7 +9,11 @@ import java.text.DecimalFormat
|
||||
import java.text.DecimalFormatSymbols
|
||||
|
||||
class RecentMangaAdapter(val delegate: RecentsInterface) :
|
||||
BaseChapterAdapter<IFlexible<RecentMangaHolder>>(delegate) {
|
||||
BaseChapterAdapter<IFlexible<*>>(delegate) {
|
||||
|
||||
init {
|
||||
setDisplayHeadersAtStartUp(true)
|
||||
}
|
||||
|
||||
val decimalFormat = DecimalFormat("#.###", DecimalFormatSymbols()
|
||||
.apply { decimalSeparator = '.' })
|
||||
@ -17,9 +21,11 @@ class RecentMangaAdapter(val delegate: RecentsInterface) :
|
||||
interface RecentsInterface : RecentMangaInterface, DownloadInterface
|
||||
|
||||
interface RecentMangaInterface {
|
||||
fun onHeaderClick(position: Int)
|
||||
fun onCoverClick(position: Int)
|
||||
fun markAsRead(position: Int)
|
||||
fun setCover(manga: Manga, view: ImageView)
|
||||
fun isSearching(): Boolean
|
||||
}
|
||||
|
||||
override fun onItemSwiped(position: Int, direction: Int) {
|
||||
|
@ -0,0 +1,68 @@
|
||||
package eu.kanade.tachiyomi.ui.recents
|
||||
|
||||
import android.view.View
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import eu.davidea.flexibleadapter.FlexibleAdapter
|
||||
import eu.davidea.flexibleadapter.items.AbstractHeaderItem
|
||||
import eu.davidea.flexibleadapter.items.IFlexible
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.ui.base.holder.BaseFlexibleViewHolder
|
||||
import eu.kanade.tachiyomi.ui.library.LibraryHeaderItem
|
||||
import kotlinx.android.synthetic.main.recents_header_item.*
|
||||
|
||||
class RecentMangaHeaderItem(val recentsType: Int) :
|
||||
AbstractHeaderItem<RecentMangaHeaderItem.Holder>() {
|
||||
|
||||
override fun getLayoutRes(): Int {
|
||||
return R.layout.recents_header_item
|
||||
}
|
||||
|
||||
override fun createViewHolder(
|
||||
view: View,
|
||||
adapter: FlexibleAdapter<IFlexible<RecyclerView.ViewHolder>>
|
||||
): Holder {
|
||||
return Holder(view, adapter as RecentMangaAdapter)
|
||||
}
|
||||
|
||||
override fun bindViewHolder(
|
||||
adapter: FlexibleAdapter<IFlexible<RecyclerView.ViewHolder>>,
|
||||
holder: Holder,
|
||||
position: Int,
|
||||
payloads: MutableList<Any?>?
|
||||
) {
|
||||
holder.bind(recentsType)
|
||||
}
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (other is LibraryHeaderItem) {
|
||||
return recentsType == recentsType
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
override fun isDraggable(): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun isSwipeable(): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
return recentsType.hashCode()
|
||||
}
|
||||
|
||||
class Holder(val view: View, adapter: RecentMangaAdapter) : BaseFlexibleViewHolder(view, adapter,
|
||||
true) {
|
||||
|
||||
fun bind(recentsType: Int) {
|
||||
title.setText(when (recentsType) {
|
||||
RecentsItem.CONTINUE_READING -> R.string.continue_reading
|
||||
RecentsItem.NEW_CHAPTERS -> R.string.new_chapters
|
||||
RecentsItem.NEWLY_ADDED -> R.string.new_additions
|
||||
else -> R.string.continue_reading
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
@ -17,46 +17,55 @@ class RecentMangaHolder(
|
||||
) : BaseChapterHolder(view, adapter) {
|
||||
|
||||
init {
|
||||
cover_thumbnail.setOnClickListener { adapter.delegate.onCoverClick(adapterPosition) }
|
||||
cover_thumbnail?.setOnClickListener { adapter.delegate.onCoverClick(adapterPosition) }
|
||||
}
|
||||
|
||||
fun bind(recentsType: Int) {
|
||||
when (recentsType) {
|
||||
RecentsItem.CONTINUE_READING -> {
|
||||
title.setText(R.string.view_history)
|
||||
}
|
||||
RecentsItem.NEW_CHAPTERS -> {
|
||||
title.setText(R.string.view_all_updates)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun bind(item: RecentMangaItem) {
|
||||
download_button.visibleIf(item.mch.manga.source != LocalSource.ID)
|
||||
title.text = item.mch.manga.title
|
||||
val holder = (adapter.delegate as RecentsHolder)
|
||||
val isSearch =
|
||||
(holder.adapter.getItem(holder.adapterPosition) as RecentsItem).recentType == RecentsItem.SEARCH
|
||||
val isSearch = adapter.delegate.isSearching()
|
||||
subtitle.text = item.chapter.name
|
||||
body.text = if (isSearch) when {
|
||||
val notValidNum = item.mch.chapter.chapter_number <= 0
|
||||
body.text = when {
|
||||
item.mch.chapter.id == null -> body.context.getString(
|
||||
R.string.added_x, DateUtils.getRelativeTimeSpanString(
|
||||
item.mch.manga.date_added, Date().time, DateUtils.MINUTE_IN_MILLIS
|
||||
).toString()
|
||||
)
|
||||
item.chapter.id != item.mch.chapter.id -> body.context.getString(
|
||||
R.string.last_read_chapter_x, adapter.decimalFormat.format(
|
||||
item.mch.chapter.chapter_number
|
||||
) + " (${DateUtils.getRelativeTimeSpanString(
|
||||
if (notValidNum) R.string.last_read_x else R.string.last_read_chapter_x,
|
||||
if (notValidNum) item.mch.chapter.name else adapter.decimalFormat.format(item.mch.chapter.chapter_number) +
|
||||
" (${DateUtils.getRelativeTimeSpanString(
|
||||
item.mch.history.last_read, Date().time, DateUtils.MINUTE_IN_MILLIS
|
||||
)})"
|
||||
)
|
||||
item.mch.history.id == null -> body.context.getString(
|
||||
R.string.uploaded_x, DateUtils.getRelativeTimeSpanString(
|
||||
R.string.updated_x, DateUtils.getRelativeTimeSpanString(
|
||||
item.chapter.date_upload, Date().time, DateUtils.HOUR_IN_MILLIS
|
||||
).toString()
|
||||
)
|
||||
else -> body.context.getString(
|
||||
R.string.last_read_x, DateUtils.getRelativeTimeSpanString(
|
||||
!isSearch && item.chapter.pages_left > 0 -> itemView.resources.getQuantityString(
|
||||
R.plurals.pages_left, item.chapter.pages_left, item.chapter.pages_left
|
||||
) +
|
||||
" (${DateUtils.getRelativeTimeSpanString(
|
||||
item.mch.history.last_read, Date().time, DateUtils.MINUTE_IN_MILLIS
|
||||
)})"
|
||||
isSearch -> body.context.getString(
|
||||
R.string.read_x, DateUtils.getRelativeTimeSpanString(
|
||||
item.mch.history.last_read, Date().time, DateUtils.MINUTE_IN_MILLIS
|
||||
).toString()
|
||||
)
|
||||
} else when {
|
||||
item.chapter.id != item.mch.chapter.id -> body.context.getString(
|
||||
R.string.last_read_chapter_x, adapter.decimalFormat.format(
|
||||
item.mch.chapter.chapter_number
|
||||
)
|
||||
)
|
||||
item.mch.history.id == null -> DateUtils.getRelativeTimeSpanString(
|
||||
item.chapter.date_upload, Date().time, DateUtils.HOUR_IN_MILLIS
|
||||
).toString()
|
||||
item.chapter.pages_left > 0 -> itemView.resources.getQuantityString(
|
||||
R.plurals.pages_left, item.chapter.pages_left, item.chapter.pages_left
|
||||
)
|
||||
else -> ""
|
||||
}
|
||||
adapter.delegate.setCover(item.mch.manga, cover_thumbnail)
|
||||
|
@ -6,14 +6,21 @@ 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.ChapterImpl
|
||||
import eu.kanade.tachiyomi.data.database.models.MangaChapterHistory
|
||||
import eu.kanade.tachiyomi.ui.manga.chapter.BaseChapterItem
|
||||
|
||||
class RecentMangaItem(val mch: MangaChapterHistory, chapter: Chapter) :
|
||||
BaseChapterItem<RecentMangaHolder>(chapter) {
|
||||
class RecentMangaItem(
|
||||
val mch: MangaChapterHistory = MangaChapterHistory.createBlank(),
|
||||
chapter: Chapter = ChapterImpl(),
|
||||
header:
|
||||
RecentMangaHeaderItem?
|
||||
) :
|
||||
BaseChapterItem<RecentMangaHolder, RecentMangaHeaderItem>(chapter, header) {
|
||||
|
||||
override fun getLayoutRes(): Int {
|
||||
return R.layout.recent_manga_item
|
||||
return if (mch.manga.id == null) R.layout.recents_footer_item
|
||||
else R.layout.recent_manga_item
|
||||
}
|
||||
|
||||
override fun createViewHolder(
|
||||
@ -23,12 +30,31 @@ class RecentMangaItem(val mch: MangaChapterHistory, chapter: Chapter) :
|
||||
return RecentMangaHolder(view, adapter as RecentMangaAdapter)
|
||||
}
|
||||
|
||||
override fun isSwipeable(): Boolean {
|
||||
return mch.manga.id != null
|
||||
}
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (other is RecentMangaItem) {
|
||||
return if (mch.manga.id == null) header?.recentsType == other.header?.recentsType
|
||||
else chapter.id == other.chapter.id
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
return if (mch.manga.id == null) -(header?.recentsType ?: 0).hashCode()
|
||||
else (chapter.id ?: 0L).hashCode()
|
||||
}
|
||||
|
||||
override fun bindViewHolder(
|
||||
adapter: FlexibleAdapter<IFlexible<RecyclerView.ViewHolder>>,
|
||||
holder: RecentMangaHolder,
|
||||
position: Int,
|
||||
payloads: MutableList<Any?>?
|
||||
) {
|
||||
holder.bind(this)
|
||||
if (mch.manga.id == null) holder.bind(header?.recentsType ?: 0)
|
||||
else holder.bind(this)
|
||||
}
|
||||
}
|
||||
|
@ -10,6 +10,7 @@ import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.ImageView
|
||||
import androidx.appcompat.widget.SearchView
|
||||
import androidx.recyclerview.widget.ItemTouchHelper
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import com.bluelinelabs.conductor.Controller
|
||||
import com.bluelinelabs.conductor.ControllerChangeHandler
|
||||
@ -48,8 +49,9 @@ import kotlinx.android.synthetic.main.recently_read_controller.*
|
||||
* UI related actions should be called from here.
|
||||
*/
|
||||
class RecentsController(bundle: Bundle? = null) : BaseController(bundle),
|
||||
FlexibleAdapter.OnUpdateListener,
|
||||
RecentMangaAdapter.RecentsInterface,
|
||||
RecentsAdapter.RecentsInterface,
|
||||
FlexibleAdapter.OnItemClickListener,
|
||||
RootSearchInterface {
|
||||
|
||||
init {
|
||||
@ -59,10 +61,11 @@ class RecentsController(bundle: Bundle? = null) : BaseController(bundle),
|
||||
/**
|
||||
* Adapter containing the recent manga.
|
||||
*/
|
||||
private val adapter = RecentsAdapter(this)
|
||||
// private val adapter = RecentsAdapter(this)
|
||||
private var adapter = RecentMangaAdapter(this)
|
||||
|
||||
private var presenter = RecentsPresenter(this)
|
||||
private var recentItems: List<RecentsItem>? = null
|
||||
private var recentItems: List<RecentMangaItem>? = null
|
||||
private var snack: Snackbar? = null
|
||||
private var lastChapterId: Long? = null
|
||||
|
||||
@ -83,10 +86,20 @@ class RecentsController(bundle: Bundle? = null) : BaseController(bundle),
|
||||
super.onViewCreated(view)
|
||||
view.applyWindowInsetsForRootController(activity!!.bottom_nav)
|
||||
// Initialize adapter
|
||||
adapter = RecentMangaAdapter(this)
|
||||
recycler.adapter = adapter
|
||||
recycler.layoutManager = LinearLayoutManager(view.context)
|
||||
recycler.setHasFixedSize(true)
|
||||
|
||||
recycler.recycledViewPool.setMaxRecycledViews(0, 0)
|
||||
adapter.isSwipeEnabled = true
|
||||
/*recycler.addItemDecoration(
|
||||
DividerItemDecoration(
|
||||
recycler.context, DividerItemDecoration.VERTICAL
|
||||
)
|
||||
)*/
|
||||
adapter.itemTouchHelperCallback.setSwipeFlags(
|
||||
ItemTouchHelper.LEFT
|
||||
)
|
||||
scrollViewWith(recycler, skipFirstSnap = true)
|
||||
|
||||
if (recentItems != null) adapter.updateDataSet(recentItems!!.toList())
|
||||
@ -108,37 +121,42 @@ class RecentsController(bundle: Bundle? = null) : BaseController(bundle),
|
||||
|
||||
fun refresh() = presenter.getRecents()
|
||||
|
||||
fun showLists(recents: List<RecentsItem>) {
|
||||
fun showLists(recents: List<RecentMangaItem>) {
|
||||
recentItems = recents
|
||||
adapter.updateDataSet(recents)
|
||||
if (lastChapterId != null) {
|
||||
refreshItem(lastChapterId ?: 0L)
|
||||
lastChapterId = null
|
||||
}
|
||||
}
|
||||
|
||||
override fun onUpdateEmptyView(size: Int) {
|
||||
if (size > 0) {
|
||||
empty_view?.hide()
|
||||
} else {
|
||||
empty_view?.show(R.drawable.ic_history_white_128dp, R.string
|
||||
.information_no_recent_manga)
|
||||
}
|
||||
// recycler.removeItemDecorationAt(0)
|
||||
}
|
||||
|
||||
fun updateChapterDownload(download: Download) {
|
||||
if (view == null) return
|
||||
for (i in 0 until adapter.itemCount) {
|
||||
val id = download.chapter.id ?: return
|
||||
val holder = recycler.findViewHolderForItemId(id) as? RecentMangaHolder ?: return
|
||||
holder.notifyStatus(download.status, download.progress)
|
||||
/* (i in 0 until adapter.itemCount) {
|
||||
val holder = recycler.findViewHolderForAdapterPosition(i) as? RecentsHolder ?: continue
|
||||
if (holder.updateChapterDownload(download)) break
|
||||
}
|
||||
}*/
|
||||
}
|
||||
|
||||
private fun refreshItem(chapterId: Long) {
|
||||
val recentItemPos = adapter.currentItems.indexOfFirst {
|
||||
it is RecentMangaItem &&
|
||||
it.mch.chapter.id == chapterId }
|
||||
if (recentItemPos > -1) adapter.notifyItemChanged(recentItemPos)
|
||||
/*holder.notifyStatus(download.status, download.progress)
|
||||
for (i in 0 until adapter.itemCount) {
|
||||
val holder = recycler.findViewHolderForAdapterPosition(i) as? RecentsHolder ?: continue
|
||||
holder.refreshChapter(chapterId)
|
||||
}
|
||||
}*/
|
||||
}
|
||||
|
||||
override fun downloadChapter(position: Int) {
|
||||
val item = adapter.getItem(position) as? RecentMangaItem ?: return
|
||||
downloadChapter(item)
|
||||
}
|
||||
|
||||
override fun downloadChapter(item: RecentMangaItem) {
|
||||
@ -153,18 +171,49 @@ class RecentsController(bundle: Bundle? = null) : BaseController(bundle),
|
||||
}
|
||||
}
|
||||
|
||||
override fun startDownloadNow(position: Int) {
|
||||
val chapter = (adapter.getItem(position) as? RecentMangaItem)?.chapter ?: return
|
||||
presenter.startDownloadChapterNow(chapter)
|
||||
}
|
||||
|
||||
override fun downloadChapterNow(chapter: Chapter) {
|
||||
presenter.startDownloadChapterNow(chapter)
|
||||
}
|
||||
|
||||
override fun onCoverClick(position: Int) {
|
||||
val manga = (adapter.getItem(position) as? RecentMangaItem)?.mch?.manga ?: return
|
||||
router.pushController(MangaDetailsController(manga).withFadeTransaction())
|
||||
}
|
||||
|
||||
override fun showManga(manga: Manga) = router.pushController(MangaDetailsController(manga).withFadeTransaction())
|
||||
|
||||
override fun onItemClick(view: View?, position: Int): Boolean {
|
||||
val item = adapter.getItem(position) ?: return false
|
||||
if (item is RecentMangaItem) {
|
||||
if (item.mch.manga.id == null) {
|
||||
val headerItem = adapter.getHeaderOf(item) as? RecentMangaHeaderItem
|
||||
val controller: Controller = when (headerItem?.recentsType) {
|
||||
RecentsItem.NEW_CHAPTERS -> RecentChaptersController()
|
||||
RecentsItem.CONTINUE_READING -> RecentlyReadController()
|
||||
else -> return false
|
||||
}
|
||||
router.pushController(controller.withFadeTransaction())
|
||||
} else resumeManga(item.mch.manga, item.chapter)
|
||||
} else if (item is RecentMangaHeaderItem) return false // onHeaderClick(position)
|
||||
return true
|
||||
}
|
||||
|
||||
override fun resumeManga(manga: Manga, chapter: Chapter) {
|
||||
val activity = activity ?: return
|
||||
val intent = ReaderActivity.newIntent(activity, manga, chapter)
|
||||
startActivity(intent)
|
||||
}
|
||||
|
||||
override fun markAsRead(position: Int) {
|
||||
val item = adapter.getItem(position) as? RecentMangaItem ?: return
|
||||
markAsRead(item.mch.manga, item.chapter)
|
||||
}
|
||||
|
||||
override fun markAsRead(manga: Manga, chapter: Chapter) {
|
||||
val lastRead = chapter.last_page_read
|
||||
val pagesLeft = chapter.pages_left
|
||||
@ -191,6 +240,8 @@ class RecentsController(bundle: Bundle? = null) : BaseController(bundle),
|
||||
(activity as? MainActivity)?.setUndoSnackBar(snack)
|
||||
}
|
||||
|
||||
override fun isSearching() = presenter.query.isNotEmpty()
|
||||
|
||||
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
|
||||
inflater.inflate(R.menu.recents, menu)
|
||||
val searchItem = menu.findItem(R.id.action_search)
|
||||
@ -229,8 +280,9 @@ class RecentsController(bundle: Bundle? = null) : BaseController(bundle),
|
||||
.signature(ObjectKey(MangaImpl.getLastCoverFetch(manga.id!!).toString())).into(view)
|
||||
}
|
||||
|
||||
override fun viewAll(position: Int) {
|
||||
val recentsType = (adapter.getItem(position) as? RecentsItem)?.recentType ?: return
|
||||
override fun onHeaderClick(position: Int) {
|
||||
val recentsType = (adapter.getItem(position) as? RecentMangaHeaderItem)?.recentsType
|
||||
?: return
|
||||
val controller: Controller = when (recentsType) {
|
||||
RecentsItem.NEW_CHAPTERS -> RecentChaptersController()
|
||||
RecentsItem.CONTINUE_READING -> RecentlyReadController()
|
||||
@ -239,6 +291,16 @@ class RecentsController(bundle: Bundle? = null) : BaseController(bundle),
|
||||
router.pushController(controller.withFadeTransaction())
|
||||
}
|
||||
|
||||
override fun viewAll(position: Int) {
|
||||
/*val recentsType = (adapter.getItem(position) as? RecentsItem)?.recentType ?: return
|
||||
val controller: Controller = when (recentsType) {
|
||||
RecentsItem.NEW_CHAPTERS -> RecentChaptersController()
|
||||
RecentsItem.CONTINUE_READING -> RecentlyReadController()
|
||||
else -> return
|
||||
}
|
||||
router.pushController(controller.withFadeTransaction())*/
|
||||
}
|
||||
|
||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||
when (item.itemId) {
|
||||
R.id.action_refresh -> {
|
||||
|
@ -129,4 +129,8 @@ class RecentsHolder(
|
||||
val item = (subAdapter.getItem(position) as RecentMangaItem)
|
||||
adapter.delegate.markAsRead(item.mch.manga, item.chapter)
|
||||
}
|
||||
|
||||
override fun onHeaderClick(position: Int) {
|
||||
}
|
||||
override fun isSearching() = false
|
||||
}
|
||||
|
@ -32,9 +32,12 @@ class RecentsPresenter(
|
||||
|
||||
private var scope = CoroutineScope(Job() + Dispatchers.Default)
|
||||
|
||||
private var recentItems = listOf<RecentMangaItem>()
|
||||
var groupedRecentItems = listOf<RecentsItem>()
|
||||
var recentItems = listOf<RecentMangaItem>()
|
||||
// var groupedRecentItems = listOf<RecentsItem>()
|
||||
var query = ""
|
||||
var newAdditionsHeader = RecentMangaHeaderItem(RecentsItem.NEWLY_ADDED)
|
||||
var newChaptersHeader = RecentMangaHeaderItem(RecentsItem.NEW_CHAPTERS)
|
||||
var continueReadingHeader = RecentMangaHeaderItem(RecentsItem.CONTINUE_READING)
|
||||
|
||||
fun onCreate() {
|
||||
downloadManager.addListener(this)
|
||||
@ -43,51 +46,75 @@ class RecentsPresenter(
|
||||
}
|
||||
|
||||
fun getRecents() {
|
||||
val oldQuery = query
|
||||
scope.launch {
|
||||
val cal = Calendar.getInstance()
|
||||
cal.time = Date()
|
||||
if (query.isNotEmpty()) cal.add(Calendar.YEAR, -50)
|
||||
else cal.add(Calendar.MONTH, -1)
|
||||
|
||||
val calWeek = Calendar.getInstance()
|
||||
calWeek.time = Date()
|
||||
if (query.isNotEmpty()) calWeek.add(Calendar.YEAR, -50)
|
||||
else calWeek.add(Calendar.DAY_OF_YEAR, -1)
|
||||
|
||||
val cReading =
|
||||
if (query.isEmpty())
|
||||
db.getRecentsWithUnread(cal.time, query).executeOnIO()
|
||||
else
|
||||
db.getRecentMangaLimit(cal.time, 8, query).executeOnIO()
|
||||
if (query.isEmpty()) db.getRecentsWithUnread(cal.time, query).executeOnIO()
|
||||
else db.getRecentMangaLimit(cal.time, 8, query).executeOnIO()
|
||||
val rUpdates = db.getUpdatedManga(cal.time, query).executeOnIO()
|
||||
rUpdates.forEach {
|
||||
it.history.last_read = it.chapter.date_upload
|
||||
it.history.last_read = it.chapter.date_fetch
|
||||
}
|
||||
val mangaList = (cReading + rUpdates).sortedByDescending {
|
||||
val nAdditions = db.getRecentlyAdded(calWeek.time, query).executeOnIO()
|
||||
nAdditions.forEach {
|
||||
it.history.last_read = it.manga.date_added
|
||||
}
|
||||
if (query != oldQuery) return@launch
|
||||
val mangaList = (cReading + rUpdates + nAdditions).sortedByDescending {
|
||||
it.history.last_read
|
||||
}.distinctBy {
|
||||
if (query.isEmpty()) it.manga.id else it.chapter.id }
|
||||
recentItems = mangaList.mapNotNull {
|
||||
val chapter = if (it.chapter.read) getNextChapter(it.manga)
|
||||
if (query.isEmpty()) it.manga.id else it.chapter.id
|
||||
}
|
||||
val pairs = mangaList.mapNotNull {
|
||||
val chapter = if (it.chapter.read || it.chapter.id == null) getNextChapter(it.manga)
|
||||
else it.chapter
|
||||
if (chapter == null) if (query.isNotEmpty()) RecentMangaItem(it, it.chapter)
|
||||
if (chapter == null) if (query.isNotEmpty() && it.chapter.id != null) Pair(
|
||||
it, it.chapter
|
||||
)
|
||||
else null
|
||||
else RecentMangaItem(it, chapter)
|
||||
else Pair(it, chapter)
|
||||
}
|
||||
if (query.isEmpty()) {
|
||||
val nChaptersItems =
|
||||
pairs.filter { it.first.history.id == null && it.first.chapter.id != null }
|
||||
.sortedByDescending { it.second.date_upload }
|
||||
.take(4).map {
|
||||
RecentMangaItem(
|
||||
it.first,
|
||||
it.second,
|
||||
newChaptersHeader
|
||||
)
|
||||
} +
|
||||
RecentMangaItem(header = newChaptersHeader)
|
||||
val cReadingItems =
|
||||
pairs.filter { it.first.history.id != null }.take(9 - nChaptersItems.size).map {
|
||||
RecentMangaItem(
|
||||
it.first,
|
||||
it.second,
|
||||
continueReadingHeader
|
||||
)
|
||||
} + RecentMangaItem(header = continueReadingHeader)
|
||||
val nAdditionsItems = pairs.filter { it.first.chapter.id == null }.take(4)
|
||||
.map { RecentMangaItem(it.first, it.second, newAdditionsHeader) }
|
||||
recentItems =
|
||||
listOf(nChaptersItems, cReadingItems, nAdditionsItems).sortedByDescending {
|
||||
it.firstOrNull()?.mch?.history?.last_read ?: 0L
|
||||
}.flatten()
|
||||
} else {
|
||||
recentItems = pairs.map { RecentMangaItem(it.first, it.second, null) }
|
||||
}
|
||||
setDownloadedChapters(recentItems)
|
||||
if (query.isEmpty()) {
|
||||
val nChaptersItems = RecentsItem(
|
||||
RecentsItem.NEW_CHAPTERS,
|
||||
recentItems.filter { it.mch.history.id == null }.take(4)
|
||||
)
|
||||
val cReadingItems = RecentsItem(
|
||||
RecentsItem.CONTINUE_READING,
|
||||
recentItems.filter { it.mch.history.id != null }.take(
|
||||
8 - nChaptersItems.mangaList.size
|
||||
)
|
||||
)
|
||||
// TODO: Add Date Added
|
||||
groupedRecentItems = listOf(cReadingItems, nChaptersItems).sortedByDescending {
|
||||
it.mangaList.firstOrNull()?.mch?.history?.last_read ?: 0
|
||||
}
|
||||
} else {
|
||||
groupedRecentItems = listOf(RecentsItem(RecentsItem.SEARCH, recentItems))
|
||||
}
|
||||
withContext(Dispatchers.Main) { controller.showLists(groupedRecentItems) }
|
||||
withContext(Dispatchers.Main) { controller.showLists(recentItems) }
|
||||
}
|
||||
}
|
||||
|
||||
@ -147,7 +174,7 @@ class RecentsPresenter(
|
||||
download = null
|
||||
}
|
||||
|
||||
controller.showLists(groupedRecentItems)
|
||||
controller.showLists(recentItems)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -173,5 +173,14 @@
|
||||
android:layout_height="6dp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/bottom_line" />
|
||||
|
||||
<View
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="1dp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
android:layout_marginStart="10dp"
|
||||
android:background="?android:attr/divider"/>
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
</FrameLayout>
|
34
app/src/main/res/layout/recents_footer_item.xml
Normal file
34
app/src/main/res/layout/recents_footer_item.xml
Normal file
@ -0,0 +1,34 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/recent_item"
|
||||
android:background="?selectable_list_drawable"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<com.google.android.material.textview.MaterialTextView
|
||||
android:id="@+id/title"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="24dp"
|
||||
android:layout_marginTop="6dp"
|
||||
android:layout_marginBottom="12dp"
|
||||
android:textAppearance="@style/TextAppearance.MaterialComponents.Body1"
|
||||
app:layout_constraintStart_toEndOf="@id/arrow"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
android:textColor="?colorAccent"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
tools:text="@string/view_all_updates" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/arrow"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:src="@drawable/ic_arrow_forward_white_24dp"
|
||||
android:tint="?colorAccent"
|
||||
android:layout_marginStart="12dp"
|
||||
app:layout_constraintTop_toTopOf="@id/title"
|
||||
app:layout_constraintBottom_toBottomOf="@id/title"
|
||||
app:layout_constraintStart_toStartOf="parent" />
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
23
app/src/main/res/layout/recents_header_item.xml
Normal file
23
app/src/main/res/layout/recents_header_item.xml
Normal file
@ -0,0 +1,23 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/recent_item"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<com.google.android.material.textview.MaterialTextView
|
||||
android:id="@+id/title"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="12dp"
|
||||
android:layout_marginTop="5dp"
|
||||
android:layout_marginBottom="8dp"
|
||||
android:textAppearance="@style/TextAppearance.MaterialComponents.Headline6"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintHorizontal_bias="0.0"
|
||||
tools:text="@string/label_recent_updates" />
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
@ -21,7 +21,7 @@
|
||||
<string name="label_catalogues">Catalogues</string>
|
||||
<string name="label_recent_updates">Recent updates</string>
|
||||
<string name="new_chapters">New chapters</string>
|
||||
<string name="new_additions">New additions</string>
|
||||
<string name="new_additions">Newly added</string>
|
||||
<string name="label_selected">Selected: %1$d</string>
|
||||
<string name="label_migration">Source migration</string>
|
||||
<string name="label_extensions">Extensions</string>
|
||||
@ -761,7 +761,9 @@
|
||||
<string name="previously_read_chapter">Previously read Chapter %1$s</string>
|
||||
<string name="last_read_chapter_x">Last read Chapter %1$s</string>
|
||||
<string name="last_read_x">Last read %1$s</string>
|
||||
<string name="uploaded_x">Uploaded %1$s</string>
|
||||
<string name="read_x">Read %1$s</string>
|
||||
<string name="updated_x">Updated %1$s</string>
|
||||
<string name="added_x">Added %1$s</string>
|
||||
<string name="view_history">View history</string>
|
||||
<string name="view_all_updates">View all updates</string>
|
||||
<string name="marked_as_read">Marked as read</string>
|
||||
|
Loading…
x
Reference in New Issue
Block a user