Merge branch 'Timer-with-interval' of https://gitlab.com/Crondung/hsma_cpd into Timer-with-interval

main
Parricc35 2023-02-20 21:28:41 +01:00
commit 6978c40656
15 changed files with 335 additions and 72 deletions

View File

@ -26,7 +26,7 @@ apply plugin: 'kotlin-android'
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
android { android {
compileSdkVersion flutter.compileSdkVersion compileSdkVersion 33
ndkVersion flutter.ndkVersion ndkVersion flutter.ndkVersion
compileOptions { compileOptions {
@ -47,7 +47,7 @@ android {
applicationId "com.example.smoke_cess_app" applicationId "com.example.smoke_cess_app"
// You can update the following values to match your application needs. // 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. // For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-build-configuration.
minSdkVersion flutter.minSdkVersion minSdkVersion 21
targetSdkVersion flutter.targetSdkVersion targetSdkVersion flutter.targetSdkVersion
versionCode flutterVersionCode.toInteger() versionCode flutterVersionCode.toInteger()
versionName flutterVersionName versionName flutterVersionName

15
assets/group1.json 100644
View File

@ -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
}
}

19
assets/group3.json 100644
View File

@ -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
}
}

View File

@ -2,6 +2,8 @@
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0"> <plist version="1.0">
<dict> <dict>
<key>NSCameraUsageDescription</key>
<string>This app needs camera access to scan QR codes</string>
<key>CFBundleDevelopmentRegion</key> <key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string> <string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleDisplayName</key> <key>CFBundleDisplayName</key>

View File

@ -10,6 +10,6 @@ class MyApp extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return const MaterialApp(title: _title, home: MyHomePage()); return MaterialApp(title: _title, home: MyHomePage());
} }
} }

View File

@ -0,0 +1,45 @@
import 'package:smoke_cess_app/service/json_service.dart';
class Settings {
final int group;
final List<String>? relapseCategories;
final QueryConfig moodQuery;
final QueryConfig sleepQuery;
final TimeConfig? chessTime;
Settings(this.group, this.relapseCategories, this.moodQuery, this.sleepQuery,
this.chessTime);
Settings.fromJson(Map<String, dynamic> 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<String>? days;
QueryConfig(this.hours, this.minutes, this.days);
QueryConfig.fromJson(Map<String, dynamic> 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<String, dynamic> json)
: hours = json['hours'] as int,
minutes = json['minutes'] as int;
}

View File

@ -1,9 +1,11 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:smoke_cess_app/pages/mood_page.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/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/sleep_page.dart';
import 'package:smoke_cess_app/pages/timer_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 { class MyHomePage extends StatefulWidget {
const MyHomePage({super.key}); const MyHomePage({super.key});
@ -13,30 +15,47 @@ class MyHomePage extends StatefulWidget {
} }
class MyHomePageState extends State<MyHomePage> { class MyHomePageState extends State<MyHomePage> {
int _selectedIndex = 2; int _selectedIndex = 4;
int? _gruppe;
final List<String> _titles = [ final List<String> _titles = [
'Stimmung', 'Stimmung',
'Schlaf', 'Schlaf',
'Timer', 'Timer',
'Rückfall', 'Rückfall',
'Einstellungen' 'Scanner'
]; ];
static const List<Widget> _widgetOptions = <Widget>[ static const List<Widget> _widgetOptions = <Widget>[
MoodPage(), MoodPage(),
SleepPage(), SleepPage(),
IntervalTimerPage(), IntervalTimerPage(),
RelapsePage(), RelapsePage(),
SettingsPage(), ScannerPage(),
]; ];
void _onItemTapped(int index) { Future<void> _onItemTapped(int index) async {
setState(() => _selectedIndex = index); _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 @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(
appBar: AppBar(title: Text(_titles[_selectedIndex])), appBar: AppBar(
title: Text(
'${_titles[_selectedIndex]} ${_gruppe != null ? "Gruppe $_gruppe" : ""}')),
body: _widgetOptions.elementAt(_selectedIndex), body: _widgetOptions.elementAt(_selectedIndex),
bottomNavigationBar: NavigationBar( bottomNavigationBar: NavigationBar(
onDestinationSelected: _onItemTapped, onDestinationSelected: _onItemTapped,
@ -58,7 +77,7 @@ class MyHomePageState extends State<MyHomePage> {
icon: Icon(Icons.smoke_free_outlined, color: Colors.black), icon: Icon(Icons.smoke_free_outlined, color: Colors.black),
label: 'Rückfall'), label: 'Rückfall'),
NavigationDestination( NavigationDestination(
icon: Icon(Icons.settings, color: Colors.black), icon: Icon(Icons.camera_alt_outlined, color: Colors.black),
label: 'Settings'), label: 'Settings'),
], ],

View File

@ -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<StatefulWidget> createState() => ScannerPageState();
}
class ScannerPageState extends State<ScannerPage> {
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<Barcode> barcodes = capture.barcodes;
for (final barcode in barcodes) {
if (barcode.rawValue != null) {
String qrText = barcode.rawValue!;
Map<String, dynamic> 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'),
)
],
));
}
}

View File

@ -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'));
}
}

View File

@ -0,0 +1,14 @@
import 'package:flutter/services.dart';
import 'dart:convert';
const String configJSONPath = 'assets/group3.json';
Future<Map<String, dynamic>> loadLocalConfigJSON() async {
String content = await rootBundle.loadString(configJSONPath);
return jsonDecode(content);
}
List<String>? jsonPropertyAsList(dynamic property) =>
property != null ? List.from(property) : null;
Map<String, dynamic> stringToJSON(String jsonString) => jsonDecode(jsonString);

View File

@ -1,21 +1,67 @@
import 'package:shared_preferences/shared_preferences.dart'; 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 { //access group setting which was saved in local storage
final Future<SharedPreferences> _prefs = SharedPreferences.getInstance(); Future<int?> getGroup() => _getIntSetting('group');
void setStringSetting(String settingKey, String settingValue) => Future<List<String>?> getRelapseCategories() =>
_prefs.then((pref) => pref.setString(settingKey, settingValue)); _getStringListSetting('relapse_categories');
Future<String?> getStringSetting(String settingKey) => Future<List<String>?> getSleepQueryDaysCategories() =>
_prefs.then((pref) => pref.getString(settingKey)); _getStringListSetting('sleep_query_days');
void setIntSetting(String settingKey, int settingValue) => Future<int?> getSleepQueryHours() => _getIntSetting('sleep_query_hours');
_prefs.then((pref) => pref.setInt(settingKey, settingValue)); Future<int?> getSleepQueryMinutes() => _getIntSetting('sleep_query_minutes');
Future<int?> getIntSetting(String settingKey) => Future<List<String>?> getMoodQueryDaysCategories() =>
_prefs.then((pref) => pref.getInt(settingKey)); _getStringListSetting('mood_query_days');
//Add other setters and getters if needed Future<int?> getMoodQueryHours() => _getIntSetting('mood_query_hours');
//other possible SharedPreferences Types: Int, Bool, Double, StringList Future<int?> getMoodQueryMinutes() => _getIntSetting('mood_query_minutes');
//see https://pub.dev/packages/shared_preferences
Future<int?> getChessHours() => _getIntSetting('chess_hours');
Future<int?> getChessMinutes() => _getIntSetting('chess_minutes');
void _setStringSetting(String settingKey, String settingValue) =>
SharedPreferences.getInstance()
.then((pref) => pref.setString(settingKey, settingValue));
Future<String?> _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<int?> _getIntSetting(String settingKey) =>
SharedPreferences.getInstance().then((pref) => pref.getInt(settingKey));
void _setStringListSetting(String settingKey, List<String> list) =>
SharedPreferences.getInstance()
.then((pref) => pref.setStringList(settingKey, list));
Future<List<String>?> _getStringListSetting(String settingKey) =>
SharedPreferences.getInstance()
.then((pref) => pref.getStringList(settingKey));
Future<void> loadSettingsFromLocalJSON() async {
Map<String, dynamic> 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);
}
} }

View File

@ -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: <Widget>[
Text(text),
],
),
);
}
}

View File

@ -215,7 +215,14 @@ packages:
sha256: "12307e7f0605ce3da64cf0db90e5fcab0869f3ca03f76be6bb2991ce0a55e82b" sha256: "12307e7f0605ce3da64cf0db90e5fcab0869f3ca03f76be6bb2991ce0a55e82b"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted 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: path:
dependency: transitive dependency: transitive
description: description:

View File

@ -37,6 +37,8 @@ dependencies:
cupertino_icons: ^1.0.2 cupertino_icons: ^1.0.2
shared_preferences: ^2.0.17 shared_preferences: ^2.0.17
audioplayers: ^3.0.1 audioplayers: ^3.0.1
mobile_scanner: ^3.0.0
dev_dependencies: dev_dependencies:
flutter_test: flutter_test:
@ -67,6 +69,8 @@ flutter:
- warmUp.mp3 - warmUp.mp3
- cool_down.mp3 - cool_down.mp3
- finish.mp3 - finish.mp3
- group1.json
- group3.json
# - images/a_dot_burr.jpeg # - images/a_dot_burr.jpeg
# - images/a_dot_ham.jpeg # - images/a_dot_ham.jpeg

View File

@ -14,23 +14,23 @@
This is a placeholder for base href that will be replaced by the value of This is a placeholder for base href that will be replaced by the value of
the `--base-href` argument provided to `flutter build`. the `--base-href` argument provided to `flutter build`.
--> -->
<base href="$FLUTTER_BASE_HREF"> <base href="$FLUTTER_BASE_HREF" />
<meta charset="UTF-8"> <meta charset="UTF-8" />
<meta content="IE=Edge" http-equiv="X-UA-Compatible"> <meta content="IE=Edge" http-equiv="X-UA-Compatible" />
<meta name="description" content="A new Flutter project."> <meta name="description" content="A new Flutter project." />
<!-- iOS meta tags & icons --> <!-- iOS meta tags & icons -->
<meta name="apple-mobile-web-app-capable" content="yes"> <meta name="apple-mobile-web-app-capable" content="yes" />
<meta name="apple-mobile-web-app-status-bar-style" content="black"> <meta name="apple-mobile-web-app-status-bar-style" content="black" />
<meta name="apple-mobile-web-app-title" content="smoke_cess_app"> <meta name="apple-mobile-web-app-title" content="smoke_cess_app" />
<link rel="apple-touch-icon" href="icons/Icon-192.png"> <link rel="apple-touch-icon" href="icons/Icon-192.png" />
<!-- Favicon --> <!-- Favicon -->
<link rel="icon" type="image/png" href="favicon.png" /> <link rel="icon" type="image/png" href="favicon.png" />
<title>smoke_cess_app</title> <title>smoke_cess_app</title>
<link rel="manifest" href="manifest.json"> <link rel="manifest" href="manifest.json" />
<script> <script>
// The value below is injected by flutter build, do not touch. // The value below is injected by flutter build, do not touch.
@ -43,16 +43,20 @@
<script> <script>
window.addEventListener('load', function (ev) { window.addEventListener('load', function (ev) {
// Download main.dart.js // Download main.dart.js
_flutter.loader.loadEntrypoint({ _flutter.loader
.loadEntrypoint({
serviceWorker: { serviceWorker: {
serviceWorkerVersion: serviceWorkerVersion, serviceWorkerVersion: serviceWorkerVersion,
} },
}).then(function(engineInitializer) { })
.then(function (engineInitializer) {
return engineInitializer.initializeEngine(); return engineInitializer.initializeEngine();
}).then(function(appRunner) { })
.then(function (appRunner) {
return appRunner.runApp(); return appRunner.runApp();
}); });
}); });
</script> </script>
<script src="https://cdn.jsdelivr.net/npm/jsqr@1.4.0/dist/jsQR.min.js"></script>
</body> </body>
</html> </html>