diff --git a/lib/main.dart b/lib/main.dart index bf9da73..2fe1f09 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,7 +1,7 @@ import 'package:flutter/material.dart'; import 'package:smoke_cess_app/pages/main_page.dart'; -import 'package:smoke_cess_app/service/database_service.dart'; -import 'package:smoke_cess_app/service/notification_service.dart'; +import 'package:smoke_cess_app/services/database_service.dart'; +import 'package:smoke_cess_app/services/notification_service.dart'; import 'package:timezone/data/latest.dart' as tz; void main() { diff --git a/lib/models/settings.dart b/lib/models/settings.dart index 6691e91..0e7cf30 100644 --- a/lib/models/settings.dart +++ b/lib/models/settings.dart @@ -1,4 +1,4 @@ -import 'package:smoke_cess_app/service/json_service.dart'; +import 'package:smoke_cess_app/services/json_service.dart'; class Settings { final int group; diff --git a/lib/pages/main_page.dart b/lib/pages/main_page.dart index ca3c95d..2af4f13 100644 --- a/lib/pages/main_page.dart +++ b/lib/pages/main_page.dart @@ -4,7 +4,7 @@ import 'package:smoke_cess_app/pages/relapse_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/interval_page.dart'; -import 'package:smoke_cess_app/service/settings_service.dart'; +import 'package:smoke_cess_app/services/settings_service.dart'; import 'package:smoke_cess_app/widgets/missing_config_popup.dart'; class MyHomePage extends StatefulWidget { diff --git a/lib/pages/scanner_page.dart b/lib/pages/scanner_page.dart index c382990..7bded71 100644 --- a/lib/pages/scanner_page.dart +++ b/lib/pages/scanner_page.dart @@ -2,10 +2,10 @@ import 'package:flutter/material.dart'; import 'package:mobile_scanner/mobile_scanner.dart'; import 'package:smoke_cess_app/models/mood.dart'; import 'package:smoke_cess_app/models/settings.dart'; -import 'package:smoke_cess_app/service/database_service.dart'; -import 'package:smoke_cess_app/service/json_service.dart'; -import 'package:smoke_cess_app/service/settings_service.dart'; -import 'package:smoke_cess_app/service/notification_service.dart'; +import 'package:smoke_cess_app/services/database_service.dart'; +import 'package:smoke_cess_app/services/json_service.dart'; +import 'package:smoke_cess_app/services/settings_service.dart'; +import 'package:smoke_cess_app/services/notification_service.dart'; import '../models/sleep.dart'; import '../widgets/missing_config_popup.dart'; diff --git a/lib/services/database_service.dart b/lib/services/database_service.dart new file mode 100644 index 0000000..26973f4 --- /dev/null +++ b/lib/services/database_service.dart @@ -0,0 +1,108 @@ +import 'dart:async'; + +import 'package:path/path.dart'; +import 'package:smoke_cess_app/models/mood.dart'; +import 'package:sqflite/sqflite.dart'; +// ignore: depend_on_referenced_packages +import 'package:path_provider/path_provider.dart'; +import 'dart:io'; + +import '../models/sleep.dart'; + +class DatabaseService { + DatabaseService._privateConstructor(); + static final DatabaseService instance = DatabaseService._privateConstructor(); + + static Database? _database; + Future get database async => _database ??= await _initDatabase(); + + Future _initDatabase() async { + Directory documentsDirectory = await getApplicationDocumentsDirectory(); + String path = join(documentsDirectory.path, 'database.db'); + return await openDatabase(path, + version: 1, onCreate: _onCreate, onOpen: _createTablesIfNotExists); + } + + Future _onCreate(Database db, int version) async { + await _createTablesIfNotExists(db); + } + + Future _createTablesIfNotExists(Database db) async { + await db.execute(_createMoodTable); + await db.execute(_createSleepTable); + await db.execute(_createRelapseTable); + await db.execute(_createWorkoutTable); + } + + //TODO use generic function? + Future> getMoodRecords() async { + Database db = await instance.database; + var moodRecords = await db.query('mood'); + List moodList = moodRecords.isNotEmpty + ? moodRecords.map((e) => Mood.fromDatabase(e)).toList() + : []; + return moodList; + } + + Future> getSleepRecords() async { + Database db = await instance.database; + var sleepRecords = await db.query('sleep'); + List sleepList = sleepRecords.isNotEmpty + ? sleepRecords.map((e) => Sleep.fromDatabase(e)).toList() + : []; + return sleepList; + } + + Future addMood(Mood mood) async { + Database db = await instance.database; + return await db.insert('mood', mood.toMap()); + } + + Future addSleep(Sleep sleep) async { + Database db = await instance.database; + return await db.insert('sleep', sleep.toMap()); + } +} + +String _createMoodTable = ''' + CREATE TABLE IF NOT EXISTS mood( + id INTEGER PRIMARY KEY, + value INTEGER, + date TEXT, + comment TEXT + ) + '''; + +String _createSleepTable = ''' + CREATE TABLE IF NOT EXISTS sleep( + id INTEGER PRIMARY KEY, + value INTEGER, + date TEXT, + comment TEXT, + sleepedAtHour INTEGER, + sleepedAtMinute INTEGER, + wokeUpAtHour INTEGER, + wokeUpAtMinute INTEGER + ) + '''; + +String _createRelapseTable = ''' + CREATE TABLE IF NOT EXISTS relapse( + id INTEGER PRIMARY KEY, + date TEXT, + comment TEXT, + reason TEXT + ) + '''; + +String _createWorkoutTable = ''' + CREATE TABLE IF NOT EXISTS workout( + id INTEGER PRIMARY KEY, + date TEXT, + motivationBefore INTEGER, + commentBefore TEXT, + motivationAfter INTEGER, + commentAfter TEXT, + completed INTEGER + ) + '''; diff --git a/lib/services/date_service.dart b/lib/services/date_service.dart new file mode 100644 index 0000000..61e0567 --- /dev/null +++ b/lib/services/date_service.dart @@ -0,0 +1,63 @@ +import 'package:smoke_cess_app/services/settings_service.dart'; +import 'package:timezone/timezone.dart'; + +const int trainingTime = 40; + +const weekDays = { + "Montag": 1, + "Dienstag": 2, + "Mittwoch": 3, + "Donnerstag": 4, + "Freitag": 5, + "Samstag": 6, + "Sonntag": 7, +}; + +Future> getDatesforAll() async { + List allDates = []; + List moodDates = await getDatesforMood(); + List sleepDates = await getDatesforSleep(); + allDates.addAll(moodDates); + allDates.addAll(sleepDates); + return allDates; +} + +Future> getDatesforMood() async { + final List? selectedDays = await getMoodQueryDaysCategories(); + final int? selectedHours = await getMoodQueryHours(); + final int? selectedMinutes = await getMoodQueryMinutes(); + return createTZDateTimes(selectedDays, selectedHours, selectedMinutes); +} + +Future> getDatesforSleep() async { + final List? selectedDays = await getSleepQueryDaysCategories(); + final int? selectedHours = await getSleepQueryHours(); + final int? selectedMinutes = await getSleepQueryMinutes(); + return createTZDateTimes(selectedDays, selectedHours, selectedMinutes); +} + +List createTZDateTimes( + List? selectedDays, int? selectedHours, int? selectedMinutes) { + final List tzDateTimes = []; + final DateTime now = DateTime.now(); + final Duration offset = now.timeZoneOffset; + if (selectedDays == null || + selectedHours == null || + selectedMinutes == null) { + return tzDateTimes; + } + final Iterable selectedDaysInt = + selectedDays.map((day) => weekDays[day]); + for (int i = 0; i < trainingTime; i++) { + final DateTime date = DateTime(now.year, now.month, now.day, selectedHours, + selectedMinutes, 0, 0, 0) + .add(Duration(days: i)); + if (selectedDaysInt.contains(date.weekday) && + date.isAfter(DateTime.now())) { + tzDateTimes.add(TZDateTime.local(date.year, date.month, date.day, + selectedHours, selectedMinutes, 0, 0, 0) + .subtract(offset)); + } + } + return tzDateTimes; +} diff --git a/lib/services/notification_service.dart b/lib/services/notification_service.dart new file mode 100644 index 0000000..ea65122 --- /dev/null +++ b/lib/services/notification_service.dart @@ -0,0 +1,105 @@ +import 'package:flutter_local_notifications/flutter_local_notifications.dart'; +import 'package:smoke_cess_app/services/date_service.dart'; +import 'package:timezone/timezone.dart'; + +class NotificationService { + static final NotificationService _notificationService = + NotificationService._internal(); + + factory NotificationService() { + return _notificationService; + } + + final FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin = + FlutterLocalNotificationsPlugin(); + + NotificationService._internal(); + + Future initNotification() async { + // Android initialization + const AndroidInitializationSettings initializationSettingsAndroid = + AndroidInitializationSettings('mipmap/ic_launcher'); + + // ios initialization + const DarwinInitializationSettings initializationSettingsIOS = + DarwinInitializationSettings( + requestAlertPermission: false, + requestBadgePermission: false, + requestSoundPermission: false, + ); + + const InitializationSettings initializationSettings = + InitializationSettings( + android: initializationSettingsAndroid, + iOS: initializationSettingsIOS); + // the initialization settings are initialized after they are setted + await flutterLocalNotificationsPlugin.initialize(initializationSettings); + } + + Future showNotification() async { + await flutterLocalNotificationsPlugin.show( + 0, + 'test', + 'test', +//schedule the notification to show after 2 seconds. + const NotificationDetails( + // Android details + android: AndroidNotificationDetails('main_channel', 'Main Channel', + channelDescription: "ashwin", + importance: Importance.max, + priority: Priority.max), + // iOS details + iOS: DarwinNotificationDetails( + sound: 'default.wav', + presentAlert: true, + presentBadge: true, + presentSound: true, + ), + ), + ); + } + + Future setAllNotifications() async { + List moodDates = await getDatesforMood(); + List sleepDates = await getDatesforSleep(); + int index = 0; + for (var date in moodDates) { + setNotification(index, "Mood", "Evaluate your mood", date); + index++; + } + for (var date in sleepDates) { + setNotification(index, "Sleep", "Evaluate your sleep", date); + index++; + } + } + + Future setNotification( + int id, String title, String body, TZDateTime tzDateTime) async { + await flutterLocalNotificationsPlugin.zonedSchedule( + id, + title, + body, + tzDateTime, //schedule the notification to show after 2 seconds. + const NotificationDetails( + // Android details + android: AndroidNotificationDetails('main_channel', 'Main Channel', + channelDescription: "ashwin", + importance: Importance.max, + priority: Priority.max), + // iOS details + iOS: DarwinNotificationDetails( + sound: 'default.wav', + presentAlert: true, + presentBadge: true, + presentSound: true, + ), + ), + + // Type of time interpretation + uiLocalNotificationDateInterpretation: + UILocalNotificationDateInterpretation.absoluteTime, + androidAllowWhileIdle: + true, // To show notification even when the app is closed + ); + } +} diff --git a/lib/services/settings_service.dart b/lib/services/settings_service.dart new file mode 100644 index 0000000..52028bd --- /dev/null +++ b/lib/services/settings_service.dart @@ -0,0 +1,67 @@ +import 'package:shared_preferences/shared_preferences.dart'; +import 'package:smoke_cess_app/models/settings.dart'; +import 'package:smoke_cess_app/services/json_service.dart'; + +//access group setting which was saved in local storage +Future getGroup() => _getIntSetting('group'); + +Future?> getRelapseCategories() => + _getStringListSetting('relapse_categories'); + +Future?> getSleepQueryDaysCategories() => + _getStringListSetting('sleep_query_days'); + +Future getSleepQueryHours() => _getIntSetting('sleep_query_hours'); +Future getSleepQueryMinutes() => _getIntSetting('sleep_query_minutes'); + +Future?> getMoodQueryDaysCategories() => + _getStringListSetting('mood_query_days'); + +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/mood_form.dart b/lib/widgets/mood_form.dart index f6fd0e0..c859d3f 100644 --- a/lib/widgets/mood_form.dart +++ b/lib/widgets/mood_form.dart @@ -1,6 +1,6 @@ import 'package:flutter/material.dart'; import 'package:smoke_cess_app/models/mood.dart'; -import 'package:smoke_cess_app/service/database_service.dart'; +import 'package:smoke_cess_app/services/database_service.dart'; import 'package:smoke_cess_app/widgets/slider.dart'; import 'package:smoke_cess_app/widgets/submit_form_button.dart'; import 'package:smoke_cess_app/widgets/text_formfield.dart'; diff --git a/lib/widgets/sleep_form.dart b/lib/widgets/sleep_form.dart index eb86e89..a36318e 100644 --- a/lib/widgets/sleep_form.dart +++ b/lib/widgets/sleep_form.dart @@ -1,6 +1,6 @@ import 'package:flutter/material.dart'; import 'package:smoke_cess_app/models/sleep.dart'; -import 'package:smoke_cess_app/service/database_service.dart'; +import 'package:smoke_cess_app/services/database_service.dart'; import 'package:smoke_cess_app/widgets/elevated_card.dart'; import 'package:smoke_cess_app/widgets/slider.dart'; import 'package:smoke_cess_app/widgets/submit_form_button.dart';