From e27ce8db34db92d4526f49718dd03e283159bc82 Mon Sep 17 00:00:00 2001 From: Fransolet Thomas Date: Sat, 10 Sep 2022 23:56:46 +0200 Subject: [PATCH] Add check QRcode + add translation when invalid QR Code --- .flutter-plugins-dependencies | 2 +- lib/Components/LanguageSelection.dart | 21 ++- lib/Components/Loading.dart | 4 - lib/Components/ScannerBouton.dart | 8 +- lib/Components/ScannerDialog.dart | 145 ++++-------------- lib/Helpers/DatabaseHelper.dart | 12 +- lib/Models/visitContext.dart | 9 +- lib/Screens/Article/article.dart | 8 +- lib/Screens/Home/home.dart | 26 ++-- .../Visit/components/section_card.dart | 2 +- lib/Screens/Visit/visit.dart | 2 +- lib/translations.dart | 12 +- 12 files changed, 86 insertions(+), 165 deletions(-) diff --git a/.flutter-plugins-dependencies b/.flutter-plugins-dependencies index 21cb07a..7b1a26a 100644 --- a/.flutter-plugins-dependencies +++ b/.flutter-plugins-dependencies @@ -1 +1 @@ -{"info":"This is a generated file; do not edit or check into version control.","plugins":{"ios":[{"name":"qr_code_scanner","path":"C:\\\\Users\\\\thoma\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dartlang.org\\\\qr_code_scanner-1.0.1\\\\","native_build":true,"dependencies":[]},{"name":"sqflite","path":"C:\\\\Users\\\\thoma\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dartlang.org\\\\sqflite-2.0.3+1\\\\","native_build":true,"dependencies":[]}],"android":[{"name":"qr_code_scanner","path":"C:\\\\Users\\\\thoma\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dartlang.org\\\\qr_code_scanner-1.0.1\\\\","native_build":true,"dependencies":[]},{"name":"sqflite","path":"C:\\\\Users\\\\thoma\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dartlang.org\\\\sqflite-2.0.3+1\\\\","native_build":true,"dependencies":[]}],"macos":[{"name":"sqflite","path":"C:\\\\Users\\\\thoma\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dartlang.org\\\\sqflite-2.0.3+1\\\\","native_build":true,"dependencies":[]}],"linux":[],"windows":[],"web":[]},"dependencyGraph":[{"name":"qr_code_scanner","dependencies":[]},{"name":"sqflite","dependencies":[]}],"date_created":"2022-09-10 22:13:09.391856","version":"3.0.3"} \ No newline at end of file +{"info":"This is a generated file; do not edit or check into version control.","plugins":{"ios":[{"name":"qr_code_scanner","path":"C:\\\\Users\\\\thoma\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dartlang.org\\\\qr_code_scanner-1.0.1\\\\","native_build":true,"dependencies":[]},{"name":"sqflite","path":"C:\\\\Users\\\\thoma\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dartlang.org\\\\sqflite-2.0.3+1\\\\","native_build":true,"dependencies":[]}],"android":[{"name":"qr_code_scanner","path":"C:\\\\Users\\\\thoma\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dartlang.org\\\\qr_code_scanner-1.0.1\\\\","native_build":true,"dependencies":[]},{"name":"sqflite","path":"C:\\\\Users\\\\thoma\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dartlang.org\\\\sqflite-2.0.3+1\\\\","native_build":true,"dependencies":[]}],"macos":[{"name":"sqflite","path":"C:\\\\Users\\\\thoma\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dartlang.org\\\\sqflite-2.0.3+1\\\\","native_build":true,"dependencies":[]}],"linux":[],"windows":[],"web":[]},"dependencyGraph":[{"name":"qr_code_scanner","dependencies":[]},{"name":"sqflite","dependencies":[]}],"date_created":"2022-09-10 23:41:31.829523","version":"3.0.3"} \ No newline at end of file diff --git a/lib/Components/LanguageSelection.dart b/lib/Components/LanguageSelection.dart index 7fa27a3..3d9bc7f 100644 --- a/lib/Components/LanguageSelection.dart +++ b/lib/Components/LanguageSelection.dart @@ -60,20 +60,17 @@ class _LanguageSelection extends State with TickerProviderSta return menuItems; }, onSelected:(value) async { - // Update App context + update local DB VisitAppContext visitAppContext = appContext.getContext(); - visitAppContext.language = languagesEnable![int.parse(value.toString())]; - appContext.setContext(visitAppContext); + String chosenLanguage = languagesEnable![int.parse(value.toString())]; - // Update local DB - await DatabaseHelper.instance.insert(DatabaseTableType.main, visitAppContext.toMap()); - /*if(value == 0){ - print("My account menu is selected."); - }else if(value == 1){ - print("Settings menu is selected."); - }else if(value == 2){ - print("Logout menu is selected."); - }*/ + if(visitAppContext.language != chosenLanguage) { + // Update App context + update local DB + visitAppContext.language = languagesEnable![int.parse(value.toString())]; + appContext.setContext(visitAppContext); + + // Update local DB + await DatabaseHelper.instance.insert(DatabaseTableType.main, visitAppContext.toMap()); + } } ); } diff --git a/lib/Components/Loading.dart b/lib/Components/Loading.dart index 096b0be..9303dce 100644 --- a/lib/Components/Loading.dart +++ b/lib/Components/Loading.dart @@ -17,10 +17,6 @@ class _LoadingState extends State with TickerProviderStateMixin { duration: const Duration(milliseconds: 5000), vsync: this, )..repeat(); - /*_controller = widget.tween.animate( - CurvedAnimation(parent: _controller, curve: Curves.easeIn));*/ - - super.initState(); } diff --git a/lib/Components/ScannerBouton.dart b/lib/Components/ScannerBouton.dart index 53a0e8a..cf19259 100644 --- a/lib/Components/ScannerBouton.dart +++ b/lib/Components/ScannerBouton.dart @@ -1,11 +1,13 @@ import 'package:flutter/material.dart'; +import 'package:mymuseum_visitapp/app_context.dart'; import 'package:mymuseum_visitapp/constants.dart'; import 'ScannerDialog.dart'; class ScannerBouton extends StatefulWidget { - const ScannerBouton({Key? key, required this.isReplacement}) : super(key: key); + ScannerBouton({Key? key, required this.appContext, required this.isReplacement}) : super(key: key); + final AppContext appContext; final bool isReplacement; @override @@ -14,9 +16,9 @@ class ScannerBouton extends StatefulWidget { class _ScannerBoutonState extends State { - void _onItemTapped() { + _onItemTapped() { setState(() { - showScannerDialog(context); + showScannerDialog(context, widget.appContext); }); } diff --git a/lib/Components/ScannerDialog.dart b/lib/Components/ScannerDialog.dart index f32aff1..57ca369 100644 --- a/lib/Components/ScannerDialog.dart +++ b/lib/Components/ScannerDialog.dart @@ -1,19 +1,24 @@ import 'package:flutter/material.dart'; import 'dart:io'; import 'package:flutter/foundation.dart'; +import 'package:mymuseum_visitapp/Helpers/translationHelper.dart'; +import 'package:mymuseum_visitapp/Models/visitContext.dart'; import 'package:mymuseum_visitapp/Screens/Article/article.dart'; +import 'package:mymuseum_visitapp/app_context.dart'; import 'package:mymuseum_visitapp/constants.dart'; import 'package:qr_code_scanner/qr_code_scanner.dart'; -class ScannerTEST extends StatefulWidget { - const ScannerTEST({Key? key}) : super(key: key); +class ScannerDialog extends StatefulWidget { + const ScannerDialog({Key? key, required this.appContext}) : super(key: key); + + final AppContext? appContext; @override - State createState() => _ScannerTESTState(); + State createState() => _ScannerDialogState(); } -class _ScannerTESTState extends State { +class _ScannerDialogState extends State { Barcode? result; QRViewController? controller; final GlobalKey qrKey = GlobalKey(debugLabel: 'QR'); @@ -22,7 +27,6 @@ class _ScannerTESTState extends State { // is android, or resume the camera if the platform is iOS. @override void reassemble() { - //print("reassemble "); super.reassemble(); if (Platform.isAndroid) { controller!.pauseCamera(); @@ -92,95 +96,8 @@ class _ScannerTESTState extends State { return const Icon(Icons.flip_camera_android, color: Colors.white); }, )), - /*child: FutureBuilder( - future: controller?.getFlashStatus(), - builder: (context, snapshot) { - return Icon(Icons.flip_camera_android, color: Colors.white); - }, - )*/), + ), ), - /*Column( - children: [ - Expanded(flex: 4, child: _buildQrView(context)), - Expanded( - flex: 1, - child: FittedBox( - fit: BoxFit.contain, - child: Column( - mainAxisAlignment: MainAxisAlignment.spaceEvenly, - children: [ - Row( - mainAxisAlignment: MainAxisAlignment.center, - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - Container( - margin: const EdgeInsets.all(8), - child: ElevatedButton( - onPressed: () async { - await controller?.toggleFlash(); - setState(() {}); - }, - child: FutureBuilder( - future: controller?.getFlashStatus(), - builder: (context, snapshot) { - return Icon(Icons.flash_on); - }, - )), - ), - Container( - margin: const EdgeInsets.all(8), - child: ElevatedButton( - onPressed: () async { - await controller?.flipCamera(); - setState(() {}); - }, - child: FutureBuilder( - future: controller?.getCameraInfo(), - builder: (context, snapshot) { - if (snapshot.data != null) { - return Text( - 'Camera facing ${describeEnum(snapshot.data!)}'); - } else { - return const Text('loading'); - } - }, - )), - ) - ], - ), - Row( - mainAxisAlignment: MainAxisAlignment.center, - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - Container( - margin: const EdgeInsets.all(8), - child: ElevatedButton( - onPressed: () async { - await controller?.pauseCamera(); - }, - child: const Text('pause', - style: TextStyle(fontSize: 20)), - ), - ), - Container( - margin: const EdgeInsets.all(8), - child: ElevatedButton( - onPressed: () async { - print(controller); - await controller?.resumeCamera(); - }, - child: const Text('resume', - style: TextStyle(fontSize: 20)), - ), - ) - ], - ), - ], - ), - ), - ) - ], - ),*/ ], ), ); @@ -192,6 +109,7 @@ class _ScannerTESTState extends State { MediaQuery.of(context).size.height < 400) ? 225.0 : 300.0; + // To ensure the Scanner view is properly sizes after rotation // we need to listen for Flutter SizeChanged notification and update controller return QRView( @@ -207,7 +125,7 @@ class _ScannerTESTState extends State { ); } - void _onQRViewCreated(QRViewController controller) { + _onQRViewCreated(QRViewController controller) { setState(() { this.controller = controller; }); @@ -218,8 +136,6 @@ class _ScannerTESTState extends State { controller.scannedDataStream.listen((scanData) { setState(() { result = scanData; - /*print(result); - print(result!.code.toString());*/ var code = result == null ? "" : result!.code.toString(); if(result!.format == BarcodeFormat.qrcode) { controller.pauseCamera(); @@ -228,20 +144,24 @@ class _ScannerTESTState extends State { SnackBar(content: Text('QR CODE FOUND - ${code.toString()}')), );*/ - // TODO search in local if data == Id of Article - //Navigator.of(context).pop(); + VisitAppContext visitAppContext = widget.appContext!.getContext(); - // If so, navigate to article view - Navigator.pushReplacement( - context, - MaterialPageRoute( - builder: (context) { - return ArticlePage(articleId: code); - }, - ), - ); + if(!visitAppContext!.sectionIds!.contains(code)) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar(content: Text(TranslationHelper.getFromLocale('invalidQRCode', widget.appContext!)), backgroundColor: kBlue2), + ); + Navigator.of(context).pop(); - //Navigator.of(context).pop(); + } else { + Navigator.pushReplacement( + context, + MaterialPageRoute( + builder: (context) { + return ArticlePage(articleId: code); + }, + ), + ); + } } }); }); @@ -263,14 +183,13 @@ class _ScannerTESTState extends State { } } -showScannerDialog (BuildContext context) { +showScannerDialog (BuildContext context, AppContext appContext) { showDialog( - builder: (BuildContext context) => const AlertDialog( - shape: RoundedRectangleBorder( + builder: (BuildContext context) => AlertDialog( + shape: const RoundedRectangleBorder( borderRadius: BorderRadius.all(Radius.circular(10.0)) ), - content: ScannerTEST( - ), + content: ScannerDialog(appContext: appContext), contentPadding: EdgeInsets.zero, ), context: context ); diff --git a/lib/Helpers/DatabaseHelper.dart b/lib/Helpers/DatabaseHelper.dart index c6b6488..f72763d 100644 --- a/lib/Helpers/DatabaseHelper.dart +++ b/lib/Helpers/DatabaseHelper.dart @@ -159,12 +159,22 @@ class DatabaseHelper { return res; } - Future>> queryWithId(DatabaseTableType type, String id) async { + Future>> queryWithColumnId(DatabaseTableType type, String id) async { Database db = await instance.database; var res = await db.query(_getTableName(type), where: '$columnId = ?', whereArgs: [id]); return res; } + Future> queryWithConfigurationId(DatabaseTableType type, String id) async { + List sections = []; + Database db = await instance.database; + var res = await db.query(_getTableName(type), where: '$columnConfigurationId = ?', whereArgs: [id]); + res.forEach((element) { + sections.add(getSectionFromDB(element)); + }); + return sections; + } + Future delete(String id, DatabaseTableType type) async { Database db = await instance.database; return await db.delete(_getTableName(type), where: '$columnId = ?', whereArgs: [id]); diff --git a/lib/Models/visitContext.dart b/lib/Models/visitContext.dart index d89adba..fc61375 100644 --- a/lib/Models/visitContext.dart +++ b/lib/Models/visitContext.dart @@ -1,37 +1,30 @@ -import 'dart:convert'; - import 'package:flutter/material.dart'; import 'package:manager_api/api.dart'; class VisitAppContext with ChangeNotifier{ String? id = ""; - //String? token = ""; - //String? host = ""; String? language = ""; List? configurations; ConfigurationDTO? configuration; + List? sectionIds; // Use to valid QR code found VisitAppContext({this.language, this.id, this.configuration}); Map toMap() { return { 'id': id, - //'host': host, 'language': language, - //'configuration': configuration == null ? null : jsonEncode(configuration?.toJson()), }; } factory VisitAppContext.fromJson(Map json) { return VisitAppContext( id: json['id'] as String, - /*host: json['host'] as String,*/ language: json['language'] as String, configuration: json['configuration'] == null ? null : ConfigurationDTO.fromJson(json['configuration']), ); } - // Implement toString to make it easier to see information about @override String toString() { return 'VisitAppContext{id: $id, language: $language, configuration: $configuration}'; diff --git a/lib/Screens/Article/article.dart b/lib/Screens/Article/article.dart index 6d7eddd..2fcbba9 100644 --- a/lib/Screens/Article/article.dart +++ b/lib/Screens/Article/article.dart @@ -11,6 +11,7 @@ import 'package:mymuseum_visitapp/Helpers/DatabaseHelper.dart'; import 'package:mymuseum_visitapp/Helpers/networkCheck.dart'; import 'package:mymuseum_visitapp/Helpers/translationHelper.dart'; import 'package:mymuseum_visitapp/Models/resourceModel.dart'; +import 'package:mymuseum_visitapp/Models/visitContext.dart'; import 'package:mymuseum_visitapp/app_context.dart'; import 'package:mymuseum_visitapp/client.dart'; import 'package:mymuseum_visitapp/constants.dart'; @@ -36,6 +37,7 @@ class _ArticlePageState extends State { final appContext = Provider.of(context); Size size = MediaQuery.of(context).size; final notchInset = MediaQuery.of(context).padding; + return Scaffold( key: _scaffoldKey, appBar: CustomAppBar( @@ -215,7 +217,7 @@ class _ArticlePageState extends State { try { bool isOnline = await hasNetwork(); if(sectionDTO == null || articleDTO == null) { - await DatabaseHelper.instance.queryWithId(DatabaseTableType.sections, articleId).then((value) async { + await DatabaseHelper.instance.queryWithColumnId(DatabaseTableType.sections, articleId).then((value) async { setState(() { sectionDTO = DatabaseHelper.instance.getSectionFromDB(value.first); if(sectionDTO!.type == SectionType.Article) { @@ -223,13 +225,13 @@ class _ArticlePageState extends State { } }); if(sectionDTO!.imageId != null) { - await DatabaseHelper.instance.queryWithId(DatabaseTableType.resources, sectionDTO!.imageId!).then((value) { + await DatabaseHelper.instance.queryWithColumnId(DatabaseTableType.resources, sectionDTO!.imageId!).then((value) { resourcesModel.add(DatabaseHelper.instance.getResourceFromDB(value.first)); }); } if(articleDTO!.images!.isNotEmpty) { for (var image in articleDTO!.images!) { - await DatabaseHelper.instance.queryWithId(DatabaseTableType.resources, image.resourceId!).then((value) { + await DatabaseHelper.instance.queryWithColumnId(DatabaseTableType.resources, image.resourceId!).then((value) { resourcesModel.add(DatabaseHelper.instance.getResourceFromDB(value.first)); }); } diff --git a/lib/Screens/Home/home.dart b/lib/Screens/Home/home.dart index 16c7cf7..86aa2f2 100644 --- a/lib/Screens/Home/home.dart +++ b/lib/Screens/Home/home.dart @@ -18,7 +18,6 @@ import 'package:mymuseum_visitapp/app_context.dart'; import 'package:mymuseum_visitapp/client.dart'; import 'package:mymuseum_visitapp/constants.dart'; import 'package:provider/provider.dart'; -import 'package:flutter_gen/gen_l10n/app_localizations.dart'; class HomePage extends StatefulWidget { const HomePage({Key? key}) : super(key: key); @@ -30,12 +29,14 @@ class HomePage extends StatefulWidget { class _HomePageState extends State { List configurations = []; List alreadyDownloaded = []; + VisitAppContext? visitAppContext; @override Widget build(BuildContext context) { Size size = MediaQuery.of(context).size; final appContext = Provider.of(context); - VisitAppContext visitAppContext = appContext.getContext(); + visitAppContext = appContext.getContext(); + return Scaffold( appBar: CustomAppBar( title: TranslationHelper.getFromLocale("visitTitle", appContext), @@ -61,14 +62,14 @@ class _HomePageState extends State { //physics: NeverScrollableScrollPhysics(),//This prevents scrolling, but may inhibit refresh indicator, remove as you need itemBuilder: (BuildContext context, int index) { return InkWell( - onTap: () { - //print(configurations[index].label); - // Update context - VisitAppContext visitAppContext = appContext.getContext(); - visitAppContext.configuration = configurations[index]; - appContext.setContext(visitAppContext); - + onTap: () async { if(configurations[index].isOffline! && alreadyDownloaded.any((c) => c == configurations[index].id)) { + // Update context + VisitAppContext visitAppContext = appContext.getContext(); + visitAppContext.configuration = configurations[index]; + visitAppContext.sectionIds = List.from(await DatabaseHelper.instance.queryWithConfigurationId(DatabaseTableType.sections, visitAppContext.configuration!.id!)).map((e) => e.id).toList(); + appContext.setContext(visitAppContext); + Navigator.of(context).pushReplacement(MaterialPageRoute( builder: (context) => VisitPage(configurationId: configurations[index].id!), )); @@ -117,7 +118,6 @@ class _HomePageState extends State { ), ) ) - ], ), ), @@ -140,7 +140,7 @@ class _HomePageState extends State { ), ), ), - floatingActionButton: const ScannerBouton(isReplacement: false), + floatingActionButton: ScannerBouton(isReplacement: false, appContext: appContext!), //floatingActionButtonLocation: FloatingActionButtonLocation.miniCenterFloat, ); } @@ -212,7 +212,7 @@ class _HomePageState extends State { //return file; } else { - //return null; // TODO return local list.. + //return null; } } catch (e) { print(e); @@ -249,7 +249,6 @@ class _HomePageState extends State { newOrder = newOrder + 1; } } - } Map sectionToMap(SectionDTO section) { @@ -267,7 +266,6 @@ class _HomePageState extends State { 'data': section.data, 'dateCreation': section.dateCreation!.toUtc().toIso8601String(), 'orderOfElement': section.order, - //'token': token }; } } diff --git a/lib/Screens/Visit/components/section_card.dart b/lib/Screens/Visit/components/section_card.dart index 34f9c6b..90b6eea 100644 --- a/lib/Screens/Visit/components/section_card.dart +++ b/lib/Screens/Visit/components/section_card.dart @@ -32,7 +32,7 @@ class SectionCard extends StatelessWidget { ResourceModel? resourceModel; getResource(String? imageId) async { - await DatabaseHelper.instance.queryWithId(DatabaseTableType.resources, sectionDTO!.imageId!).then((value) { + await DatabaseHelper.instance.queryWithColumnId(DatabaseTableType.resources, sectionDTO!.imageId!).then((value) { resourceModel = DatabaseHelper.instance.getResourceFromDB(value.first); }); } diff --git a/lib/Screens/Visit/visit.dart b/lib/Screens/Visit/visit.dart index 1a7b204..7801378 100644 --- a/lib/Screens/Visit/visit.dart +++ b/lib/Screens/Visit/visit.dart @@ -34,7 +34,7 @@ class _VisitPageState extends State { ), backgroundColor: kBackgroundGrey, body: Body(configurationId: configuration!.id), // TODO handle error.. - floatingActionButton: const ScannerBouton(isReplacement: false), + floatingActionButton: ScannerBouton(isReplacement: false, appContext: appContext), ); } } \ No newline at end of file diff --git a/lib/translations.dart b/lib/translations.dart index 15c1cc9..5468639 100644 --- a/lib/translations.dart +++ b/lib/translations.dart @@ -4,21 +4,25 @@ List translations = [ Translation(language: "FR", data: { "visitTitle": "Liste des visites", "visitDownloadWarning": "Pour suivre cette visite, il faut d'abord la télécharger", - "noData": "Pas de données" + "noData": "Pas de données", + "invalidQRCode": "code QR invalide" }), Translation(language: "EN", data: { "visitTitle": "List of tours", "visitDownloadWarning": "To follow this tour, you must first download it", - "noData": "No data" + "noData": "No data", + "invalidQRCode": "Invalid QR code" }), Translation(language: "DE", data: { "visitTitle": "Liste der Touren", "visitDownloadWarning": "Um dieser Tour zu folgen, müssen Sie sie zuerst herunterladen", - "noData": "keine Daten" + "noData": "keine Daten", + "invalidQRCode": "Ungültiger QR-Code" }), Translation(language: "NL", data: { "visitTitle": "Lijst met rondleidingen", "visitDownloadWarning": "Om deze tour te volgen, moet je deze eerst downloaden", - "noData": "Geen gegevens" + "noData": "Geen gegevens", + "invalidQRCode": "Ongeldige QR-code" }), ]; \ No newline at end of file