update
This commit is contained in:
parent
2c59a4e06d
commit
27e07dae05
@ -2,7 +2,7 @@ package me.lsong.mytv.epg
|
||||
|
||||
import androidx.compose.runtime.Immutable
|
||||
import kotlinx.serialization.Serializable
|
||||
import me.lsong.mytv.iptv.TVChannel
|
||||
import me.lsong.mytv.providers.TVChannel
|
||||
import me.lsong.mytv.epg.EpgChannel.Companion.currentProgrammes
|
||||
import me.lsong.mytv.epg.EpgProgramme.Companion.isLive
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
package me.lsong.mytv.iptv
|
||||
package me.lsong.mytv.providers
|
||||
|
||||
import android.util.Log
|
||||
import androidx.compose.runtime.Immutable
|
@ -7,12 +7,19 @@ import androidx.compose.foundation.focusable
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.fillMaxHeight
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.sizeIn
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.layout.widthIn
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Settings
|
||||
import androidx.compose.material3.LinearProgressIndicator
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
@ -38,6 +45,7 @@ import androidx.compose.ui.unit.dp
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
import androidx.tv.foundation.lazy.list.TvLazyColumn
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
@ -46,16 +54,16 @@ import me.lsong.mytv.R
|
||||
import me.lsong.mytv.epg.EpgList
|
||||
import me.lsong.mytv.epg.EpgList.Companion.currentProgrammes
|
||||
import me.lsong.mytv.epg.EpgRepository
|
||||
import me.lsong.mytv.iptv.IPTVProvider
|
||||
import me.lsong.mytv.iptv.TVChannel
|
||||
import me.lsong.mytv.iptv.TVGroupList
|
||||
import me.lsong.mytv.iptv.TVGroupList.Companion.channels
|
||||
import me.lsong.mytv.iptv.TVGroupList.Companion.findGroupIndex
|
||||
import me.lsong.mytv.iptv.TVProvider
|
||||
import me.lsong.mytv.providers.IPTVProvider
|
||||
import me.lsong.mytv.providers.TVChannel
|
||||
import me.lsong.mytv.providers.TVGroupList
|
||||
import me.lsong.mytv.providers.TVGroupList.Companion.channels
|
||||
import me.lsong.mytv.providers.TVGroupList.Companion.findGroupIndex
|
||||
import me.lsong.mytv.providers.TVProvider
|
||||
import me.lsong.mytv.ui.components.LeanbackVisible
|
||||
import me.lsong.mytv.ui.components.MonitorScreen
|
||||
import me.lsong.mytv.ui.components.MyTvMenu
|
||||
import me.lsong.mytv.ui.components.MyTvMenuItem
|
||||
import me.lsong.mytv.ui.components.MyTvMenuItemList
|
||||
import me.lsong.mytv.ui.components.MyTvNowPlaying
|
||||
import me.lsong.mytv.ui.player.MyTvVideoScreen
|
||||
import me.lsong.mytv.ui.player.rememberLeanbackVideoPlayerState
|
||||
@ -143,7 +151,7 @@ fun MyTvMenuWidget(
|
||||
channelProvider: () -> TVChannel = { TVChannel() },
|
||||
groupListProvider: () -> TVGroupList = { TVGroupList() },
|
||||
onSelected: (TVChannel) -> Unit = {},
|
||||
onSettings: () -> Unit = {},
|
||||
onSettings: (() -> Unit)? = null,
|
||||
onUserAction: () -> Unit = {}
|
||||
) {
|
||||
val groupList = groupListProvider()
|
||||
@ -179,21 +187,64 @@ fun MyTvMenuWidget(
|
||||
} ?: emptyList()
|
||||
}
|
||||
|
||||
Row {
|
||||
MyTvMenu(
|
||||
groups = groups,
|
||||
itemsProvider = itemsProvider,
|
||||
currentGroup = currentGroup,
|
||||
currentItem = currentMenuItem,
|
||||
onItemSelected = { selectedItem ->
|
||||
val selectedChannel = groupList.channels.first { it.title == selectedItem.title }
|
||||
var focusedGroup by remember { mutableStateOf(currentGroup) }
|
||||
var focusedItem by remember { mutableStateOf(currentMenuItem) }
|
||||
var items by remember { mutableStateOf(itemsProvider(focusedGroup.title)) }
|
||||
val rightListFocusRequester = remember { FocusRequester() }
|
||||
|
||||
Row(modifier = modifier) {
|
||||
Column (
|
||||
verticalArrangement = Arrangement.SpaceBetween,
|
||||
modifier = Modifier
|
||||
.width(250.dp)
|
||||
.fillMaxHeight(),
|
||||
) {
|
||||
MyTvMenuItemList(
|
||||
items = groups,
|
||||
selectedItem = focusedGroup,
|
||||
onFocused = { menuItem ->
|
||||
focusedGroup = menuItem
|
||||
items = itemsProvider(menuItem.title)
|
||||
},
|
||||
onSelected = { menuItem ->
|
||||
focusedGroup = menuItem
|
||||
items = itemsProvider(menuItem.title)
|
||||
focusedItem = items.firstOrNull() ?: MyTvMenuItem()
|
||||
rightListFocusRequester.requestFocus()
|
||||
},
|
||||
onUserAction = onUserAction,
|
||||
modifier = Modifier.weight(1f)
|
||||
)
|
||||
LeanbackVisible ({ onSettings != null }) {
|
||||
TvLazyColumn(
|
||||
modifier = Modifier.width(250.dp),
|
||||
contentPadding = PaddingValues(8.dp),
|
||||
) {
|
||||
item {
|
||||
MyTvMenuItem(
|
||||
item = MyTvMenuItem(icon = Icons.Default.Settings, title = "Settings"),
|
||||
onSelected = onSettings!!
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
MyTvMenuItemList(
|
||||
items = items,
|
||||
selectedItem = focusedItem,
|
||||
onSelected = { menuItem ->
|
||||
focusedItem = menuItem
|
||||
val selectedChannel = groupList.channels.first { it.title == menuItem.title }
|
||||
onSelected(selectedChannel)
|
||||
},
|
||||
modifier = modifier,
|
||||
onSettings = onSettings,
|
||||
onUserAction = onUserAction,
|
||||
focusRequester = rightListFocusRequester
|
||||
)
|
||||
}
|
||||
|
||||
LaunchedEffect(Unit) {
|
||||
rightListFocusRequester.requestFocus()
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
|
@ -10,13 +10,11 @@ import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.runtime.setValue
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
import me.lsong.mytv.iptv.TVChannel
|
||||
import me.lsong.mytv.iptv.TVGroupList
|
||||
import me.lsong.mytv.iptv.TVGroupList.Companion.channels
|
||||
import me.lsong.mytv.iptv.TVGroupList.Companion.findChannelIndex
|
||||
import me.lsong.mytv.utils.Constants
|
||||
import me.lsong.mytv.providers.TVChannel
|
||||
import me.lsong.mytv.providers.TVGroupList
|
||||
import me.lsong.mytv.providers.TVGroupList.Companion.channels
|
||||
import me.lsong.mytv.providers.TVGroupList.Companion.findChannelIndex
|
||||
import me.lsong.mytv.ui.player.LeanbackVideoPlayerState
|
||||
import me.lsong.mytv.ui.player.rememberLeanbackVideoPlayerState
|
||||
import me.lsong.mytv.utils.Settings
|
||||
|
@ -17,7 +17,7 @@ import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import me.lsong.mytv.iptv.TVChannel
|
||||
import me.lsong.mytv.providers.TVChannel
|
||||
import me.lsong.mytv.ui.theme.LeanbackTheme
|
||||
import me.lsong.mytv.utils.isIPv6
|
||||
|
||||
|
@ -1,30 +1,19 @@
|
||||
package me.lsong.mytv.ui.components
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.fillMaxHeight
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.layout.wrapContentHeight
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Settings
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.CompositionLocalProvider
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.runtime.snapshotFlow
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.alpha
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.focus.FocusRequester
|
||||
import androidx.compose.ui.focus.focusRequester
|
||||
import androidx.compose.ui.focus.onFocusChanged
|
||||
@ -59,7 +48,7 @@ fun MyTvMenuItem(
|
||||
isSelected: Boolean = false,
|
||||
onFocused: () -> Unit = {},
|
||||
onSelected: () -> Unit = {},
|
||||
onFavoriteToggle: () -> Unit = {},
|
||||
onLongSelect: () -> Unit = {},
|
||||
focusRequester: FocusRequester = remember { FocusRequester() },
|
||||
) {
|
||||
LaunchedEffect(isSelected) {
|
||||
@ -70,20 +59,15 @@ fun MyTvMenuItem(
|
||||
CompositionLocalProvider(
|
||||
LocalContentColor provides if (isFocused) MaterialTheme.colorScheme.background
|
||||
else MaterialTheme.colorScheme.onBackground
|
||||
) {
|
||||
Box(
|
||||
modifier = Modifier.clip(ListItemDefaults.shape().shape),
|
||||
) {
|
||||
androidx.tv.material3.ListItem(
|
||||
modifier = modifier
|
||||
.align(Alignment.Center)
|
||||
.focusRequester(focusRequester)
|
||||
.onFocusChanged { if (it.isFocused) onFocused() }
|
||||
.align(Alignment.Center)
|
||||
.handleLeanbackKeyEvents(
|
||||
key = item.hashCode(),
|
||||
onSelect = onSelected,
|
||||
onLongSelect = onFavoriteToggle,
|
||||
onLongSelect = onLongSelect,
|
||||
),
|
||||
colors = ListItemDefaults.colors(
|
||||
focusedContentColor = MaterialTheme.colorScheme.background,
|
||||
@ -135,66 +119,8 @@ fun MyTvMenuItem(
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun MyTvMenu(
|
||||
groups: List<MyTvMenuItem>,
|
||||
itemsProvider: (String) -> List<MyTvMenuItem>,
|
||||
currentGroup: MyTvMenuItem,
|
||||
currentItem: MyTvMenuItem,
|
||||
onGroupFocused: (MyTvMenuItem) -> Unit = {},
|
||||
onGroupSelected: (MyTvMenuItem) -> Unit = {},
|
||||
onItemSelected: (MyTvMenuItem) -> Unit = {},
|
||||
modifier: Modifier = Modifier,
|
||||
onUserAction: () -> Unit = {},
|
||||
onSettings: () -> Unit = {}
|
||||
) {
|
||||
var focusedGroup by remember { mutableStateOf(currentGroup) }
|
||||
var focusedItem by remember { mutableStateOf(currentItem) }
|
||||
var items by remember { mutableStateOf(itemsProvider(focusedGroup.title)) }
|
||||
val rightListFocusRequester = remember { FocusRequester() }
|
||||
|
||||
Row(modifier = modifier) {
|
||||
MyTvMenuItemList(
|
||||
items = groups,
|
||||
onSettings = onSettings,
|
||||
selectedItem = focusedGroup,
|
||||
onFocused = { menuGroupItem ->
|
||||
focusedGroup = menuGroupItem
|
||||
items = itemsProvider(menuGroupItem.title)
|
||||
onGroupFocused(focusedGroup)
|
||||
},
|
||||
onSelected = { menuGroupItem ->
|
||||
focusedGroup = menuGroupItem
|
||||
items = itemsProvider(menuGroupItem.title)
|
||||
focusedItem = items.firstOrNull() ?: MyTvMenuItem()
|
||||
onGroupSelected(focusedGroup)
|
||||
rightListFocusRequester.requestFocus()
|
||||
},
|
||||
onUserAction = onUserAction
|
||||
)
|
||||
MyTvMenuItemList(
|
||||
items = items,
|
||||
selectedItem = focusedItem,
|
||||
onSelected = { menuItem ->
|
||||
focusedItem = menuItem
|
||||
onItemSelected(focusedItem)
|
||||
},
|
||||
onUserAction = onUserAction,
|
||||
focusRequester = rightListFocusRequester
|
||||
)
|
||||
|
||||
}
|
||||
|
||||
LaunchedEffect(Unit) {
|
||||
rightListFocusRequester.requestFocus()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Composable
|
||||
fun MyTvMenuItemList(
|
||||
items: List<MyTvMenuItem>,
|
||||
@ -202,15 +128,12 @@ fun MyTvMenuItemList(
|
||||
onUserAction: () -> Unit = {},
|
||||
onFocused: (MyTvMenuItem) -> Unit = {},
|
||||
onSelected: (MyTvMenuItem) -> Unit = {},
|
||||
onFavoriteToggle: (MyTvMenuItem) -> Unit = {},
|
||||
onLongSelect: (MyTvMenuItem) -> Unit = {},
|
||||
focusRequester: FocusRequester = remember { FocusRequester() },
|
||||
modifier: Modifier = Modifier,
|
||||
onSettings: (() -> Unit)? = null,
|
||||
) {
|
||||
var focusedItem by remember { mutableStateOf(selectedItem) }
|
||||
val selectedIndex = remember(selectedItem, items) { items.indexOf(selectedItem) }
|
||||
val itemFocusRequesterList = remember(items) { List(items.size) { FocusRequester() } }
|
||||
val settingsFocusRequester = remember { FocusRequester() }
|
||||
val listState = rememberTvLazyListState()
|
||||
|
||||
LaunchedEffect(listState) {
|
||||
@ -224,51 +147,27 @@ fun MyTvMenuItemList(
|
||||
listState.scrollToItem(maxOf(0, index))
|
||||
}
|
||||
|
||||
Column(
|
||||
modifier = modifier
|
||||
.fillMaxHeight()
|
||||
.width(250.dp)
|
||||
.background(MaterialTheme.colorScheme.background.copy(0.8f))
|
||||
.focusRequester(focusRequester)
|
||||
) {
|
||||
TvLazyColumn(
|
||||
state = listState,
|
||||
contentPadding = PaddingValues(8.dp),
|
||||
verticalArrangement = Arrangement.spacedBy(8.dp),
|
||||
modifier = Modifier.weight(1f).align(Alignment.CenterHorizontally)
|
||||
modifier = modifier
|
||||
.width(250.dp)
|
||||
.background(MaterialTheme.colorScheme.background.copy(0.8f))
|
||||
.focusRequester(focusRequester)
|
||||
) {
|
||||
itemsIndexed(items, key = { _, item -> item.hashCode() }) { index, item ->
|
||||
MyTvMenuItem(
|
||||
item = item,
|
||||
focusRequester = itemFocusRequesterList[index],
|
||||
isSelected = selectedIndex == index,
|
||||
isFocused = selectedIndex == index,
|
||||
isSelected = selectedIndex == index,
|
||||
onFocused = { onFocused(item) },
|
||||
onSelected = { onSelected(item) },
|
||||
onFocused = {
|
||||
focusedItem = item
|
||||
onFocused(item)
|
||||
},
|
||||
onFavoriteToggle = { onFavoriteToggle(item) }
|
||||
onLongSelect = { onLongSelect(item) },
|
||||
focusRequester = itemFocusRequesterList[index],
|
||||
)
|
||||
}
|
||||
}
|
||||
// Settings button at the bottom
|
||||
if (onSettings != null) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.background(MaterialTheme.colorScheme.background)
|
||||
.padding(8.dp)
|
||||
) {
|
||||
MyTvMenuItem(
|
||||
item = MyTvMenuItem(icon = Icons.Default.Settings, title = "Settings"),
|
||||
focusRequester = settingsFocusRequester,
|
||||
onSelected = onSettings
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@Preview
|
||||
|
@ -17,7 +17,7 @@ import androidx.compose.ui.unit.dp
|
||||
import me.lsong.mytv.rememberLeanbackChildPadding
|
||||
import me.lsong.mytv.epg.EpgList
|
||||
import me.lsong.mytv.epg.EpgList.Companion.getEpgChannel
|
||||
import me.lsong.mytv.iptv.TVChannel
|
||||
import me.lsong.mytv.providers.TVChannel
|
||||
import me.lsong.mytv.ui.player.LeanbackVideoPlayer
|
||||
import me.lsong.mytv.ui.theme.LeanbackTheme
|
||||
|
||||
@ -56,13 +56,13 @@ fun MyTvNowPlaying(
|
||||
channelSourceIndexProvider = sourceIndexProvider,
|
||||
)
|
||||
|
||||
val epg = epgListProvider().getEpgChannel(channelProvider())
|
||||
if (epg != null) {
|
||||
MyTvEpgView(
|
||||
modifier = modifier,
|
||||
epgProvider = { epg },
|
||||
)
|
||||
}
|
||||
// val epg = epgListProvider().getEpgChannel(channelProvider())
|
||||
// if (epg != null) {
|
||||
// MyTvEpgView(
|
||||
// modifier = modifier,
|
||||
// epgProvider = { epg },
|
||||
// )
|
||||
// }
|
||||
MyTvPlayerInfo(
|
||||
modifier = modifier.padding(start = childPadding.start, bottom = childPadding.bottom),
|
||||
metadataProvider = videoPlayerMetadataProvider
|
||||
|
Loading…
x
Reference in New Issue
Block a user