mymuseum-visitapp/lib/Components/GlassesStatusWidget.dart
Thomas Fransolet d39949a6bd Lunettes Meta, lecteur audio en onglet, et badge de version dans les Parametres
Ce commit boucle le travail en cours sur la branche (assistant lunettes Meta,
passage du lecteur audio flottant a un onglet, ajustements scanner / liste de
configurations / telechargement) et y ajoute le badge de version.

Le badge, en bas de la feuille Parametres, affiche « flavor . version . commit ».
Un APK pose sur une tablette du terrain n'etait rattachable a aucun commit
precis : la version du pubspec ne bougeait pas d'un build a l'autre et rien
n'indiquait le flavor reellement installe. kGitSha suit le meme schema que
kApiBaseUrl, injecte par --dart-define, et kFlavor expose le flavor deja calcule.

package_info_plus etait deja une dependance transitive ; il devient direct,
puisqu'il est desormais importe.

/!\ Un --dart-define modifie n'est PAS pris en compte sans `flutter clean` sur
ce projet : verifie a la sentinelle, le SHA restait absent de libapp.so tant que
le cache Dart n'etait pas vide. Un build de release destine au terrain doit donc
toujours passer par un clean, sinon le badge affiche le SHA du build precedent.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-08 22:43:53 +02:00

89 lines
3.4 KiB
Dart

import 'package:flutter/material.dart';
import 'package:mymuseum_visitapp/Components/GlassPill.dart';
import 'package:mymuseum_visitapp/Components/VoiceModeSheet.dart';
import 'package:mymuseum_visitapp/Models/visitContext.dart';
import 'package:mymuseum_visitapp/Services/meta_glasses_service.dart';
import 'package:mymuseum_visitapp/Services/voice_controller.dart';
import 'package:provider/provider.dart';
/// Icône circulaire positionnée en haut à droite de l'écran d'accueil.
///
/// Visible si :
/// - La fonctionnalité est activée sur l'instance
/// - OU un mode vocal est encore actif — porte de sortie pour le couper si
/// l'instance a été désactivée entre-temps.
///
/// Des lunettes appairées ne suffisent pas : sans la fonctionnalité sur
/// l'instance, il n'y a rien à en faire.
///
/// Tap → ouvre VoiceModeSheet.
class GlassesStatusWidget extends StatelessWidget {
final VisitAppContext visitAppContext;
const GlassesStatusWidget({super.key, required this.visitAppContext});
@override
Widget build(BuildContext context) {
return Consumer<VoiceController>(
builder: (context, controller, _) {
return ValueListenableBuilder<GlassesState>(
valueListenable: MetaGlassesService.instance.state,
builder: (context, glassesState, _) {
final isConnected = glassesState == GlassesState.connected ||
glassesState == GlassesState.streaming;
final modeActive = controller.mode != VoiceMode.none;
final featureAvailable = controller.isFeatureAvailable(visitAppContext);
if (!featureAvailable && !modeActive) return const SizedBox.shrink();
final Color iconColor = switch ((isConnected, controller.mode)) {
(true, VoiceMode.glasses) => Colors.greenAccent,
(_, VoiceMode.voiceOnly) => Colors.lightBlueAccent,
(true, _) => Colors.white70,
_ => Colors.white38,
};
return GlassPill(
size: 46,
onTap: () => VoiceModeSheet.show(context, visitAppContext: visitAppContext),
child: SizedBox(
width: 46,
height: 46,
child: Stack(
alignment: Alignment.center,
children: [
Icon(
controller.mode == VoiceMode.voiceOnly
? Icons.mic
: Icons.remove_red_eye_outlined,
color: iconColor,
size: 22,
),
// Point vert en bas à droite si mode actif
if (modeActive)
Positioned(
bottom: 7,
right: 7,
child: Container(
width: 8,
height: 8,
decoration: BoxDecoration(
color: controller.mode == VoiceMode.glasses
? Colors.greenAccent
: Colors.lightBlueAccent,
shape: BoxShape.circle,
border: Border.all(color: Colors.black54, width: 1),
),
),
),
],
),
),
);
},
);
},
);
}
}