fix: improve display of EPG folding

This commit is contained in:
tunbmoon
2026-03-02 17:06:45 +08:00
parent ed5f6cc716
commit 6653047fe8
2 changed files with 52 additions and 35 deletions
@@ -2,12 +2,14 @@ package com.antoniegil.astronia.ui.component
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.foundation.BorderStroke
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.LazyListState
import androidx.compose.foundation.lazy.LazyRow
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.PlayArrow
@@ -20,6 +22,7 @@ import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clipToBounds
import androidx.compose.ui.platform.LocalConfiguration
import androidx.compose.ui.platform.LocalView
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextOverflow
@@ -30,7 +33,6 @@ import com.antoniegil.astronia.util.M3U8Channel
import kotlinx.coroutines.launch
import java.text.SimpleDateFormat
import java.util.Date
import java.util.Locale
@Composable
fun ChannelListSection(
@@ -290,7 +292,7 @@ fun ChannelItem(
modifier = Modifier
.fillMaxWidth()
.defaultMinSize(minHeight = 96.dp)
.padding(horizontal = 16.dp, vertical = 12.dp),
.padding(horizontal = 16.dp, vertical = 6.dp),
verticalAlignment = Alignment.CenterVertically
) {
Column(modifier = Modifier.weight(1f)) {
@@ -362,15 +364,12 @@ fun ChannelItem(
if (upcomingPrograms.isNotEmpty()) {
val currentProgram = upcomingPrograms.first()
HorizontalDivider(
modifier = Modifier.padding(horizontal = 16.dp),
color = MaterialTheme.colorScheme.outlineVariant.copy(alpha = 0.3f)
)
Column(
modifier = Modifier
.fillMaxWidth()
.clickable { isEpgExpanded = !isEpgExpanded }
.padding(horizontal = 16.dp, vertical = 4.dp)
.padding(horizontal = 16.dp)
.padding(top = 0.dp, bottom = 12.dp)
) {
Row(
modifier = Modifier.fillMaxWidth(),
@@ -379,7 +378,7 @@ fun ChannelItem(
) {
Text(
text = currentProgram.title,
style = MaterialTheme.typography.bodySmall,
style = MaterialTheme.typography.bodyMedium,
color = MaterialTheme.colorScheme.primary,
maxLines = 1,
overflow = TextOverflow.Ellipsis,
@@ -397,18 +396,47 @@ fun ChannelItem(
Column(
modifier = Modifier
.fillMaxWidth()
.padding(top = 8.dp),
verticalArrangement = Arrangement.spacedBy(6.dp)
.padding(top = 8.dp)
) {
val dateFormat = remember { SimpleDateFormat("HH:mm", Locale.getDefault()) }
upcomingPrograms.take(10).forEach { program ->
val locale = LocalConfiguration.current.locales[0]
val dateFormat = remember(locale) { SimpleDateFormat("HH:mm", locale) }
upcomingPrograms.take(10).forEachIndexed { index, program ->
val startTimeStr = dateFormat.format(Date(program.startTime))
val stopTimeStr = dateFormat.format(Date(program.stopTime))
Text(
text = "${program.title} - $startTimeStr - $stopTimeStr",
style = MaterialTheme.typography.bodySmall,
color = MaterialTheme.colorScheme.onSurfaceVariant
)
Row(
modifier = Modifier.fillMaxWidth(),
verticalAlignment = Alignment.CenterVertically
) {
Column(
horizontalAlignment = Alignment.CenterHorizontally,
modifier = Modifier.padding(vertical = 6.dp)
) {
Box(
modifier = Modifier
.size(6.dp)
.background(
color = MaterialTheme.colorScheme.primary,
shape = CircleShape
)
)
if (index < upcomingPrograms.take(10).size - 1) {
Spacer(
modifier = Modifier
.width(1.dp)
.height(24.dp)
.background(MaterialTheme.colorScheme.primary.copy(alpha = 0.6f))
)
}
}
Text(
text = "$startTimeStr - ${program.title}",
style = MaterialTheme.typography.bodyMedium,
color = MaterialTheme.colorScheme.onSurfaceVariant,
modifier = Modifier
.weight(1f)
.padding(start = 8.dp)
.padding(vertical = 6.dp)
)
}
}
}
}
@@ -36,7 +36,6 @@ object M3U8Parser {
private var epgUrl: String? = null
private var epgData: Map<String, List<EpgProgram>> = emptyMap()
private var epgNameMap: Map<String, String> = emptyMap()
suspend fun parseM3U8(content: String): Result<List<M3U8Channel>> = withContext(Dispatchers.IO) {
try {
@@ -74,7 +73,7 @@ object M3U8Parser {
val name = currentName.ifEmpty { "Channel ${channels.size + 1}" }
val finalId = "${trimmedLine.hashCode()}_${channels.size}"
val epgChannelId = epgNameMap[currentTvgName] ?: currentTvgId
val epgChannelId = currentTvgId
val programs = epgData[epgChannelId] ?: emptyList()
val currentProgram = programs.find { currentTime in it.startTime..it.stopTime }
val epgTitle = currentProgram?.title ?: ""
@@ -137,7 +136,7 @@ object M3U8Parser {
loadEpgData()
val currentTime = System.currentTimeMillis()
val updatedChannels = result.channels.map { channel ->
val epgChannelId = epgNameMap[channel.tvgName] ?: channel.tvgId
val epgChannelId = channel.tvgId
val programs = epgData[epgChannelId] ?: emptyList()
val currentProgram = programs.find { currentTime in it.startTime..it.stopTime }
val epgTitle = currentProgram?.title ?: ""
@@ -164,7 +163,7 @@ object M3U8Parser {
loadEpgData()
val currentTime = System.currentTimeMillis()
val updatedChannels = result.channels.map { channel ->
val epgChannelId = epgNameMap[channel.tvgName] ?: channel.tvgId
val epgChannelId = channel.tvgId
val programs = epgData[epgChannelId] ?: emptyList()
val currentProgram = programs.find { currentTime in it.startTime..it.stopTime }
val epgTitle = currentProgram?.title ?: ""
@@ -336,26 +335,16 @@ object M3U8Parser {
val xmlContent = response.body.string()
val (data, nameMap) = parseEpgXml(xmlContent)
epgData = data
epgNameMap = nameMap
epgData = parseEpgXml(xmlContent)
} catch (e: Exception) {
}
}
}
private fun parseEpgXml(xml: String): Pair<Map<String, List<EpgProgram>>, Map<String, String>> {
private fun parseEpgXml(xml: String): Map<String, List<EpgProgram>> {
val result = mutableMapOf<String, MutableList<EpgProgram>>()
val nameMap = mutableMapOf<String, String>()
val currentTime = System.currentTimeMillis()
val channelRegex = Regex("""<channel\s+id="([^"]+)"[^>]*>.*?<display-name[^>]*>([^<]+)</display-name>.*?</channel>""", RegexOption.DOT_MATCHES_ALL)
channelRegex.findAll(xml).forEach { match ->
val channelId = match.groupValues[1]
val displayName = match.groupValues[2]
nameMap[displayName] = channelId
}
val programmeRegex = Regex("""<programme\s+start="([^"]+)"\s+stop="([^"]+)"\s+channel="([^"]+)"[^>]*>(.*?)</programme>""", RegexOption.DOT_MATCHES_ALL)
val titleRegex = Regex("""<title[^>]*>([^<]+)</title>""")
@@ -386,7 +375,7 @@ object M3U8Parser {
result.values.forEach { it.sortBy { p -> p.startTime } }
return Pair(result, nameMap)
return result
}
private fun parseEpgTime(timeStr: String): Long {