diff --git a/CHANGELOG.md b/CHANGELOG.md
index c431a1099..224008f7e 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,24 +1,83 @@
+
# Changelog
+## [1.0.8-rc33] - unstable
+
+### Added
+
+### Changed
+
+### Fixed
+
+* [PostIt.Android] Avoid applying camera sensor rotation twice: account for the rotation already included in Camera2's SurfaceTexture matrix before rotating encoder input, while retaining portrait output dimensions.
+* [PostIt.Android] Fix zero-second camera captures after adding OpenGL rotation: align Camera2 timestamps with the recorder's monotonic clock, preserve frame intervals, ignore duplicate frames and start camera requests only after the encoder is ready. Add a real Camera2/microphone regression check for segment duration and H.264/AAC tracks.
+* [PostIt.Android] Keep the `VideoLAN.LibVLC.Android` JNI/native bindings alive in Release builds with a ProGuard rule set so the bundled `libvlc`/`libc++_shared` libraries are not stripped.
+* [PostIt.Android] Rotate camera frames before H.264 encoding using an OpenGL surface, based on sensor orientation, lens facing and display rotation at capture start. Portrait captures swap the encoded dimensions; live MPEG-TS/HLS and replays no longer depend on an ineffective MP4-only orientation hint. Restore compilation by reading display orientation from the activity rather than the capture service.
+
+## [1.0.8-rc32] - unstable
+
+### Added
+
+### Changed
+
+### Fixed
+
+* Performer notification audience
+* [PostIt.Android] Upgrade `VideoLAN.LibVLC.Android` to 3.7.0-beta to resolve the runtime `failed to load libc++_shared.so` problem on 16 KiB-page Android devices and document the native-library compatibility requirement.
+
+## [1.0.8-rc31] - unstable
+
+### Added
+
+* [Yavsc.Org] Permalien public de podcasts performer avec jeton a durée
+ limitée, page Razor de presentation et bandeau de prospection vers
+ l'inscription Yavsc et le téléchargement de PostIt.
+
+### Changed
+
+### Fixed
+
+* Podcast availability from Android
+* avoid NRE in MailSender on rdv query notifications
+* Android emulator smoke test skipped in CI because Calabash instrumentation signature mismatches the target APK on hosted runners
+
+## [1.0.8-rc30] - unstable
+
+### Added
+
+### Changed
+
+### Fixed
+
+* Podcast availability from Android
+
## [1.0.8-rc29] - unstable
### Added
* [PostIt.Android] Lecture des podcasts directs et replays dans la page
- video partagee avec le desktop : surface LibVLC native Android et moteur
- embarque, transport authentifie commun et controles uniques. Liberation
- du lecteur en arriere-plan ; reouverture du flux au retour dans l'application.
+ video partagée avec le desktop : surface LibVLC native Android et moteur
+ embarque, transport authentifie commun et contrôles uniques. Liberation
+ du lecteur en arrière-plan ; réouverture du flux au retour dans l'application.
* [PostIt.Android] Emission depuis la camera et le microphone Android
(Android 8 minimum), encodage H.264/AAC natif et segmentation MPEG-TS
- sur images cles. Interface, envoi authentifie, retries et finalisation
+ sur images clés. Interface, envoi authentifie, retries et finalisation
du replay partages avec le desktop. Service de premier plan avec
- notification d'arret et maintien de la capture en arriere-plan.
+ notification d'arrêt et maintien de la capture en arrière-plan.
### Changed
* [PostIt] Mise a jour d'Avalonia vers 12.1.4.
+### Fixed
+
+* [Yavsc.Api, Yavsc.Server] Une demande de rendez-vous ne provoque plus
+ d'erreur 500 (NullReferenceException dans MailSender) : l'API lie
+ désormais la section Smtp, le modèle appsettings-api fournit Site.Owner
+ et Smtp, et un envoi sans configuration mail complete est journalise
+ puis ignore.
+
## [1.0.8-rc28] - unstable
@@ -27,38 +86,38 @@
* [Yavsc.Api, Yavsc.Server, Yavsc.Api.Client] Creation explicite et
idempotente d'une facture depuis un devis doublement signe, avec lignes,
total et parties figes. Historique des encaissements hors ligne attestes
- par le prestataire et des transactions PayPal verifiees cote serveur ;
- references uniques, cumul atomique et protection contre les ecritures
- concurrentes. Routes documentees, client HTTP et migration associes.
+ par le prestataire et des transactions PayPal vérifiées cote serveur ;
+ references uniques, cumul atomique et protection contre les écritures
+ concurrentes. Routes documentées, client HTTP et migration associes.
-* [Yavsc.Abstract, Yavsc.Server] Modele de solde des commandes facturables :
- montants dus et payes persistants, reste a payer et etats non paye,
+* [Yavsc.Abstract, Yavsc.Server] Modèle de solde des commandes facturables :
+ montants dus et payes persistants, reste a payer et états non paye,
partiellement paye ou acquitte. L'acquittement depend des montants,
et non du seul statut PayPal ; migration sans montant historique invente.
* [PostIt.Desktop, Yavsc.Api] Podcasts en direct avec replay HLS conserve :
capture camera et microphone par LibVLC (H.264/AAC, segments MPEG-TS),
- envoi ordonne et idempotent, catalogue et lecture authentifiee dans PostIt.
+ envoi ordonne et idempotent, catalogue et lecture authentifiée dans PostIt.
L'auteur peut terminer ou supprimer ses emissions ; stockage prive sur
- disque, quotas dedies et conservation des segments recus apres interruption.
+ disque, quotas dédiés et conservation des segments reçus apres interruption.
### Changed
### Fixed
-* [Facturation] Les commandes facturees et leur devis ne peuvent plus etre
+* [Facturation] Les commandes facturées et leur devis ne peuvent plus être
supprimes via les routes de commande usuelles. Les additions distinguent
montant verse, solde restant et acquittement ; les anciennes routes de
- generation exigent une facture etablie depuis un devis signe.
+ generation exigent une facture établie depuis un devis signe.
## [1.0.8-rc27] - unstable
### Added
* [PostIt.Desktop] Installeur NSIS Windows x64 par utilisateur, sans
- elevation : publication autonome, raccourci du menu Demarrer,
- protocole `postit://` pour la connexion et desinstallation via Windows.
- Instructions de generation ajoutees dans `INSTALL.md`.
+ elevation : publication autonome, raccourci du menu Démarrer,
+ protocole `postit://` pour la connexion et désinstallation via Windows.
+ Instructions de generation ajoutées dans `INSTALL.md`.
* [PostIt, Yavsc.Api] Portage des outils des contrôleurs web
d’administration : tableau de bord, rôles et affectations, clients OIDC
diff --git a/INSTALL.md b/INSTALL.md
index 99c4f04b5..5cea2b6f0 100644
--- a/INSTALL.md
+++ b/INSTALL.md
@@ -166,6 +166,15 @@ at IDR frames, with timestamps used for segment durations. Closed segments
enter the same ordered upload/retry/quota/replay pipeline as desktop.
Devices without a supported camera/encoder report a startup error.
+Camera frames are rotated through an OpenGL surface before encoding, so both
+live playback and replays have upright pixels without relying on rotation
+metadata (not supported by the MPEG-TS recorder). The sensor, front/rear lens
+and display orientation are sampled when capture starts; portrait output swaps
+width and height. Camera2 may already include sensor rotation in the
+SurfaceTexture transform; only the remaining rotation is applied by OpenGL.
+Keep that device orientation during a broadcast, or stop and
+start a new broadcast after turning the device.
+
A camera/microphone foreground service and a partial wake lock maintain the
broadcast when PostIt is backgrounded or the screen locks. Its ongoing
notification returns to the app and offers **Arreter et publier**. Stopping
@@ -176,12 +185,13 @@ camera; segments already received by the server remain available under the
interrupted-replay rules above. Device power policies can impose additional
restrictions and must be tested on the intended physical devices.
-**Native library limitation:** bundled `VideoLAN.LibVLC.Android` 3.6.5 emits
-`XA0141`: its native libraries are not aligned for 16 KiB memory pages.
-Playback on 16 KiB-page devices and store distribution requiring this alignment
-are not validated/supported by this package choice. A compatible native LibVLC
-release is required before claiming that support; a successful APK build or
-a test on a 4 KiB-page emulator does not establish it.
+**Native library compatibility:** the Android app now uses
+`VideoLAN.LibVLC.Android` 3.7.0-beta, which is the minimum compatible package
+for 16 KiB-page devices. Older 3.6.5 binaries can fail at runtime with
+`failed to load libc++_shared.so` or the linker error `XA0141` because the
+native libraries are not aligned for 16 KiB memory pages. A successful APK
+build or a test on a 4 KiB-page emulator does not establish compatibility
+for 16 KiB-page devices or Play distribution requiring that alignment.
Build with the Android workload and SDK configured:
diff --git a/README.md b/README.md
index 79e711258..6d4784bad 100644
--- a/README.md
+++ b/README.md
@@ -138,11 +138,9 @@ Dans le cas des arrhes, à tout moment, jusqu'avant la date et l'heure de la pre
## Limitations
-* **Aujourd'hui** : une prestation par commande, sur un axe client → prestataire unique, sans sous-traitance. **Cible roadmap** : montages multi-parties (plusieurs clients et/ou plusieurs fournisseurs collaborant autour d'un même projet, avec sous-traitance validée par le client) — voir la [ROADMAP](../ROADMAP.md).
* **Annulation : partielle et non testée.** Côté client, `HairCutCommandController.ClientCancel` / `ClientCancelConfirm` supprime la commande **sans déclencher de refund PayPal** ni logique arrhes vs avance ; aucune action d'annulation côté prestataire. La description du README (arrhes +20%, avance non-annulable) **n'est pas implémentée**. Cible : câbler `RefundTransaction` et le workflow complet, voir [ROADMAP](../ROADMAP.md).
* **Réclamation post-prestation : aucun support applicatif.** Passée la date de la prestation, le client n'a aucun chemin dans l'UI pour signaler un litige — aucune route, aucun contrôleur, aucun modèle. La fonctionnalité de conciliation est planifiée au **Jalon 5** (cf. [doc/ddd-exploration-2026-06-14.md](../doc/ddd-exploration-2026-06-14.md)) : double vue client/prestataire, dossier de conciliation, décision. En attendant, tout passe par e-mail au propriétaire de l'installation.
* **Paiement : PayPal uniquement, via l'API NVP/SOAP.** Aucun autre PSP n'est intégré (pas de Stripe, pas d'Adyen). L'API NVP/SOAP que PayPal a dépréciée au profit de REST reste fonctionnelle mais n'est plus recommandée par PayPal ; aucune migration n'est planifiée à court terme.
-* **Périmètre métier étroit.** Pas de saisie de structures de projets complexes ; pas de ticketing associé à la prestation.
* **Collecte centralisée.** Tous les paiements PayPal transitent par le compte du propriétaire de l'installation — c'est le seul identifiant configuré côté PSP. Les professionnels apparaissent comme tierces parties du point de vue PayPal.
* **Pas de gestion de paie.** Aucune édition de fiche de paye, ni paiement en masse. Seuls les paiements unitaires sus-cités sont supportés.
diff --git a/contrib/template.appsettings-api.json b/contrib/template.appsettings-api.json
index b364eeef1..afe693a4f 100644
--- a/contrib/template.appsettings-api.json
+++ b/contrib/template.appsettings-api.json
@@ -6,7 +6,17 @@
"https://preprod-blogs.$DOMAIN"
],
"Bills": "bills",
- "TempDir": "temp"
+ "TempDir": "temp",
+ "Owner": {
+ "Name": "Paul Schneider",
+ "EMail": "paul@pschneider.fr"
+ }
+ },
+ "Smtp": {
+ "Host": "mercure.pschneider.fr",
+ "Port": 465,
+ "UserName": "paul",
+ "Password": "$SMTP_PASSWORD"
},
"Logging": {
"LogLevel": {
diff --git a/doc/README.md b/doc/README.md
index fb9bea943..10e32b0c9 100644
--- a/doc/README.md
+++ b/doc/README.md
@@ -16,6 +16,7 @@ La racine de l'architecture est [Architecture.md](Architecture.md).
| [architecture/offres-frontmatter.md](architecture/offres-frontmatter.md) | Offre fournisseur, ClasseFormulaire, ClasseDevis, parsing frontmatter |
| [architecture/postit-oidc.md](architecture/postit-oidc.md) | Client desktop PostIt, custom URI scheme, silent refresh |
| [architecture/postit.md](architecture/postit.md) | PostIt — topologie des projets, ViewLocator custo, navigation, DI, conventions de binding |
+| [architecture/notifications.md](architecture/notifications.md) | Notifications persistées, messages Web et événements temps réel côté serveur, Web et PostIt |
| [architecture/decoupage-organisation.md](architecture/decoupage-organisation.md) | Découpage des projets .NET (Abstract, Server, Org, Api, Blogs, Web, Org.Tests) |
| [testing.md](testing.md) | Stratégie de test : conventions des dossiers, EF Core in-memory, auth stubs, scaffold partagé |
| [onboarding-agents.md](onboarding-agents.md) | Parcours pas-à-pas pour prise en main agents IA + architecture + tests |
diff --git a/doc/architecture/notifications.md b/doc/architecture/notifications.md
new file mode 100644
index 000000000..8cf85cfb8
--- /dev/null
+++ b/doc/architecture/notifications.md
@@ -0,0 +1,229 @@
+# Notifications Yavsc : serveur, Web et PostIt
+
+Ce document décrit les mécanismes de notification actuellement présents
+dans le dépôt. Le terme « notification » recouvre plusieurs flux distincts :
+
+1. les **annonces persistées** en base, affichées dans le site Web et sur
+ l'accueil de PostIt ;
+2. les **messages temporaires de l'interface Web**, attachés à une réponse
+ MVC par `ViewBag.Notify` ;
+3. les **événements temps réel** du chat et les événements métier diffusés par
+ SignalR.
+
+Ces flux n'ont ni le même stockage, ni le même cycle de vie, ni les mêmes
+clients.
+
+## Vue d'ensemble
+
+| Flux | Serveur / transport | Web | PostIt |
+|---|---|---|---|
+| Annonce persistée | Entité `Notification` en base, API HTTP | Affichée dans le layout et masquable | Liste sur `HomePage`, chargée par HTTP et masquable |
+| Message d'interface | Liste de portée requête dans `ViewBag.Notify` | Affichée comme alerte dans le layout | Non consommé |
+| Événement de chat | Hub SignalR authentifié à `/chathub` | Le site Web n'en consomme pas dans ses vues MVC | `notifyUser` et `notifyRoom` alimentent les journaux du chat |
+| Événement métier `push` | SignalR vers les connexions actives, plus e-mail | Pas de consommateur identifié dans les vues Web | Pas de gestionnaire `push` dans le client PostIt actuel |
+
+Les notifications persistées sont donc différentes d'une notification système
+native (notification du navigateur ou du système d'exploitation). Le client
+PostIt affiche une liste dans sa page d'accueil ; il ne demande pas de
+permission de notification native.
+
+## 1. Annonces persistées
+
+### Modèle et stockage
+
+L'entité partagée [`Notification`](../../src/Yavsc.Abstract/Messaging/Notification.cs)
+est enregistrée dans la table `Notification` via `ApplicationDbContext`.
+Elle contient :
+
+- `Id`, identifiant généré ;
+- `title` et `body`, obligatoires, limités respectivement à 1 024 et 512
+ caractères ;
+- `icon`, `sound`, `tag`, `color`, propriétés optionnelles ;
+- `click_action`, obligatoire, utilisé comme libellé d'action dans les
+ interfaces ;
+- `Target`, chaîne de ciblage. Les valeurs documentées par le modèle sont
+ `null` (tout le monde), `user/{UserId}`, `pub/{topic}` et
+ `administration`.
+
+Le masquage d'une annonce pour un utilisateur connecté est stocké dans
+`DismissClicked`, avec une clé composée de l'identifiant utilisateur et de
+l'identifiant de notification. Le même avis peut ainsi être masqué
+indépendamment par chaque utilisateur.
+
+### API
+
+[`NotificationsApiController`](../../src/Yavsc.Api/Controllers/Administration/NotificationsApiController.cs)
+expose les deux routes GET suivantes, sous le préfixe `/api/v1` :
+
+| Route | Accès | Résultat |
+|---|---|---|
+| `GET /api/v1/notifications` | Anonyme ou authentifié | Notifications visibles à l'appelant, non masquées |
+| `GET /api/v1/notifications/me` | Anonyme ou authentifié | Même action et mêmes règles que la route sans suffixe |
+
+La réponse est une liste JSON triée par `Id` décroissant. L'API exclut les
+notifications masquées par l'utilisateur authentifié, puis applique les
+règles d'audience : cible vide, cible `pub/…`, cible correspondant à
+`user/{identifiant courant}`, ou cible `administration` pour un administrateur.
+Une requête anonyme n'a ni identifiant utilisateur ni rôle administrateur.
+
+[`DismissClicksApiController`](../../src/Yavsc.Api/Controllers/Administration/DismissClicksApiController.cs)
+est réservé aux utilisateurs authentifiés :
+
+| Route | Effet |
+|---|---|
+| `GET /api/v1/dismiss` | Liste les identifiants masqués par l'utilisateur |
+| `POST /api/v1/dismiss/click/{notificationId}` | Masque l'avis pour l'utilisateur ; l'opération est idempotente |
+| `DELETE /api/v1/dismiss/click/{notificationId}` | Rétablit l'avis s'il était masqué ; renvoie `404` sinon |
+
+Le POST renvoie `404` si l'identifiant de notification n'existe pas. Le modèle
+de réponse est [`DismissedNotification`](../../src/Yavsc.Abstract/Messaging/DismissedNotification.cs).
+
+### Création et notifications métier
+
+Le contrôleur MVC
+[`NotificationsController`](../../src/Yavsc.Org/Controllers/Communicating/NotificationsController.cs)
+permet la gestion CRUD des enregistrements de notification.
+
+Le service [`YavscMessageSender`](../../src/Yavsc.Server/Services/YavscMessageSender.cs)
+crée aussi une notification persistée, ciblée sur `user/{id}`, pour les
+événements `RdvQueryEvent` et `ClientEstimateSignedEvent`. L'enregistrement
+est effectué avant l'envoi d'e-mail. Le même service tente ensuite d'envoyer
+un e-mail si l'adresse est disponible et confirmée, puis diffuse l'événement
+`push` aux connexions SignalR actives de l'utilisateur.
+
+Cette diffusion `push` est conditionnée par la présence de connexions actives ;
+l'avis stocké en base reste, lui, disponible lors d'une récupération ultérieure
+par l'API.
+
+## 2. Site Web
+
+### Annonces persistées sur l'accueil
+
+[`HomeController.Index`](../../src/Yavsc.Org/Controllers/HomeController.cs)
+charge les notifications non masquées et les place dans `ViewBag.Notify`.
+[`_Layout.cshtml`](../../src/Yavsc.Org/Views/Shared/_Layout.cshtml) les rend
+comme des alertes Bootstrap avec icône, titre, corps et action. Le titre et le
+corps passent par les composants Markdown du site.
+
+Le clic sur l'action appelle `POST /Home/DismissNotification/{id}` via
+[`site.js`](../../src/Yavsc.Org/wwwroot/js/site.js) :
+
+- pour un utilisateur connecté, le masquage est enregistré dans
+ `DismissClicked` ;
+- pour un visiteur anonyme, les identifiants sont conservés dans la session
+ Web (`clicked`).
+
+La route renvoie `404` si l'avis n'existe pas et `204` après le masquage.
+Comme l'API, l'accueil Web filtre `Target` : les cibles `user/{id}` ne sont
+visibles qu'à l'utilisateur correspondant, et la cible `administration`
+uniquement aux administrateurs. Un visiteur anonyme ne reçoit que les
+notifications publiques (`Target` vide ou préfixe `pub/`).
+
+### Messages temporaires par requête
+
+Les helpers [`NotifyInfo`, `NotifyWarning` et `Notify`](../../src/Yavsc.Org/Helpers/ControllerHelpers.cs)
+ajoutent des objets à `ViewBag.Notify`. Ce mécanisme n'écrit pas en base :
+il ne vit que dans la réponse MVC qui rend le layout. Les méthodes
+`NotifyInfo` et `NotifyWarning` partagent actuellement le même rendu générique
+`alert-info`.
+
+## 3. PostIt
+
+[`NotificationsApiClient`](../../src/Yavsc.Api.Client/NotificationsApiClient.cs)
+construit les routes à partir de la base d'API de l'hôte métier configuré.
+Il fournit les méthodes de lecture publique et authentifiée, ainsi que les
+opérations de lecture, masquage et restauration.
+
+Sur [`HomePage`](../../src/PostIt/PostIt/Views/Layout/HomePage.axaml),
+[`HomePageViewModel`](../../src/PostIt/PostIt/ViewModels/Layout/HomePageViewModel.cs)
+charge la liste :
+
+- sans session valide, il appelle la route publique
+ `GET /api/v1/notifications` ;
+- avec une session valide, il appelle
+ `GET /api/v1/notifications/me` avec le client HTTP authentifié.
+
+Le chargement est lancé à l'ouverture de la page et relancé lorsque l'état de
+connexion change. Un compteur de version ignore les réponses obsolètes si un
+chargement plus récent a déjà commencé. L'interface distingue l'état de
+chargement, la liste vide et l'erreur de chargement ; l'utilisateur peut aussi
+actualiser manuellement.
+
+Le bouton d'une notification utilise `click_action` comme libellé et masque
+l'élément. En session authentifiée, PostIt appelle
+`POST /api/v1/dismiss/click/{id}` avant de retirer l'élément de la liste. Si
+l'appel échoue, l'élément reste affiché et une erreur est présentée. Sans
+session, l'élément est retiré de la liste locale, sans persistance du
+masquage côté serveur.
+
+## 4. Événements temps réel
+
+### Hub du chat
+
+Le hub [`ChatHub`](../../src/Yavsc.Server/Hubs/ChatHub.cs) est publié par
+Yavsc.Api à la racine sous `/chathub` (et non sous `/api/v1`) et porte
+`[Authorize]`. Il s'appuie sur SignalR et sur `IConnexionManager` pour suivre
+les utilisateurs et les salons.
+
+Les événements de notification émis par le hub comprennent notamment :
+
+| Événement SignalR | Données | Utilisation PostIt |
+|---|---|---|
+| `notifyUser` | `type`, identifiant ou nom cible, message | Ajout au journal système |
+| `notifyRoom` | `type`, salon, utilisateur ou détail | Ajout au journal du salon ; met à jour la liste d'utilisateurs sur `UserJoin` |
+| `joint` | `ChatRoomInfo` | Initialise la vue du salon rejoint |
+| `ReceiveMessage` | message avec nom, salon et corps | Ajoute le message à l'historique du salon |
+| `notifyUserInRoom` | `type`, salon, message | Pas de gestionnaire correspondant dans `ChatViewModel` actuellement |
+
+Les constantes de types du chat sont déclarées dans
+[`NotificationTypes`](../../src/Yavsc.Abstract/Messaging/NotificationTypes.cs).
+Les messages `notifyUser` et `notifyRoom` ne sont pas enregistrés en base :
+ils décrivent l'état temps réel de connexion, de salon ou les erreurs de
+conversation.
+
+Le [`ChatViewModel`](../../src/PostIt/PostIt/ViewModels/Chat/ChatViewModel.cs)
+ouvre le hub avec le jeton d'accès PostIt, tente un renouvellement silencieux
+du jeton, et active la reconnexion automatique. Après reconnexion, il rejoint
+à nouveau le salon sélectionné, car la connexion SignalR précédente et ses
+groupes ne sont plus actifs. Les callbacks mettent à jour les collections sur
+le thread UI d'Avalonia.
+
+### Événement métier `push`
+
+En plus des événements du chat, `YavscMessageSender` utilise le même contexte
+SignalR pour émettre `push` avec le topic et une charge utile sérialisée. Ce
+nom d'événement est distinct de `notifyUser` et `notifyRoom`. Aucun
+gestionnaire `push` n'est présent dans le client PostIt actuel : les annonces
+métier persistées sont visibles dans PostIt lors du prochain chargement ou
+rafraîchissement HTTP.
+
+## 5. Points d'attention pour les évolutions
+
+- Conserver les règles d'audience alignées entre l'API et le site Web ; en
+ particulier, appliquer un filtrage `Target` côté Web avant d'exposer les
+ avis dans le layout.
+- Ne pas confondre masquage utilisateur (`DismissClicked`) et suppression
+ globale de la notification : le masquage API est par utilisateur.
+- Les sessions anonymes du site Web et PostIt n'ont pas le même comportement :
+ le site Web conserve le masquage en session, tandis que PostIt ne le garde
+ que dans la liste affichée jusqu'au prochain rechargement.
+- Ajouter un nouveau type de cible ou un nouvel événement temps réel implique
+ d'examiner séparément son filtrage, son transport et les clients qui
+ l'écoutent.
+- Les références à GCM et le nom historique de `YavscMessageSender` ne
+ signifient pas que PostIt reçoit une notification native hors ligne : le
+ chemin observé ici est l'e-mail, l'événement SignalR temps réel et la
+ notification persistée récupérée par HTTP.
+
+## Références d'implémentation
+
+- [`Notification`](../../src/Yavsc.Abstract/Messaging/Notification.cs)
+- [`NotificationsApiController`](../../src/Yavsc.Api/Controllers/Administration/NotificationsApiController.cs)
+- [`DismissClicksApiController`](../../src/Yavsc.Api/Controllers/Administration/DismissClicksApiController.cs)
+- [`NotificationsController`](../../src/Yavsc.Org/Controllers/Communicating/NotificationsController.cs)
+- [`HomeController`](../../src/Yavsc.Org/Controllers/HomeController.cs)
+- [`YavscMessageSender`](../../src/Yavsc.Server/Services/YavscMessageSender.cs)
+- [`ChatHub`](../../src/Yavsc.Server/Hubs/ChatHub.cs)
+- [`NotificationsApiClient`](../../src/Yavsc.Api.Client/NotificationsApiClient.cs)
+- [`HomePageViewModel`](../../src/PostIt/PostIt/ViewModels/Layout/HomePageViewModel.cs)
+- [`ChatViewModel`](../../src/PostIt/PostIt/ViewModels/Chat/ChatViewModel.cs)
diff --git a/doc/onboarding-agents.md b/doc/onboarding-agents.md
index 14a727a17..18852fec1 100644
--- a/doc/onboarding-agents.md
+++ b/doc/onboarding-agents.md
@@ -65,6 +65,20 @@ Rituels recommandes:
- Mensuel: mise a jour du present guide et du playbook.
- A chaque incident: ajouter un anti-pattern dans le playbook.
+### Reseau de versionnement / git flow pour le CHANGELOG
+
+Regle obligatoire:
+- La section courante du CHANGELOG représente la prochaine version a publier, pas un tag deja créé.
+- Un tag deja publie bloque définitivement la section correspondante du CHANGELOG.
+- Au commit suivant, si la modification impacte le CHANGELOG, il faut incrémenter la version exposée dans le changelog (version du prochain tag attendu), par exemple: `1.0.8-rc30 -> 1.0.8-rc31`.
+
+Ordre a respecter a chaque commit qui modifie le produit:
+1. Verifier si le dernier tag du CHANGELOG existe deja; si oui, incrémenter la version de la section suivante au lieu de re-écrire la dernière section bloquée.
+2. Ouvrir une nouvelle section du CHANGELOG avec ce numero de version incrémenté.
+3. Apres seulement, alimenter cette section avec la description du changement.
+
+En pratique: la version du CHANGELOG est la version "future" a tagger, et elle est incrémentée avant l'écriture de la description; le tag n'est pas créé avant la section, il est seulement établi ensuite lors de la publication effective.
+
## Check-list de validation
- Le changement indique son impact architecture.
diff --git a/doc/testing.md b/doc/testing.md
index 8b5e8eff1..7b5de174f 100644
--- a/doc/testing.md
+++ b/doc/testing.md
@@ -97,6 +97,59 @@ decodage authentifie du replay. Aucun FFmpeg n'est requis par ces tests ni
par l'application (le fichier d'entree peut provenir de n'importe quelle source).
Les tests materiels camera/microphone Windows/Linux restent manuels.
+### Rotation de la capture Android
+
+Le calcul capteur/ecran/camera avant et les quatre transformations de texture
+sont couverts sans appareil :
+
+```bash
+dotnet test --project src/PostIt/PostIt.Tests/PostIt.Tests.csproj \
+ --filter-class '*CameraFrameOrientationTests' --filter-class '*CameraFrameClockTests'
+```
+
+Un test Android supplementaire verifie le chemin OpenGL et l'encodeur reels,
+sans ouvrir la camera, sans microphone et sans contacter l'API :
+
+```bash
+dotnet build src/PostIt/PostIt.Android/PostIt.Android.csproj -c Debug \
+ -p:EmbedAssembliesIntoApk=true
+adb install -r -t src/PostIt/PostIt.Android/bin/Debug/net10.0-android/fr.pschneider.postit-Signed.apk
+adb shell am start -n fr.pschneider.postit/fr.pschneider.postit.CameraFrameRotationTestActivity
+adb shell run-as fr.pschneider.postit cat files/camera-rotation-test-result.txt
+```
+
+Attendre la fin du test : `RUNNING` doit devenir `PASS`. Il encode quatre
+quadrants colores en MPEG-TS pour 0, 90, 180 et 270 degres, decode une image et
+verifie les dimensions (640x480 ou 480x640) et les couleurs aux quatre positions.
+La tolerance de chaque composante RGB est de 40 pour tenir compte de H.264.
+Une erreur de rendu, d'encodage, de decodage ou de comparaison produit `FAIL`
+avec l'exception. Cette activite est exclue des builds Release.
+
+Le test synthetique ne couvre pas l'horloge Camera2 ni sa synchronisation avec
+le microphone. Avec l'accord de la personne utilisant le telephone, lancer
+aussi le test local reel (camera et microphone actifs, aucun envoi a l'API) :
+
+```bash
+adb shell am start -n fr.pschneider.postit/fr.pschneider.postit.PodcastCaptureTestActivity --es microphone true
+adb shell run-as fr.pschneider.postit cat files/podcast-capture-test-result.txt
+```
+
+Attendre `PASS` : au moins trois segments, 8 a 16 secondes capturees,
+pistes H.264 et AAC presentes, et progression des segments en arriere-plan.
+Ce test detecte notamment les captures de zero seconde dues au decalage entre
+l'horloge Camera2 et celle de MediaRecorder. Les fichiers restent dans le
+repertoire prive indique par le resultat pour verification locale.
+Le resultat inclut les angles capteur/ecran et la premiere matrice
+SurfaceTexture recue. Les tests unitaires couvrent notamment la matrice
+reelle `[0,-1,0,0,-1,0,0,0,0,0,1,0,1,1,0,1]` : pour un capteur a 90 degres
+et un ecran a 0, elle porte deja la rotation et ne doit pas recevoir 90 degres
+supplementaires. Le test synthetique seul ne detecte pas cette double rotation.
+
+Verifier aussi manuellement les cameras avant/arriere en portrait et paysage,
+le son, le direct et le replay, et la poursuite de l'emission en arriere-plan.
+L'orientation est fixee au demarrage d'une emission ; tourner le telephone
+pendant l'emission ne reconfigure pas le format encode.
+
## Quand ne PAS écrire de test
Un test qui ne détecte rien n'est pas un test. Si l'invariant
diff --git a/src/PostIt/Directory.Packages.props b/src/PostIt/Directory.Packages.props
index a6a656368..ae6708992 100644
--- a/src/PostIt/Directory.Packages.props
+++ b/src/PostIt/Directory.Packages.props
@@ -19,7 +19,7 @@
-
+
diff --git a/src/PostIt/PostIt.Android/Application.cs b/src/PostIt/PostIt.Android/Application.cs
index 040b01ca3..d8d758579 100644
--- a/src/PostIt/PostIt.Android/Application.cs
+++ b/src/PostIt/PostIt.Android/Application.cs
@@ -9,6 +9,7 @@ using Microsoft.Extensions.DependencyInjection;
using Avalonia.Controls;
using Avalonia.Styling;
using Yavsc.Api.Client;
+using PostIt.Services;
[assembly: UsesPermission(Manifest.Permission.AccessFineLocation)]
[assembly: UsesPermission(Manifest.Permission.AccessCoarseLocation)]
@@ -20,6 +21,7 @@ namespace PostIt.Android
{
protected Application(nint javaReference, JniHandleOwnership transfer) : base(javaReference, transfer)
{
+ Platform.IsPodcastSupported = true;
}
protected override AppBuilder CustomizeAppBuilder(AppBuilder builder)
diff --git a/src/PostIt/PostIt.Android/PostIt.Android.csproj b/src/PostIt/PostIt.Android/PostIt.Android.csproj
index 0923e8a12..5b7f4f968 100644
--- a/src/PostIt/PostIt.Android/PostIt.Android.csproj
+++ b/src/PostIt/PostIt.Android/PostIt.Android.csproj
@@ -31,6 +31,7 @@
+