Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ class CalendarAdapter2(

override fun getItemViewType(position: Int): Int {
val item = getItem(position)
val isHeader = item != null && item.episode == null
val isHeader = item.episode == null
return if (isHeader) {
VIEW_TYPE_HEADER
} else {
Expand All @@ -55,11 +55,11 @@ class CalendarAdapter2(

override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
val currentItem = getItem(position)
val previousPosition = position - 1
val previousItem = if (previousPosition >= 0) getItem(previousPosition) else null
// val previousPosition = position - 1
// val previousItem = if (previousPosition >= 0) getItem(previousPosition) else null
when (holder) {
is CalendarHeaderViewHolder -> holder.bind(context, currentItem)
is CalendarItemViewHolder -> holder.bind(context, currentItem, previousItem)
is CalendarItemViewHolder -> holder.bind(context, currentItem, null)
else -> throw IllegalArgumentException("Unknown view holder type")
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
package com.battlelancer.seriesguide.ui.shows

import android.content.Context
import androidx.paging.PositionalDataSource
import com.battlelancer.seriesguide.model.EpisodeWithShow
import com.battlelancer.seriesguide.ui.shows.CalendarFragment2ViewModel.CalendarItem
import com.battlelancer.seriesguide.util.TimeTools
import java.util.Calendar
import java.util.LinkedList

class CalendarDataSource(
private val context: Context,
private val source: PositionalDataSource<EpisodeWithShow>
) : PositionalDataSource<CalendarItem>() {

override fun addInvalidatedCallback(onInvalidatedCallback: InvalidatedCallback) {
source.addInvalidatedCallback(onInvalidatedCallback)
}

override fun removeInvalidatedCallback(onInvalidatedCallback: InvalidatedCallback) {
source.removeInvalidatedCallback(onInvalidatedCallback)
}

override fun invalidate() {
source.invalidate()
}

override fun isInvalid(): Boolean {
return source.isInvalid
}

override fun loadInitial(
params: LoadInitialParams,
callback: LoadInitialCallback<CalendarItem>
) {
source.loadInitial(
params,
object : PositionalDataSource.LoadInitialCallback<EpisodeWithShow>() {
override fun onResult(data: List<EpisodeWithShow>, position: Int, totalCount: Int) {
// not using placeholders, can not know the number of headers in advance
callback.onResult(convert(data), position)
}

override fun onResult(data: List<EpisodeWithShow>, position: Int) {
throw IllegalStateException("onResult called by delegate data source.")
}
})
}

override fun loadRange(params: LoadRangeParams, callback: LoadRangeCallback<CalendarItem>) {
// TODO reduce params.startPosition by amount of previously added headers?
source.loadRange(
params,
object : PositionalDataSource.LoadRangeCallback<EpisodeWithShow>() {
override fun onResult(data: List<EpisodeWithShow>) {
val converted = convert(data)
if (converted.size > params.loadSize) {
val cutData = LinkedList<CalendarItem>()
for (i in 0 until params.loadSize) {
cutData.add(converted[i])
}
callback.onResult(cutData)
} else {
callback.onResult(converted)
}
}
})
}

// TODO would also need item before this list if not at position 0
private fun convert(episodes: List<EpisodeWithShow>): MutableList<CalendarItem> {
val mapped = LinkedList<CalendarItem>()

val calendar = Calendar.getInstance()
var previousHeaderTime: Long = 0
episodes.forEachIndexed { index, episode ->
// insert header if first item or previous item has different header time
val headerTime = calculateHeaderTime(
context,
calendar,
episode.episode_firstairedms
)
if (index == 0 || headerTime != previousHeaderTime) {
mapped.add(CalendarItem(headerTime, null))
}
previousHeaderTime = headerTime

mapped.add(CalendarItem(headerTime, episode))
}

return mapped
}

private fun calculateHeaderTime(context: Context, calendar: Calendar, releaseTime: Long): Long {
val actualRelease = TimeTools.applyUserOffset(context, releaseTime)

calendar.time = actualRelease
// not midnight because upcoming->recent is delayed 1 hour
// so header would display wrong relative time close to midnight
calendar.set(Calendar.HOUR_OF_DAY, 1)
calendar.set(Calendar.MINUTE, 0)
calendar.set(Calendar.SECOND, 0)
calendar.set(Calendar.MILLISECOND, 0)

return calendar.timeInMillis
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@ import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.Transformations
import androidx.paging.Config
import androidx.paging.DataSource
import androidx.paging.PagedList
import androidx.paging.PositionalDataSource
import androidx.paging.toLiveData
import androidx.sqlite.db.SimpleSQLiteQuery
import com.battlelancer.seriesguide.model.EpisodeWithShow
Expand All @@ -24,25 +26,26 @@ class CalendarFragment2ViewModel(application: Application) : AndroidViewModel(ap
val upcomingEpisodesLiveData: LiveData<PagedList<CalendarItem>>

private val calendarItemPagingConfig = Config(
pageSize = 50,
pageSize = 10,
enablePlaceholders = false /* some items may have a header, so their height differs */
)

init {
upcomingEpisodesLiveData = Transformations.switchMap(queryLiveData) { queryString ->
SgRoomDatabase.getInstance(getApplication()).episodeHelper()
.getEpisodesWithShow(SimpleSQLiteQuery(queryString, null))
.mapByPage { episodes ->
val calendar = Calendar.getInstance()
episodes.map { episode ->
val headerTime = calculateHeaderTime(
getApplication(),
calendar,
episode.episode_firstairedms
)
CalendarItem(headerTime, episode)
}
}.toLiveData(config = calendarItemPagingConfig)
object : DataSource.Factory<Int, CalendarItem>() {
override fun create(): DataSource<Int, CalendarItem> {
return CalendarDataSource(
getApplication(),
SgRoomDatabase.getInstance(getApplication()).episodeHelper()
.getEpisodesWithShow(
SimpleSQLiteQuery(
queryString,
null
)
).create() as PositionalDataSource<EpisodeWithShow>
)
}
}.toLiveData(config = calendarItemPagingConfig)
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ import com.battlelancer.seriesguide.util.TextTools
import com.battlelancer.seriesguide.util.TimeTools
import com.battlelancer.seriesguide.widgets.WatchedBox
import com.uwetrottmann.androidutils.CheatSheet
import java.util.Date

class CalendarItemViewHolder(
parent: ViewGroup,
Expand Down Expand Up @@ -73,14 +72,15 @@ class CalendarItemViewHolder(
this.item = item

// optional header
val isShowingHeader = previousItem == null || previousItem.headerTime != item.headerTime
headerTextView.isGone = !isShowingHeader
headerTextView.text = if (isShowingHeader) {
// display headers like "Mon in 3 days", also "today" when applicable
TimeTools.formatToLocalDayAndRelativeWeek(context, Date(item.headerTime))
} else {
null
}
headerTextView.isGone = true
// val isShowingHeader = previousItem == null || previousItem.headerTime != item.headerTime
// headerTextView.isGone = !isShowingHeader
// headerTextView.text = if (isShowingHeader) {
// // display headers like "Mon in 3 days", also "today" when applicable
// TimeTools.formatToLocalDayAndRelativeWeek(context, Date(item.headerTime))
// } else {
// null
// }

val episode = item.episode!!

Expand Down