FreeTLab c3cc4120c0 fix(android): stop the resource shrinker from deleting Dart-named drawables
Root cause found, and it is not the stale build cache I claimed earlier.
flutter clean was good hygiene and changed nothing here, because nothing
was cached: the resources were being deliberately removed.

Flutter's own Gradle plugin enables shrinking on every release build --
FlutterPlugin.kt, `releaseBuildType.isMinifyEnabled = true` and
`isShrinkResources = true` -- no matter what app/build.gradle.kts says. The
shrinker keeps what it can see referenced, and it cannot see
`MediaControl.custom(androidIcon: 'drawable/ic_auto_eq_on')`: that is a
string inside Dart, resolved at runtime through getIdentifier. So both
equalizer icons were stripped from every release APK ever built.

The evidence that pins it, from the APK pulled off the device:

  ic_stat_pluriwave   present   <- referenced as R.drawable from Kotlin,
                                   4 call sites in the alarm notifications
  ic_auto_eq_on       absent    <- named only in a Dart string
  ic_auto_eq_off      absent    <- named only in a Dart string

Same folder, same file shape, same commit range. The only difference is
whether a real R.drawable reference exists, which is exactly what the
shrinker looks for.

The consequence was never a blank button. getResourceId returns 0 for an
unresolvable name, PlaybackStateCompat.CustomAction.Builder throws on a 0
icon, and that throw aborts AudioService.setState before the media session
is activated -- so Android Auto held a frozen, inactive session. Dead
playback screen, play that never became pause, the app losing its pane to
any app with a live session, audio playing "as if it were not the app".
One shrunk file, four symptoms, since 31 July (2540556).

Two protections, because they fail differently:
- res/raw/keep.xml with tools:keep is the official mechanism for
  dynamically resolved resources and is what actually binds the shrinker;
- RecursosResueltosPorNombre.kt gives them genuine R.drawable references,
  the same thing that kept ic_stat_pluriwave alive all along.

station_art_* are kept too. They are reached the same way, through
android.resource:// URIs built in Dart, and survived only by luck.

Tests: 1165, unchanged -- this is a build-configuration fix, and no Dart
test can see it. The CI resource guard is what verifies it now.
2026-08-07 12:52:55 +02:00

PluriWave

Radio mundial con ecualizador personalizable, reconocimiento de canciones y UI premium.

Features

  • +53.000 emisoras de 238 países (Radio Browser API)
  • Ecualizador por emisora — guarda tu preset favorito para cada radio
  • Reconocimiento de canciones — "¿Qué suena?" sin salir de la app
  • Timer de auto-apagado — perfecto para dormir
  • Reproducción en segundo plano — sigue sonando con la pantalla apagada
  • Favoritos — acceso rápido a emisoras preferidas
  • Compartir — envía emisoras a tus amigos
  • UI premium — Material You, visualizador de audio, animaciones fluidas

Monetización

  • 14 días PRO gratis al instalar
  • Free: radio + favoritos + timer + background (con ads)
  • PRO €2.99/año: sin ads + ecualizador + reconocimiento ilimitado

Stack

  • Frontend: Flutter (Android + iOS)
  • Radio API: Radio Browser (gratis, +53K emisoras)
  • Audio: just_audio + audio_service
  • Ecualizador: just_audio equalizer (Android nativo)
  • Reconocimiento: AudD API (1000 req/mes free)
  • Ads: Google AdMob
  • Compras: in_app_purchase

CI/CD

Workflow Gitea Actions en .gitea/workflows/ci.yml, runner macmini-flutter.

Jobs: flutter pub getflutter testbuild apk --releasebuild appbundle --release

Artifacts: APK y AAB guardados en Gitea con nombre pluriwave-apk-<sha> / pluriwave-aab-<sha>.

Notificaciones: Telegram al completar (éxito / fallo ).

Secrets necesarios en el repo:

Secret Uso
TELEGRAM_BOT_TOKEN Notificaciones CI
TELEGRAM_CHAT_ID Canal de destino

Signing: build de release usa clave debug (válido para CI interno). Para Play Store se requiere keystore como secret adicional.

Desarrollador

FreeTimeLab — freetimelab.es

Licencia

MIT

S
Description
PluriWave — Radio mundial con ecualizador, reconocimiento de canciones y UI premium
Readme
49 MiB
Languages
Dart 86%
HTML 7.1%
Kotlin 5.1%
JavaScript 1.6%
Swift 0.1%