No description
  • Kotlin 91.9%
  • Shell 7%
  • Python 1.1%
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
Olivier Cortès 79a43e3e1b Never track the client mTLS PKCS12
The historical reconstruction carried androidClientCertPassword in
gradle.properties for tags v0.1.1 through v0.3.6. That password has
been rotated and is no longer in use. Ignore *.p12 so the client
certificate provisioned at build time can never be tracked again.

Assisted-by: Ventoline <ventdunord@asami.rocks>
2026-10-30 19:11:48 +00:00
app Reconstruct demonstrated Android release v0.7.4 2026-09-24 19:17:23 +00:00
docs Reconstruct demonstrated Android release v0.6.2 2026-09-24 19:17:22 +00:00
gradle Reconstruct demonstrated Android release v0.4.1 2026-09-24 19:17:21 +00:00
scripts Add build environment preflight 2026-09-24 20:10:16 +00:00
.gitignore Never track the client mTLS PKCS12 2026-10-30 19:11:48 +00:00
build-environment.json Rename canonical build environment manifest 2026-09-24 19:58:14 +00:00
build.gradle.kts Reconstruct demonstrated Android release v0.1.0 2026-09-24 19:17:21 +00:00
gradle.properties.example Reconstruct demonstrated Android release v0.4.0 2026-09-24 19:17:21 +00:00
gradlew Make Gradle wrapper executable 2026-09-24 20:03:21 +00:00
gradlew.bat Reconstruct demonstrated Android release v0.1.0 2026-09-24 19:17:21 +00:00
keystore.properties.example Reconstruct demonstrated Android release v0.1.0 2026-09-24 19:17:21 +00:00
LICENSE Adopt GPL-3.0-or-later license 2026-09-24 19:20:37 +00:00
radio-craig-pelton-0.4.0-verification-report.txt Reconstruct demonstrated Android release v0.6.2 2026-09-24 19:17:22 +00:00
radio-craig-pelton-0.4.1-verification-report.txt Reconstruct demonstrated Android release v0.6.2 2026-09-24 19:17:22 +00:00
radio-craig-pelton-0.5.0-verification-report.txt Reconstruct demonstrated Android release v0.6.2 2026-09-24 19:17:22 +00:00
README.md Adopt GPL-3.0-or-later license 2026-09-24 19:20:37 +00:00
RECONSTRUCTION.md Reconstruct demonstrated Android release v0.7.4 2026-09-24 19:17:23 +00:00
settings.gradle.kts Reconstruct demonstrated Android release v0.1.0 2026-09-24 19:17:21 +00:00

Radio Craig Pelton Android

Application Android native pour lire https://radio.lodlp.net/radio.opus avec authentification TLS cliente.

Identité

  • Nom visible : Radio Pelton
  • Identifiant Android : net.lodlp.radiocraigpelton
  • Version courante : 0.7.4
  • versionCode : 29
  • Android minimal : API 26
  • Pile : Kotlin, Jetpack Compose, Media3 ExoPlayer, OkHttp, Coil

Fonctionnement

Au premier lancement, l'application demande le passage en batterie sans restriction, puis l'autorisation de notification sur Android 13+. Après retour des réglages, la lecture démarre seule. Aux lancements suivants, l'ouverture de l'application démarre directement le flux.

Le bouton principal a deux états iconographiques :

  • icône lecture : démarre une connexion neuve au flux ;
  • icône stop : coupe la sortie audio, mais garde ExoPlayer connecté et conserve le tampon pour reprendre immédiatement.

La notification ajoute une action de fermeture séparée : la croix ferme réellement le service, vide le tampon, annule les reconnexions et retire la notification.

Les métadonnées riches viennent de https://radio.lodlp.net/now-playing.json et la pochette de https://radio.lodlp.net/cover. Si le morceau n'est pas taggué, l'application garde Radio Craig Pelton comme artiste et utilise le titre nettoyé par le sidecar.

Règle réseau impérative : toutes les requêtes vers https://radio.lodlp.net/ doivent utiliser le certificat client mTLS Android, quel que soit le chemin (/radio.opus, /now-playing.json, /cover, /client/latest.json, APK de mise à jour, etc.). Ne pas créer de client HTTP nu pour ce domaine ; réutiliser MtlsOkHttpClientFactory.

Le flux est lu par un MediaSessionService avec service de premier plan mediaPlayback. Android garde le choix normal de sortie audio : haut-parleur, jack, Bluetooth, etc. Aucune permission Bluetooth n'est demandée.

Build rapide

Depuis la racine du projet :

./scripts/bootstrap-jdk.sh
./scripts/bootstrap-android-sdk.sh
./scripts/bootstrap-gradle-wrapper.sh
./scripts/build-release.sh

Le script de build produit un journal daté dans build-logs/, lance les tests unitaires, construit app:assembleRelease, vérifie la signature quand apksigner est disponible, contrôle la présence du P12 et du GIF embarqué dans l'APK, puis génère les sommes SHA-256.

Signature

Le build release lit keystore.properties à la racine du projet. Ce fichier est volontairement ignoré par Git.

Format attendu :

storeFile=deliverables/radio-craig-pelton-release.jks
storePassword=...
keyAlias=radio-craig-pelton-release
keyPassword=...

La clé de signature doit être conservée avec ses mots de passe. Sa perte empêche de publier une mise à jour installable par-dessus l'APK déjà posé sur un téléphone.

Tests réseau

Avant d'accuser l'application, vérifier que le serveur accepte bien le certificat client Android :

./scripts/check-mtls.sh app/src/main/res/raw/radio_pelton_android_v1.p12

Le script utilise curl --fail-with-body : un refus HTTP 400/401/403 conserve donc le corps de réponse dans le terminal mais renvoie bien une erreur.

Dans l'environnement Work du 2026-08-06, le serveur répondait HTTP 400 No required SSL certificate was sent même avec le P12 fourni à curl. Cela indique que le VHost radio.lodlp.net doit probablement ajouter l'autorité cliente Android dans son fichier d'autorités mTLS, sans retirer l'autorité historique.

Note 0.1.1 : le P12 est reconditionné avec un mot de passe non vide défini par androidClientCertPassword dans gradle.properties. Android peut refuser certains PKCS12 protégés par un mot de passe vide avec IllegalArgumentException: password empty.

Note 0.2.0 : les callbacks réseau ne relancent plus le flux si ExoPlayer est déjà en lecture ou en tampon actif. Cela évite une remise en mémoire tampon artificielle au retour de veille quand Android réannonce simplement le réseau courant.

Note 0.2.1 : le bloc de mise en mémoire tampon et les tags ICY utilisent des fondus de 1 seconde. Pendant la connexion ou le tampon, les tags passent à opacity=0.25 et une teinte inactive, puis reviennent progressivement à l'état actif. La notification ne pose plus de grande pochette à droite ; elle utilise une notification média compacte centrée sur l'action lecture/stop.

Note 0.2.2 : une commande de démarrage reçue pendant une lecture, une connexion ou un tampon déjà actif ne recrée plus le flux. Cela évite de vider le tampon ExoPlayer quand Android recrée l'activité ou renvoie une intention de démarrage alors que la radio tourne déjà.

Note 0.3.0 : le flux passe à /radio.opus, le tampon cible passe à 5 secondes, la pause devient un mute avec connexion maintenue, et la fermeture réelle est séparée. La notification affiche Radio Craig Pelton en titre, Artiste – Titre (Année) en sous-ligne, la pochette en artwork quand elle est disponible, un bouton de fermeture, et une progression représentant le remplissage estimé du tampon. En erreur réseau ou HTTP, l'application affiche l'état maintenance et réessaie indéfiniment toutes les 10 secondes après une courte montée progressive.

Note 0.4.0 : les jaquettes manquantes utilisent un visuel nocover_* embarqué choisi aléatoirement, l'ouverture à froid affiche un écran plein écran fullscreen_opening_*, et le titre Radio Craig Pelton apparaît en couleurs LGBT au-dessus de la jaquette.

Note 0.4.1 : le splash système Android utilise un thème de démarrage dédié, un fond uni aligné sur les PNG d'ouverture et une icône transparente 1 dp. Le splash PNG plein écran reste affiché par l'interface Compose.

Note 0.5.0 : le service observe les pages Ogg reçues par ExoPlayer et utilise les pages BOS contenant OpusHead comme début de morceau. La durée vient de now-playing.json; l'état de progression partagé alimente l'interface principale et la notification persistante.

Note 0.5.1 : correction bugfix de la notification persistante et des transitions Ogg. La ligne de morceau utilise Artiste: Titre (année), la progression est rafraîchie depuis l'état partagé, et un STATE_BUFFERING bref sur changement de logical stream n'affiche plus le GIF de buffering.

Note 0.5.2 : la progression de morceau vient exclusivement de now-playing.json: track_started_at en timestamp Unix et duration_seconds/duration/duration_ms. Les frontières Ogg/OpusTags ne pilotent plus l'état de progression afin de préserver la continuité du flux Android.

Note 0.6.0 : ajout de la mise à jour hors Play Store via https://radio.lodlp.net/client/latest.json. L'APK est téléchargé en HTTPS, vérifié par SHA-256, puis ouvert via l'installateur Android et FileProvider. Le build release génère aussi radio-pelton-<versionName>-<versionCode>.apk et latest.json dans le dossier de sortie.

Note 0.6.1 : build de diagnostic notification. Le rendu visible n'utilise plus MediaStyle pour le texte/progression, la notification foreground est republiée explicitement via NotificationManager.notify, les métadonnées MediaSession sont synchronisées, et un préfixe visible [TEST NOTIF] est ajouté au titre de notification pour test terrain.

Note 0.6.2 : validation du chemin notification après test terrain. Le préfixe [TEST NOTIF] est retiré; le rendu visible reste piloté par NotificationCompat republié via NotificationManager.notify, avec texte, progression, bouton de fermeture et pochette visibles.

Note 0.6.2 complément : le client de mise à jour utilise aussi le certificat client mTLS Android. Sans cela, https://radio.lodlp.net/client/latest.json répond 400 et aucune nouvelle version n'est proposée. Une version déjà installée qui ne sait pas utiliser mTLS pour /client doit être mise à jour une fois par sideload, ou bien le serveur doit rendre temporairement /client/latest.json et l'APK accessibles sans mTLS pour amorcer la correction.

Note 0.6.3 : correction du recalcul de progression après pause longue. A la reprise, le service recalcule immédiatement la position depuis track_started_at et l'heure epoch courante avant de republier l'état UI/notification.

Note 0.6.4 : intégration de nouveaux visuels nocover_* jusqu'à nocover_053 et affichage des métadonnées principales sur une seule ligne avec défilement horizontal au lieu d'un retour à la ligne.

Note 0.6.5 : durcissement du rendu de l'écran d'ouverture. Les PNG fullscreen_opening_* sont chargés via Coil plutôt que painterResource, avec décodage non hardware, et le choix du visuel est loggué pour faciliter l'analyse d'un crash au lancement.

Note 0.6.6 : correctif crash lancement Samsung/HWUI. Les traces ADB montrent un crash natif RenderThread dans RenderNode::prepareTreeImpl, typique d'une récursion/stack overflow côté rendu. Le défilement Compose basicMarquee() des lignes titre/album introduit en 0.6.4 est désactivé; les textes restent sur une seule ligne avec ellipse, sans retour à la ligne. Dans l'interface principale, l'année est fusionnée avec l'album sous la forme Album (année) pour supprimer une ligne d'affichage.

Note 0.7.0 : pendant une mise à jour, le bouton Installer nouvelle version est remplacé par une barre de progression alimentée par le téléchargement APK streamé et rafraîchie au plus toutes les 250 ms. Le fond jaquette de l'écran principal reçoit aussi un calque noir à 50 % pour améliorer la lisibilité des textes.

Note 0.7.1 : éclaircissement de 25 % vers le blanc des couleurs bleue et violette du titre Radio Craig Pelton dans l'interface principale.

Note 0.7.2 : éclaircissement de 25 % vers le blanc de la couleur verte du titre Radio Craig Pelton dans l'interface principale.

Note 0.7.3 : intégration des assets nocover_* mis à jour et ajout de nocover_054 dans la rotation aléatoire des jaquettes de remplacement.

Note 0.7.4 : conversion en JPEG qualité 90 des assets plein écran fullscreen_opening_* et nocover_*. Les 56 PNG source pesaient 78 759 166 octets; les JPEG générés pèsent 9 823 986 octets. Les PNG originaux sont préservés hors du répertoire res/, dans app/src/main/original-png-assets/drawable-nodpi, afin de ne pas être packagés dans l'APK.

Build release signé :

./gradlew assembleRelease generateLatestJson

Le fichier keystore.properties local doit pointer vers le keystore release historique :

storeFile=/chemin/vers/radio-craig-pelton-release.jks
storePassword=...
keyAlias=...
keyPassword=...

La propriété Gradle androidClientCertPassword reste requise pour charger le certificat client Android embarqué. Ne pas commiter keystore.properties, gradle.properties réel, .jks, .keystore ou .p12 privé.

Ressources distantes embarquées

Pendant le build, la tâche Gradle preparePeltonAssets utilise d'abord app/src/main/assets/dean_pelton_loading.gif, puis les caches générés, et ne contacte Tenor qu'en dernier recours. Elle copie le GIF de chargement en res/raw, extrait une image fixe, génère les icônes Android et l'image de maintenance. L'APK final ne dépend donc pas de Tenor à l'exécution.

Maintenance

Voir docs/maintenance.md pour :

  • remplacer ou révoquer le certificat client ;
  • construire une mise à jour avec la même clé Android ;
  • modifier l'URL du flux ;
  • ajuster les durées de tampon ;
  • ajuster la stratégie de reconnexion.

License

Radio Craig Pelton for Android is free software licensed under the GNU General Public License, version 3 or (at your option) any later version (GPL-3.0-or-later). See LICENSE.