Skip to content

Commit

Permalink
🎨 经典界面多天节目单
Browse files Browse the repository at this point in the history
  • Loading branch information
yaoxieyoulei committed Jun 25, 2024
1 parent 3b3b93d commit 3d18824
Show file tree
Hide file tree
Showing 3 changed files with 146 additions and 39 deletions.
1 change: 1 addition & 0 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:networkSecurityConfig="@xml/network_security_config"
android:screenOrientation="sensorLandscape"
android:supportsRtl="true"
android:theme="@style/Theme.MyTV"
android:usesCleartextTraffic="true"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,7 @@ private fun LeanbackClassicPanelScreenContent(
focusedIptvGroup.iptvList
},
epgListProvider = epgListProvider,
initialIptvProvider = { focusedIptv },
initialIptvProvider = currentIptvProvider,
onIptvSelected = onIptvSelected,
onIptvFavoriteToggle = onIptvFavoriteToggle,
onIptvFocused = { iptv, focusRequester ->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,11 @@ package top.yogiczy.mytv.ui.screens.leanback.classicpanel.components

import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement
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.width
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.PlayArrow
Expand All @@ -14,6 +17,7 @@ import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
Expand All @@ -22,12 +26,15 @@ import androidx.compose.runtime.snapshotFlow
import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.alpha
import androidx.compose.ui.focus.FocusDirection
import androidx.compose.ui.focus.FocusRequester
import androidx.compose.ui.focus.focusProperties
import androidx.compose.ui.focus.focusRequester
import androidx.compose.ui.focus.onFocusChanged
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.tv.foundation.ExperimentalTvFoundationApi
import androidx.tv.foundation.lazy.list.TvLazyColumn
import androidx.tv.foundation.lazy.list.TvLazyListState
import androidx.tv.foundation.lazy.list.items
Expand All @@ -43,46 +50,86 @@ import java.text.SimpleDateFormat
import java.util.Locale
import kotlin.math.max

@OptIn(ExperimentalComposeUiApi::class)
@OptIn(ExperimentalComposeUiApi::class, ExperimentalTvFoundationApi::class)
@Composable
fun LeanbackClassicPanelEpgList(
modifier: Modifier = Modifier,
epgProvider: () -> Epg? = { Epg() },
exitFocusRequesterProvider: () -> FocusRequester = { FocusRequester.Default },
onUserAction: () -> Unit = {},
) {
val dateFormat = SimpleDateFormat("E MM-dd", Locale.getDefault())
val epg = epgProvider()

if (epg != null && epg.programmes.isNotEmpty()) {
val listState = remember(epg) {
TvLazyListState(max(0, epg.programmes.indexOfFirst { it.isLive() } - 2))
val programmesGroup = remember(epg) {
epg.programmes.groupBy { dateFormat.format(it.startAt) }
}
var currentDay by remember(programmesGroup) { mutableStateOf(dateFormat.format(System.currentTimeMillis())) }
val programmes = remember(currentDay, programmesGroup) {
programmesGroup.getOrElse(currentDay) { emptyList() }
}

val programmesListState = remember(programmes) {
TvLazyListState(max(0, programmes.indexOfFirst { it.isLive() } - 2))
}
val daysListState = remember(programmesGroup) {
TvLazyListState(max(0, programmesGroup.keys.indexOf(currentDay) - 2))
}

LaunchedEffect(listState) {
snapshotFlow { listState.isScrollInProgress }
LaunchedEffect(programmesListState) {
snapshotFlow { programmesListState.isScrollInProgress }
.distinctUntilChanged()
.collect { _ -> onUserAction() }
}
LaunchedEffect(daysListState) {
snapshotFlow { daysListState.isScrollInProgress }
.distinctUntilChanged()
.collect { _ -> onUserAction() }
}

Row {
TvLazyColumn(
state = programmesListState,
contentPadding = PaddingValues(vertical = 8.dp, horizontal = 12.dp),
verticalArrangement = Arrangement.spacedBy(8.dp),
modifier = modifier
.fillMaxHeight()
.width(240.dp)
.background(MaterialTheme.colorScheme.background.copy(0.7f))
.focusProperties {
exit = {
if (it == FocusDirection.Left) exitFocusRequesterProvider()
else FocusRequester.Default
}
},
) {
items(programmes) { programme ->
LeanbackClassicPanelEpgItem(
epgProgrammeProvider = { programme },
)
}
}

TvLazyColumn(
state = listState,
contentPadding = PaddingValues(vertical = 8.dp, horizontal = 12.dp),
verticalArrangement = Arrangement.spacedBy(8.dp),
modifier = modifier
.fillMaxHeight()
.width(240.dp)
.background(MaterialTheme.colorScheme.background.copy(0.7f))
.focusProperties {
exit = {
exitFocusRequesterProvider()
if (programmesGroup.size > 1) {
TvLazyColumn(
state = daysListState,
contentPadding = PaddingValues(vertical = 8.dp, horizontal = 12.dp),
verticalArrangement = Arrangement.spacedBy(8.dp),
modifier = modifier
.fillMaxHeight()
.width(100.dp)
.background(MaterialTheme.colorScheme.background.copy(0.7f))
) {

items(programmesGroup.keys.toList()) {
LeanbackClassicPanelEpgDayItem(
dayProvider = { it },
currentDayProvider = { currentDay },
onChangeCurrentDay = { currentDay = it },
)
}
},
) {
items(epg.programmes) { programme ->
LeanbackClassicPanelEpgItem(
epgProgrammeProvider = { programme },
)
}
}
}
}
Expand Down Expand Up @@ -146,6 +193,75 @@ private fun LeanbackClassicPanelEpgItem(
}
}

@Composable
private fun LeanbackClassicPanelEpgDayItem(
modifier: Modifier = Modifier,
dayProvider: () -> String = { "" },
currentDayProvider: () -> String = { "" },
onChangeCurrentDay: () -> Unit = {},
) {
val day = dayProvider()

val dateFormat = SimpleDateFormat("E MM-dd", Locale.getDefault())
val today = dateFormat.format(System.currentTimeMillis())
val tomorrow = dateFormat.format(System.currentTimeMillis() + 24 * 3600 * 1000)
val dayAfterTomorrow =
dateFormat.format(System.currentTimeMillis() + 48 * 3600 * 1000)

val focusRequester = remember { FocusRequester() }
val isSelected by remember(currentDayProvider()) { derivedStateOf { day == currentDayProvider() } }
var isFocused by remember { mutableStateOf(false) }

CompositionLocalProvider(
LocalContentColor provides if (isFocused) MaterialTheme.colorScheme.background
else MaterialTheme.colorScheme.onBackground
) {
androidx.tv.material3.ListItem(
modifier = modifier
.focusRequester(focusRequester)
.onFocusChanged {
isFocused = it.isFocused || it.hasFocus
}
.handleLeanbackKeyEvents(
onSelect = {
if (isFocused) onChangeCurrentDay()
else focusRequester.requestFocus()
}
),
colors = ListItemDefaults.colors(
focusedContainerColor = MaterialTheme.colorScheme.onBackground,
selectedContainerColor = MaterialTheme.colorScheme.surfaceVariant.copy(
alpha = 0.5f
),
),
selected = isSelected,
onClick = {},
headlineContent = {
Column {
val key = day.split(" ")

Text(
text = when (day) {
today -> "今天"
tomorrow -> "明天"
dayAfterTomorrow -> "后天"
else -> key[0]
},
modifier = Modifier.fillMaxWidth(),
textAlign = TextAlign.Center,
)

Text(
text = key[1],
modifier = Modifier.fillMaxWidth(),
textAlign = TextAlign.Center,
)
}
},
)
}
}

@Preview
@Composable
private fun LeanbackClassicPanelEpgListPreview() {
Expand All @@ -154,23 +270,13 @@ private fun LeanbackClassicPanelEpgListPreview() {
epgProvider = {
Epg(
channel = "CCTV1", programmes = EpgProgrammeList(
listOf(
List(200) { index ->
EpgProgramme(
title = "节目1".repeat(10),
startAt = System.currentTimeMillis(),
endAt = System.currentTimeMillis() + 3600_000,
),
EpgProgramme(
title = "节目2",
startAt = System.currentTimeMillis() + 3600_000,
endAt = System.currentTimeMillis() + 7200_000,
),
EpgProgramme(
title = "节目3",
startAt = System.currentTimeMillis() + 7200_000,
endAt = System.currentTimeMillis() + 10800_000,
),
)
title = "节目$index",
startAt = System.currentTimeMillis() - 3600 * 1000 * 24 * 5 + index * 3600 * 1000,
endAt = System.currentTimeMillis() - 3600 * 1000 * 24 * 5 + index * 3600 * 1000 + 3600 * 1000
)
}
)
)
}
Expand Down

0 comments on commit 3d18824

Please sign in to comment.