From 721d76dac0af7e851e0b28494d7ce2c57d3d3c3d Mon Sep 17 00:00:00 2001 From: Crondung <1922635@stud.hs-mannheim.de> Date: Mon, 6 Mar 2023 13:48:15 +0100 Subject: [PATCH 1/7] use awesome dialog for taskdone popup --- lib/utils/timer_util.dart | 4 +- lib/widgets/popup/popup_for_task_done.dart | 68 +++++++++++----------- 2 files changed, 38 insertions(+), 34 deletions(-) diff --git a/lib/utils/timer_util.dart b/lib/utils/timer_util.dart index 42a9180..57b9f9c 100644 --- a/lib/utils/timer_util.dart +++ b/lib/utils/timer_util.dart @@ -8,7 +8,9 @@ String formatTime(int seconds) { String hours = twoDigits(duration.inHours.remainder(24)); String minutes = twoDigits(duration.inMinutes.remainder(60)); String formattedSeconds = twoDigits(duration.inSeconds.remainder(60)); - if (duration.inDays != 0) formattedTime += '$days:'; + if (duration.inDays != 0) { + formattedTime += '$days Tag ${duration.inDays > 1 ? "e" : ""}, '; + } if (duration.inHours != 0) formattedTime += '$hours:'; formattedTime += '$minutes:'; formattedTime += formattedSeconds; diff --git a/lib/widgets/popup/popup_for_task_done.dart b/lib/widgets/popup/popup_for_task_done.dart index cf7f7ed..4565aca 100644 --- a/lib/widgets/popup/popup_for_task_done.dart +++ b/lib/widgets/popup/popup_for_task_done.dart @@ -1,43 +1,45 @@ +import 'package:awesome_dialog/awesome_dialog.dart'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; -import 'package:smoke_cess_app/providers/timer_provider.dart'; -import 'package:smoke_cess_app/widgets/timer_widget.dart'; import 'package:smoke_cess_app/services/date_service.dart'; import 'package:smoke_cess_app/services/pages_service.dart'; +import 'package:smoke_cess_app/widgets/timer_widget.dart'; + +import '../../providers/timer_provider.dart'; void showTaskDonePopup(BuildContext context, Pages page) async { Duration duration = await getTimeTill(page); if (context.mounted) { - await showDialog( - context: context, - builder: (BuildContext context) { - return ChangeNotifierProvider( - create: (context) => TimerProvider(), - child: TaskDonePopup( - duration: duration, - ), - ); - }, - ); - } -} - -class TaskDonePopup extends StatelessWidget { - final Duration duration; - const TaskDonePopup({super.key, required this.duration}); - - @override - Widget build(BuildContext context) { - TimerProvider timerProvider = context.read(); - timerProvider.startTimer(duration); - return AlertDialog( - title: const Text('Schon gemacht'), - content: Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - const Text('Nächstes mal wieder:'), - TimerWidget(duration: duration) - ])); + AwesomeDialog( + context: context, + dialogType: DialogType.info, + body: Column( + children: [ + Text( + '${pages[page]?['title']} schon eingegeben', + style: const TextStyle(fontWeight: FontWeight.bold, fontSize: 16), + ), + const SizedBox( + height: 10, + ), + const Text( + 'Nächste Abfrage in', + ), + const SizedBox( + height: 8, + ), + ChangeNotifierProvider( + create: (context) => TimerProvider(), + builder: (context, child) { + TimerProvider timerProvider = context.read(); + timerProvider.startTimer(duration); + return TimerWidget(duration: duration); + }, + ), + const SizedBox( + height: 15, + ), + ], + )).show(); } } From d910e3780f57984cd5564f2e610913caf28df206 Mon Sep 17 00:00:00 2001 From: Crondung <1922635@stud.hs-mannheim.de> Date: Mon, 6 Mar 2023 14:25:38 +0100 Subject: [PATCH 2/7] use provider for scanner --- lib/pages/scanner_page.dart | 2 +- lib/providers/settings_provider.dart | 7 ++ lib/widgets/buttons/round_button_widget.dart | 4 +- lib/widgets/scanner.dart | 77 +++++++++----------- lib/widgets/view_form/view_form_page.dart | 2 +- 5 files changed, 44 insertions(+), 48 deletions(-) diff --git a/lib/pages/scanner_page.dart b/lib/pages/scanner_page.dart index ebba252..14b944c 100644 --- a/lib/pages/scanner_page.dart +++ b/lib/pages/scanner_page.dart @@ -16,7 +16,7 @@ class ScannerPage extends StatelessWidget { } void loadJSON(BuildContext context) async { - var settingsModel = context.read(); + SettingsProvider settingsModel = context.read(); await loadSettingsFromLocalJSON(); settingsModel.initSettings(); NotificationService().setAllNotifications(); diff --git a/lib/providers/settings_provider.dart b/lib/providers/settings_provider.dart index f51c5e1..121ff0d 100644 --- a/lib/providers/settings_provider.dart +++ b/lib/providers/settings_provider.dart @@ -6,9 +6,16 @@ import '../models/settings.dart'; class SettingsProvider extends ChangeNotifier { Settings? _settings; bool _initialized = false; + bool _scanning = false; Settings? get settings => _settings; bool get initialized => _initialized; + bool get scanning => _scanning; + + set scanning(bool value) { + _scanning = value; + notifyListeners(); + } SettingsProvider() { initSettings(); diff --git a/lib/widgets/buttons/round_button_widget.dart b/lib/widgets/buttons/round_button_widget.dart index 14efe2d..bf31238 100644 --- a/lib/widgets/buttons/round_button_widget.dart +++ b/lib/widgets/buttons/round_button_widget.dart @@ -1,10 +1,10 @@ import 'package:flutter/material.dart'; -class RoundAddButton extends StatelessWidget { +class RoundButton extends StatelessWidget { final VoidCallback onPressed; final IconData iconData; - const RoundAddButton( + const RoundButton( {super.key, required this.onPressed, required this.iconData}); @override diff --git a/lib/widgets/scanner.dart b/lib/widgets/scanner.dart index b635d67..a4740b5 100644 --- a/lib/widgets/scanner.dart +++ b/lib/widgets/scanner.dart @@ -7,64 +7,57 @@ import 'package:smoke_cess_app/services/json_service.dart'; import 'package:smoke_cess_app/services/settings_service.dart'; import '../providers/settings_provider.dart'; import '../services/notification_service.dart'; +import 'buttons/round_button_widget.dart'; -class MyScanner extends StatefulWidget { +class MyScanner extends StatelessWidget { const MyScanner({super.key}); @override - State createState() => MyScannerState(); -} + Widget build(BuildContext context) { + SettingsProvider settingsProvider = context.watch(); -class MyScannerState extends State { - bool scanning = false; + void handleSucces(String? rawValue) { + String qrText = rawValue!; + Map json = stringToJSON(qrText); + Settings settings = Settings.fromJson(json); + saveSettings(settings); + settingsProvider.initSettings(); + NotificationService().setAllNotifications(); + settingsProvider.scanning = false; - void handleSucces(String? rawValue) { - String qrText = rawValue!; - Map json = stringToJSON(qrText); - Settings settings = Settings.fromJson(json); - saveSettings(settings); - var settingsModel = context.read(); - settingsModel.initSettings(); - NotificationService().setAllNotifications(); - setState(() { - scanning = false; AwesomeDialog( context: context, dialogType: DialogType.success, title: 'Geschafft', desc: 'Der Code wurde erfolgreich gescannt!', ).show(); - }); - } + } + + void handleError() { + settingsProvider.scanning = false; - void handleError() { - setState(() { - scanning = false; AwesomeDialog( context: context, dialogType: DialogType.error, title: 'Fehler', desc: 'Der QR-Code war fehlerhaft!', ).show(); - }); - } - - void onDetect(capture) { - try { - final List barcodes = capture.barcodes; - for (final barcode in barcodes) { - if (barcode.rawValue != null) { - return handleSucces(barcode.rawValue); - } - } - } catch (e) { - handleError(); } - } - @override - Widget build(BuildContext context) { - return scanning + void onDetect(capture) { + try { + final List barcodes = capture.barcodes; + for (final barcode in barcodes) { + if (barcode.rawValue != null) { + return handleSucces(barcode.rawValue); + } + } + } catch (e) { + handleError(); + } + } + + return settingsProvider.scanning ? Expanded( child: MobileScanner( fit: BoxFit.contain, @@ -72,13 +65,9 @@ class MyScannerState extends State { detectionTimeoutMs: 2000, ), onDetect: onDetect)) - : ElevatedButton( - style: ElevatedButton.styleFrom( - textStyle: const TextStyle(fontSize: 20)), - onPressed: () { - setState(() => scanning = true); - }, - child: const Text('Scan QR Code'), + : RoundButton( + onPressed: () => settingsProvider.scanning = true, + iconData: Icons.qr_code_scanner_outlined, ); } } diff --git a/lib/widgets/view_form/view_form_page.dart b/lib/widgets/view_form/view_form_page.dart index 0b348b6..ffa0d98 100644 --- a/lib/widgets/view_form/view_form_page.dart +++ b/lib/widgets/view_form/view_form_page.dart @@ -32,7 +32,7 @@ class ViewFormPage extends StatelessWidget { if (!pageProvider.showForm) Container( margin: EdgeInsets.symmetric(vertical: height * 0.02), - child: RoundAddButton( + child: RoundButton( iconData: Icons.add_outlined, onPressed: tasksProvider.tasks[page] ?? true ? () => pageProvider.swap() From 3ea43d019b2a631618d1089e7118044bb41a6862 Mon Sep 17 00:00:00 2001 From: Crondung <1922635@stud.hs-mannheim.de> Date: Mon, 6 Mar 2023 14:56:10 +0100 Subject: [PATCH 3/7] update build gradle to use media query in android --- android/app/build.gradle | 2 ++ 1 file changed, 2 insertions(+) diff --git a/android/app/build.gradle b/android/app/build.gradle index af69fb2..24b12bc 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -70,5 +70,7 @@ flutter { dependencies { implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" + implementation 'androidx.window:window:1.0.0' + implementation 'androidx.window:window-java:1.0.0' coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.5' } From a99fed7fa2049e4a8b9d30da0e33d2ac50cd096f Mon Sep 17 00:00:00 2001 From: Crondung <1922635@stud.hs-mannheim.de> Date: Mon, 6 Mar 2023 15:22:02 +0100 Subject: [PATCH 4/7] add scanfield --- lib/widgets/scanner.dart | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/lib/widgets/scanner.dart b/lib/widgets/scanner.dart index a4740b5..a30f1e1 100644 --- a/lib/widgets/scanner.dart +++ b/lib/widgets/scanner.dart @@ -59,12 +59,24 @@ class MyScanner extends StatelessWidget { return settingsProvider.scanning ? Expanded( - child: MobileScanner( + child: Stack( + alignment: Alignment.center, + children: [ + MobileScanner( fit: BoxFit.contain, controller: MobileScannerController( detectionTimeoutMs: 2000, ), - onDetect: onDetect)) + onDetect: onDetect, + ), + ClipRRect( + borderRadius: BorderRadius.circular(20), + child: Container( + height: MediaQuery.of(context).size.height / 3, + width: MediaQuery.of(context).size.width * 0.8, + color: Colors.white.withOpacity(0.4))), + ], + )) : RoundButton( onPressed: () => settingsProvider.scanning = true, iconData: Icons.qr_code_scanner_outlined, From 6d53158b64e2a1aebf7c8c66c0719a4c38bad5e5 Mon Sep 17 00:00:00 2001 From: Crondung <1922635@stud.hs-mannheim.de> Date: Mon, 6 Mar 2023 15:54:13 +0100 Subject: [PATCH 5/7] use texticonbutton for scan and export, use global bool to enable local json config --- lib/globals.dart | 5 +++- lib/pages/scanner_page.dart | 30 +++++++++++++---------- lib/widgets/buttons/text_icon_button.dart | 29 ++++++++++++++++++++++ lib/widgets/scanner.dart | 7 +++--- 4 files changed, 54 insertions(+), 17 deletions(-) create mode 100644 lib/widgets/buttons/text_icon_button.dart diff --git a/lib/globals.dart b/lib/globals.dart index 58229d9..3cdf823 100644 --- a/lib/globals.dart +++ b/lib/globals.dart @@ -3,5 +3,8 @@ library app.globals; import 'package:smoke_cess_app/mock/db_mock.dart'; import 'package:smoke_cess_app/services/database_service.dart'; -DatabaseService databaseService = DatabaseMock(); +DatabaseService databaseService = DatabaseMock(); // DatabaseService databaseService = DatabaseService.instance; + +// set this to read settings from local json file instead of scanning a qr code +bool useLocalConfig = false; diff --git a/lib/pages/scanner_page.dart b/lib/pages/scanner_page.dart index 14b944c..7ba67d4 100644 --- a/lib/pages/scanner_page.dart +++ b/lib/pages/scanner_page.dart @@ -4,8 +4,11 @@ import 'package:provider/provider.dart'; import 'package:smoke_cess_app/services/export_service.dart'; import 'package:smoke_cess_app/services/settings_service.dart'; import 'package:smoke_cess_app/services/notification_service.dart'; +import 'package:smoke_cess_app/widgets/buttons/round_button_widget.dart'; +import 'package:smoke_cess_app/widgets/buttons/text_icon_button.dart'; import 'package:smoke_cess_app/widgets/scanner.dart'; import '../providers/settings_provider.dart'; +import '../globals.dart' as globals; class ScannerPage extends StatelessWidget { const ScannerPage({super.key}); @@ -32,25 +35,26 @@ class ScannerPage extends StatelessWidget { @override Widget build(BuildContext context) { + SettingsProvider settingsProvider = context.watch(); + return Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ const MyScanner(), const SizedBox(height: 30), - ElevatedButton( - style: ElevatedButton.styleFrom( - textStyle: const TextStyle(fontSize: 20)), - onPressed: () => loadJSON(context), - child: const Text('Read JSON'), - ), - const SizedBox(height: 30), - ElevatedButton( - style: ElevatedButton.styleFrom( - textStyle: const TextStyle(fontSize: 20)), - onPressed: export, - child: const Text('Export'), - ) + if (!settingsProvider.scanning) + TextIconButton( + text: 'Export', + onPressed: ExportService().exportData, + iconData: Icons.upload), + if (globals.useLocalConfig && !settingsProvider.scanning) + ElevatedButton( + style: ElevatedButton.styleFrom( + textStyle: const TextStyle(fontSize: 20)), + onPressed: () => loadJSON(context), + child: const Text('Read JSON'), + ), ], )); } diff --git a/lib/widgets/buttons/text_icon_button.dart b/lib/widgets/buttons/text_icon_button.dart new file mode 100644 index 0000000..85534e2 --- /dev/null +++ b/lib/widgets/buttons/text_icon_button.dart @@ -0,0 +1,29 @@ +import 'package:flutter/material.dart'; + +class TextIconButton extends StatelessWidget { + final String text; + final VoidCallback onPressed; + final IconData iconData; + + const TextIconButton( + {super.key, + required this.text, + required this.onPressed, + required this.iconData}); + + @override + Widget build(BuildContext context) { + return SizedBox( + width: MediaQuery.of(context).size.width * 0.4, + child: FloatingActionButton.extended( + label: Text(text), + backgroundColor: Theme.of(context).colorScheme.primary, + icon: Icon( + iconData, + size: 24.0, + ), + onPressed: onPressed, + ), + ); + } +} diff --git a/lib/widgets/scanner.dart b/lib/widgets/scanner.dart index a30f1e1..5d45bbe 100644 --- a/lib/widgets/scanner.dart +++ b/lib/widgets/scanner.dart @@ -5,6 +5,7 @@ import 'package:provider/provider.dart'; import 'package:smoke_cess_app/models/settings.dart'; import 'package:smoke_cess_app/services/json_service.dart'; import 'package:smoke_cess_app/services/settings_service.dart'; +import 'package:smoke_cess_app/widgets/buttons/text_icon_button.dart'; import '../providers/settings_provider.dart'; import '../services/notification_service.dart'; import 'buttons/round_button_widget.dart'; @@ -77,9 +78,9 @@ class MyScanner extends StatelessWidget { color: Colors.white.withOpacity(0.4))), ], )) - : RoundButton( + : TextIconButton( + text: "Scan", onPressed: () => settingsProvider.scanning = true, - iconData: Icons.qr_code_scanner_outlined, - ); + iconData: Icons.qr_code_scanner_outlined); } } From 8ee2218ff9223a7a758915c9d1258b267df9568c Mon Sep 17 00:00:00 2001 From: Crondung <1922635@stud.hs-mannheim.de> Date: Mon, 6 Mar 2023 16:02:07 +0100 Subject: [PATCH 6/7] update readme --- README.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/README.md b/README.md index 274732f..ee7623a 100644 --- a/README.md +++ b/README.md @@ -14,3 +14,13 @@ Die App lässt sich als Android- und iOS App ausführen und zu Debugzwecken eben ## App bedienen Um die App nutzen zu können, müssen Settings eingelesen werden. Dazu kann entweder ein in die App integrierter QR-Code Scanner (mit dem beigefügten [QR-Code](./gruppe1_QR.png)) benutzt werden, oder zu Debugzwecken mit dem dafür vorgesehenen Button auf der Scannerseite über eine lokale JSON-Datei. +Es ist zu beachten, dass das verwendete QR-Code Scanner Package für Mobilgeräte optimiert ist. Deshalb kann es zu Abstürzen beim Verwenden des Scanners mit der Web-Version kommen. +Beim Verwenden der Web-Version sollten die Settings mit der lokalen JSON-Datei eingelesen werden. +Der Button zum Einlesen der lokalen JSON-Datei kann über die Variable bool useLocalConfig in der Datei [globals.dart](./lib/globals.dart) ein- und ausgeblendet werden. + +## Authoren + +- Hinrik Ehrenfried 2012537 +- Patrick Meßmer 1911768 +- Kai Mannweiler 2012491 +- Julian Gegner 1922635 From 80bc5985e78f8c84ebb97923e8cf86f4e0c73068 Mon Sep 17 00:00:00 2001 From: Crondung <1922635@stud.hs-mannheim.de> Date: Mon, 6 Mar 2023 16:05:20 +0100 Subject: [PATCH 7/7] remove unused imports --- lib/pages/scanner_page.dart | 1 - lib/widgets/scanner.dart | 1 - 2 files changed, 2 deletions(-) diff --git a/lib/pages/scanner_page.dart b/lib/pages/scanner_page.dart index 7ba67d4..eee7dd0 100644 --- a/lib/pages/scanner_page.dart +++ b/lib/pages/scanner_page.dart @@ -4,7 +4,6 @@ import 'package:provider/provider.dart'; import 'package:smoke_cess_app/services/export_service.dart'; import 'package:smoke_cess_app/services/settings_service.dart'; import 'package:smoke_cess_app/services/notification_service.dart'; -import 'package:smoke_cess_app/widgets/buttons/round_button_widget.dart'; import 'package:smoke_cess_app/widgets/buttons/text_icon_button.dart'; import 'package:smoke_cess_app/widgets/scanner.dart'; import '../providers/settings_provider.dart'; diff --git a/lib/widgets/scanner.dart b/lib/widgets/scanner.dart index 5d45bbe..c292bf4 100644 --- a/lib/widgets/scanner.dart +++ b/lib/widgets/scanner.dart @@ -8,7 +8,6 @@ import 'package:smoke_cess_app/services/settings_service.dart'; import 'package:smoke_cess_app/widgets/buttons/text_icon_button.dart'; import '../providers/settings_provider.dart'; import '../services/notification_service.dart'; -import 'buttons/round_button_widget.dart'; class MyScanner extends StatelessWidget { const MyScanner({super.key});