mirror of
https://github.com/JustinZeus/Wholphin.git
synced 2026-07-08 23:51:21 +02:00
Fix some focus issues & document it
This commit is contained in:
parent
dc6a921693
commit
52b8cceea6
2 changed files with 50 additions and 20 deletions
|
|
@ -34,7 +34,7 @@ import javax.inject.Inject
|
||||||
import kotlin.time.Duration
|
import kotlin.time.Duration
|
||||||
import kotlin.time.Duration.Companion.seconds
|
import kotlin.time.Duration.Companion.seconds
|
||||||
|
|
||||||
const val MAX_HOURS = 24L
|
const val MAX_HOURS = 48L
|
||||||
|
|
||||||
@HiltViewModel
|
@HiltViewModel
|
||||||
class LiveTvViewModel
|
class LiveTvViewModel
|
||||||
|
|
@ -55,8 +55,6 @@ class LiveTvViewModel
|
||||||
val fetchingItem = MutableLiveData<LoadingState>(LoadingState.Pending)
|
val fetchingItem = MutableLiveData<LoadingState>(LoadingState.Pending)
|
||||||
val fetchedItem = MutableLiveData<BaseItem?>(null)
|
val fetchedItem = MutableLiveData<BaseItem?>(null)
|
||||||
|
|
||||||
private val programMap = mutableMapOf<UUID, MutableLiveData<List<TvProgram?>>>()
|
|
||||||
|
|
||||||
fun init() {
|
fun init() {
|
||||||
loading.value = LoadingState.Loading
|
loading.value = LoadingState.Loading
|
||||||
viewModelScope.launch(Dispatchers.IO + LoadingExceptionHandler(loading, "Could not fetch channels")) {
|
viewModelScope.launch(Dispatchers.IO + LoadingExceptionHandler(loading, "Could not fetch channels")) {
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,7 @@ import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.focus.FocusDirection
|
import androidx.compose.ui.focus.FocusDirection
|
||||||
import androidx.compose.ui.focus.FocusRequester
|
import androidx.compose.ui.focus.FocusRequester
|
||||||
import androidx.compose.ui.focus.focusRequester
|
import androidx.compose.ui.focus.focusRequester
|
||||||
|
import androidx.compose.ui.focus.onFocusChanged
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
import androidx.compose.ui.input.key.Key
|
import androidx.compose.ui.input.key.Key
|
||||||
import androidx.compose.ui.input.key.KeyEventType
|
import androidx.compose.ui.input.key.KeyEventType
|
||||||
|
|
@ -301,14 +302,16 @@ fun TvGuideGrid(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// fun programsBeforeChannel(channelIndex: Int): Int = channels.subList(0, channelIndex).map { it.id }.sumOf { programs[it]?.size ?: 0 }
|
var gridHasFocus by remember { mutableStateOf(false) }
|
||||||
|
|
||||||
ProgramGuide(
|
ProgramGuide(
|
||||||
state = state,
|
state = state,
|
||||||
dimensions = dimensions,
|
dimensions = dimensions,
|
||||||
modifier =
|
modifier =
|
||||||
modifier
|
modifier
|
||||||
.focusable()
|
.onFocusChanged {
|
||||||
|
gridHasFocus = it.hasFocus
|
||||||
|
}.focusable()
|
||||||
.onPreviewKeyEvent {
|
.onPreviewKeyEvent {
|
||||||
if (it.type == KeyEventType.KeyUp) {
|
if (it.type == KeyEventType.KeyUp) {
|
||||||
return@onPreviewKeyEvent false
|
return@onPreviewKeyEvent false
|
||||||
|
|
@ -351,37 +354,66 @@ fun TvGuideGrid(
|
||||||
|
|
||||||
Key.DirectionUp -> {
|
Key.DirectionUp -> {
|
||||||
val start = programList[focusedProgramIndex].startHours
|
val start = programList[focusedProgramIndex].startHours
|
||||||
focusedChannelIndex = (focusedChannelIndex - 1)
|
val newFocusedChannelIndex = (focusedChannelIndex - 1)
|
||||||
if (focusedChannelIndex < 0) {
|
if (newFocusedChannelIndex < 0) {
|
||||||
focusManager.moveFocus(FocusDirection.Up)
|
focusManager.moveFocus(FocusDirection.Up)
|
||||||
null
|
null
|
||||||
} else {
|
} else {
|
||||||
val channelId = channels[focusedChannelIndex].id
|
focusedChannelIndex = newFocusedChannelIndex.coerceAtLeast(0)
|
||||||
val pIndex =
|
|
||||||
programs[channelId]?.indexOfFirst { start in (it.startHours..<it.endHours) }
|
|
||||||
?: -1
|
|
||||||
if (pIndex >= 0) {
|
|
||||||
programsBeforeChannel.get(focusedChannelIndex) + pIndex
|
|
||||||
} else {
|
|
||||||
programsBeforeChannel.get(focusedChannelIndex) + programs[channelId]!!.size
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Key.DirectionDown -> {
|
|
||||||
val start = programList[focusedProgramIndex].startHours
|
|
||||||
focusedChannelIndex =
|
|
||||||
(focusedChannelIndex + 1).coerceAtMost(channels.size - 1)
|
|
||||||
val channelId = channels[focusedChannelIndex].id
|
val channelId = channels[focusedChannelIndex].id
|
||||||
val pro = programs[channelId].orEmpty()
|
val pro = programs[channelId].orEmpty()
|
||||||
val pIndex =
|
val pIndex =
|
||||||
pro.indexOfFirst { start in (it.startHours..<it.endHours) }
|
pro.indexOfFirst { start in (it.startHours..<it.endHours) }
|
||||||
|
if (pIndex >= 0) {
|
||||||
|
programsBeforeChannel.get(focusedChannelIndex) + pIndex
|
||||||
|
} else {
|
||||||
|
val pIndex = pro.indexOfFirst { it.startHours >= start }
|
||||||
if (pIndex >= 0) {
|
if (pIndex >= 0) {
|
||||||
programsBeforeChannel.get(focusedChannelIndex) + pIndex
|
programsBeforeChannel.get(focusedChannelIndex) + pIndex
|
||||||
} else {
|
} else {
|
||||||
programsBeforeChannel.get(focusedChannelIndex) + programs[channelId]!!.size
|
programsBeforeChannel.get(focusedChannelIndex) + programs[channelId]!!.size
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Key.DirectionDown -> {
|
||||||
|
// When the currently focused program starts
|
||||||
|
val start = programList[focusedProgramIndex].startHours
|
||||||
|
// Move channel focus down
|
||||||
|
val newFocusedChannelIndex = (focusedChannelIndex + 1)
|
||||||
|
if (newFocusedChannelIndex >= channels.size) {
|
||||||
|
// If trying to move below the final channel, then move focus out of the grid
|
||||||
|
focusManager.moveFocus(FocusDirection.Down)
|
||||||
|
null
|
||||||
|
} else {
|
||||||
|
// Otherwise, moving to a new row
|
||||||
|
focusedChannelIndex =
|
||||||
|
newFocusedChannelIndex.coerceAtMost(channels.size - 1)
|
||||||
|
// Get the new row/channel's programs
|
||||||
|
val channelId = channels[focusedChannelIndex].id
|
||||||
|
val pro = programs[channelId].orEmpty()
|
||||||
|
// Find the first program where the start time (of the previously focused program) is in the middle of a program
|
||||||
|
val pIndex =
|
||||||
|
pro.indexOfFirst { start in (it.startHours..<it.endHours) }
|
||||||
|
if (pIndex >= 0) {
|
||||||
|
// Found one, so focus on it
|
||||||
|
// Get the sum of all of the previous channels' program sizes, plus the index found to convert a relative program index into absolute
|
||||||
|
programsBeforeChannel.get(focusedChannelIndex) + pIndex
|
||||||
|
} else {
|
||||||
|
// Didn't find one, probably due to missing data
|
||||||
|
// So now first the first one that starts after the previously focused program
|
||||||
|
val pIndex = pro.indexOfFirst { it.startHours >= start }
|
||||||
|
if (pIndex >= 0) {
|
||||||
|
// Found one, so focus on it
|
||||||
|
programsBeforeChannel.get(focusedChannelIndex) + pIndex
|
||||||
|
} else {
|
||||||
|
// Did not find one, so focus on the final program in the list
|
||||||
|
programsBeforeChannel.get(focusedChannelIndex) + programs[channelId]!!.size
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Key.DirectionCenter, Key.Enter, Key.NumPadEnter -> {
|
Key.DirectionCenter, Key.Enter, Key.NumPadEnter -> {
|
||||||
val program = programList[focusedProgramIndex]
|
val program = programList[focusedProgramIndex]
|
||||||
|
|
@ -450,7 +482,7 @@ fun TvGuideGrid(
|
||||||
},
|
},
|
||||||
) { programIndex ->
|
) { programIndex ->
|
||||||
val program = programList[programIndex]
|
val program = programList[programIndex]
|
||||||
val focused = programIndex == focusedProgramIndex
|
val focused = gridHasFocus && programIndex == focusedProgramIndex
|
||||||
val background =
|
val background =
|
||||||
if (focused) {
|
if (focused) {
|
||||||
MaterialTheme.colorScheme.inverseSurface
|
MaterialTheme.colorScheme.inverseSurface
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue