mirror of
https://github.com/JustinZeus/Wholphin.git
synced 2026-07-08 23:51:21 +02:00
Handle DST and ranges
This commit is contained in:
parent
bf28209255
commit
8792c7dcb2
2 changed files with 85 additions and 65 deletions
|
|
@ -59,13 +59,11 @@ class LiveTvViewModel
|
|||
val channels = MutableLiveData<List<TvChannel>>()
|
||||
val channelProgramCount = mutableMapOf<UUID, Int>()
|
||||
val programs = MutableLiveData<FetchedPrograms>()
|
||||
// val programsByChannel = MutableLiveData<Map<UUID, List<TvProgram>>>(mapOf())
|
||||
|
||||
val fetchingItem = MutableLiveData<LoadingState>(LoadingState.Pending)
|
||||
val fetchedItem = MutableLiveData<BaseItem?>(null)
|
||||
|
||||
private val range = 8
|
||||
// val fetchedRange = MutableLiveData<IntRange>(0..<range)
|
||||
private val range = 150
|
||||
|
||||
fun init(firstLoad: Boolean) {
|
||||
start = LocalDateTime.now().truncatedTo(ChronoUnit.HOURS)
|
||||
|
|
@ -115,7 +113,7 @@ class LiveTvViewModel
|
|||
) = mutex.withLock {
|
||||
val maxStartDate = start.plusHours(MAX_HOURS).minusMinutes(1)
|
||||
val minEndDate = start.plusMinutes(1L)
|
||||
val channelsToFetch = channels.subList(range.first, range.last)
|
||||
val channelsToFetch = channels.subList(range.first, range.last + 1)
|
||||
Timber.v("Fetching programs for $range channels ${channelsToFetch.size}")
|
||||
val request =
|
||||
GetProgramsDto(
|
||||
|
|
@ -199,20 +197,13 @@ class LiveTvViewModel
|
|||
}
|
||||
val finalProgramList =
|
||||
(programs + fake)
|
||||
.filterNot {
|
||||
// TODO filter out items that start after end
|
||||
// This can happen due to daylight savings switch or just be bad data
|
||||
it.startHours >= it.endHours
|
||||
}.sortedWith(
|
||||
.sortedWith(
|
||||
compareBy(
|
||||
{ channelsIdToIndex[it.channelId]!! },
|
||||
{ it.start },
|
||||
),
|
||||
)
|
||||
Timber.d("Got ${programs.size} programs & ${fake.size} fake programs")
|
||||
finalProgramList
|
||||
.filter { it.startHours == it.endHours }
|
||||
.let { Timber.e("Items with same start & end!!! %s", it) }
|
||||
withContext(Dispatchers.Main) {
|
||||
this@LiveTvViewModel.programs.value =
|
||||
FetchedPrograms(range, finalProgramList, finalProgramsByChannel)
|
||||
|
|
@ -251,11 +242,10 @@ class LiveTvViewModel
|
|||
viewModelScope.launchIO(ExceptionHandler(autoToast = true)) {
|
||||
if (series) {
|
||||
api.liveTvApi.cancelSeriesTimer(timerId)
|
||||
fetchProgramsWithLoading(channels.value.orEmpty(), programs.value!!.range)
|
||||
} else {
|
||||
api.liveTvApi.cancelTimer(timerId)
|
||||
refreshProgram(programIndex, programId)
|
||||
}
|
||||
fetchProgramsWithLoading(channels.value.orEmpty(), programs.value!!.range)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -269,7 +259,6 @@ class LiveTvViewModel
|
|||
val d by api.liveTvApi.getDefaultTimer(programId.toServerString())
|
||||
if (series) {
|
||||
api.liveTvApi.createSeriesTimer(d)
|
||||
fetchProgramsWithLoading(channels.value.orEmpty(), programs.value!!.range)
|
||||
} else {
|
||||
val payload =
|
||||
TimerInfoDto(
|
||||
|
|
@ -295,8 +284,8 @@ class LiveTvViewModel
|
|||
keepUntil = d.keepUntil,
|
||||
)
|
||||
api.liveTvApi.createTimer(payload)
|
||||
refreshProgram(programIndex, programId)
|
||||
}
|
||||
fetchProgramsWithLoading(channels.value.orEmpty(), programs.value!!.range)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -325,17 +314,33 @@ class LiveTvViewModel
|
|||
loading.setValueOnMain(LoadingState.Success)
|
||||
}
|
||||
|
||||
fun onFocusChannel(position: RowColumn): Job? {
|
||||
return channels.value?.let { channels ->
|
||||
private var focusLoadingJob: Job? = null
|
||||
|
||||
fun onFocusChannel(position: RowColumn) {
|
||||
channels.value?.let { channels ->
|
||||
val fetchedRange = programs.value!!.range
|
||||
val quarter = (fetchedRange.last - fetchedRange.start) / 4
|
||||
val rangeStart = fetchedRange.start + quarter
|
||||
val rangeEnd = fetchedRange.last - quarter
|
||||
val quarter = range / 4
|
||||
var rangeStart = fetchedRange.start + quarter
|
||||
var rangeEnd = fetchedRange.last - quarter
|
||||
|
||||
if (rangeEnd - rangeStart < range) {
|
||||
if (position.row < range / 2) {
|
||||
// Close to beginning
|
||||
rangeStart = 0
|
||||
} else if (position.row > (channels.size - range / 2)) {
|
||||
// Close to the end
|
||||
rangeEnd = channels.size
|
||||
}
|
||||
}
|
||||
val testRange = rangeStart..<rangeEnd
|
||||
|
||||
// Timber.v(
|
||||
// "onFocusChannel: position=$position, fetchedRange=$fetchedRange, testRange=$testRange",
|
||||
// )
|
||||
Timber.v(
|
||||
"onFocusChannel: position=%s, fetchedRange=%s, testRange=%s",
|
||||
position,
|
||||
fetchedRange,
|
||||
testRange,
|
||||
)
|
||||
|
||||
val fetchStart = (position.row - range).coerceAtLeast(0)
|
||||
val fetchEnd = (position.row + range).coerceAtMost(channels.size)
|
||||
val newFetchRange = fetchStart..<fetchEnd
|
||||
|
|
@ -344,11 +349,12 @@ class LiveTvViewModel
|
|||
// Fetch new data
|
||||
if (position.row !in testRange && !newFetchRange.within(fetchedRange)) {
|
||||
Timber.v("Loading more programs for channels $newFetchRange")
|
||||
return viewModelScope.launchIO {
|
||||
focusLoadingJob?.cancel()
|
||||
focusLoadingJob =
|
||||
viewModelScope.launchIO {
|
||||
fetchProgramsWithLoading(channels, newFetchRange)
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -51,7 +51,6 @@ import com.github.damontecres.wholphin.ui.components.LoadingPage
|
|||
import com.github.damontecres.wholphin.ui.data.RowColumn
|
||||
import com.github.damontecres.wholphin.ui.enableMarquee
|
||||
import com.github.damontecres.wholphin.ui.nav.Destination
|
||||
import com.github.damontecres.wholphin.ui.playback.isDpad
|
||||
import com.github.damontecres.wholphin.ui.rememberPosition
|
||||
import com.github.damontecres.wholphin.ui.tryRequestFocus
|
||||
import com.github.damontecres.wholphin.util.ExceptionHandler
|
||||
|
|
@ -65,6 +64,7 @@ import timber.log.Timber
|
|||
import java.time.LocalDateTime
|
||||
import java.time.OffsetDateTime
|
||||
import java.util.UUID
|
||||
import kotlin.math.abs
|
||||
|
||||
@Composable
|
||||
fun TvGuideGrid(
|
||||
|
|
@ -254,10 +254,6 @@ fun TvGuideGrid(
|
|||
if (it.type == KeyEventType.KeyUp) {
|
||||
return@onPreviewKeyEvent false
|
||||
}
|
||||
if (isDpad(it) && loading) {
|
||||
// Prevent movement during loading
|
||||
return@onPreviewKeyEvent true
|
||||
}
|
||||
val item = focusedItem
|
||||
val newFocusedItem =
|
||||
when (it.key) {
|
||||
|
|
@ -304,12 +300,16 @@ fun TvGuideGrid(
|
|||
val currentChannel = channels[item.row].id
|
||||
val currentProgram =
|
||||
programs.programsByChannel[currentChannel]?.get(item.column)
|
||||
val newChannelId = channels[newChannelIndex].id
|
||||
val newChannelPrograms =
|
||||
programs.programsByChannel[newChannelId]
|
||||
if (newChannelPrograms == null) {
|
||||
return@onPreviewKeyEvent true
|
||||
}
|
||||
if (currentProgram == null) {
|
||||
item
|
||||
} else {
|
||||
val start = currentProgram.startHours
|
||||
val newChannelPrograms =
|
||||
programs.programsByChannel[channels[newChannelIndex].id].orEmpty()
|
||||
val pIndex =
|
||||
newChannelPrograms.indexOfFirst { start in (it.startHours..<it.endHours) }
|
||||
if (pIndex >= 0) {
|
||||
|
|
@ -335,11 +335,20 @@ fun TvGuideGrid(
|
|||
// If trying to move below the final channel, then move focus out of the grid
|
||||
focusManager.moveFocus(FocusDirection.Down)
|
||||
null
|
||||
} else if (channelColumnFocused) {
|
||||
} else {
|
||||
// Otherwise, moving to a new row
|
||||
// Get the new row/channel's programs
|
||||
val newChannelId = channels[newChannelIndex].id
|
||||
val newChannelPrograms =
|
||||
programs.programsByChannel[newChannelId]
|
||||
if (newChannelPrograms == null) {
|
||||
// This means there is no data for the new channel and it is loading in the background
|
||||
return@onPreviewKeyEvent true
|
||||
}
|
||||
if (channelColumnFocused) {
|
||||
// If focused on the channel column, move down a channel
|
||||
RowColumn(newChannelIndex, 0)
|
||||
} else {
|
||||
// Otherwise, moving to a new row
|
||||
// Get current program & its start time
|
||||
val currentChannel = channels[item.row].id
|
||||
val currentProgram =
|
||||
|
|
@ -349,9 +358,6 @@ fun TvGuideGrid(
|
|||
item
|
||||
} else {
|
||||
val start = currentProgram.startHours
|
||||
// Get the new row/channel's programs
|
||||
val newChannelPrograms =
|
||||
programs.programsByChannel[channels[newChannelIndex].id].orEmpty()
|
||||
// Find the first program where the start time (of the previously focused program) is in the middle of a program
|
||||
val pIndex =
|
||||
newChannelPrograms.indexOfFirst { start in (it.startHours..<it.endHours) }
|
||||
|
|
@ -377,6 +383,7 @@ fun TvGuideGrid(
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Key.DirectionCenter, Key.Enter, Key.NumPadEnter -> {
|
||||
if (channelColumnFocused) {
|
||||
|
|
@ -416,7 +423,7 @@ fun TvGuideGrid(
|
|||
column =
|
||||
newFocusedItem.column.coerceIn(
|
||||
0,
|
||||
programs.size - 1,
|
||||
(programs.size - 1).coerceAtLeast(0),
|
||||
),
|
||||
)
|
||||
focusedItem = toFocus
|
||||
|
|
@ -486,7 +493,14 @@ fun TvGuideGrid(
|
|||
layoutInfo = { programIndex ->
|
||||
val program = programs.programs[programIndex]
|
||||
val channelIndex = channels.indexOfFirst { it.id == program.channelId }
|
||||
ProgramGuideItem.Program(channelIndex, program.startHours, program.endHours)
|
||||
// Using the duration for endHour accounts for daylight savings switch
|
||||
// Eg a 1:30am-1:00am show
|
||||
val duration = abs(program.endHours - program.startHours)
|
||||
ProgramGuideItem.Program(
|
||||
channelIndex,
|
||||
program.startHours,
|
||||
program.startHours + duration,
|
||||
)
|
||||
},
|
||||
) { programIndex ->
|
||||
val program = programs.programs[programIndex]
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue