Add history date section headers

This commit is contained in:
arkon 2020-03-06 23:13:48 -05:00
parent 29eb87b7ef
commit 52434819c3
11 changed files with 59 additions and 43 deletions

View File

@ -19,7 +19,7 @@ import eu.kanade.tachiyomi.ui.base.controller.DialogController
import eu.kanade.tachiyomi.ui.main.ChangelogDialogController import eu.kanade.tachiyomi.ui.main.ChangelogDialogController
import eu.kanade.tachiyomi.ui.setting.SettingsController import eu.kanade.tachiyomi.ui.setting.SettingsController
import eu.kanade.tachiyomi.util.lang.launchNow import eu.kanade.tachiyomi.util.lang.launchNow
import eu.kanade.tachiyomi.util.lang.toTimestampString import eu.kanade.tachiyomi.util.lang.toDateTimestampString
import eu.kanade.tachiyomi.util.preference.defaultValue import eu.kanade.tachiyomi.util.preference.defaultValue
import eu.kanade.tachiyomi.util.preference.onChange import eu.kanade.tachiyomi.util.preference.onChange
import eu.kanade.tachiyomi.util.preference.onClick import eu.kanade.tachiyomi.util.preference.onClick
@ -180,7 +180,7 @@ class AboutController : SettingsController() {
DateFormat.MEDIUM, DateFormat.SHORT, Locale.getDefault()) DateFormat.MEDIUM, DateFormat.SHORT, Locale.getDefault())
outputDf.timeZone = TimeZone.getDefault() outputDf.timeZone = TimeZone.getDefault()
return buildTime.toTimestampString(dateFormat) return buildTime.toDateTimestampString(dateFormat)
} catch (e: ParseException) { } catch (e: ParseException) {
return BuildConfig.BUILD_TIME return BuildConfig.BUILD_TIME
} }

View File

@ -1,4 +1,4 @@
package eu.kanade.tachiyomi.ui.recent.updates package eu.kanade.tachiyomi.ui.recent
import android.text.format.DateUtils import android.text.format.DateUtils
import android.view.View import android.view.View
@ -11,10 +11,10 @@ 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) : AbstractHeaderItem<DateItem.Holder>() { class DateSectionItem(val date: Date) : AbstractHeaderItem<DateSectionItem.Holder>() {
override fun getLayoutRes(): Int { override fun getLayoutRes(): Int {
return R.layout.updates_section_item return R.layout.recent_section_item
} }
override fun createViewHolder(view: View, adapter: FlexibleAdapter<IFlexible<RecyclerView.ViewHolder>>): Holder { override fun createViewHolder(view: View, adapter: FlexibleAdapter<IFlexible<RecyclerView.ViewHolder>>): Holder {
@ -27,7 +27,7 @@ class DateItem(val date: Date) : AbstractHeaderItem<DateItem.Holder>() {
override fun equals(other: Any?): Boolean { override fun equals(other: Any?): Boolean {
if (this === other) return true if (this === other) return true
if (other is DateItem) { if (other is DateSectionItem) {
return date == other.date return date == other.date
} }
return false return false
@ -43,7 +43,7 @@ class DateItem(val date: Date) : AbstractHeaderItem<DateItem.Holder>() {
val section_text: TextView = view.findViewById(R.id.section_text) val section_text: TextView = view.findViewById(R.id.section_text)
fun bind(item: DateItem) { fun bind(item: DateSectionItem) {
section_text.text = DateUtils.getRelativeTimeSpanString(item.date.time, now, DateUtils.DAY_IN_MILLIS) section_text.text = DateUtils.getRelativeTimeSpanString(item.date.time, now, DateUtils.DAY_IN_MILLIS)
} }
} }

View File

@ -1,10 +1,7 @@
package eu.kanade.tachiyomi.ui.recent.history package eu.kanade.tachiyomi.ui.recent.history
import eu.davidea.flexibleadapter.FlexibleAdapter import eu.davidea.flexibleadapter.FlexibleAdapter
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 java.text.DateFormat
import java.text.DecimalFormat import java.text.DecimalFormat
import java.text.DecimalFormatSymbols import java.text.DecimalFormatSymbols
import uy.kohesive.injekt.injectLazy import uy.kohesive.injekt.injectLazy
@ -34,9 +31,10 @@ class HistoryAdapter(controller: HistoryController) :
val decimalFormat = DecimalFormat("#.###", DecimalFormatSymbols() val decimalFormat = DecimalFormat("#.###", DecimalFormatSymbols()
.apply { decimalSeparator = '.' }) .apply { decimalSeparator = '.' })
private val preferences: PreferencesHelper by injectLazy() init {
setDisplayHeadersAtStartUp(true)
val dateFormat: DateFormat = preferences.dateFormat().getOrDefault() setStickyHeaders(true)
}
interface OnResumeClickListener { interface OnResumeClickListener {
fun onResumeClick(position: Int) fun onResumeClick(position: Int)

View File

@ -61,7 +61,7 @@ class HistoryHolder(
.format(adapter.sourceManager.getOrStub(manga.source).toString(), formattedNumber) .format(adapter.sourceManager.getOrStub(manga.source).toString(), formattedNumber)
// Set last read timestamp title // Set last read timestamp title
last_read.text = Date(history.last_read).toTimestampString(adapter.dateFormat) last_read.text = Date(history.last_read).toTimestampString()
// Set cover // Set cover
GlideApp.with(itemView.context).clear(cover) GlideApp.with(itemView.context).clear(cover)

View File

@ -3,12 +3,14 @@ package eu.kanade.tachiyomi.ui.recent.history
import android.view.View import android.view.View
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import eu.davidea.flexibleadapter.FlexibleAdapter import eu.davidea.flexibleadapter.FlexibleAdapter
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem import eu.davidea.flexibleadapter.items.AbstractSectionableItem
import eu.davidea.flexibleadapter.items.IFlexible import eu.davidea.flexibleadapter.items.IFlexible
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.database.models.MangaChapterHistory import eu.kanade.tachiyomi.data.database.models.MangaChapterHistory
import eu.kanade.tachiyomi.ui.recent.DateSectionItem
class HistoryItem(val mch: MangaChapterHistory) : AbstractFlexibleItem<HistoryHolder>() { class HistoryItem(val mch: MangaChapterHistory, header: DateSectionItem) :
AbstractSectionableItem<HistoryHolder, DateSectionItem>(header) {
override fun getLayoutRes(): Int { override fun getLayoutRes(): Int {
return R.layout.history_item return R.layout.history_item
@ -24,7 +26,6 @@ class HistoryItem(val mch: MangaChapterHistory) : AbstractFlexibleItem<HistoryHo
position: Int, position: Int,
payloads: List<Any?>? payloads: List<Any?>?
) { ) {
holder.bind(mch) holder.bind(mch)
} }

View File

@ -5,10 +5,14 @@ import eu.kanade.tachiyomi.data.database.DatabaseHelper
import eu.kanade.tachiyomi.data.database.models.Chapter import eu.kanade.tachiyomi.data.database.models.Chapter
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.database.models.MangaChapterHistory
import eu.kanade.tachiyomi.ui.base.presenter.BasePresenter import eu.kanade.tachiyomi.ui.base.presenter.BasePresenter
import eu.kanade.tachiyomi.ui.recent.DateSectionItem
import eu.kanade.tachiyomi.util.lang.toDateKey
import java.util.Calendar import java.util.Calendar
import java.util.Comparator import java.util.Comparator
import java.util.Date import java.util.Date
import java.util.TreeMap
import rx.Observable import rx.Observable
import rx.android.schedulers.AndroidSchedulers import rx.android.schedulers.AndroidSchedulers
import uy.kohesive.injekt.injectLazy import uy.kohesive.injekt.injectLazy
@ -38,13 +42,21 @@ class HistoryPresenter : BasePresenter<HistoryController>() {
* @return list of history * @return list of history
*/ */
fun getRecentMangaObservable(): Observable<List<HistoryItem>> { fun getRecentMangaObservable(): Observable<List<HistoryItem>> {
// Set date for recent manga // Set date limit for recent manga
val cal = Calendar.getInstance() val cal = Calendar.getInstance()
cal.time = Date() cal.time = Date()
cal.add(Calendar.MONTH, -1) cal.add(Calendar.MONTH, -1)
return db.getRecentManga(cal.time).asRxObservable() return db.getRecentManga(cal.time).asRxObservable()
.map { recents -> recents.map(::HistoryItem) } .map { recents ->
val map = TreeMap<Date, MutableList<MangaChapterHistory>> { d1, d2 -> d2.compareTo(d1) }
val byDay = recents
.groupByTo(map, { it.history.last_read.toDateKey() })
byDay.flatMap {
val dateItem = DateSectionItem(it.key)
it.value.map { HistoryItem(it, dateItem) }
}
}
.observeOn(AndroidSchedulers.mainThread()) .observeOn(AndroidSchedulers.mainThread())
} }

View File

@ -9,9 +9,10 @@ import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.database.models.Chapter import eu.kanade.tachiyomi.data.database.models.Chapter
import eu.kanade.tachiyomi.data.database.models.Manga import eu.kanade.tachiyomi.data.database.models.Manga
import eu.kanade.tachiyomi.data.download.model.Download import eu.kanade.tachiyomi.data.download.model.Download
import eu.kanade.tachiyomi.ui.recent.DateSectionItem
class UpdatesItem(val chapter: Chapter, val manga: Manga, header: DateItem) : class UpdatesItem(val chapter: Chapter, val manga: Manga, header: DateSectionItem) :
AbstractSectionableItem<UpdatesHolder, DateItem>(header) { AbstractSectionableItem<UpdatesHolder, DateSectionItem>(header) {
private var _status: Int = 0 private var _status: Int = 0
@ -41,7 +42,6 @@ class UpdatesItem(val chapter: Chapter, val manga: Manga, header: DateItem) :
position: Int, position: Int,
payloads: List<Any?>? payloads: List<Any?>?
) { ) {
holder.bind(this) holder.bind(this)
} }

View File

@ -8,6 +8,8 @@ import eu.kanade.tachiyomi.data.download.model.Download
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.base.presenter.BasePresenter import eu.kanade.tachiyomi.ui.base.presenter.BasePresenter
import eu.kanade.tachiyomi.ui.recent.DateSectionItem
import eu.kanade.tachiyomi.util.lang.toDateKey
import java.util.Calendar import java.util.Calendar
import java.util.Date import java.util.Date
import java.util.TreeMap import java.util.TreeMap
@ -60,9 +62,9 @@ class UpdatesPresenter(
.map { mangaChapters -> .map { mangaChapters ->
val map = TreeMap<Date, MutableList<MangaChapter>> { d1, d2 -> d2.compareTo(d1) } val map = TreeMap<Date, MutableList<MangaChapter>> { d1, d2 -> d2.compareTo(d1) }
val byDay = mangaChapters val byDay = mangaChapters
.groupByTo(map, { getMapKey(it.chapter.date_fetch) }) .groupByTo(map, { it.chapter.date_fetch.toDateKey() })
byDay.flatMap { byDay.flatMap {
val dateItem = DateItem(it.key) val dateItem = DateSectionItem(it.key)
it.value it.value
.sortedWith(compareBy({ it.chapter.date_fetch }, { it.chapter.chapter_number })).asReversed() .sortedWith(compareBy({ it.chapter.date_fetch }, { it.chapter.chapter_number })).asReversed()
.map { UpdatesItem(it.chapter, it.manga, dateItem) } .map { UpdatesItem(it.chapter, it.manga, dateItem) }
@ -84,22 +86,6 @@ class UpdatesPresenter(
} }
} }
/**
* 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
}
/** /**
* Returns observable containing chapter status. * Returns observable containing chapter status.
* *

View File

@ -1,10 +1,31 @@
package eu.kanade.tachiyomi.util.lang package eu.kanade.tachiyomi.util.lang
import java.text.DateFormat import java.text.DateFormat
import java.util.Calendar
import java.util.Date import java.util.Date
fun Date.toTimestampString(dateFormatter: DateFormat): String { fun Date.toDateTimestampString(dateFormatter: DateFormat): String {
val date = dateFormatter.format(this) val date = dateFormatter.format(this)
val time = DateFormat.getTimeInstance(DateFormat.SHORT).format(this) val time = DateFormat.getTimeInstance(DateFormat.SHORT).format(this)
return "$date $time" return "$date $time"
} }
fun Date.toTimestampString(): String {
return DateFormat.getTimeInstance(DateFormat.SHORT).format(this)
}
/**
* Get date as time key
*
* @param date desired date
* @return date as time key
*/
fun Long.toDateKey(): Date {
val cal = Calendar.getInstance()
cal.time = Date(this)
cal[Calendar.HOUR_OF_DAY] = 0
cal[Calendar.MINUTE] = 0
cal[Calendar.SECOND] = 0
cal[Calendar.MILLISECOND] = 0
return cal.time
}

View File

@ -10,8 +10,6 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:clipToPadding="false" android:clipToPadding="false"
android:paddingTop="4dp"
android:paddingBottom="4dp"
tools:listitem="@layout/history_item" /> tools:listitem="@layout/history_item" />
<eu.kanade.tachiyomi.widget.EmptyView <eu.kanade.tachiyomi.widget.EmptyView