diff --git a/.idea/libraries/Dart_Packages.xml b/.idea/libraries/Dart_Packages.xml
new file mode 100644
index 0000000..86624d6
--- /dev/null
+++ b/.idea/libraries/Dart_Packages.xml
@@ -0,0 +1,1286 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/libraries/Dart_SDK.xml b/.idea/libraries/Dart_SDK.xml
new file mode 100644
index 0000000..563d1c4
--- /dev/null
+++ b/.idea/libraries/Dart_SDK.xml
@@ -0,0 +1,27 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
new file mode 100644
index 0000000..de791dd
--- /dev/null
+++ b/.idea/misc.xml
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/filcnaplo/lib/database/init.dart b/filcnaplo/lib/database/init.dart
index 7c29324..b256cdc 100644
--- a/filcnaplo/lib/database/init.dart
+++ b/filcnaplo/lib/database/init.dart
@@ -20,7 +20,7 @@ const settingsDB = DatabaseStruct("settings", {
"grade_color4": int, "grade_color5": int, // grade colors
"vibration_strength": int, "ab_weeks": int, "swap_ab_weeks": int,
"notifications": int, "notifications_bitfield": int,
- "notification_poll_interval": int, // notifications
+ "notification_poll_interval": int, "notifications_grades":int, "notifications_absences":int, "notifications_messages": int, "notifications_lessons":int, // notifications
"x_filc_id": String, "graph_class_avg": int, "presentation_mode": int,
"bell_delay": int, "bell_delay_enabled": int,
"grade_opening_fun": int, "icon_pack": String, "premium_scopes": String,
diff --git a/filcnaplo/lib/helpers/notification_helper.dart b/filcnaplo/lib/helpers/notification_helper.dart
index 568bb62..7ee3a39 100644
--- a/filcnaplo/lib/helpers/notification_helper.dart
+++ b/filcnaplo/lib/helpers/notification_helper.dart
@@ -1,35 +1,90 @@
-import 'dart:math';
import 'dart:ui';
import 'package:filcnaplo/api/providers/database_provider.dart';
import 'package:filcnaplo/api/providers/status_provider.dart';
import 'package:filcnaplo/api/providers/user_provider.dart';
+import 'package:filcnaplo/database/init.dart';
import 'package:filcnaplo/models/settings.dart';
import 'package:filcnaplo/helpers/notification_helper.i18n.dart';
+import 'package:filcnaplo_kreta_api/client/api.dart';
import 'package:filcnaplo_kreta_api/client/client.dart';
+import 'package:filcnaplo_kreta_api/models/absence.dart';
import 'package:filcnaplo_kreta_api/models/grade.dart';
+import 'package:filcnaplo_kreta_api/models/lesson.dart';
+import 'package:filcnaplo_kreta_api/models/week.dart';
import 'package:filcnaplo_kreta_api/providers/grade_provider.dart';
-import 'package:flutter_local_notifications/flutter_local_notifications.dart';
+import 'package:filcnaplo_kreta_api/providers/timetable_provider.dart';
+import 'package:flutter_local_notifications/flutter_local_notifications.dart' hide Message;
+import 'package:i18n_extension/i18n_widget.dart';
+import 'package:intl/intl.dart';
+import 'package:filcnaplo_kreta_api/models/message.dart';
class NotificationsHelper {
+ late DatabaseProvider database;
+ late SettingsProvider settingsProvider;
+ late UserProvider userProvider;
+ late KretaClient kretaClient;
+ FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin = FlutterLocalNotificationsPlugin();
+
+ List combineLists(List list1, List list2, K Function(T) keyExtractor,) {
+ Set uniqueKeys = Set();
+ List combinedList = [];
+
+ for (T item in list1) {
+ K key = keyExtractor(item);
+ if (!uniqueKeys.contains(key)) {
+ uniqueKeys.add(key);
+ combinedList.add(item);
+ }
+ }
+
+ for (T item in list2) {
+ K key = keyExtractor(item);
+ if (!uniqueKeys.contains(key)) {
+ uniqueKeys.add(key);
+ combinedList.add(item);
+ }
+ }
+
+ return combinedList;
+ }
+ String dayTitle(DateTime date) {
+ try {
+ return DateFormat("EEEE", I18n.locale.languageCode)
+ .format(date);
+ } catch (e) {
+ return "Unknown";
+ }
+ }
+
@pragma('vm:entry-point')
void backgroundJob() async {
// initialize providers
FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin =
FlutterLocalNotificationsPlugin();
- DatabaseProvider database = DatabaseProvider();
+ database = DatabaseProvider();
+ var db = await initDB(database);
await database.init();
- SettingsProvider settingsProvider =
+ settingsProvider =
await database.query.getSettings(database);
- UserProvider userProvider = await database.query.getUsers(settingsProvider);
+ userProvider = await database.query.getUsers(settingsProvider);
if (userProvider.id != null && settingsProvider.notificationsEnabled) {
- // refresh grades
+ // refresh kreta login
final status = StatusProvider();
- final kretaClient = KretaClient(
+ kretaClient = KretaClient(
user: userProvider, settings: settingsProvider, status: status);
kretaClient.refreshLogin();
- GradeProvider gradeProvider = GradeProvider(
+ if(settingsProvider.notificationsGradesEnabled) gradeNotification();
+ if(settingsProvider.notificationsAbsencesEnabled) absenceNotification();
+ if(settingsProvider.notificationsMessagesEnabled) messageNotification();
+ if(settingsProvider.notificationsLessonsEnabled) lessonNotification();
+ }
+ }
+
+ void gradeNotification() async {
+ // fetch grades
+ GradeProvider gradeProvider = GradeProvider(
settings: settingsProvider,
user: userProvider,
database: database,
@@ -45,39 +100,425 @@ class NotificationsHelper {
// if grade is not a normal grade (1-5), don't show it
if ([1, 2, 3, 4, 5].contains(grade.value.value)) {
// if the grade was added over a week ago, don't show it to avoid notification spam
- if (grade.seenDate.isAfter(lastSeenGrade) &&
- grade.date.difference(DateTime.now()).inDays * -1 < 7) {
+ if (grade.seenDate.isAfter(lastSeenGrade) && grade.date.difference(DateTime.now()).inDays * -1 < 7) {
// send notificiation about new grade
const AndroidNotificationDetails androidNotificationDetails =
- AndroidNotificationDetails(
- 'GRADES',
- 'Jegyek',
- channelDescription: 'Értesítés jegyek beírásakor',
- importance: Importance.max,
- priority: Priority.max,
- color: Color(0xFF3D7BF4),
- ticker: 'Jegyek',
- groupKey: 'refilc.notifications.GRADES_GROUP',
- );
- const NotificationDetails notificationDetails =
- NotificationDetails(android: androidNotificationDetails);
- await flutterLocalNotificationsPlugin.show(
- // probably shouldn't use a random int
- Random().nextInt(432234 * 2),
- "title".i18n,
- "body".i18n.fill([
- grade.value.value.toString(),
- grade.subject.isRenamed &&
- settingsProvider.renamedSubjectsEnabled
- ? grade.subject.renamedTo!
- : grade.subject.name
- ]),
- notificationDetails);
- }
- }
+ AndroidNotificationDetails('GRADES', 'Jegyek',
+ channelDescription: 'Értesítés jegyek beírásakor',
+ importance: Importance.max,
+ priority: Priority.max,
+ color: const Color(0xFF3D7BF4),
+ ticker: 'Jegyek');
+ const NotificationDetails notificationDetails = NotificationDetails(android: androidNotificationDetails);
+ if(userProvider.getUsers().length == 1) {
+ await flutterLocalNotificationsPlugin.show(
+ grade.id.hashCode,
+ "title_grade".i18n,
+ "body_grade".i18n.fill([
+ grade.value.value.toString(),
+ grade.subject.isRenamed &&
+ settingsProvider.renamedSubjectsEnabled
+ ? grade.subject.renamedTo!
+ : grade.subject.name
+ ]),
+ notificationDetails);
+ } else { // multiple users are added, also display student name
+ await flutterLocalNotificationsPlugin.show(
+ grade.id.hashCode,
+ "title_grade".i18n,
+ "body_grade_multiuser".i18n.fill([
+ userProvider.displayName!,
+ grade.value.value.toString(),
+ grade.subject.isRenamed &&
+ settingsProvider.renamedSubjectsEnabled
+ ? grade.subject.renamedTo!
+ : grade.subject.name
+ ]),
+ notificationDetails);
+ }
+ }
+ }
}
// set grade seen status
gradeProvider.seenAll();
+ }
+ void absenceNotification() async {
+ // get absences from api
+ List? absenceJson = await kretaClient.getAPI(KretaAPI.absences(userProvider.instituteCode ?? ""));
+ List storedAbsences = await database.userQuery.getAbsences(userId: userProvider.id!);
+ if(absenceJson == null) {
+ return;
+ }
+ // format api absences to correct format while preserving isSeen value
+ List absences = absenceJson.map((e) {
+ Absence apiAbsence = Absence.fromJson(e);
+ Absence storedAbsence = storedAbsences.firstWhere(
+ (stored) => stored.id == apiAbsence.id,
+ orElse: () => apiAbsence);
+ apiAbsence.isSeen = storedAbsence.isSeen;
+ return apiAbsence;
+ }).toList();
+ List modifiedAbsences = [];
+ if(absences != storedAbsences) {
+ // remove absences that are not new
+ absences.removeWhere((element) => storedAbsences.contains(element));
+ for(Absence absence in absences) {
+ if(!absence.isSeen) {
+ absence.isSeen = true;
+ modifiedAbsences.add(absence);
+ const AndroidNotificationDetails androidNotificationDetails =
+ AndroidNotificationDetails('ABSENCES', 'Hiányzások',
+ channelDescription: 'Értesítés hiányzások beírásakor',
+ importance: Importance.max,
+ priority: Priority.max,
+ color: const Color(0xFF3D7BF4),
+ ticker: 'Hiányzások');
+ const NotificationDetails notificationDetails = NotificationDetails(android: androidNotificationDetails);
+ if(userProvider.getUsers().length == 1) {
+ await flutterLocalNotificationsPlugin.show(
+ absence.id.hashCode,
+ "title_absence".i18n,
+ "body_absence".i18n.fill([
+ DateFormat("yyyy-MM-dd").format(absence.date),
+ absence.subject.isRenamed &&
+ settingsProvider.renamedSubjectsEnabled
+ ? absence.subject.renamedTo!
+ : absence.subject.name
+ ]),
+ notificationDetails);
+ } else {
+ await flutterLocalNotificationsPlugin.show(
+ absence.id.hashCode,
+ "title_absence".i18n,
+ "body_absence_multiuser".i18n.fill([
+ userProvider.displayName!,
+ DateFormat("yyyy-MM-dd").format(absence.date),
+ absence.subject.isRenamed &&
+ settingsProvider.renamedSubjectsEnabled
+ ? absence.subject.renamedTo!
+ : absence.subject.name
+ ]),
+ notificationDetails);
+ }
+ }
+ }
+ }
+ // combine modified absences and storedabsences list and save them to the database
+ List combinedAbsences = combineLists(
+ modifiedAbsences,
+ storedAbsences,
+ (Absence absence) => absence.id,
+ );
+ await database.userStore.storeAbsences(combinedAbsences, userId: userProvider.id!);
+ }
+
+ void messageNotification() async {
+ // get messages from api
+ List? messageJson = await kretaClient.getAPI(KretaAPI.messages("beerkezett"));
+ List storedmessages = await database.userQuery.getMessages(userId: userProvider.id!);
+ if(messageJson == null) {
+ return;
+ }
+ // format api messages to correct format while preserving isSeen value
+ // Parse messages
+ List messages = [];
+ await Future.wait(List.generate(messageJson.length, (index) {
+ return () async {
+ Map message = messageJson!.cast