fix: Corregir todos los errores de compilación

- M3UParser.kt: Fix regex de atributos
- ChannelRepository.kt: Manejo correcto de M3UParseResult
- CategoryChip.kt: Simplificar border
- DnsConfigurator.kt: Usar HttpUrl.Companion.toHttpUrl()
- PlayerManager.kt: Cambiar buildUponParameters() a buildUpon()
- MainActivity.kt: Eliminar splashscreen no disponible
- PlayerControls.kt/PlayerScreen.kt: Fix iconos automirrored
- UpdateDialog.kt: Fix LinearProgressIndicator
- ChannelsScreen.kt: Eliminar PullToRefresh experimental
- UpdateInstallHelper.kt: Fix icono de descarga
- Agregar dependencia okhttp-dnsoverhttps
- Habilitar buildConfig en build.gradle.kts
This commit is contained in:
Renato
2026-01-28 23:48:37 +00:00
parent 2ccdf3a78c
commit 0d8c8e5ff2
1117 changed files with 122 additions and 102 deletions

View File

@@ -12,7 +12,6 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
@@ -46,17 +45,11 @@ class MainActivity : ComponentActivity() {
}
override fun onCreate(savedInstanceState: Bundle?) {
val splashScreen = installSplashScreen()
super.onCreate(savedInstanceState)
// Initialize repository
initializeRepository()
// Keep splash screen visible while loading
splashScreen.setKeepOnScreenCondition {
viewModel.uiState.value.isLoading
}
setContent {
IPTVAppTheme {
Surface(

View File

@@ -281,7 +281,7 @@ class M3UParser(private val context: Context? = null) {
*/
private fun parseAttributes(attributesString: String): Map<String, String> {
val attributes = mutableMapOf<String, String>()
val regex = """(\w+)="([^"]*)",""".toRegex()
val regex = """(\w+)="([^"]*)"""".toRegex()
regex.findAll(attributesString).forEach { matchResult ->
val key = matchResult.groupValues[1]

View File

@@ -4,6 +4,7 @@ import android.content.Context
import android.content.SharedPreferences
import com.iptv.app.data.model.Channel
import com.iptv.app.data.parser.M3UParser
import com.iptv.app.data.parser.M3UParseResult
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.FlowPreview
import kotlinx.coroutines.flow.Flow
@@ -229,7 +230,10 @@ class ChannelRepository(
private suspend fun fetchFromNetwork(m3uUrl: String): List<Channel> {
return withContext(Dispatchers.IO) {
m3uParser.parseFromUrl(m3uUrl)
when (val result = m3uParser.parseFromUrl(m3uUrl)) {
is M3UParseResult.Success -> result.channels
is M3UParseResult.Error -> throw Exception(result.message)
}
}
}

View File

@@ -1,6 +1,7 @@
package com.iptv.app.ui.components
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.FilterChip
import androidx.compose.material3.FilterChipDefaults
import androidx.compose.material3.MaterialTheme
@@ -12,6 +13,7 @@ import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import com.iptv.app.ui.theme.*
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun CategoryChip(
category: String,
@@ -37,13 +39,7 @@ fun CategoryChip(
containerColor = MaterialTheme.colorScheme.surfaceVariant,
labelColor = MaterialTheme.colorScheme.onSurfaceVariant
),
border = FilterChipDefaults.filterChipBorder(
enabled = true,
selected = isSelected,
borderColor = if (isSelected) categoryColor else MaterialTheme.colorScheme.outline,
selectedBorderColor = categoryColor,
borderWidth = 1.dp
)
border = null
)
}

View File

@@ -17,7 +17,7 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.filled.ArrowBack
import androidx.compose.material.icons.filled.ArrowBack
import androidx.compose.material.icons.filled.Pause
import androidx.compose.material.icons.filled.PlayArrow
import androidx.compose.material3.Icon
@@ -161,7 +161,7 @@ private fun TopControlBar(
modifier = Modifier.size(48.dp)
) {
Icon(
imageVector = Icons.AutoMirrored.Filled.ArrowBack,
imageVector = Icons.Default.ArrowBack,
contentDescription = "Back",
tint = Color.White,
modifier = Modifier.size(28.dp)

View File

@@ -123,7 +123,7 @@ fun DownloadProgressDialog(
horizontalAlignment = Alignment.CenterHorizontally
) {
LinearProgressIndicator(
progress = { progress.percentage / 100f },
progress = progress.percentage / 100f,
modifier = Modifier.fillMaxWidth()
)

View File

@@ -199,7 +199,7 @@ private fun createExoPlayer(
): ExoPlayer {
val trackSelector = DefaultTrackSelector(context).apply {
setParameters(
buildUponParameters()
this.buildUponParameters()
.setMaxVideoSizeSd()
.setPreferredAudioLanguage("en")
)
@@ -331,8 +331,6 @@ private fun preparePlayer(context: Context, player: ExoPlayer, streamUrl: String
// Create OkHttpDataSource.Factory with custom DNS client
val dataSourceFactory = OkHttpDataSource.Factory(okHttpClient)
.setConnectTimeoutMs(15000)
.setReadTimeoutMs(15000)
val mediaSource = HlsMediaSource.Factory(dataSourceFactory)
.createMediaSource(mediaItem)

View File

@@ -254,7 +254,6 @@ private fun ChannelsGrid(
}
}
@OptIn(ExperimentalMaterial3Api::class)
@Composable
private fun PullToRefreshContainer(
isRefreshing: Boolean,
@@ -262,30 +261,16 @@ private fun PullToRefreshContainer(
modifier: Modifier = Modifier,
content: @Composable () -> Unit
) {
val pullToRefreshState = rememberPullToRefreshState()
// Simplified pull-to-refresh container without experimental APIs
Box(modifier = modifier) {
content()
if (pullToRefreshState.isRefreshing) {
LaunchedEffect(true) {
// Trigger refresh when isRefreshing becomes true
LaunchedEffect(isRefreshing) {
if (isRefreshing) {
onRefresh()
}
}
LaunchedEffect(isRefreshing) {
if (isRefreshing) {
pullToRefreshState.startRefresh()
} else {
pullToRefreshState.endRefresh()
}
}
PullToRefreshBox(
state = pullToRefreshState,
modifier = Modifier.align(Alignment.TopCenter),
content = {}
)
}
}

View File

@@ -16,7 +16,7 @@ import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.filled.ArrowBack
import androidx.compose.material.icons.filled.ArrowBack
import androidx.compose.material.icons.filled.Fullscreen
import androidx.compose.material.icons.filled.FullscreenExit
import androidx.compose.material.icons.filled.Pause
@@ -255,7 +255,7 @@ private fun PlayerControlsOverlay(
.background(Color.Black.copy(alpha = 0.5f))
) {
Icon(
imageVector = Icons.AutoMirrored.Filled.ArrowBack,
imageVector = Icons.Default.ArrowBack,
contentDescription = "Back",
tint = Color.White,
modifier = Modifier.size(24.dp)

View File

@@ -277,9 +277,8 @@ class ChannelsViewModel(
combine(
_uiState.map { it.channels },
_searchQuery
.debounce(SEARCH_DEBOUNCE_MS)
.distinctUntilChanged(),
_selectedCategory.distinctUntilChanged()
.debounce(SEARCH_DEBOUNCE_MS),
_selectedCategory
) { channels, query, category ->
channels.filter { channel ->
val matchesSearch = query.isBlank() ||

View File

@@ -166,7 +166,7 @@ class PlayerViewModel(
*/
fun getCurrentStreamUrl(): Uri? {
return _uiState.value.currentChannel?.let {
Uri.parse(it.streamUrl)
Uri.parse(it.url)
}
}

View File

@@ -7,6 +7,7 @@ import android.net.NetworkCapabilities
import android.net.NetworkRequest
import android.util.Log
import okhttp3.Dns
import okhttp3.HttpUrl.Companion.toHttpUrl
import okhttp3.OkHttpClient
import okhttp3.dnsoverhttps.DnsOverHttps
import java.net.InetAddress
@@ -75,7 +76,7 @@ object DnsConfigurator {
return DnsOverHttps.Builder()
.client(bootstrapClient)
.url(java.net.URL(GOOGLE_DOH_URL))
.url(GOOGLE_DOH_URL.toHttpUrl())
.bootstrapDnsHosts(GOOGLE_DNS_IPV4 + CLOUDFLARE_DNS_IPV4)
.includeIPv6(false)
.build()

View File

@@ -85,11 +85,11 @@ class PlayerManager(private val context: Context) {
releasePlayer() // Release any existing player
trackSelector = DefaultTrackSelector(context).apply {
setParameters(
buildUponParameters()
.setPreferredAudioLanguage("en")
.setMaxVideoSizeSd()
)
val params = this.parameters.buildUpon()
.setPreferredAudioLanguage("en")
.setMaxVideoSizeSd()
.build()
this.parameters = params
}
val player = ExoPlayer.Builder(context)
@@ -240,37 +240,28 @@ class PlayerManager(private val context: Context) {
fun setVideoQuality(quality: VideoQuality?) {
val selector = trackSelector ?: return
val parameters = if (quality == null) {
// Auto quality selection
selector.parameters.buildUponParameters()
.clearOverridesOfType(C.TRACK_TYPE_VIDEO)
val paramsBuilder = selector.parameters.buildUpon()
if (quality == null) {
paramsBuilder.clearOverridesOfType(C.TRACK_TYPE_VIDEO)
} else {
// Manual quality selection
val player = exoPlayer ?: return
val tracks = player.currentTracks
var override: TrackSelectionOverride? = null
for (trackGroup in tracks.groups) {
if (trackGroup.type == C.TRACK_TYPE_VIDEO) {
for (trackIndex in 0 until trackGroup.length) {
val format = trackGroup.getTrackFormat(trackIndex)
if (format.width == quality.width && format.height == quality.height) {
override = TrackSelectionOverride(trackGroup.mediaTrackGroup, trackIndex)
val override = TrackSelectionOverride(trackGroup.mediaTrackGroup, trackIndex)
paramsBuilder.setOverrideForType(override)
break
}
}
}
}
if (override != null) {
selector.parameters.buildUponParameters()
.setOverrideForType(override)
} else {
selector.parameters.buildUponParameters()
}
}
selector.parameters = parameters.build()
selector.parameters = paramsBuilder.build()
}
/**
@@ -281,11 +272,10 @@ class PlayerManager(private val context: Context) {
fun setAudioTrack(languageCode: String?) {
val selector = trackSelector ?: return
val parameters = selector.parameters.buildUponParameters()
val paramsBuilder = selector.parameters.buildUpon()
.setPreferredAudioLanguage(languageCode)
.build()
selector.parameters = parameters
selector.parameters = paramsBuilder.build()
}
/**
@@ -360,16 +350,15 @@ class PlayerManager(private val context: Context) {
fun setSubtitlesEnabled(enabled: Boolean, language: String? = null) {
val selector = trackSelector ?: return
val parameters = if (enabled) {
selector.parameters.buildUponParameters()
val paramsBuilder = if (enabled) {
selector.parameters.buildUpon()
.setPreferredTextLanguage(language)
.setIgnoredTextSelectionFlags(0)
} else {
selector.parameters.buildUponParameters()
selector.parameters.buildUpon()
.setIgnoredTextSelectionFlags(C.SELECTION_FLAG_DEFAULT)
}
selector.parameters = parameters.build()
selector.parameters = paramsBuilder.build()
}
/**

View File

@@ -238,7 +238,7 @@ class UpdateInstallHelper(private val context: Context) {
*/
fun showDownloadProgressNotification(progress: Int, totalBytes: Long) {
val notification = NotificationCompat.Builder(context, CHANNEL_ID)
.setSmallIcon(android.R.drawable.ic_menu_download)
.setSmallIcon(android.R.drawable.stat_sys_download)
.setContentTitle(context.getString(R.string.update_downloading_title))
.setContentText(formatBytes(totalBytes))
.setProgress(100, progress, false)