Fixes 9 of 10 review findings (10th requires a manual Play Console
step, no code change):
1. app.dart/banner_anuncio_superior.dart: move the top SafeArea inside
BannerAnuncioSuperior so it only reserves status-bar height when an
ad actually renders, restoring edge-to-edge layout for premium and
free-unloaded users.
2. servicio_anuncios.dart: bound every interstitial await (load,
presentation, and the injected implementation itself) with
injectable timeouts so a callback that never fires can no longer
hang a caller.
3. estado_entitlement.dart/hoja_premium.dart: expose a typed
resultadoUsuario signal for purchase/restore failures and
restore-found-nothing, with dedicated localized messages
(compraError, restauracionSinCompras) across all 13 locales --
never the raw developer/exception string.
4. main.dart/servicio_consentimiento.dart: add a GDPR/UMP consent flow
(ConsentInformation/ConsentForm) that gates Mobile Ads SDK init on
canRequestAds(); premium users never see a consent form; failures
degrade to no ads instead of crashing or blocking startup.
6. servicio_anuncios.dart: track real ad presentation
(onAdShowedFullScreenContent) so a failed-to-show interstitial no
longer consumes a session cap slot.
7. banner_anuncio_superior.dart: add an explicit load-attempted guard
so repeated didChangeDependencies (e.g. entitlement notifyListeners
during a purchase) can only ever trigger one banner load attempt.
8. servicio_anuncios.dart: make esPremium a required constructor
parameter, matching the hardened contract already applied to
EstadoAlarmas/EstadoGrabacion/EstadoRadio.
9. hoja_premium.dart: add a dedicated premiumActivo localized string
instead of reusing the equalizer's equalizerActive translation,
across all 13 locales.
All fixes implemented RED-first (failing test before production
code). Full suite: 1261 passed, 2 pre-existing skips, 0 failures.
flutter analyze: 5 pre-existing issues only, 0 new.
[version set]
App id always uses the real value (SDK init only, no ad-serving risk).
Banner/interstitial pick the real unit id in release builds and Google's
test unit id everywhere else, so debug/profile builds can never serve
(or accidentally tap) a real ad.
Adds a permanent, non-consumable premium unlock (EstadoEntitlement +
PuertoCompras/ServicioComprasPlayBilling) that removes ads and unlocks
alarm vacations, alarms past a 5-alarm free cap, recording start, and
full Android Auto browsing. The phone equalizer stays free for everyone.
- Entitlement is prefs-backed (compra_premium_v1), fail-open, and
resolvable headlessly via esPremiumPersistido() for the Android Auto
audio handler, which registers before runApp.
- Android Auto reduced mode keeps the real root folder labels for free
users; browsing into any of them (and playFromMediaId/playFromSearch/
skipToNext/skipToPrevious) is blocked at the getChildren/servicio_audio
choke points, with a locked "Función Premium" item as the backstop.
Current-station play/pause/stop stays untouched. A free -> premium
transition actively invalidates the head unit's cached browse tree.
- Ads (top banner + capped interstitial before adding a station or an
alarm) are gated behind entitlement via ServicioAnuncios, using
official Google test ad unit IDs pending AdMob provisioning.
- Alarm cap UX shows an explanatory message with a secondary unlock
action rather than a bare paywall jump; existing data is grandfathered.
- 4 new localization keys translated across all 13 supported locales.
Co-located tests use strict TDD (RED test before implementation) for
every new pure-logic unit; full existing suite passes unchanged.