update
This commit is contained in:
parent
e74c15571a
commit
b88cd7f48f
@ -13,12 +13,6 @@ import me.lsong.mytv.utils.Settings
|
||||
import okhttp3.OkHttpClient
|
||||
import okhttp3.Request
|
||||
|
||||
// 接口定义
|
||||
interface TVProvider {
|
||||
suspend fun load()
|
||||
fun groups(): TVGroupList
|
||||
suspend fun epg(): EpgList
|
||||
}
|
||||
|
||||
// 数据类定义
|
||||
@Immutable
|
||||
@ -149,10 +143,13 @@ class IPTVProvider(private val epgRepository: EpgRepository) : TVProvider {
|
||||
groupList = process(sources)
|
||||
epgList = fetchEPGData(epgUrls)
|
||||
}
|
||||
override fun groups(): TVGroupList {
|
||||
return groupList
|
||||
}
|
||||
|
||||
override suspend fun epg(): EpgList = epgList
|
||||
override fun groups(): TVGroupList = groupList
|
||||
|
||||
override fun channels(groupTitle: String): TVChannelList {
|
||||
return groupList.find { it.title == groupTitle }?.channels ?: TVChannelList()
|
||||
}
|
||||
|
||||
private suspend fun fetchIPTVSources(): Pair<List<TVSource>, List<String>> {
|
||||
val allSources = mutableListOf<TVSource>()
|
||||
@ -228,3 +225,22 @@ class IPTVProvider(private val epgRepository: EpgRepository) : TVProvider {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Interface definition
|
||||
interface TVProvider {
|
||||
suspend fun load()
|
||||
fun groups(): TVGroupList
|
||||
fun channels(groupTitle: String): TVChannelList
|
||||
}
|
||||
|
||||
class MyTvProviderManager : TVProvider {
|
||||
private val providers: List<TVProvider> = listOf(
|
||||
IPTVProvider(EpgRepository())
|
||||
)
|
||||
override suspend fun load() {
|
||||
providers.forEach { it.load() }
|
||||
}
|
||||
override fun groups(): TVGroupList = TVGroupList(providers.flatMap { it.groups() })
|
||||
override fun channels(groupTitle: String): TVChannelList = TVChannelList(providers.flatMap { it.channels(groupTitle) })
|
||||
}
|
@ -1,5 +1,6 @@
|
||||
package me.lsong.mytv.ui
|
||||
|
||||
import android.util.Log
|
||||
import androidx.activity.compose.BackHandler
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.background
|
||||
@ -11,9 +12,7 @@ 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
|
||||
@ -51,15 +50,10 @@ import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
import kotlinx.coroutines.launch
|
||||
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.providers.IPTVProvider
|
||||
import me.lsong.mytv.providers.MyTvProviderManager
|
||||
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.MyTvMenuItem
|
||||
@ -75,88 +69,35 @@ import me.lsong.mytv.utils.handleLeanbackDragGestures
|
||||
import me.lsong.mytv.utils.handleLeanbackKeyEvents
|
||||
|
||||
@Composable
|
||||
private fun StartScreen(state: LeanbackMainUiState) {
|
||||
var isSettingsVisible by remember { mutableStateOf(false) }
|
||||
BackHandler(enabled = !isSettingsVisible) {
|
||||
isSettingsVisible = true
|
||||
}
|
||||
Box(
|
||||
contentAlignment = Alignment.Center,
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.background(MaterialTheme.colorScheme.background)
|
||||
.onPreviewKeyEvent { event ->
|
||||
if (event.key == Key.Menu && event.type == KeyEventType.KeyUp) {
|
||||
isSettingsVisible = !isSettingsVisible
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
},
|
||||
fun MainScreen(
|
||||
modifier: Modifier = Modifier,
|
||||
mainViewModel: MainViewModel = viewModel(),
|
||||
) {
|
||||
Column(
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
verticalArrangement = Arrangement.spacedBy(10.dp)
|
||||
) {
|
||||
Image(
|
||||
painter = painterResource(id = R.mipmap.ic_launcher),
|
||||
contentDescription = "DuckTV",
|
||||
modifier = Modifier.size(96.dp)
|
||||
val uiState by mainViewModel.uiState.collectAsState()
|
||||
when (val state = uiState) {
|
||||
is LeanbackMainUiState.Loading,
|
||||
is LeanbackMainUiState.Error -> StartScreen(state)
|
||||
is LeanbackMainUiState.Ready -> MainContent(
|
||||
modifier = modifier,
|
||||
providerManager = state.providerManager,
|
||||
)
|
||||
Text(
|
||||
text = Constants.APP_NAME,
|
||||
style = MaterialTheme.typography.headlineSmall,
|
||||
color = MaterialTheme.colorScheme.onBackground,
|
||||
)
|
||||
|
||||
when (state) {
|
||||
is LeanbackMainUiState.Loading -> {
|
||||
LinearProgressIndicator(
|
||||
modifier = Modifier
|
||||
.widthIn(300.dp, 800.dp)
|
||||
.height(8.dp)
|
||||
)
|
||||
state.message?.let { message ->
|
||||
Text(
|
||||
text = message,
|
||||
style = MaterialTheme.typography.bodySmall,
|
||||
color = MaterialTheme.colorScheme.onBackground.copy(alpha = 0.8f),
|
||||
modifier = Modifier.sizeIn(maxWidth = 500.dp),
|
||||
)
|
||||
}
|
||||
}
|
||||
is LeanbackMainUiState.Error -> {
|
||||
state.message?.let { message ->
|
||||
Text(
|
||||
text = message,
|
||||
style = MaterialTheme.typography.bodySmall,
|
||||
color = MaterialTheme.colorScheme.error.copy(alpha = 0.8f),
|
||||
modifier = Modifier.sizeIn(maxWidth = 500.dp),
|
||||
)
|
||||
}
|
||||
}
|
||||
else -> {} // This case should never happen
|
||||
}
|
||||
}
|
||||
}
|
||||
LeanbackVisible({ isSettingsVisible }) {
|
||||
SettingsScreen()
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun MyTvMenuWidget(
|
||||
modifier: Modifier = Modifier,
|
||||
epgListProvider: () -> EpgList = { EpgList() },
|
||||
providerManager: MyTvProviderManager,
|
||||
// epgListProvider: () -> EpgList = { EpgList() },
|
||||
// groupListProvider: () -> TVGroupList = { TVGroupList() },
|
||||
channelProvider: () -> TVChannel = { TVChannel() },
|
||||
groupListProvider: () -> TVGroupList = { TVGroupList() },
|
||||
onSelected: (TVChannel) -> Unit = {},
|
||||
onSettings: (() -> Unit)? = null,
|
||||
onUserAction: () -> Unit = {}
|
||||
) {
|
||||
val groupList = groupListProvider()
|
||||
// val epgList = epgListProvider()
|
||||
val groupList = providerManager.groups();
|
||||
val currentChannel = channelProvider()
|
||||
val epgList = epgListProvider()
|
||||
|
||||
val groups = remember(groupList) {
|
||||
groupList.map { group ->
|
||||
@ -165,15 +106,17 @@ fun MyTvMenuWidget(
|
||||
}
|
||||
|
||||
val currentGroup = remember(groupList, currentChannel) {
|
||||
groups.firstOrNull { it.title == groupList[groupList.findGroupIndex(currentChannel)].title }
|
||||
groups.firstOrNull { it.title == currentChannel.groupTitle }
|
||||
?: MyTvMenuItem()
|
||||
}
|
||||
|
||||
Log.d("currentGroup", "$currentGroup $currentChannel")
|
||||
|
||||
val currentMenuItem = remember(currentChannel) {
|
||||
MyTvMenuItem(
|
||||
icon = currentChannel.logo ?: "",
|
||||
title = currentChannel.title,
|
||||
description = epgList.currentProgrammes(currentChannel)?.now?.title
|
||||
// description = epgList.currentProgrammes(currentChannel)?.now?.title
|
||||
)
|
||||
}
|
||||
|
||||
@ -182,7 +125,7 @@ fun MyTvMenuWidget(
|
||||
MyTvMenuItem(
|
||||
icon = channel.logo ?: "",
|
||||
title = channel.title,
|
||||
description = epgList.currentProgrammes(channel)?.now?.title
|
||||
// description = epgList.currentProgrammes(channel)?.now?.title
|
||||
)
|
||||
} ?: emptyList()
|
||||
}
|
||||
@ -205,11 +148,11 @@ fun MyTvMenuWidget(
|
||||
selectedItem = focusedGroup,
|
||||
onFocused = { menuItem ->
|
||||
focusedGroup = menuItem
|
||||
items = itemsProvider(menuItem.title)
|
||||
items = itemsProvider(focusedGroup.title)
|
||||
},
|
||||
onSelected = { menuItem ->
|
||||
focusedGroup = menuItem
|
||||
items = itemsProvider(menuItem.title)
|
||||
items = itemsProvider(focusedGroup.title)
|
||||
focusedItem = items.firstOrNull() ?: MyTvMenuItem()
|
||||
rightListFocusRequester.requestFocus()
|
||||
},
|
||||
@ -232,6 +175,9 @@ fun MyTvMenuWidget(
|
||||
}
|
||||
MyTvMenuItemList(
|
||||
items = items,
|
||||
modifier = Modifier
|
||||
.fillMaxHeight()
|
||||
.background(androidx.tv.material3.MaterialTheme.colorScheme.background.copy(0.8f)),
|
||||
selectedItem = focusedItem,
|
||||
onSelected = { menuItem ->
|
||||
focusedItem = menuItem
|
||||
@ -240,7 +186,6 @@ fun MyTvMenuWidget(
|
||||
},
|
||||
onUserAction = onUserAction,
|
||||
focusRequester = rightListFocusRequester,
|
||||
modifier = Modifier.background(androidx.tv.material3.MaterialTheme.colorScheme.background.copy(0.8f)),
|
||||
)
|
||||
}
|
||||
|
||||
@ -249,43 +194,16 @@ fun MyTvMenuWidget(
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun MainScreen(
|
||||
modifier: Modifier = Modifier,
|
||||
mainViewModel: MainViewModel = viewModel(),
|
||||
) {
|
||||
val uiState by mainViewModel.uiState.collectAsState()
|
||||
when (val state = uiState) {
|
||||
is LeanbackMainUiState.Loading,
|
||||
is LeanbackMainUiState.Error -> StartScreen(state)
|
||||
is LeanbackMainUiState.Ready -> MainContent(
|
||||
modifier = modifier,
|
||||
groups = state.groups,
|
||||
epgList = state.epgList,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
sealed interface LeanbackMainUiState {
|
||||
data class Loading(val message: String? = null) : LeanbackMainUiState
|
||||
data class Error(val message: String? = null) : LeanbackMainUiState
|
||||
data class Ready(
|
||||
val groups: TVGroupList = TVGroupList(),
|
||||
val epgList: EpgList = EpgList(),
|
||||
) : LeanbackMainUiState
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun MainContent(
|
||||
modifier: Modifier = Modifier,
|
||||
epgList: EpgList = EpgList(),
|
||||
groups: TVGroupList = TVGroupList(),
|
||||
providerManager: MyTvProviderManager,
|
||||
settingsViewModel: MyTvSettingsViewModel = viewModel(),
|
||||
) {
|
||||
val videoPlayerState = rememberLeanbackVideoPlayerState()
|
||||
val mainContentState = rememberMainContentState(
|
||||
providerManager = providerManager,
|
||||
videoPlayerState = videoPlayerState,
|
||||
groups = groups,
|
||||
)
|
||||
|
||||
BackHandler (
|
||||
@ -324,6 +242,8 @@ fun MainContent(
|
||||
onNumber = {},
|
||||
)
|
||||
.handleLeanbackDragGestures(
|
||||
onSwipeLeft = { mainContentState.changeToPrevSource() },
|
||||
onSwipeRight = { mainContentState.changeToNextSource() },
|
||||
onSwipeDown = {
|
||||
if (settingsViewModel.iptvChannelChangeFlip) mainContentState.changeCurrentChannelToNext()
|
||||
else mainContentState.changeCurrentChannelToPrev()
|
||||
@ -332,19 +252,12 @@ fun MainContent(
|
||||
if (settingsViewModel.iptvChannelChangeFlip) mainContentState.changeCurrentChannelToPrev()
|
||||
else mainContentState.changeCurrentChannelToNext()
|
||||
},
|
||||
onSwipeLeft = {
|
||||
mainContentState.changeToPrevSource()
|
||||
},
|
||||
onSwipeRight = {
|
||||
mainContentState.changeToNextSource()
|
||||
},
|
||||
),
|
||||
)
|
||||
|
||||
LeanbackVisible({ mainContentState.isMenuVisible && !mainContentState.isChannelInfoVisible }) {
|
||||
MyTvMenuWidget(
|
||||
epgListProvider = { epgList },
|
||||
groupListProvider = { groups },
|
||||
providerManager = providerManager,
|
||||
channelProvider = { mainContentState.currentChannel },
|
||||
onSelected = { channel -> mainContentState.changeCurrentChannel(channel) },
|
||||
onSettings = { mainContentState.showSettings() }
|
||||
@ -354,7 +267,6 @@ fun MainContent(
|
||||
LeanbackVisible({ mainContentState.isChannelInfoVisible }) {
|
||||
MyTvNowPlaying(
|
||||
modifier = modifier,
|
||||
epgListProvider = { epgList },
|
||||
channelProvider = { mainContentState.currentChannel },
|
||||
channelIndexProvider = { mainContentState.currentChannelIndex },
|
||||
sourceIndexProvider = { mainContentState.currentSourceIndex },
|
||||
@ -372,41 +284,6 @@ fun MainContent(
|
||||
}
|
||||
}
|
||||
|
||||
// MainViewModel.kt
|
||||
class MainViewModel : ViewModel() {
|
||||
private val providers: List<TVProvider> = listOf(
|
||||
IPTVProvider(EpgRepository())
|
||||
)
|
||||
private val _uiState = MutableStateFlow<LeanbackMainUiState>(LeanbackMainUiState.Loading())
|
||||
val uiState: StateFlow<LeanbackMainUiState> = _uiState.asStateFlow()
|
||||
|
||||
init {
|
||||
viewModelScope.launch {
|
||||
refreshData()
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun refreshData() {
|
||||
try {
|
||||
_uiState.value = LeanbackMainUiState.Loading("Initializing providers...")
|
||||
providers.forEachIndexed { index, provider ->
|
||||
_uiState.value = LeanbackMainUiState.Loading("Initializing provider ${index + 1}/${providers.size}...")
|
||||
provider.load()
|
||||
}
|
||||
|
||||
val groupList = providers.flatMap { it.groups() }
|
||||
val epgList = providers.map { it.epg() }.reduce { acc, epgList -> (acc + epgList) as EpgList }
|
||||
|
||||
_uiState.value = LeanbackMainUiState.Ready(
|
||||
groups = TVGroupList(groupList),
|
||||
epgList = epgList
|
||||
)
|
||||
} catch (error: Exception) {
|
||||
_uiState.value = LeanbackMainUiState.Error(error.message)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(device = "id:pixel_5")
|
||||
@Composable
|
||||
private fun MyTvMainScreenPreview() {
|
||||
|
@ -7,10 +7,8 @@ import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableIntStateOf
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.runtime.setValue
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.launch
|
||||
import me.lsong.mytv.providers.MyTvProviderManager
|
||||
import me.lsong.mytv.providers.TVChannel
|
||||
import me.lsong.mytv.providers.TVGroupList
|
||||
import me.lsong.mytv.providers.TVGroupList.Companion.channels
|
||||
@ -22,10 +20,10 @@ import kotlin.math.max
|
||||
|
||||
@Stable
|
||||
class MainContentState(
|
||||
coroutineScope: CoroutineScope,
|
||||
private val videoPlayerState: LeanbackVideoPlayerState,
|
||||
private val groups: TVGroupList,
|
||||
providerManager: MyTvProviderManager,
|
||||
) {
|
||||
private val groups: TVGroupList = providerManager.groups();
|
||||
private var _currentChannel by mutableStateOf(TVChannel())
|
||||
val currentChannel get() = _currentChannel
|
||||
|
||||
@ -58,15 +56,10 @@ class MainContentState(
|
||||
|
||||
init {
|
||||
changeCurrentChannel(groups.channels.getOrElse(Settings.iptvLastIptvIdx) {
|
||||
groups.firstOrNull()?.channels?.firstOrNull() ?: TVChannel()
|
||||
groups.channels.firstOrNull() ?: TVChannel()
|
||||
})
|
||||
|
||||
videoPlayerState.onReady {
|
||||
coroutineScope.launch {
|
||||
// val name = _currentChannel.name
|
||||
// val urlIdx = _currentIptvUrlIdx
|
||||
}
|
||||
|
||||
// 记忆可播放的域名
|
||||
Settings.iptvPlayableHostList += getUrlHost(_currentChannel.urls[_currentIptvUrlIdx])
|
||||
}
|
||||
@ -100,13 +93,10 @@ class MainContentState(
|
||||
}
|
||||
|
||||
fun changeCurrentChannel(channel: TVChannel, urlIdx: Int? = null) {
|
||||
// isChannelInfoVisible = false
|
||||
if (channel == _currentChannel && urlIdx == null) return
|
||||
if (channel == _currentChannel && urlIdx != _currentIptvUrlIdx) {
|
||||
Settings.iptvPlayableHostList -= getUrlHost(_currentChannel.urls[_currentIptvUrlIdx])
|
||||
}
|
||||
// _isTempPanelVisible = true
|
||||
|
||||
_currentChannel = channel
|
||||
Settings.iptvLastIptvIdx = currentChannelIndex
|
||||
|
||||
@ -131,7 +121,6 @@ class MainContentState(
|
||||
changeCurrentChannel(getNextChannel())
|
||||
}
|
||||
|
||||
|
||||
fun changeToPrevSource(){
|
||||
if (currentChannel.urls.size > 1) {
|
||||
changeCurrentChannel(
|
||||
@ -149,16 +138,18 @@ class MainContentState(
|
||||
}
|
||||
}
|
||||
|
||||
fun showChannelInfo() {
|
||||
isMenuVisible = false
|
||||
isChannelInfoVisible = true
|
||||
}
|
||||
|
||||
fun showMenu() {
|
||||
isMenuVisible = true
|
||||
isSettingsVisale = false
|
||||
isChannelInfoVisible = false
|
||||
}
|
||||
|
||||
fun showChannelInfo() {
|
||||
isMenuVisible = false
|
||||
isSettingsVisale = false
|
||||
isChannelInfoVisible = true
|
||||
}
|
||||
|
||||
fun showSettings() {
|
||||
isMenuVisible = false
|
||||
isSettingsVisale = true
|
||||
@ -168,14 +159,12 @@ class MainContentState(
|
||||
|
||||
@Composable
|
||||
fun rememberMainContentState(
|
||||
coroutineScope: CoroutineScope = rememberCoroutineScope(),
|
||||
providerManager: MyTvProviderManager,
|
||||
videoPlayerState: LeanbackVideoPlayerState = rememberLeanbackVideoPlayerState(),
|
||||
groups: TVGroupList = TVGroupList(),
|
||||
) = remember {
|
||||
MainContentState(
|
||||
coroutineScope = coroutineScope,
|
||||
providerManager = providerManager,
|
||||
videoPlayerState = videoPlayerState,
|
||||
groups = groups,
|
||||
)
|
||||
}
|
||||
|
||||
|
41
app/src/main/java/me/lsong/mytv/ui/MainViewModel.kt
Normal file
41
app/src/main/java/me/lsong/mytv/ui/MainViewModel.kt
Normal file
@ -0,0 +1,41 @@
|
||||
package me.lsong.mytv.ui
|
||||
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
import kotlinx.coroutines.launch
|
||||
import me.lsong.mytv.providers.MyTvProviderManager
|
||||
|
||||
|
||||
// MainViewModel.kt
|
||||
class MainViewModel : ViewModel() {
|
||||
private val providerManager = MyTvProviderManager()
|
||||
private val _uiState = MutableStateFlow<LeanbackMainUiState>(LeanbackMainUiState.Loading())
|
||||
val uiState: StateFlow<LeanbackMainUiState> = _uiState.asStateFlow()
|
||||
|
||||
init {
|
||||
viewModelScope.launch {
|
||||
loadData()
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun loadData() {
|
||||
try {
|
||||
_uiState.value = LeanbackMainUiState.Loading("Initializing providers...")
|
||||
providerManager.load()
|
||||
_uiState.value = LeanbackMainUiState.Ready(providerManager)
|
||||
} catch (error: Exception) {
|
||||
_uiState.value = LeanbackMainUiState.Error(error.message)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sealed interface LeanbackMainUiState {
|
||||
data class Loading(val message: String? = null) : LeanbackMainUiState
|
||||
data class Error(val message: String? = null) : LeanbackMainUiState
|
||||
data class Ready(
|
||||
val providerManager: MyTvProviderManager,
|
||||
) : LeanbackMainUiState
|
||||
}
|
104
app/src/main/java/me/lsong/mytv/ui/StartScreen.kt
Normal file
104
app/src/main/java/me/lsong/mytv/ui/StartScreen.kt
Normal file
@ -0,0 +1,104 @@
|
||||
package me.lsong.mytv.ui
|
||||
|
||||
import androidx.activity.compose.BackHandler
|
||||
import androidx.compose.foundation.Image
|
||||
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.fillMaxSize
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.sizeIn
|
||||
import androidx.compose.foundation.layout.widthIn
|
||||
import androidx.compose.material3.LinearProgressIndicator
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.input.key.Key
|
||||
import androidx.compose.ui.input.key.KeyEventType
|
||||
import androidx.compose.ui.input.key.key
|
||||
import androidx.compose.ui.input.key.onPreviewKeyEvent
|
||||
import androidx.compose.ui.input.key.type
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import me.lsong.mytv.R
|
||||
import me.lsong.mytv.ui.components.LeanbackVisible
|
||||
import me.lsong.mytv.ui.settings.SettingsScreen
|
||||
import me.lsong.mytv.utils.Constants
|
||||
|
||||
@Composable
|
||||
fun StartScreen(state: LeanbackMainUiState) {
|
||||
var isSettingsVisible by remember { mutableStateOf(false) }
|
||||
BackHandler(enabled = !isSettingsVisible) {
|
||||
isSettingsVisible = true
|
||||
}
|
||||
Box(
|
||||
contentAlignment = Alignment.Center,
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.background(MaterialTheme.colorScheme.background)
|
||||
.onPreviewKeyEvent { event ->
|
||||
if (event.key == Key.Menu && event.type == KeyEventType.KeyUp) {
|
||||
isSettingsVisible = !isSettingsVisible
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
},
|
||||
) {
|
||||
Column(
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
verticalArrangement = Arrangement.spacedBy(10.dp)
|
||||
) {
|
||||
Image(
|
||||
painter = painterResource(id = R.mipmap.ic_launcher),
|
||||
contentDescription = "DuckTV",
|
||||
modifier = Modifier.size(96.dp)
|
||||
)
|
||||
Text(
|
||||
text = Constants.APP_NAME,
|
||||
style = MaterialTheme.typography.headlineSmall,
|
||||
color = MaterialTheme.colorScheme.onBackground,
|
||||
)
|
||||
|
||||
when (state) {
|
||||
is LeanbackMainUiState.Loading -> {
|
||||
LinearProgressIndicator(
|
||||
modifier = Modifier
|
||||
.widthIn(300.dp, 800.dp)
|
||||
.height(8.dp)
|
||||
)
|
||||
state.message?.let { message ->
|
||||
Text(
|
||||
text = message,
|
||||
style = MaterialTheme.typography.bodySmall,
|
||||
color = MaterialTheme.colorScheme.onBackground.copy(alpha = 0.8f),
|
||||
modifier = Modifier.sizeIn(maxWidth = 500.dp),
|
||||
)
|
||||
}
|
||||
}
|
||||
is LeanbackMainUiState.Error -> {
|
||||
state.message?.let { message ->
|
||||
Text(
|
||||
text = message,
|
||||
style = MaterialTheme.typography.bodySmall,
|
||||
color = MaterialTheme.colorScheme.error.copy(alpha = 0.8f),
|
||||
modifier = Modifier.sizeIn(maxWidth = 500.dp),
|
||||
)
|
||||
}
|
||||
}
|
||||
else -> {} // This case should never happen
|
||||
}
|
||||
}
|
||||
}
|
||||
LeanbackVisible({ isSettingsVisible }) {
|
||||
SettingsScreen()
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user