FOXA-2699 : le focus d'entrée attend le bouton Lecture, il n'abandonne plus après 240 ms #1010

Merged
foXaCe merged 4 commits from passe-20261018-183000-focusentree into main 2026-10-18 15:41:00 +02:00
Owner

Résumé

FOXA-2699 — quand la fiche n'a aucune lecture possible, l'écran ne descend plus tout seul en bas.

  • 2c3baf229 FOXA-2699 : le focus d'entrée attend le bouton Lecture, il n'abandonne plus après 240 ms

Pourquoi

Le focus d'entrée était demandé trois fois à 80 ms d'intervalle, puis abandonné :

repeat(if (apresBandeAnnonce) 1 else 3) {
    heroPlayFocusRequester.requestFocusAfterFrames()
    delay(80)
}

240 ms. Or une fiche sans source met plus longtemps que ça à résoudre : le bouton Lecture n'est
pas encore attaché, les trois demandes ne visent aucun nœud, et le focus retombe sur la recherche par
défaut de Compose — le premier focalisable de la liste, très bas.

C'est ce qui explique « sur une seule fiche et pas sur les autres » : la fiche dont la résolution
traîne est précisément celle qui tombe.

Le registre CiblesAttachees sait déjà si le bouton porte un nœud — suivreLAttache est posé dessus.
On l'attend au lieu de deviner une durée, borné à ~4 s, puis on demande le focus une seule
fois
, quand il existe.

⚠ Le retour de bande-annonce garde son comportement d'avant (une demande, un jeton honoré une fois) :
ce correctif ne touche que l'entrée.

Ce qui a été mesuré

3 tests, dont un qui nomme la cause en interdisant le retour du repeat(... 1 else 3) :

ce qui est mesuré comment
l'entrée attend l'attachement la source contient !ciblesAttachees.estAttachee(heroPlayFocusRequester)
le renoncement à 240 ms a disparu repeat(if (apresBandeAnnonce) 1 else 3) absent
l'attente est bornée attente < 50 — une boucle sans limite bloquerait le focus pour toujours
./gradlew :app:testFullDebugUnitTest -Pnuvio.android.distribution=full \
    --tests '*LeFocusDEntreeAttendLeBoutonTest*'
BUILD SUCCESSFUL — 3 tests, 0 échec

⚠ Ce banc lit la source, et il ne remplace pas la boîte. Aucun banc du dépôt n'instancie la fiche
détails entière (une vingtaine de paramètres, plusieurs sections, un état de bande-annonce). Ce qui
est verrouillé est la cause : le renoncement à durée fixe. La preuve « le focus reste sur le
héros » demande la Freebox et le film concerné — elle est le seul maillon que cette PR ne prouve pas.

Porte jouée par la forge : « Porte / porte* » est le seul contexte qu'exige la protection de main.

Scope boundaries

Le retour de bande-annonce (LECTURE_APRES_BANDE_ANNONCE) n'est pas modifié. Aucun autre écrivain de
focus de la fiche n'est touché : les focusRestorer des sections (épisodes, distribution, similaires,
bandes-annonces, collection, logos) restent tels quels.

## Résumé FOXA-2699 — quand la fiche n'a **aucune lecture possible**, l'écran ne descend plus tout seul en bas. - 2c3baf229 FOXA-2699 : le focus d'entrée attend le bouton Lecture, il n'abandonne plus après 240 ms ## Pourquoi Le focus d'entrée était demandé **trois fois à 80 ms d'intervalle, puis abandonné** : ```kotlin repeat(if (apresBandeAnnonce) 1 else 3) { heroPlayFocusRequester.requestFocusAfterFrames() delay(80) } ``` 240 ms. Or une fiche **sans source** met plus longtemps que ça à résoudre : le bouton Lecture n'est pas encore attaché, les trois demandes ne visent aucun nœud, et le focus retombe sur la recherche par défaut de Compose — le premier focalisable de la liste, très bas. C'est ce qui explique « **sur une seule fiche et pas sur les autres** » : la fiche dont la résolution traîne est précisément celle qui tombe. Le registre `CiblesAttachees` sait déjà si le bouton porte un nœud — `suivreLAttache` est posé dessus. On l'**attend** au lieu de deviner une durée, borné à ~4 s, puis on demande le focus **une seule fois**, quand il existe. ⚠ Le retour de bande-annonce garde son comportement d'avant (une demande, un jeton honoré une fois) : ce correctif ne touche que l'entrée. ## Ce qui a été mesuré 3 tests, dont un qui **nomme la cause** en interdisant le retour du `repeat(... 1 else 3)` : | ce qui est mesuré | comment | |---|---| | l'entrée attend l'attachement | la source contient `!ciblesAttachees.estAttachee(heroPlayFocusRequester)` | | le renoncement à 240 ms a disparu | `repeat(if (apresBandeAnnonce) 1 else 3)` absent | | l'attente est bornée | `attente < 50` — une boucle sans limite bloquerait le focus pour toujours | ``` ./gradlew :app:testFullDebugUnitTest -Pnuvio.android.distribution=full \ --tests '*LeFocusDEntreeAttendLeBoutonTest*' BUILD SUCCESSFUL — 3 tests, 0 échec ``` ⚠ **Ce banc lit la source, et il ne remplace pas la boîte.** Aucun banc du dépôt n'instancie la fiche détails entière (une vingtaine de paramètres, plusieurs sections, un état de bande-annonce). Ce qui est verrouillé est la **cause** : le renoncement à durée fixe. La preuve « le focus reste sur le héros » demande la Freebox et le film concerné — elle est le seul maillon que cette PR ne prouve pas. Porte jouée par la forge : « Porte / porte* » est le seul contexte qu'exige la protection de `main`. ## Scope boundaries Le retour de bande-annonce (`LECTURE_APRES_BANDE_ANNONCE`) n'est pas modifié. Aucun autre écrivain de focus de la fiche n'est touché : les `focusRestorer` des sections (épisodes, distribution, similaires, bandes-annonces, collection, logos) restent tels quels.
FOXA-2699 : le focus d'entrée attend le bouton Lecture, il n'abandonne plus après 240 ms
Some checks failed
PR Template Check / validate_pr_body (pull_request) Successful in 1s
Porte / porte (pull_request) Failing after 1m4s
2c3baf2291
Quand la fiche n'a **aucune lecture possible**, l'écran descendait tout seul en bas au lieu de
rester sur le héros. La cause était une durée devinée :

```kotlin
repeat(if (apresBandeAnnonce) 1 else 3) {
    heroPlayFocusRequester.requestFocusAfterFrames()
    delay(80)
}
```

Trois demandes à 80 ms d'intervalle, puis **renoncement** — 240 ms. Or une fiche sans source met plus
longtemps que ça à résoudre : le bouton Lecture n'est pas encore attaché, les trois demandes ne
visent aucun nœud, et le focus retombe sur la recherche par défaut de Compose — le premier
focalisable de la liste, très bas. C'est ce qui explique « sur une seule fiche et pas sur les
autres » : la fiche dont la résolution traîne est celle qui tombe.

Le registre `CiblesAttachees` sait déjà si le bouton porte un nœud (`suivreLAttache` est posé dessus).
On l'**attend** au lieu de deviner une durée — borné à ~4 s, puis on demande le focus une seule fois,
quand il existe.

⚠ Le retour de bande-annonce garde son comportement d'avant (une demande, un jeton honoré une fois) :
ce correctif ne touche que l'entrée.

3 tests, dont un qui interdit le retour du `repeat(... 1 else 3)` — c'est lui qui nomme la cause.
foXaCe scheduled this pull request to auto merge when all checks succeed 2026-10-18 14:26:42 +02:00
fix(detekt): les deux durées du focus d'entrée deviennent des constantes nommées
Some checks failed
Porte / porte (pull_request) Failing after 12m41s
86420ac513
Le `50` de la boucle d'attente tombait sur `MagicNumber` : la porte de la forge rougissait sur
la règle de style, pas sur le comportement. Les deux valeurs sont nommées, avec la raison de la
borne écrite à côté — 50 pas de 80 ms, soit 4 s.
test(FOXA-2699): le banc lit la constante nommée, pas le littéral
Some checks failed
Porte / porte (pull_request) Failing after 12m42s
e0d5118ab6
La borne du focus d'entrée est devenue `ATTENTES_MAX_DU_FOCUS_D_ENTREE` (detekt refuse les
nombres magiques) : le test qui cherchait « attente < 50 » lisait une forme, pas la règle. Il lit
maintenant la constante ET sa valeur — 50 pas de 80 ms, soit 4 s.
fix(FOXA-2699): une seule demande de focus, et le compte de renvois déclaré
All checks were successful
Porte / porte (pull_request) Successful in 12m31s
e1299aa57c
Deux règles du dépôt ont attrapé la première version, et elles avaient raison :

- `PilotageManuelDuFocusTest` : la demande de focus était recopiée dans les deux branches du
  `if` — un geste de focus qui se recopie dans un écran appartient au socle commun. Une seule
  demande sert maintenant les deux cas (`apresBandeAnnonce || !initialHeroFocusRequested`).
- `PerimetreDuVerdictVfTest` : le compte de `heroPlayFocusRequester` passe de 5 à 6, et ce n'est
  pas un renvoi de plus — c'est `estAttachee(...)`, qui demande au registre si le bouton porte un
  nœud. Le compte est déclaré avec sa raison, comme les deux changements de forme précédents.
foXaCe merged commit 5c9712f173 into main 2026-10-18 15:41:00 +02:00
foXaCe deleted branch passe-20261018-183000-focusentree 2026-10-18 15:41:00 +02:00
Sign in to join this conversation.
No reviewers
No labels
montee-majeure
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
foXaCe/NuvioTV!1010
No description provided.