Appearance
Feuille de route refactorisation « application »
Concept
Cette feuille de route vise à refactoriser la logique liée au lancement d'applications (schemes, commandes internes, purges) en sortant des classes historiques pour créer des services réutilisables par toutes les plateformes. L'objectif est d'harmoniser les invariants, réduire le couplage, et faciliter l'ajout de nouvelles plateformes en centralisant les helpers communs.
Cette feuille de route vise à sortir la logique liée au lancement d'applications (schemes, commandes internes, purges) des classes historiques pour converger vers des services réutilisables par toutes les plateformes.
État des lieux (août 2024)
- Services communs : le module
$lib/applicationexpose toujours le parser uniqueparseAppLaunchCommand, les helpers de typage (isModalCommand,isPurgeCommand) et l'ensemble des stratégies de fallback (ensureSamsungLaunchableCommand,ensureChromecastLaunchableCommand,ensurePhilipsLaunchableCommand) pour partager les invariants de lancement entre plateformes.【F:src/lib/application/commands.ts†L3-L198】 - Actions transverses :
handlePurgeCommandorchestre désormais stores de statut, file de toasts, reboot optionnel et toasts natifs tandis quehandleModalCommandpilote le store de navigation des modales ; les deux helpers sont injectés dans les lanceurs LG et Android viadefaultApplicationActionspour factoriser les effets secondaires.【F:src/lib/application/actions.ts†L32-L198】【F:src/lib/classes/platforms/appLauncher.ts†L69-L140】【F:src/lib/classes/platforms/appLauncher.ts†L167-L235】 - Lanceurs existants : chaque implémentation s'appuie sur
parseAppLaunchCommandetensure*LaunchableCommandpour harmoniser le fallback. Samsung redirige vers Smart Hub en cas de purge ou modale, Chromecast n'expose qu'un feedback utilisateur, LG et Android délèguent purges/modales aux helpers partagés et Philips publie désormais un toast d'erreur dansapplicationToastStoreen plus du toast natif.【F:src/lib/classes/platforms/appLauncher.ts†L28-L263】 - Tests et qualité : des suites Vitest couvrent le parser, les stratégies de fallback, les actions de purge/modale et la factory de lanceurs afin de sécuriser les régressions ; les service workers hérités sont exclus du lint via
.eslintignorepour réduire le bruit.【F:src/lib/application/commands.spec.ts†L1-L121】【F:src/lib/application/actions.spec.ts†L1-L172】【F:src/lib/classes/platforms/appLauncher.spec.ts†L1-L95】【F:.eslintignore†L10-L21】 - Points de vigilance : les fallbacks Samsung/Chromecast déclenchent encore uniquement des toasts natifs sans alimenter le store partagé, et
AppLauncherFactoryinstancie directement les implémentations sans injecter les dépendances (stores, helpers, plugin toast), ce qui limite la personnalisation lors de futurs chantiers multi-constructeurs.【F:src/lib/classes/platforms/appLauncher.ts†L28-L165】【F:src/lib/classes/platforms/appLauncherFactory.ts†L1-L34】
Phase 1 – Socle de services communs
- [x] Étape 1 – Centraliser les schemes et la détection de commandes : créer un module
$lib/applicationexposant une analyse unique despackageName(modales, purge, lancement standard) afin que les factories ne dupliquent plus la logique string-based. - [x] Étape 2 – Isoler les actions transverses : déplacer la logique de purge et les effets secondaires partagés (toast, reboot) dans des helpers dédiés pour permettre leur réutilisation côté Android, LG et futurs adaptateurs.
- [x] Étape 3 – Documenter les invariants : les invariants couvrent désormais la gestion des stores de statut (
applicationPurgeStore,applicationModalStore) et des toasts partagés (applicationToastStore).
Phase 2 – Adaptation des lanceurs existants
- [x] Étape 4 – Migrer
AndroidAppLaunchervers les helpers communs, en supprimant la détection inline des schemes et en s'appuyant sur des types clairs pour la gestion des modales. - [x] Étape 5 – Migrer
LgAppLauncherpour qu'il repose sur les mêmes helpers (sauf traitementlivetvspécifique), réduisant le couplage au parsing string. - [x] Étape 6 – Préparer l'ouverture aux autres plateformes :
ensureLaunchableCommandest exposé par$lib/applicationet la factory retourne désormais une implémentation Philips dédiée branchée surensurePhilipsLaunchableCommandtout en conservant les fallbacks Android intentionnels (Vestel, TCL, G4K, Unknown).
Phase 3 – Tests et montée en qualité
- [x] Étape 7 – Ajouter des tests unitaires : des suites Vitest couvrent le parser (
parseAppLaunchCommand), les helpers d'actions (handlePurgeCommand,handleModalCommand) et les lanceurs (factory + Philips) pour sécuriser les comportements historiques (purge, fallback, modales). - [x] Étape 8 – Durcir ESLint : des règles ciblées (
consistent-type-imports,explicit-module-boundary-types) s'appliquent désormais sur$lib/applicationetclasses/platformspour éviter les imports implicites et rendre les signatures explicites. - [x] Étape 9 – Mettre à jour la documentation : la présente feuille de route détaille les invariants, les stores partagés et le fallback Philips ; la spec fallback mentionne désormais l'usage d'
applicationToastStore. - [x] Étape 10 – Exclure les service workers hérités du lint en ajoutant
/src/unable_serviceworker/*aux ignore afin d'éviter le bruit tant que la migration vers les workers modernes n'est pas engagée.【F:.eslintignore†L10-L21】
Invariants fonctionnels
Les helpers introduits par le module $lib/application s'appuient sur des conventions partagées :
- Les schemes
MODAL_SCHEME(content.modal.) etPURGE_SCHEME(system.action.purge) servent de référence unique pour différencier les commandes de modale, de purge ou de lancement standard. Toute nouvelle logique de parsing doit s'aligner sur ces constantes exposées parcommands.ts. - Les scénarios de purge déclenchés via
PURGE_SCHEMEpilotentapplicationPurgeStorequi suit le cycleidle → pending → success|erroret publient un retour utilisateur cohérent dansapplicationToastStore. Les toasts natifs (@capacitor/toast) ne sont envoyés qu'en cas de succès/échec lorsque l'implémentation fournittoastOptionsouerrorToastOptions. - Les commandes modales
MODAL_SCHEMEalimententapplicationModalStoreafin de tracer le dernier identifiant demandé et l'état de résolution (pending,success,error). - L'ouverture d'une modale via
MODAL_SCHEMEdoit renseigner le store de navigation associé aux modales avec l'identifiantcontentIdretourné par le parser, en gérant explicitement le cas où l'identifiant est absent (null).
Intégration de nouvelles plateformes ou de nouveaux schemes
Pour intégrer une nouvelle plateforme ou ajouter un scheme applicatif, étendre le module $lib/application : utiliser parseAppLaunchCommand pour déléguer toute détection de scheme, puis brancher les helpers (purge, modale, fallback standard) depuis le lanceur spécifique à la plateforme. Les nouvelles plateformes doivent implémenter uniquement la traduction des commandes vers leurs APIs natives, en s'appuyant sur les helpers partagés pour les effets transverses afin de préserver la cohérence avec Android et LG.
Déclarer un fallback de plateforme
- Utiliser
ensureLaunchableCommandpour sécuriser les plateformes dont les API ne couvrent pas l'ensemble des commandes parsées. - Centraliser les stratégies dans
$lib/applicationvia des helpers dédiés (ensureSamsungLaunchableCommand,ensureChromecastLaunchableCommand, ...). - Documenter chaque fallback dans
docs/technique/features/platform-fallback-spec.md(type de stratégie, message utilisateur ou package alternatif) avant d'exposer un nouveau lanceur. - Harmoniser le feedback utilisateur (toast + log structuré) afin de conserver un comportement comparable aux implémentations Android/LG.
Problème identifié avec capacitor-intents-for-android
Le plugin capacitor-intents-for-android (v1.4.0) a été modifié via un patch personnalisé pour supporter les fonctionnalités avancées (callbacks, composants, broadcasts) nécessaires aux cas d'usage TV/hotspot. Cependant, ces modifications ont cassé l'implémentation native Android de startActivity(), causant l'erreur "CapacitorIntentsPlugin.startActivity()" is not implemented on android.
Solution temporaire mise en place
- Ajout de
capacitor-intent-sender(v1.1.0) comme workaround temporaire uniquement pour l'ouverture des paramètres Android - Conservation de
capacitor-intents-for-androidpour les autres fonctionnalités (broadcasts hotspot, receivers) - Architecture hybride dans
androidTransport.tsavec routage automatique selon le type d'intent
Phase 4 – Correction du plugin capacitor-intents-for-android ✅ TERMINÉ
- [x] Étape 11 – Analyser les modifications du patch : Le plugin original
capacitor-intents-for-androidne possédait pas d'implémentationstartActivity(). Les modifications personnalisées avaient ajouté cette fonctionnalité mais l'avaient cassée. - [x] Étape 12 – Créer un patch minimal : Ajout de la méthode
startActivity()dansCapacitorIntents.javaavec gestion complète des extras (bundles, types primitifs, etc.) - [x] Étape 13 – Tester la compatibilité : Validation que les fonctionnalités broadcast existantes sont préservées et que
startActivity()fonctionne désormais - [x] Étape 14 – Migrer vers le patch corrigé : Suppression de
capacitor-intent-senderdupackage.jsonet reconstruction du plugin - [x] Étape 15 – Nettoyer l'architecture hybride : Simplification d'
androidTransport.tspour utiliser uniquementcapacitor-intents-for-android
Critères de succès ✅ ATTEINTS
- Ouverture des paramètres Android fonctionnelle avec
capacitor-intents-for-android - Fonctionnalités hotspot/broadcast préservées (callbacks, composants)
- Suppression complète de
capacitor-intent-senderet de l'architecture hybride - Tests de régression passant sur tous les cas d'usage Android
Risques et mitigation ✅ GÉRÉS
- Risque : Le patch minimal pourrait ne pas supporter tous les cas d'usage TV avancés
- Mitigation : Tests exhaustifs des fonctionnalités hotspot validés ✅
- Risque : Régression sur d'autres plateformes utilisant les intents
- Mitigation : Validation croisée sur tous les appareils de test ✅
Support des Extras de Lancement Android
Objectif
Permettre à l'app Capacitor d'accepter des paramètres au démarrage via les extras de l'intent de lancement Android (ip, mac, tvcast_mac, token), afin d'éviter la requête whoami et d'optimiser les performances de lancement.
Contexte
- Capacitor n'expose pas nativement les extras du launch intent.
- L'approche actuelle utilise des paramètres URL (deep links), mais l'utilisateur souhaite des vrais extras Android pour une intégration plus native (ex: lancement depuis une autre app ou ADB).
Approche Technique
Créer un plugin Capacitor personnalisé pour capturer et exposer les extras du launch intent.
1. Structure du Plugin
- Nom :
@comminter/capacitor-launch-extras(ou intégré dans le projet). - Fichiers :
android/src/main/java/com/comminter/capacitor/launch/LaunchExtrasPlugin.java: Code Android.src/plugins/launch-extras/definitions.ts: Définitions TypeScript.src/plugins/launch-extras/web.ts: Implémentation web (vide).src/plugins/launch-extras/index.ts: Export.
2. Implantation Android (LaunchExtrasPlugin.java)
java
package com.comminter.capacitor.launch;
import android.content.Intent;
import android.os.Bundle;
import com.getcapacitor.JSObject;
import com.getcapacitor.Plugin;
import com.getcapacitor.PluginCall;
import com.getcapacitor.PluginMethod;
import com.getcapacitor.annotation.CapacitorPlugin;
@CapacitorPlugin(name = "LaunchExtras")
public class LaunchExtrasPlugin extends Plugin {
private Bundle launchExtras;
@Override
protected void handleOnNewIntent(Intent intent) {
super.handleOnNewIntent(intent);
if (intent != null && intent.getExtras() != null) {
launchExtras = intent.getExtras();
}
}
@Override
protected void handleOnCreate(Bundle savedInstanceState) {
super.handleOnCreate(savedInstanceState);
Intent intent = getActivity().getIntent();
if (intent != null && intent.getExtras() != null) {
launchExtras = intent.getExtras();
}
}
@PluginMethod
public void getLaunchExtras(PluginCall call) {
JSObject result = new JSObject();
if (launchExtras != null) {
for (String key : launchExtras.keySet()) {
Object value = launchExtras.get(key);
if (value instanceof String) {
result.put(key, (String) value);
} else if (value instanceof Integer) {
result.put(key, (Integer) value);
} // Ajouter d'autres types si nécessaire
}
}
call.resolve(result);
}
}3. Définitions TypeScript (definitions.ts)
typescript
export interface LaunchExtrasPlugin {
getLaunchExtras(): Promise<{ [key: string]: any }>;
}
export interface PluginRegistry {
LaunchExtras: LaunchExtrasPlugin;
}4. Intégration dans l'App
Ajouter dans
capacitor.config.ts:typescriptplugins: { LaunchExtras: { android: { src: 'android/src/main/java/com/comminter/capacitor/launch'; } } }Modifier
src/hooks/intentHandler.ts:typescriptimport { LaunchExtras } from '@capacitor/launch-extras'; export function initializeIntentHandler(): void { LaunchExtras.getLaunchExtras() .then((extras) => { const { ip, mac, tvcast_mac, token } = extras; if (ip && mac && tvcast_mac && token) { setWhoamiData({ ip, mac, tvcast_mac, token }); logger.log('Whoami set from launch extras'); } }) .catch((error) => { logger.error('Failed to get launch extras:', error); }); }
5. Tests et Utilisation
- Via ADB :
adb shell am start -n fr.tvcast.display/.MainActivity --es ip "192.168.1.1" --es mac "aa:bb:cc:dd:ee:ff" --es tvcast_mac "11:22:33:44:55:66" --es token "abc123" - Via Kotlin :kotlin
val intent = Intent(context, MainActivity::class.java) intent.putExtra("ip", "192.168.1.1") intent.putExtra("mac", "aa:bb:cc:dd:ee:ff") intent.putExtra("tvcast_mac", "11:22:33:44:55:66") intent.putExtra("token", "abc123") startActivity(intent) - Tests : Ajouter dans
tests/device-detection.spec.tspour simuler les extras.
Risques et Considérations
- Compatibilité : Tester sur différentes versions Android et appareils.
- Sécurité : Valider les extras (format IP/MAC) pour éviter les injections.
- Performance : Assurer que cela n'impacte pas le temps de démarrage.
- Fallback : Si extras absents, utiliser la logique whoami existante.
Priorité et Effort
- Priorité : Moyenne (amélioration UX pour intégrations Android).
- Effort estimé : 2-3 jours (développement plugin + tests).
- Dépendances : Mise à jour Capacitor si nécessaire.
Prochaines étapes
- Harmoniser les feedbacks cross-plateformes : exposer un helper partagé pour publier les fallbacks (
message/package) dansapplicationToastStoreet brancher Samsung/Chromecast dessus afin d'aligner l'UI sur les toasts natifs.【F:src/lib/classes/platforms/appLauncher.ts†L28-L165】 - Injecter les dépendances via les factories : faire évoluer
AppLauncherFactory(et futures variantes) pour fournir les helpers d'actions, stores et plugins nécessaires à chaque lanceur, ce qui facilitera les tests et la configuration constructeur spécifique.【F:src/lib/classes/platforms/appLauncher.ts†L66-L263】【F:src/lib/classes/platforms/appLauncherFactory.ts†L1-L34】 - Mutualiser la configuration des purges : factoriser les messages/paramètres utilisés par Android et LG (
toastOptions,messages,reboot) dans$lib/applicationafin de réduire la duplication et préparer l'extension à d'autres plateformes.【F:src/lib/classes/platforms/appLauncher.ts†L93-L140】【F:src/lib/classes/platforms/appLauncher.ts†L201-L223】
Diagrammes
e-novatis
Date de dernière mise à jour : date inconnue