Déclenchement proactif (lot F) — quatre branchements : la garde, le cycle de vie accroché à VoiceController, les points GPS peuplés depuis la configuration, et un réglage visiteur. ⚠️ La garde recommandée par le plan aurait coûté de l'argent. _trigger appelle le LLM d'abord et ne parle qu'ensuite via activeVoiceOrchestrator?.ttsEngine, dont le ?. avale le cas « aucun mode vocal actif ». Avec proactiveModeEnabled seul, un visiteur traversant une zone avec l'app en poche et le vocal éteint consommait des jetons Gemini facturés au client pour une phrase que personne n'entend. La garde interroge l'orchestrateur : la vraie condition n'est pas « quel matériel » mais « y a-t-il quelqu'un pour écouter ». Il n'y avait pas de troisième mode à inventer : VoiceMode.voiceOnly existe déjà et construit le même orchestrateur que le mode lunettes. Le proactif est donc un sous-réglage, pas une tuile. glassesEnabled est supprimé, ses trois usages étaient morts — dont une pastille « Lunettes / Déconnecté » jamais affichée. M3 — meterZoneGPS devient le rayon par section, la constante n'étant plus qu'un défaut. Deux pièges de format vérifiés : currentSections porte des maps JSON brutes, pas des DTO, et latitude/longitude y sont des chaînes. Voix du guide — le sélecteur Viva/Marco de manager-app écrivait dans le vide : Instance.GuideVoiceId n'était lu nulle part ici, l'app utilisant une constante de build dont le défaut était Algieba, une voix que personne n'a écoutée. Même forme que W1. Et aucun APK ne parlait avec la voix du produit : GeminiTtsEngine n'est choisi que si GEMINI_API_KEY est injectée, ce qui n'était fait nulle part, donc repli silencieux sur la voix système. Le repli reste le défaut — les tests ne coûtent alors ni jetons ni argent — mais il s'annonce dans les logs. Code mort supprimé : wake_word_service.dart, glasses_qr_scanner_service.dart et glasses_tts_service.dart, les ancêtres de Services/Glasses/. Ils portaient les 4 erreurs kElevenLabs* et l'APK se construisait quand même, personne ne les important. flutter analyze lib rend désormais zéro erreur. ⚠️ android/.../WakeWordService.kt est un homonyme bien vivant, non touché. impl/elevenlabs_tts_engine.dart est conservé comme option. flutter build apk --debug --flavor dev : vert. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
99 lines
4.0 KiB
Dart
99 lines
4.0 KiB
Dart
import 'package:flutter/material.dart';
|
|
import 'package:manager_api_new/api.dart';
|
|
import 'package:mymuseum_visitapp/Models/articleRead.dart';
|
|
import 'package:mymuseum_visitapp/Services/statisticsService.dart';
|
|
import 'package:mymuseum_visitapp/Models/beaconSection.dart';
|
|
import 'package:mymuseum_visitapp/Models/resourceModel.dart';
|
|
import 'package:mymuseum_visitapp/client.dart';
|
|
import 'package:mymuseum_visitapp/constants.dart';
|
|
|
|
class VisitAppContext with ChangeNotifier {
|
|
Client clientAPI = Client(kApiBaseUrl);
|
|
|
|
String? id = "";
|
|
String? language = "";
|
|
String? instanceId = kInstanceId;
|
|
String? apiKey;
|
|
|
|
List<ConfigurationDTO>? configurations;
|
|
ConfigurationDTO? configuration;
|
|
List<String?>? sectionIds; // Use to valid QR code found
|
|
List<BeaconSection?>? beaconSections;
|
|
List<dynamic>? currentSections;
|
|
List<SectionRead> readSections = [];
|
|
bool isContentCurrentlyShown = false;
|
|
bool isScanningBeacons = false;
|
|
bool isScanBeaconAlreadyAllowed = false;
|
|
bool isMaximizeTextSize = false;
|
|
|
|
Size? puzzleSize;
|
|
|
|
List<ResourceModel> audiosNotWorking = [];
|
|
|
|
ApplicationInstanceDTO? applicationInstanceDTO; // null = assistant non activé
|
|
StatisticsService? statisticsService;
|
|
|
|
/// Retourne l'AppConfigurationLink de l'instance mobile pour la configuration courante.
|
|
/// Contient roundedValue, isSectionImageBackground, etc.
|
|
AppConfigurationLink? get currentAppConfigurationLink {
|
|
if (applicationInstanceDTO == null || configuration == null) return null;
|
|
return applicationInstanceDTO!.configurations
|
|
?.where((c) => c.configurationId == configuration!.id)
|
|
.firstOrNull;
|
|
}
|
|
|
|
bool? isAdmin = false;
|
|
bool? isAllLanguages = false;
|
|
bool notificationsEnabled = true;
|
|
|
|
/// Voix du guide choisie par le client dans manager-app (`Sulafat` = Viva, féminine ;
|
|
/// `Umbriel` = Marco, masculine). Null tant que l'instance n'a pas été lue.
|
|
///
|
|
/// ⚠️ C'est **le CMS qui décide**, pas le build : `kGeminiTtsVoice` n'est plus qu'un
|
|
/// repli. Le sélecteur existait dans manager-app et la colonne côté serveur depuis le
|
|
/// 07/08, mais rien ne les lisait ici — le choix du client était enregistré et ignoré.
|
|
String? guideVoiceId;
|
|
|
|
/// Mode proactif : l'assistant parle automatiquement à l'approche d'un beacon
|
|
/// ou d'une zone géographique, sans que le visiteur pose de question.
|
|
///
|
|
/// Indépendant du support : vaut pour les lunettes comme pour des écouteurs.
|
|
/// C'est un **sous-réglage** du mode vocal, pas un mode à part — il n'a d'effet
|
|
/// que lorsqu'un `VoiceMode` est actif, seul cas où il existe une sortie audio.
|
|
///
|
|
/// ⚠️ Volontairement **non persisté** (absent de `toMap`), comme `VoiceController.mode` :
|
|
/// un mode qui parle tout seul et se réactiverait en silence au lancement suivant
|
|
/// est une mauvaise surprise. Opt-in par session.
|
|
bool proactiveModeEnabled = false;
|
|
|
|
String? localPath;
|
|
|
|
VisitAppContext({this.language, this.id, this.configuration, this.isAdmin, this.isAllLanguages, this.instanceId, this.apiKey, this.notificationsEnabled = true});
|
|
|
|
Map<String, dynamic> toMap() {
|
|
return {
|
|
'id': id,
|
|
'instanceId': instanceId,
|
|
'language': language,
|
|
'isAdmin': isAdmin != null ? isAdmin! ? 1 : 0 : 0,
|
|
'isAllLanguages': isAllLanguages != null ? isAllLanguages! ? 1 : 0 : 0,
|
|
'apiKey': apiKey,
|
|
'notificationsEnabled': notificationsEnabled ? 1 : 0,
|
|
};
|
|
}
|
|
|
|
factory VisitAppContext.fromJson(Map<String, dynamic> json) {
|
|
return VisitAppContext(
|
|
id: json['id'] as String,
|
|
instanceId: json['instanceId'] as String,
|
|
language: json['language'] as String,
|
|
configuration: json['configuration'] == null ? null : ConfigurationDTO.fromJson(json['configuration']),
|
|
apiKey: json['apiKey'] as String?,
|
|
);
|
|
}
|
|
|
|
@override
|
|
String toString() {
|
|
return 'VisitAppContext{id: $id, instanceId: $instanceId, language: $language, configuration: $configuration, beaconSections: $beaconSections, isAdmin: $isAdmin}';
|
|
}
|
|
} |