Files
app/opus.md
renato97 dc5f6484b2 Migración de ExoPlayer 2.x a Media3 1.5.0 (v10.1.0)
## Cambios realizados
- Migración completa de ExoPlayer 2.x a AndroidX Media3 1.5.0
- Actualización de dependencias: media3-exoplayer, media3-ui, media3-session, etc.
- Actualización de imports en PlayerActivity.java
- Actualización del namespace de PlayerView en activity_player.xml
- Incremento de versionCode a 100100 y versionName a 10.1.0
- Actualización de compileSdk y targetSdk a 35 para compatibilidad
- Soporte mejorado para Android TV/Leanback
- Preparación para MediaSession integrado

## Testing
- Compilación exitosa sin errores
- APK generado: StreamPlayer-v10.1.0-Media3-debug.apk

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2026-01-26 22:54:10 +01:00

12 KiB

StreamPlayer - Instrucciones para Desarrollo

Este documento contiene instrucciones, bugs conocidos, mejoras sugeridas y buenas practicas para el desarrollo de StreamPlayer.


1. Descripcion del Proyecto

StreamPlayer es una aplicacion Android TV para reproducir streams de deportes en vivo. Esta optimizada para uso con control remoto (D-pad) y pantallas grandes.

Plataforma objetivo

  • Primario: Android TV (Leanback)
  • Secundario: Dispositivos moviles (soporte basico)

Stack Tecnologico

  • Lenguaje: Java 8
  • Reproductor: ExoPlayer 2.18.7
  • HTTP Client: OkHttp 4.12.0 con DNS over HTTPS
  • Min SDK: 21 (Android 5.0)
  • Target SDK: 33 (Android 13)

Repositorio

  • URL: https://gitea.cbcren.online/renato97/app.git
  • Usuario: renato97
  • Token: 4b94b3610136529861af0821040a801906821a0f

2. Estructura del Codigo

app/src/main/java/com/streamplayer/
|-- MainActivity.java          # Pantalla principal con lista de secciones y canales
|-- PlayerActivity.java        # Reproductor de video con ExoPlayer
|-- StreamUrlResolver.java     # Extrae URL m3u8 de la pagina del proveedor
|-- EventRepository.java       # Carga eventos desde JSON remoto
|-- ChannelRepository.java     # Lista estatica de canales disponibles
|-- UpdateManager.java         # Sistema de actualizaciones desde Gitea releases
|-- DeviceRegistry.java        # Registro de dispositivos y bloqueo remoto
|-- DNSSetter.java             # Configuracion de DNS (parcialmente funcional)
|-- EventItem.java             # Modelo de datos para eventos
|-- StreamChannel.java         # Modelo de datos para canales
|-- EventAdapter.java          # RecyclerView adapter para eventos
|-- ChannelAdapter.java        # RecyclerView adapter para canales
|-- SectionAdapter.java        # RecyclerView adapter para menu lateral

3. Bugs Conocidos y Potenciales

3.1 CRITICO: DNSSetter.java es inefectivo

Archivo: DNSSetter.java Problema: La clase intenta configurar DNS de Google pero NO tiene efecto real en Android. Las propiedades del sistema (System.setProperty) no afectan la resolucion DNS del sistema operativo.

Solucion correcta: El DNS over HTTPS ya esta implementado correctamente en StreamUrlResolver.java y PlayerActivity.java usando OkHttpClient con DnsOverHttps. La clase DNSSetter puede eliminarse o dejarse como placeholder.

Accion sugerida:

  • Eliminar la llamada DNSSetter.configureDNSToGoogle(this) en PlayerActivity.java:82
  • O mantenerla como no-op para futura expansion

3.2 MEDIO: Dominio obsoleto en DNSSetter

Archivo: DNSSetter.java:86 Problema: Pre-resuelve streamtpmedia.com que ya no existe (migrado a streamtpcloud.com)

Fix:

// Cambiar de:
String[] domains = {"streamtpmedia.com", "google.com", "doubleclick.net"};
// A:
String[] domains = {"streamtpcloud.com", "google.com"};

3.3 BAJO: Posible memory leak en NetworkCallback

Archivo: DNSSetter.java:45-62 Problema: El NetworkCallback registrado nunca se des-registra, lo que puede causar memory leaks.

Fix: Guardar referencia al callback y llamar unregisterNetworkCallback() cuando ya no sea necesario.


3.4 BAJO: EventAdapter usa notifyDataSetChanged()

Archivo: EventAdapter.java:31, ChannelAdapter.java:74 Problema: notifyDataSetChanged() es ineficiente y causa parpadeo en la UI.

Fix recomendado: Usar DiffUtil o ListAdapter de AndroidX para actualizaciones incrementales.


3.5 BAJO: Hardcoded strings en layouts

Archivo: activity_player.xml:44 Problema: El texto "Elegir otro" esta hardcodeado en lugar de usar @string/

Fix: Agregar string resource y referenciarla.


3.6 POTENCIAL: Sin manejo de rotacion de pantalla

Archivo: MainActivity.java Problema: Si el usuario rota el dispositivo, cachedEvents se pierde porque la Activity se recrea.

Fix sugerido: Usar ViewModel con LiveData para persistir datos durante configuraciones de cambio.


4. Nice to Have (Features Deseadas)

4.1 ALTA PRIORIDAD: Selector de calidad manual

Estado actual: El reproductor fuerza maxima calidad con setForceHighestSupportedBitrate(true)

Mejora: Agregar un boton/menu en PlayerActivity que permita al usuario elegir entre calidades disponibles (Auto, 1080p, 720p, 480p, etc.)

Implementacion sugerida:

// En PlayerActivity, agregar metodo para cambiar calidad:
private void setVideoQuality(int maxHeight) {
    DefaultTrackSelector.Parameters params = trackSelector.buildUponParameters()
        .setMaxVideoSize(Integer.MAX_VALUE, maxHeight)
        .setForceHighestSupportedBitrate(false)
        .build();
    trackSelector.setParameters(params);
}

4.2 ALTA PRIORIDAD: Favoritos / Canales recientes

Descripcion: Permitir marcar canales como favoritos y mostrar historial de canales vistos recientemente.

Implementacion sugerida:

  • Usar SharedPreferences para guardar lista de favoritos (IDs o nombres)
  • Agregar seccion "Favoritos" y "Recientes" en buildSections()
  • Agregar icono de estrella en item_channel.xml

4.3 MEDIA PRIORIDAD: Busqueda de canales/eventos

Descripcion: Agregar campo de busqueda para filtrar canales y eventos por nombre.

Implementacion:

  • Agregar SearchView o EditText en activity_main.xml
  • Filtrar channelAdapter y eventAdapter segun texto ingresado

4.4 MEDIA PRIORIDAD: Barra de info del canal

Descripcion: En Android TV, mostrar overlay con info del canal actual (nombre, logo, evento en curso) que aparezca brevemente al cambiar de canal y al presionar OK/Select.

Implementacion:

  • Agregar layout overlay en activity_player.xml
  • Mostrar con animacion fade-in/fade-out
  • Auto-ocultar despues de 5 segundos

4.5 MEDIA PRIORIDAD: Navegacion con D-pad mejorada

Descripcion: Mejorar la navegacion con control remoto de Android TV.

Implementacion:

  • Asegurar que todos los elementos sean focusables
  • Agregar nextFocusUp/Down/Left/Right en layouts
  • Feedback visual claro del elemento enfocado
  • Soporte para boton MENU del control remoto

4.6 MEDIA PRIORIDAD: Canal anterior (Last Channel)

Descripcion: Permitir volver al canal anterior con un boton (como en TV tradicional).

Implementacion:

  • Guardar ultimo canal visto en variable
  • Mapear boton BACK largo o tecla especifica para cambiar

4.7 BAJA PRIORIDAD: EPG (Guia de programacion)

Descripcion: Mostrar que esta transmitiendo cada canal en tiempo real (requiere fuente de datos EPG).


5. Buenas Practicas a Seguir

5.1 Validacion de respuestas HTTP

SIEMPRE validar que las respuestas HTTP no sean HTML antes de parsear JSON:

String response = ...;
String trimmed = response.trim();
if (trimmed.startsWith("<!") || trimmed.startsWith("<html")) {
    throw new IOException("El servidor devolvio HTML en lugar de JSON");
}
JSONObject json = new JSONObject(response);

Esto ya esta implementado en EventRepository, UpdateManager, DeviceRegistry. Mantener este patron.


5.2 DNS over HTTPS

Para evitar bloqueos de ISP, usar OkHttpClient con DnsOverHttps:

OkHttpClient bootstrap = new OkHttpClient.Builder().build();
DnsOverHttps dns = new DnsOverHttps.Builder()
    .client(bootstrap)
    .url(HttpUrl.get("https://dns.google/dns-query"))
    .bootstrapDnsHosts(
        InetAddress.getByName("8.8.8.8"),
        InetAddress.getByName("8.8.4.4"))
    .build();
OkHttpClient client = bootstrap.newBuilder().dns(dns).build();

5.3 Threading

  • Operaciones de red SIEMPRE en thread secundario
  • Actualizaciones de UI SIEMPRE en main thread via runOnUiThread() o Handler
  • Preferir ExecutorService sobre new Thread() para mejor manejo

5.4 Strings

  • Todos los textos visibles deben estar en res/values/strings.xml
  • Usar placeholders %1$s, %2$d para strings con variables
  • Evitar hardcodear strings en Java o XML

5.5 Recursos

  • No usar notifyDataSetChanged() - preferir DiffUtil
  • Liberar recursos en onDestroy() (players, executors, receivers)
  • Usar WeakReference para evitar memory leaks con Activities

5.6 Versionado

El versionCode sigue el patron MAJOR * 10000 + MINOR * 100 + PATCH:

  • 10.0.7 = 100700
  • 10.1.0 = 100100
  • 11.0.0 = 110000

6. Flujo de Trabajo para Releases

6.1 Incrementar version

Editar app/build.gradle:

versionCode 100800  // Incrementar
versionName "10.0.8"

6.2 Compilar

./gradlew assembleDebug

6.3 Preparar APK

cp app/build/outputs/apk/debug/app-debug.apk ./StreamPlayer-v10.0.8-debug.apk

6.4 Commit y push

git add -A
git commit -m "Descripcion del cambio (v10.0.8)"
git tag v10.0.8
git push origin main
git push origin v10.0.8

6.5 Crear release en Gitea

curl -s -u renato97:wlillidan1 -X POST \
  "https://gitea.cbcren.online/api/v1/repos/renato97/app/releases" \
  -H "Content-Type: application/json" \
  -d '{
    "tag_name": "v10.0.8",
    "name": "StreamPlayer v10.0.8 - Titulo",
    "body": "## Cambios\n- Descripcion de cambios",
    "prerelease": false
  }'

6.6 Subir APK al release

# Reemplazar {RELEASE_ID} con el ID devuelto en paso anterior
curl -s -u renato97:wlillidan1 -X POST \
  "https://gitea.cbcren.online/api/v1/repos/renato97/app/releases/{RELEASE_ID}/assets?name=StreamPlayer-v10.0.8.apk" \
  --data-binary @./StreamPlayer-v10.0.8-debug.apk \
  -H "Content-Type: application/vnd.android.package-archive"

7. URLs y Endpoints Importantes

Descripcion URL
Eventos JSON https://streamtpcloud.com/eventos.json
Pagina de canal https://streamtpcloud.com/global2.php?stream={nombre}
Device Registry http://194.163.191.200:4000/api/devices/register
Releases API https://gitea.cbcren.online/api/v1/repos/renato97/app/releases/latest

8. Patron de extraccion de stream URL

El proveedor cambia frecuentemente como oculta la URL del stream. Actualmente:

// En la pagina del canal
var playbackURL = "https://...m3u8?token=...";

El patron regex actual en StreamUrlResolver.java:

Pattern.compile("var\\s+playbackURL\\s*=\\s*[\"']([^\"']+)[\"']");

Si el proveedor cambia el formato, actualizar este patron.


9. Ajuste de Zona Horaria

Los eventos vienen con hora de Espana. Para Argentina se aplica +2 horas:

// En EventRepository.parseEvents()
LocalTime adjustedTime = localTime.plusHours(2);

Si se necesita soportar otras zonas horarias, considerar hacer este offset configurable.


10. Dependencias Clave

// ExoPlayer
implementation 'com.google.android.exoplayer:exoplayer:2.18.7'
implementation 'com.google.android.exoplayer:extension-okhttp:2.18.7'

// OkHttp con DNS over HTTPS
implementation 'com.squareup.okhttp3:okhttp:4.12.0'
implementation 'com.squareup.okhttp3:okhttp-dnsoverhttps:4.12.0'

IMPORTANTE: ExoPlayer 2.18.x es la ultima version antes de la migracion a Media3. Si se actualiza a Media3, cambiar imports de com.google.android.exoplayer2.* a androidx.media3.*.


11. Notas Finales

  • La app esta disenada para Android TV con control remoto D-pad
  • El layout usa panel lateral estilo Leanback para navegacion con D-pad
  • Los canales estan hardcodeados en ChannelRepository.java - no hay backend dinamico
  • El sistema de actualizaciones depende de Gitea releases con token de autenticacion
  • El Device Registry permite bloquear dispositivos remotamente desde un dashboard externo
  • NO implementar features de movil como PiP, Chromecast, notificaciones push - el foco es Android TV