diff --git a/android/app/build.gradle b/android/app/build.gradle index d8c9ecd..4fef84e 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -26,7 +26,7 @@ apply plugin: 'kotlin-android' apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" android { - compileSdkVersion flutter.compileSdkVersion + compileSdkVersion 33 ndkVersion flutter.ndkVersion compileOptions { @@ -47,7 +47,7 @@ android { applicationId "com.example.smoke_cess_app" // You can update the following values to match your application needs. // For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-build-configuration. - minSdkVersion flutter.minSdkVersion + minSdkVersion 21 targetSdkVersion flutter.targetSdkVersion versionCode flutterVersionCode.toInteger() versionName flutterVersionName diff --git a/assets/group1.json b/assets/group1.json new file mode 100644 index 0000000..4e2de76 --- /dev/null +++ b/assets/group1.json @@ -0,0 +1,15 @@ +{ + "group": 1, + "HITT_time": 35, + "relapse_categories": ["App stresst mich", "langeweile", "lunge braucht es"], + "mood_query": { + "days": ["Montag", "Freitag"], + "hours": 8, + "minutes": 30 + }, + "sleep_query": { + "days": ["Dienstag", "Samstag"], + "hours": 9, + "minutes": 30 + } +} diff --git a/assets/group3.json b/assets/group3.json new file mode 100644 index 0000000..623ffc8 --- /dev/null +++ b/assets/group3.json @@ -0,0 +1,19 @@ +{ + "group": 3, + "HITT_time": 35, + "chess_time": { + "hours": 8, + "minutes": 30 + }, + "relapse_categories": ["App stresst mich", "langeweile", "lunge braucht es"], + "mood_query": { + "days": ["Montag", "Freitag"], + "hours": 10, + "minutes": 30 + }, + "sleep_query": { + "days": ["Dienstag", "Samstag"], + "hours": 11, + "minutes": 30 + } +} diff --git a/ios/Runner/Info.plist b/ios/Runner/Info.plist index 097c0f1..8285717 100644 --- a/ios/Runner/Info.plist +++ b/ios/Runner/Info.plist @@ -2,6 +2,8 @@ + NSCameraUsageDescription + This app needs camera access to scan QR codes CFBundleDevelopmentRegion $(DEVELOPMENT_LANGUAGE) CFBundleDisplayName diff --git a/lib/main.dart b/lib/main.dart index bc91015..6e5d7a5 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -10,6 +10,6 @@ class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { - return const MaterialApp(title: _title, home: MyHomePage()); + return MaterialApp(title: _title, home: MyHomePage()); } } diff --git a/lib/models/settings.dart b/lib/models/settings.dart new file mode 100644 index 0000000..6691e91 --- /dev/null +++ b/lib/models/settings.dart @@ -0,0 +1,45 @@ +import 'package:smoke_cess_app/service/json_service.dart'; + +class Settings { + final int group; + final List? relapseCategories; + final QueryConfig moodQuery; + final QueryConfig sleepQuery; + final TimeConfig? chessTime; + + Settings(this.group, this.relapseCategories, this.moodQuery, this.sleepQuery, + this.chessTime); + + Settings.fromJson(Map json) + : group = json['group'] as int, + relapseCategories = jsonPropertyAsList(json['relapse_categories']), + moodQuery = QueryConfig.fromJson(json['mood_query']), + sleepQuery = QueryConfig.fromJson(json['sleep_query']), + chessTime = json['chess_time'] != null + ? TimeConfig.fromJson(json['chess_time']) + : null; +} + +class QueryConfig { + final int hours; + final int minutes; + final List? days; + + QueryConfig(this.hours, this.minutes, this.days); + + QueryConfig.fromJson(Map json) + : hours = json['hours'] as int, + minutes = json['minutes'] as int, + days = jsonPropertyAsList(json['days']); +} + +class TimeConfig { + final int hours; + final int minutes; + + TimeConfig(this.hours, this.minutes); + + TimeConfig.fromJson(Map json) + : hours = json['hours'] as int, + minutes = json['minutes'] as int; +} diff --git a/lib/pages/main_page.dart b/lib/pages/main_page.dart index df8c8c9..904a4dd 100644 --- a/lib/pages/main_page.dart +++ b/lib/pages/main_page.dart @@ -1,9 +1,11 @@ import 'package:flutter/material.dart'; import 'package:smoke_cess_app/pages/mood_page.dart'; import 'package:smoke_cess_app/pages/relapse_page.dart'; -import 'package:smoke_cess_app/pages/settings_page.dart'; +import 'package:smoke_cess_app/pages/scanner_page.dart'; import 'package:smoke_cess_app/pages/sleep_page.dart'; import 'package:smoke_cess_app/pages/timer_page.dart'; +import 'package:smoke_cess_app/service/settings_service.dart'; +import 'package:smoke_cess_app/widgets/missing_config_popup.dart'; class MyHomePage extends StatefulWidget { const MyHomePage({super.key}); @@ -13,30 +15,47 @@ class MyHomePage extends StatefulWidget { } class MyHomePageState extends State { - int _selectedIndex = 2; + int _selectedIndex = 4; + int? _gruppe; + final List _titles = [ 'Stimmung', 'Schlaf', 'Timer', 'Rückfall', - 'Einstellungen' + 'Scanner' ]; static const List _widgetOptions = [ MoodPage(), SleepPage(), IntervalTimerPage(), RelapsePage(), - SettingsPage(), + ScannerPage(), ]; - void _onItemTapped(int index) { - setState(() => _selectedIndex = index); + Future _onItemTapped(int index) async { + _gruppe = await getGroup(); + bool isConfigured = _gruppe != null; + setState(() { + isConfigured + ? _selectedIndex = index + : showDialog( + context: context, + builder: (BuildContext context) { + return const MissingConfigPopup( + title: 'Fehlende Konfiguration', + text: 'Bitte QR Code Scannen!', + ); + }); + }); } @override Widget build(BuildContext context) { return Scaffold( - appBar: AppBar(title: Text(_titles[_selectedIndex])), + appBar: AppBar( + title: Text( + '${_titles[_selectedIndex]} ${_gruppe != null ? "Gruppe $_gruppe" : ""}')), body: _widgetOptions.elementAt(_selectedIndex), bottomNavigationBar: NavigationBar( onDestinationSelected: _onItemTapped, @@ -58,7 +77,7 @@ class MyHomePageState extends State { icon: Icon(Icons.smoke_free_outlined, color: Colors.black), label: 'Rückfall'), NavigationDestination( - icon: Icon(Icons.settings, color: Colors.black), + icon: Icon(Icons.camera_alt_outlined, color: Colors.black), label: 'Settings'), ], diff --git a/lib/pages/scanner_page.dart b/lib/pages/scanner_page.dart new file mode 100644 index 0000000..fcd2c30 --- /dev/null +++ b/lib/pages/scanner_page.dart @@ -0,0 +1,76 @@ +import 'package:flutter/material.dart'; +import 'package:mobile_scanner/mobile_scanner.dart'; +import 'package:smoke_cess_app/models/settings.dart'; +import 'package:smoke_cess_app/service/json_service.dart'; +import 'package:smoke_cess_app/service/settings_service.dart'; + +import '../widgets/missing_config_popup.dart'; + +class ScannerPage extends StatefulWidget { + const ScannerPage({super.key}); + + @override + State createState() => ScannerPageState(); +} + +class ScannerPageState extends State { + bool scanning = false; + + @override + Widget build(BuildContext context) { + return Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + scanning + ? Expanded( + child: MobileScanner( + fit: BoxFit.contain, + controller: MobileScannerController( + detectionTimeoutMs: 2000, + ), + onDetect: (capture) { + //TODO Errorhandling!! + final List barcodes = capture.barcodes; + for (final barcode in barcodes) { + if (barcode.rawValue != null) { + String qrText = barcode.rawValue!; + Map json = stringToJSON(qrText); + Settings settings = Settings.fromJson(json); + saveSettings(settings); + setState(() { + scanning = false; + showDialog( + context: context, + builder: (BuildContext context) { + return MissingConfigPopup( + title: 'Konfiguration erfolgreich', + text: 'Du gehörst zu Gruppe ${settings.group}', + ); + }); + }); + } + } + }, + )) + : ElevatedButton( + style: ElevatedButton.styleFrom( + textStyle: const TextStyle(fontSize: 20)), + onPressed: () { + setState(() => scanning = true); + }, + child: const Text('Scan QR Code'), + ), + const SizedBox(height: 30), + ElevatedButton( + style: ElevatedButton.styleFrom( + textStyle: const TextStyle(fontSize: 20)), + onPressed: () { + loadSettingsFromLocalJSON(); + }, + child: const Text('Read JSON'), + ) + ], + )); + } +} diff --git a/lib/pages/settings_page.dart b/lib/pages/settings_page.dart deleted file mode 100644 index 899b163..0000000 --- a/lib/pages/settings_page.dart +++ /dev/null @@ -1,10 +0,0 @@ -import 'package:flutter/material.dart'; - -class SettingsPage extends StatelessWidget { - const SettingsPage({super.key}); - - @override - Widget build(BuildContext context) { - return const Center(child: Text('Hier können Settings eingestellt werden')); - } -} diff --git a/lib/service/json_service.dart b/lib/service/json_service.dart new file mode 100644 index 0000000..6d09e9f --- /dev/null +++ b/lib/service/json_service.dart @@ -0,0 +1,14 @@ +import 'package:flutter/services.dart'; +import 'dart:convert'; + +const String configJSONPath = 'assets/group3.json'; + +Future> loadLocalConfigJSON() async { + String content = await rootBundle.loadString(configJSONPath); + return jsonDecode(content); +} + +List? jsonPropertyAsList(dynamic property) => + property != null ? List.from(property) : null; + +Map stringToJSON(String jsonString) => jsonDecode(jsonString); diff --git a/lib/service/settings_service.dart b/lib/service/settings_service.dart index 8923fb2..dca2839 100644 --- a/lib/service/settings_service.dart +++ b/lib/service/settings_service.dart @@ -1,21 +1,67 @@ import 'package:shared_preferences/shared_preferences.dart'; +import 'package:smoke_cess_app/models/settings.dart'; +import 'package:smoke_cess_app/service/json_service.dart'; -class SettingsService { - final Future _prefs = SharedPreferences.getInstance(); +//access group setting which was saved in local storage +Future getGroup() => _getIntSetting('group'); - void setStringSetting(String settingKey, String settingValue) => - _prefs.then((pref) => pref.setString(settingKey, settingValue)); +Future?> getRelapseCategories() => + _getStringListSetting('relapse_categories'); - Future getStringSetting(String settingKey) => - _prefs.then((pref) => pref.getString(settingKey)); +Future?> getSleepQueryDaysCategories() => + _getStringListSetting('sleep_query_days'); - void setIntSetting(String settingKey, int settingValue) => - _prefs.then((pref) => pref.setInt(settingKey, settingValue)); +Future getSleepQueryHours() => _getIntSetting('sleep_query_hours'); +Future getSleepQueryMinutes() => _getIntSetting('sleep_query_minutes'); - Future getIntSetting(String settingKey) => - _prefs.then((pref) => pref.getInt(settingKey)); +Future?> getMoodQueryDaysCategories() => + _getStringListSetting('mood_query_days'); - //Add other setters and getters if needed - //other possible SharedPreferences Types: Int, Bool, Double, StringList - //see https://pub.dev/packages/shared_preferences +Future getMoodQueryHours() => _getIntSetting('mood_query_hours'); +Future getMoodQueryMinutes() => _getIntSetting('mood_query_minutes'); + +Future getChessHours() => _getIntSetting('chess_hours'); +Future getChessMinutes() => _getIntSetting('chess_minutes'); + +void _setStringSetting(String settingKey, String settingValue) => + SharedPreferences.getInstance() + .then((pref) => pref.setString(settingKey, settingValue)); + +Future _getStringSetting(String settingKey) => + SharedPreferences.getInstance().then((pref) => pref.getString(settingKey)); + +void _setIntSetting(String settingKey, int settingValue) => + SharedPreferences.getInstance() + .then((pref) => pref.setInt(settingKey, settingValue)); + +Future _getIntSetting(String settingKey) => + SharedPreferences.getInstance().then((pref) => pref.getInt(settingKey)); + +void _setStringListSetting(String settingKey, List list) => + SharedPreferences.getInstance() + .then((pref) => pref.setStringList(settingKey, list)); + +Future?> _getStringListSetting(String settingKey) => + SharedPreferences.getInstance() + .then((pref) => pref.getStringList(settingKey)); + +Future loadSettingsFromLocalJSON() async { + Map configJSON = await loadLocalConfigJSON(); + Settings settings = Settings.fromJson(configJSON); + saveSettings(settings); +} + +void saveSettings(Settings settings) { + _setIntSetting('group', settings.group); + _setStringListSetting('relapse_categories', settings.relapseCategories!); + _setStringListSetting('mood_query_days', settings.moodQuery.days!); + _setIntSetting('mood_query_hours', settings.moodQuery.hours); + _setIntSetting('mood_query_minutes', settings.moodQuery.minutes); + _setStringListSetting('sleep_query_days', settings.sleepQuery.days!); + _setIntSetting('sleep_query_hours', settings.sleepQuery.hours); + _setIntSetting('sleep_query_minutes', settings.sleepQuery.minutes); + if (settings.chessTime != null) { + _setIntSetting('chess_hours', settings.chessTime!.hours); + _setIntSetting('chess_minutes', settings.chessTime!.minutes); + } } diff --git a/lib/widgets/missing_config_popup.dart b/lib/widgets/missing_config_popup.dart new file mode 100644 index 0000000..620774e --- /dev/null +++ b/lib/widgets/missing_config_popup.dart @@ -0,0 +1,22 @@ +import 'package:flutter/material.dart'; + +class MissingConfigPopup extends StatelessWidget { + final String title; + final String text; + const MissingConfigPopup( + {super.key, required this.title, required this.text}); + + @override + Widget build(BuildContext context) { + return AlertDialog( + title: Text(title), + content: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text(text), + ], + ), + ); + } +} diff --git a/pubspec.lock b/pubspec.lock index 3eb0e38..7de71b1 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -215,7 +215,14 @@ packages: sha256: "12307e7f0605ce3da64cf0db90e5fcab0869f3ca03f76be6bb2991ce0a55e82b" url: "https://pub.dev" source: hosted - version: "1.9.0" + version: "1.8.0" + mobile_scanner: + dependency: "direct main" + description: + name: mobile_scanner + url: "https://pub.dartlang.org" + source: hosted + version: "3.0.0" path: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index c25a5a7..3f78ee1 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -37,6 +37,8 @@ dependencies: cupertino_icons: ^1.0.2 shared_preferences: ^2.0.17 audioplayers: ^3.0.1 + mobile_scanner: ^3.0.0 + dev_dependencies: flutter_test: @@ -67,6 +69,8 @@ flutter: - warmUp.mp3 - cool_down.mp3 - finish.mp3 + - group1.json + - group3.json # - images/a_dot_burr.jpeg # - images/a_dot_ham.jpeg diff --git a/web/index.html b/web/index.html index 277f035..366d37f 100644 --- a/web/index.html +++ b/web/index.html @@ -1,7 +1,7 @@ - - - + - - - + + + - - - - - + + + + + - - + + - smoke_cess_app - + smoke_cess_app + - - - - - - + + + + + - + + +