mirror of
https://github.com/JustinZeus/Wholphin.git
synced 2026-07-08 15:50:16 +02:00
Mark recordings
This commit is contained in:
parent
753acb7b38
commit
fb4e36168c
2 changed files with 96 additions and 19 deletions
|
|
@ -4,6 +4,7 @@ import androidx.lifecycle.MutableLiveData
|
|||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.github.damontecres.wholphin.ui.detail.series.SeasonEpisode
|
||||
import com.github.damontecres.wholphin.ui.isNotNullOrBlank
|
||||
import com.github.damontecres.wholphin.ui.nav.NavigationManager
|
||||
import com.github.damontecres.wholphin.util.GetProgramsDtoHandler
|
||||
import com.github.damontecres.wholphin.util.LoadingExceptionHandler
|
||||
|
|
@ -40,7 +41,7 @@ class LiveTvViewModel
|
|||
val loading = MutableLiveData<LoadingState>(LoadingState.Pending)
|
||||
|
||||
val start =
|
||||
LocalDateTime.now().minusMinutes(30).truncatedTo(ChronoUnit.HOURS)
|
||||
LocalDateTime.now().truncatedTo(ChronoUnit.HOURS)
|
||||
|
||||
val channels = MutableLiveData<List<TvChannel>>()
|
||||
val programs = MutableLiveData<List<TvProgram>>()
|
||||
|
|
@ -91,14 +92,8 @@ class LiveTvViewModel
|
|||
channelId = dto.channelId!!,
|
||||
start = dto.startDate!!,
|
||||
end = dto.endDate!!,
|
||||
startHours =
|
||||
java.time.Duration
|
||||
.between(start, dto.startDate!!)
|
||||
.seconds / (60f * 60f),
|
||||
endHours =
|
||||
java.time.Duration
|
||||
.between(start, dto.endDate!!)
|
||||
.seconds / (60f * 60f),
|
||||
startHours = hoursBetween(start, dto.startDate!!),
|
||||
endHours = hoursBetween(start, dto.endDate!!),
|
||||
duration = dto.runTimeTicks!!.ticks,
|
||||
name = dto.seriesName ?: dto.name,
|
||||
subtitle = dto.episodeTitle.takeIf { dto.isSeries ?: false },
|
||||
|
|
@ -108,6 +103,8 @@ class LiveTvViewModel
|
|||
} else {
|
||||
null
|
||||
},
|
||||
isRecording = dto.timerId.isNotNullOrBlank(),
|
||||
isSeriesRecording = dto.seriesTimerId.isNotNullOrBlank(),
|
||||
)
|
||||
}.filter { it.startHours >= 0 && it.endHours >= 0 } // TODO shouldn't need to filter client side
|
||||
|
||||
|
|
@ -130,6 +127,8 @@ class LiveTvViewModel
|
|||
name = "No data",
|
||||
subtitle = null,
|
||||
seasonEpisode = null,
|
||||
isRecording = false,
|
||||
isSeriesRecording = false,
|
||||
)
|
||||
}
|
||||
put(channel.id, fakePrograms)
|
||||
|
|
@ -209,6 +208,17 @@ class LiveTvViewModel
|
|||
// }
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of hours between two [LocalDateTime]
|
||||
*/
|
||||
fun hoursBetween(
|
||||
start: LocalDateTime,
|
||||
target: LocalDateTime,
|
||||
): Float =
|
||||
java.time.Duration
|
||||
.between(start, target)
|
||||
.seconds / (60f * 60f)
|
||||
|
||||
data class TvChannel(
|
||||
val id: UUID,
|
||||
val number: String?,
|
||||
|
|
@ -227,4 +237,6 @@ data class TvProgram(
|
|||
val name: String?,
|
||||
val subtitle: String?,
|
||||
val seasonEpisode: SeasonEpisode?,
|
||||
val isRecording: Boolean,
|
||||
val isSeriesRecording: Boolean,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -9,16 +9,20 @@ import androidx.compose.foundation.layout.Row
|
|||
import androidx.compose.foundation.layout.fillMaxHeight
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.livedata.observeAsState
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.focus.FocusDirection
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.input.key.Key
|
||||
import androidx.compose.ui.input.key.KeyEventType
|
||||
import androidx.compose.ui.input.key.key
|
||||
|
|
@ -28,6 +32,8 @@ import androidx.compose.ui.platform.LocalFocusManager
|
|||
import androidx.compose.ui.unit.dp
|
||||
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
||||
import androidx.tv.material3.MaterialTheme
|
||||
import androidx.tv.material3.Surface
|
||||
import androidx.tv.material3.SurfaceDefaults
|
||||
import androidx.tv.material3.Text
|
||||
import androidx.tv.material3.contentColorFor
|
||||
import androidx.tv.material3.surfaceColorAtElevation
|
||||
|
|
@ -39,6 +45,8 @@ import com.github.damontecres.wholphin.ui.nav.Destination
|
|||
import com.github.damontecres.wholphin.ui.rememberInt
|
||||
import com.github.damontecres.wholphin.util.ExceptionHandler
|
||||
import com.github.damontecres.wholphin.util.LoadingState
|
||||
import com.google.common.cache.CacheBuilder
|
||||
import com.google.common.cache.CacheLoader
|
||||
import eu.wewox.programguide.ProgramGuide
|
||||
import eu.wewox.programguide.ProgramGuideDimensions
|
||||
import eu.wewox.programguide.ProgramGuideItem
|
||||
|
|
@ -103,7 +111,6 @@ fun TvGrid(
|
|||
val focusManager = LocalFocusManager.current
|
||||
val state = rememberSaveableProgramGuideState()
|
||||
val scope = rememberCoroutineScope()
|
||||
// val timeline = 0..<24
|
||||
var focusedProgramIndex by rememberInt(0)
|
||||
var focusedChannelIndex by rememberInt(0)
|
||||
|
||||
|
|
@ -116,7 +123,23 @@ fun TvGrid(
|
|||
currentTimeWidth = 2.dp,
|
||||
)
|
||||
|
||||
fun programsBeforeChannel(channelIndex: Int): Int = channels.subList(0, channelIndex).map { it.id }.sumOf { programs[it]?.size ?: 0 }
|
||||
val programsBeforeChannel =
|
||||
remember {
|
||||
CacheBuilder
|
||||
.newBuilder()
|
||||
.maximumSize(200)
|
||||
.build<Int, Int>(
|
||||
object : CacheLoader<Int, Int>() {
|
||||
override fun load(key: Int): Int =
|
||||
channels
|
||||
.subList(0, key)
|
||||
.map { it.id }
|
||||
.sumOf { programs[it]?.size ?: 0 }
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
// fun programsBeforeChannel(channelIndex: Int): Int = channels.subList(0, channelIndex).map { it.id }.sumOf { programs[it]?.size ?: 0 }
|
||||
|
||||
ProgramGuide(
|
||||
state = state,
|
||||
|
|
@ -132,7 +155,7 @@ fun TvGrid(
|
|||
when (it.key) {
|
||||
Key.DirectionRight -> {
|
||||
val nextProgramIndex = focusedProgramIndex + 1
|
||||
val programsBefore = programsBeforeChannel(focusedChannelIndex)
|
||||
val programsBefore = programsBeforeChannel.get(focusedChannelIndex)
|
||||
val relativePosition = nextProgramIndex - programsBefore
|
||||
val channelPrograms =
|
||||
programs[channels[focusedChannelIndex].id].orEmpty()
|
||||
|
|
@ -147,7 +170,8 @@ fun TvGrid(
|
|||
Key.DirectionLeft -> {
|
||||
val nextProgramIndex = focusedProgramIndex - 1
|
||||
if (nextProgramIndex >= 0) {
|
||||
val programsBefore = programsBeforeChannel(focusedChannelIndex)
|
||||
val programsBefore =
|
||||
programsBeforeChannel.get(focusedChannelIndex)
|
||||
val relativePosition = nextProgramIndex - programsBefore
|
||||
// val channelPrograms =
|
||||
// programs[channels[focusedChannel].id].orEmpty()
|
||||
|
|
@ -175,9 +199,9 @@ fun TvGrid(
|
|||
programs[channelId]?.indexOfFirst { start in (it.startHours..<it.endHours) }
|
||||
?: -1
|
||||
if (pIndex >= 0) {
|
||||
programsBeforeChannel(focusedChannelIndex) + pIndex
|
||||
programsBeforeChannel.get(focusedChannelIndex) + pIndex
|
||||
} else {
|
||||
programsBeforeChannel(focusedChannelIndex) + programs[channelId]!!.size
|
||||
programsBeforeChannel.get(focusedChannelIndex) + programs[channelId]!!.size
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -191,9 +215,9 @@ fun TvGrid(
|
|||
val pIndex =
|
||||
pro.indexOfFirst { start in (it.startHours..<it.endHours) }
|
||||
if (pIndex >= 0) {
|
||||
programsBeforeChannel(focusedChannelIndex) + pIndex
|
||||
programsBeforeChannel.get(focusedChannelIndex) + pIndex
|
||||
} else {
|
||||
programsBeforeChannel(focusedChannelIndex) + programs[channelId]!!.size
|
||||
programsBeforeChannel.get(focusedChannelIndex) + programs[channelId]!!.size
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -209,13 +233,13 @@ fun TvGrid(
|
|||
}
|
||||
}
|
||||
if (newIndex != null) {
|
||||
val before = programsBeforeChannel(focusedChannelIndex)
|
||||
val before = programsBeforeChannel.get(focusedChannelIndex)
|
||||
val max =
|
||||
before + channels[focusedChannelIndex].let { programs[it.id]!!.size } - 1
|
||||
val index = newIndex.coerceIn(before, max)
|
||||
scope.launch(ExceptionHandler()) {
|
||||
state.animateToProgram(index, Alignment.Center)
|
||||
focusedProgramIndex = index
|
||||
state.animateToProgram(index, Alignment.Center)
|
||||
}
|
||||
return@onPreviewKeyEvent true
|
||||
}
|
||||
|
|
@ -223,6 +247,20 @@ fun TvGrid(
|
|||
},
|
||||
) {
|
||||
guideStartHour = 0f
|
||||
currentTime(
|
||||
layoutInfo = {
|
||||
ProgramGuideItem.CurrentTime(
|
||||
hoursBetween(start, LocalDateTime.now()),
|
||||
)
|
||||
},
|
||||
) {
|
||||
Surface(
|
||||
colors = SurfaceDefaults.colors(MaterialTheme.colorScheme.tertiary),
|
||||
modifier = Modifier,
|
||||
) {
|
||||
// Empty
|
||||
}
|
||||
}
|
||||
timeline(
|
||||
count = MAX_HOURS.toInt(),
|
||||
layoutInfo = { index ->
|
||||
|
|
@ -295,6 +333,33 @@ fun TvGrid(
|
|||
)
|
||||
}
|
||||
}
|
||||
if (program.isSeriesRecording) {
|
||||
Box(
|
||||
modifier =
|
||||
Modifier
|
||||
.padding(4.dp)
|
||||
.size(16.dp)
|
||||
.background(Color.Red.copy(alpha = .5f), shape = CircleShape)
|
||||
.align(Alignment.BottomEnd),
|
||||
)
|
||||
Box(
|
||||
modifier =
|
||||
Modifier
|
||||
.padding(start = 4.dp, top = 4.dp, bottom = 4.dp, end = 10.dp)
|
||||
.size(16.dp)
|
||||
.background(Color.Red, shape = CircleShape)
|
||||
.align(Alignment.BottomEnd),
|
||||
)
|
||||
} else if (program.isRecording) {
|
||||
Box(
|
||||
modifier =
|
||||
Modifier
|
||||
.padding(4.dp)
|
||||
.size(16.dp)
|
||||
.background(Color.Red, shape = CircleShape)
|
||||
.align(Alignment.BottomEnd),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue