diff --git a/app/src/main/java/com/battlelancer/seriesguide/ui/shows/CalendarAdapter2.kt b/app/src/main/java/com/battlelancer/seriesguide/ui/shows/CalendarAdapter2.kt index dc5ad489e3..344f50b7a6 100644 --- a/app/src/main/java/com/battlelancer/seriesguide/ui/shows/CalendarAdapter2.kt +++ b/app/src/main/java/com/battlelancer/seriesguide/ui/shows/CalendarAdapter2.kt @@ -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 { @@ -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") } } diff --git a/app/src/main/java/com/battlelancer/seriesguide/ui/shows/CalendarDataSource.kt b/app/src/main/java/com/battlelancer/seriesguide/ui/shows/CalendarDataSource.kt new file mode 100644 index 0000000000..1e0f9569ea --- /dev/null +++ b/app/src/main/java/com/battlelancer/seriesguide/ui/shows/CalendarDataSource.kt @@ -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 +) : PositionalDataSource() { + + 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 + ) { + source.loadInitial( + params, + object : PositionalDataSource.LoadInitialCallback() { + override fun onResult(data: List, 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, position: Int) { + throw IllegalStateException("onResult called by delegate data source.") + } + }) + } + + override fun loadRange(params: LoadRangeParams, callback: LoadRangeCallback) { + // TODO reduce params.startPosition by amount of previously added headers? + source.loadRange( + params, + object : PositionalDataSource.LoadRangeCallback() { + override fun onResult(data: List) { + val converted = convert(data) + if (converted.size > params.loadSize) { + val cutData = LinkedList() + 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): MutableList { + val mapped = LinkedList() + + 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 + } +} \ No newline at end of file diff --git a/app/src/main/java/com/battlelancer/seriesguide/ui/shows/CalendarFragment2ViewModel.kt b/app/src/main/java/com/battlelancer/seriesguide/ui/shows/CalendarFragment2ViewModel.kt index 6862fb2cb3..e2a2d32860 100644 --- a/app/src/main/java/com/battlelancer/seriesguide/ui/shows/CalendarFragment2ViewModel.kt +++ b/app/src/main/java/com/battlelancer/seriesguide/ui/shows/CalendarFragment2ViewModel.kt @@ -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 @@ -24,25 +26,26 @@ class CalendarFragment2ViewModel(application: Application) : AndroidViewModel(ap val upcomingEpisodesLiveData: LiveData> 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() { + override fun create(): DataSource { + return CalendarDataSource( + getApplication(), + SgRoomDatabase.getInstance(getApplication()).episodeHelper() + .getEpisodesWithShow( + SimpleSQLiteQuery( + queryString, + null + ) + ).create() as PositionalDataSource + ) + } + }.toLiveData(config = calendarItemPagingConfig) } } diff --git a/app/src/main/java/com/battlelancer/seriesguide/ui/shows/CalendarItemViewHolder.kt b/app/src/main/java/com/battlelancer/seriesguide/ui/shows/CalendarItemViewHolder.kt index af7c112292..2c1fae13ed 100644 --- a/app/src/main/java/com/battlelancer/seriesguide/ui/shows/CalendarItemViewHolder.kt +++ b/app/src/main/java/com/battlelancer/seriesguide/ui/shows/CalendarItemViewHolder.kt @@ -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, @@ -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!!