Skip to content

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/application expose toujours le parser unique parseAppLaunchCommand, 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 : handlePurgeCommand orchestre désormais stores de statut, file de toasts, reboot optionnel et toasts natifs tandis que handleModalCommand pilote le store de navigation des modales ; les deux helpers sont injectés dans les lanceurs LG et Android via defaultApplicationActions pour 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 parseAppLaunchCommand et ensure*LaunchableCommand pour 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 dans applicationToastStore en 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 .eslintignore pour 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 AppLauncherFactory instancie 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/application exposant une analyse unique des packageName (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 AndroidAppLauncher vers 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 LgAppLauncher pour qu'il repose sur les mêmes helpers (sauf traitement livetv spécifique), réduisant le couplage au parsing string.
  • [x] Étape 6 – Préparer l'ouverture aux autres plateformes : ensureLaunchableCommand est exposé par $lib/application et la factory retourne désormais une implémentation Philips dédiée branchée sur ensurePhilipsLaunchableCommand tout 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/application et classes/platforms pour é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.) et PURGE_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 par commands.ts.
  • Les scénarios de purge déclenchés via PURGE_SCHEME pilotent applicationPurgeStore qui suit le cycle idle → pending → success|error et publient un retour utilisateur cohérent dans applicationToastStore. Les toasts natifs (@capacitor/toast) ne sont envoyés qu'en cas de succès/échec lorsque l'implémentation fournit toastOptions ou errorToastOptions.
  • Les commandes modales MODAL_SCHEME alimentent applicationModalStore afin de tracer le dernier identifiant demandé et l'état de résolution (pending, success, error).
  • L'ouverture d'une modale via MODAL_SCHEME doit renseigner le store de navigation associé aux modales avec l'identifiant contentId retourné 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 ensureLaunchableCommand pour sécuriser les plateformes dont les API ne couvrent pas l'ensemble des commandes parsées.
  • Centraliser les stratégies dans $lib/application via 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-android pour les autres fonctionnalités (broadcasts hotspot, receivers)
  • Architecture hybride dans androidTransport.ts avec 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-android ne possédait pas d'implémentation startActivity(). 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() dans CapacitorIntents.java avec 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-sender du package.json et reconstruction du plugin
  • [x] Étape 15 – Nettoyer l'architecture hybride : Simplification d'androidTransport.ts pour utiliser uniquement capacitor-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-sender et 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 :

    typescript
    plugins: {
    	LaunchExtras: {
    		android: {
    			src: 'android/src/main/java/com/comminter/capacitor/launch';
    		}
    	}
    }
  • Modifier src/hooks/intentHandler.ts :

    typescript
    import { 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.ts pour 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) dans applicationToastStore et 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/application afin 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