diff --git a/.idea/libraries/Dart_Packages.xml b/.idea/libraries/Dart_Packages.xml
index 1503a2d..326de58 100644
--- a/.idea/libraries/Dart_Packages.xml
+++ b/.idea/libraries/Dart_Packages.xml
@@ -9,6 +9,13 @@
+
+
+
+
+
+
+
@@ -215,7 +222,7 @@
-
+
@@ -282,6 +289,13 @@
+
+
+
+
+
+
+
@@ -380,6 +394,62 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -436,6 +506,27 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -478,6 +569,13 @@
+
+
+
+
+
+
+
@@ -600,7 +698,7 @@
-
+
@@ -705,14 +803,14 @@
-
+
-
+
@@ -1006,7 +1104,7 @@
-
+
@@ -1132,7 +1230,7 @@
-
+
@@ -1248,6 +1346,13 @@
+
+
+
+
+
+
+
@@ -1297,6 +1402,7 @@
+
@@ -1326,7 +1432,7 @@
-
+
@@ -1336,6 +1442,7 @@
+
@@ -1349,15 +1456,27 @@
+
+
+
+
+
+
+
+
+
+
+
+
@@ -1375,7 +1494,7 @@
-
+
@@ -1390,8 +1509,8 @@
-
-
+
+
@@ -1431,7 +1550,7 @@
-
+
@@ -1449,7 +1568,7 @@
-
+
@@ -1466,6 +1585,7 @@
+
diff --git a/.idea/libraries/Flutter_Plugins.xml b/.idea/libraries/Flutter_Plugins.xml
index e848caa..e5ebdb8 100644
--- a/.idea/libraries/Flutter_Plugins.xml
+++ b/.idea/libraries/Flutter_Plugins.xml
@@ -1,24 +1,18 @@
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
@@ -26,19 +20,32 @@
+
+
+
+
+
+
+
+
+
+
+
+
-
-
+
+
+
diff --git a/android/app/build.gradle b/android/app/build.gradle
index 361edbd..0c2fde7 100644
--- a/android/app/build.gradle
+++ b/android/app/build.gradle
@@ -23,6 +23,7 @@ if (flutterVersionName == null) {
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
+apply plugin: 'com.google.gms.google-services'
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
android {
@@ -39,7 +40,7 @@ android {
applicationId "eu.mhsl.marianum.mobile.client"
// 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 19
targetSdkVersion flutter.targetSdkVersion
versionCode flutterVersionCode.toInteger()
versionName flutterVersionName
diff --git a/android/app/google-services.json b/android/app/google-services.json
new file mode 100644
index 0000000..adab10f
--- /dev/null
+++ b/android/app/google-services.json
@@ -0,0 +1,39 @@
+{
+ "project_info": {
+ "project_number": "522850592536",
+ "project_id": "marmobile-33b10",
+ "storage_bucket": "marmobile-33b10.appspot.com"
+ },
+ "client": [
+ {
+ "client_info": {
+ "mobilesdk_app_id": "1:522850592536:android:9a355c61e6f1b0f0c2606d",
+ "android_client_info": {
+ "package_name": "eu.mhsl.marianum.mobile.client"
+ }
+ },
+ "oauth_client": [
+ {
+ "client_id": "522850592536-5urolovocke0fmr7kpd0hqvfd3gft6qo.apps.googleusercontent.com",
+ "client_type": 3
+ }
+ ],
+ "api_key": [
+ {
+ "current_key": "AIzaSyAXo66A3jSBxnAYKgpUIfucidELoHw5W3M"
+ }
+ ],
+ "services": {
+ "appinvite_service": {
+ "other_platform_oauth_client": [
+ {
+ "client_id": "522850592536-5urolovocke0fmr7kpd0hqvfd3gft6qo.apps.googleusercontent.com",
+ "client_type": 3
+ }
+ ]
+ }
+ }
+ }
+ ],
+ "configuration_version": "1"
+}
\ No newline at end of file
diff --git a/android/app/src/main/java/io/flutter/app/FlutterMultiDexApplication.java b/android/app/src/main/java/io/flutter/app/FlutterMultiDexApplication.java
new file mode 100644
index 0000000..752fc18
--- /dev/null
+++ b/android/app/src/main/java/io/flutter/app/FlutterMultiDexApplication.java
@@ -0,0 +1,25 @@
+// Generated file.
+//
+// If you wish to remove Flutter's multidex support, delete this entire file.
+//
+// Modifications to this file should be done in a copy under a different name
+// as this file may be regenerated.
+
+package io.flutter.app;
+
+import android.app.Application;
+import android.content.Context;
+import androidx.annotation.CallSuper;
+import androidx.multidex.MultiDex;
+
+/**
+ * Extension of {@link android.app.Application}, adding multidex support.
+ */
+public class FlutterMultiDexApplication extends Application {
+ @Override
+ @CallSuper
+ protected void attachBaseContext(Context base) {
+ super.attachBaseContext(base);
+ MultiDex.install(this);
+ }
+}
diff --git a/android/build.gradle b/android/build.gradle
index 3efffaf..db9efaf 100644
--- a/android/build.gradle
+++ b/android/build.gradle
@@ -8,6 +8,7 @@ buildscript {
dependencies {
classpath 'com.android.tools.build:gradle:7.1.3'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
+ classpath 'com.google.gms:google-services:4.3.15'
}
}
diff --git a/client.iml b/client.iml
index fa043b4..2fa4042 100644
--- a/client.iml
+++ b/client.iml
@@ -3,6 +3,7 @@
+
diff --git a/lib/api/mhsl/mhslApi.dart b/lib/api/mhsl/mhslApi.dart
index fa9ec02..64f6270 100644
--- a/lib/api/mhsl/mhslApi.dart
+++ b/lib/api/mhsl/mhslApi.dart
@@ -1,4 +1,5 @@
import 'dart:convert';
+import 'dart:developer';
import 'package:http/http.dart' as http;
import '../apiError.dart';
@@ -21,6 +22,10 @@ abstract class MhslApi extends ApiRequest {
throw ApiError("Request could not be dispatched!");
}
+ if(data.statusCode > 299) {
+ log("Non 200 Status code from mhsl services: $subpath: ${data.statusCode}");
+ }
+
return assemble(utf8.decode(data.bodyBytes));
}
}
\ No newline at end of file
diff --git a/lib/api/mhsl/notify/register/notifyRegister.dart b/lib/api/mhsl/notify/register/notifyRegister.dart
new file mode 100644
index 0000000..c614520
--- /dev/null
+++ b/lib/api/mhsl/notify/register/notifyRegister.dart
@@ -0,0 +1,26 @@
+
+import 'dart:convert';
+import 'dart:developer';
+
+import 'package:http/http.dart' as http;
+
+import '../../mhslApi.dart';
+import 'notifyRegisterParams.dart';
+
+class NotifyRegister extends MhslApi {
+ NotifyRegisterParams params;
+ NotifyRegister(this.params) : super("notify/register/");
+
+
+ @override
+ void assemble(String raw) {
+
+ }
+
+ @override
+ Future request(Uri uri) {
+ String requestString = jsonEncode(params.toJson());
+ log(requestString);
+ return http.post(uri, body: requestString);
+ }
+}
\ No newline at end of file
diff --git a/lib/api/mhsl/notify/register/notifyRegisterParams.dart b/lib/api/mhsl/notify/register/notifyRegisterParams.dart
new file mode 100644
index 0000000..75c7a95
--- /dev/null
+++ b/lib/api/mhsl/notify/register/notifyRegisterParams.dart
@@ -0,0 +1,19 @@
+import 'package:json_annotation/json_annotation.dart';
+
+part 'notifyRegisterParams.g.dart';
+
+@JsonSerializable()
+class NotifyRegisterParams {
+ String username;
+ String password;
+ String fcmToken;
+
+ NotifyRegisterParams({
+ required this.username,
+ required this.password,
+ required this.fcmToken
+ });
+
+ factory NotifyRegisterParams.fromJson(Map json) => _$NotifyRegisterParamsFromJson(json);
+ Map toJson() => _$NotifyRegisterParamsToJson(this);
+}
\ No newline at end of file
diff --git a/lib/api/mhsl/notify/register/notifyRegisterParams.g.dart b/lib/api/mhsl/notify/register/notifyRegisterParams.g.dart
new file mode 100644
index 0000000..0b26d8d
--- /dev/null
+++ b/lib/api/mhsl/notify/register/notifyRegisterParams.g.dart
@@ -0,0 +1,23 @@
+// GENERATED CODE - DO NOT MODIFY BY HAND
+
+part of 'notifyRegisterParams.dart';
+
+// **************************************************************************
+// JsonSerializableGenerator
+// **************************************************************************
+
+NotifyRegisterParams _$NotifyRegisterParamsFromJson(
+ Map json) =>
+ NotifyRegisterParams(
+ username: json['username'] as String,
+ password: json['password'] as String,
+ fcmToken: json['fcmToken'] as String,
+ );
+
+Map _$NotifyRegisterParamsToJson(
+ NotifyRegisterParams instance) =>
+ {
+ 'username': instance.username,
+ 'password': instance.password,
+ 'fcmToken': instance.fcmToken,
+ };
diff --git a/lib/app.dart b/lib/app.dart
index 391296b..f77ea5c 100644
--- a/lib/app.dart
+++ b/lib/app.dart
@@ -1,6 +1,7 @@
import 'dart:async';
+import 'package:firebase_messaging/firebase_messaging.dart';
import 'package:flutter/material.dart';
import 'package:persistent_bottom_nav_bar/persistent_tab_view.dart';
import 'package:provider/provider.dart';
@@ -10,6 +11,9 @@ import 'api/mhsl/breaker/getBreakers/getBreakersResponse.dart';
import 'model/breakers/Breaker.dart';
import 'model/breakers/BreakerProps.dart';
import 'model/chatList/chatListProps.dart';
+import 'notification/notificationController.dart';
+import 'notification/notifyUpdater.dart';
+import 'storage/base/settingsProvider.dart';
import 'view/pages/files/files.dart';
import 'view/pages/more/overhang.dart';
import 'view/pages/talk/chatList.dart';
@@ -42,6 +46,13 @@ class _AppState extends State {
});
});
+ if(Provider.of(context, listen: false).val().notificationSettings.enabled) {
+ NotifyUpdater.registerToServer();
+ }
+
+ FirebaseMessaging.onMessage.listen((message) => NotificationController.onForegroundMessageHandler(message, context));
+ FirebaseMessaging.onBackgroundMessage(NotificationController.onBackgroundMessageHandler);
+
super.initState();
}
diff --git a/lib/main.dart b/lib/main.dart
index d0af28b..6268f3c 100644
--- a/lib/main.dart
+++ b/lib/main.dart
@@ -1,6 +1,10 @@
import 'dart:async';
+import 'dart:developer';
import 'dart:io';
+import 'package:firebase_core/firebase_core.dart';
+import 'package:firebase_messaging/firebase_messaging.dart';
+import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:jiffy/jiffy.dart';
@@ -27,6 +31,8 @@ import 'widget/placeholderView.dart';
Future main() async {
WidgetsFlutterBinding.ensureInitialized();
+ await Firebase.initializeApp();
+ log("Firebase token: ${await FirebaseMessaging.instance.getToken() ?? "Error: no Firebase token!"}");
ByteData data = await PlatformAssetBundle().load('assets/ca/lets-encrypt-r3.pem');
SecurityContext.defaultContext.setTrustedCertificatesBytes(data.buffer.asUint8List());
diff --git a/lib/notification/notificationController.dart b/lib/notification/notificationController.dart
new file mode 100644
index 0000000..22a6b13
--- /dev/null
+++ b/lib/notification/notificationController.dart
@@ -0,0 +1,45 @@
+import 'dart:developer';
+
+import 'package:firebase_core/firebase_core.dart';
+import 'package:firebase_messaging/firebase_messaging.dart';
+import 'package:flutter/cupertino.dart';
+
+import '../api/marianumcloud/talk/room/getRoom.dart';
+import '../api/marianumcloud/talk/room/getRoomParams.dart';
+import '../model/accountData.dart';
+import 'notificationService.dart';
+
+class NotificationController {
+ @pragma('vm:entry-point')
+ static Future onBackgroundMessageHandler(RemoteMessage message) async {
+ log("Handling a background notification: ${message.messageId}");
+
+ await Firebase.initializeApp();
+ AccountData().waitForPopulation().then((value) {
+ log("User account status: $value");
+ if(value) {
+ GetRoom(
+ GetRoomParams(
+ includeStatus: false,
+ ),
+ ).run().then((value) {
+ var messageCount = value.data.map((e) => e.unreadMessages).reduce((a, b) => a + b);
+ var chatCount = value.data.map((e) => e.unreadMessages).length;
+ var people = value.data.where((e) => e.unreadMessages > 0).map((e) => e.displayName.split(" ")[0]);
+
+ final NotificationService service = NotificationService();
+ service.initializeNotifications().then((value) {
+ service.showNotification(
+ title: "Du hast $messageCount ungelesene Nachrichten!",
+ body: "In $chatCount Chats, von ${people.join(", ")}"
+ );
+ });
+ });
+ }
+ });
+ }
+
+ static Future onForegroundMessageHandler(RemoteMessage message, BuildContext context) async {
+ NotificationService().showToast(message: "Du hast eine neue Talk Nachricht!", context: context);
+ }
+}
\ No newline at end of file
diff --git a/lib/notification/notificationService.dart b/lib/notification/notificationService.dart
new file mode 100644
index 0000000..77fdc8c
--- /dev/null
+++ b/lib/notification/notificationService.dart
@@ -0,0 +1,75 @@
+import 'package:flutter/material.dart';
+import 'package:flutter_local_notifications/flutter_local_notifications.dart';
+import 'package:fluttertoast/fluttertoast.dart';
+import 'package:provider/provider.dart';
+
+import '../model/chatList/chatListProps.dart';
+import '../model/message/messageProps.dart';
+
+class NotificationService {
+ static final NotificationService _instance = NotificationService._internal();
+
+ factory NotificationService() {
+ return _instance;
+ }
+
+ NotificationService._internal();
+
+ FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin = FlutterLocalNotificationsPlugin();
+
+ Future initializeNotifications() async {
+ const AndroidInitializationSettings androidSettings = AndroidInitializationSettings(
+ '@mipmap/ic_launcher'
+ );
+
+ final DarwinInitializationSettings iosSettings = DarwinInitializationSettings(
+ onDidReceiveLocalNotification: (id, title, body, payload) {
+ // TODO Navigate to Talk section (This runs when an Notification is tapped)
+ },
+ );
+
+
+ final InitializationSettings initializationSettings = InitializationSettings(
+ android: androidSettings,
+ iOS: iosSettings,
+ );
+
+ await flutterLocalNotificationsPlugin.initialize(
+ initializationSettings,
+ );
+ }
+
+ Future showNotification({required String title, required String body}) async {
+ const AndroidNotificationDetails androidPlatformChannelSpecifics =
+ AndroidNotificationDetails(
+ 'your_channel_id',
+ 'Your Channel Name',
+ importance: Importance.defaultImportance,
+ priority: Priority.defaultPriority,
+ ticker: 'ticker',
+ );
+
+ const NotificationDetails platformChannelSpecifics = NotificationDetails(android: androidPlatformChannelSpecifics);
+
+ await flutterLocalNotificationsPlugin.show(
+ 0,
+ title,
+ body,
+ platformChannelSpecifics,
+ );
+ }
+
+ void showToast({required String message, required BuildContext context, ToastGravity gravity = ToastGravity.BOTTOM}) {
+ Fluttertoast.showToast(
+ msg: message,
+ gravity: gravity,
+ toastLength: Toast.LENGTH_SHORT,
+ backgroundColor: Theme.of(context).primaryColor,
+ textColor: Colors.white,
+ fontSize: 13.0,
+ );
+
+ Provider.of(context, listen: false).run(renew: true);
+ Provider.of(context, listen: false).run(renew: true);
+ }
+}
\ No newline at end of file
diff --git a/lib/notification/notifyUpdater.dart b/lib/notification/notifyUpdater.dart
new file mode 100644
index 0000000..72688de
--- /dev/null
+++ b/lib/notification/notifyUpdater.dart
@@ -0,0 +1,22 @@
+
+import 'package:firebase_messaging/firebase_messaging.dart';
+
+import '../api/mhsl/notify/register/notifyRegister.dart';
+import '../api/mhsl/notify/register/notifyRegisterParams.dart';
+import '../model/accountData.dart';
+
+class NotifyUpdater {
+ static void registerToServer() async {
+ String? fcmToken = await FirebaseMessaging.instance.getToken();
+
+ if(fcmToken == null) throw Exception("Failed to register push notification because there is no FBC token!");
+
+ NotifyRegister(
+ NotifyRegisterParams(
+ username: AccountData().getUsername(),
+ password: AccountData().getPassword(),
+ fcmToken: fcmToken,
+ ),
+ ).run();
+ }
+}
\ No newline at end of file
diff --git a/lib/storage/base/settings.dart b/lib/storage/base/settings.dart
index 59aca1c..5f96084 100644
--- a/lib/storage/base/settings.dart
+++ b/lib/storage/base/settings.dart
@@ -5,6 +5,7 @@ import '../file/fileSettings.dart';
import '../fileView/fileViewSettings.dart';
import '../gradeAverages/gradeAveragesSettings.dart';
import '../holidays/holidaysSettings.dart';
+import '../notification/notificationSettings.dart';
import '../talk/talkSettings.dart';
import '../timetable/timetableSettings.dart';
@@ -25,6 +26,7 @@ class Settings {
FileSettings fileSettings;
HolidaysSettings holidaysSettings;
FileViewSettings fileViewSettings;
+ NotificationSettings notificationSettings;
Settings({
required this.appTheme,
@@ -35,6 +37,7 @@ class Settings {
required this.fileSettings,
required this.holidaysSettings,
required this.fileViewSettings,
+ required this.notificationSettings,
});
static String _themeToJson(ThemeMode m) => m.name;
diff --git a/lib/storage/base/settings.g.dart b/lib/storage/base/settings.g.dart
index 5d04e65..b8ca058 100644
--- a/lib/storage/base/settings.g.dart
+++ b/lib/storage/base/settings.g.dart
@@ -21,6 +21,8 @@ Settings _$SettingsFromJson(Map json) => Settings(
json['holidaysSettings'] as Map),
fileViewSettings: FileViewSettings.fromJson(
json['fileViewSettings'] as Map),
+ notificationSettings: NotificationSettings.fromJson(
+ json['notificationSettings'] as Map),
);
Map _$SettingsToJson(Settings instance) => {
@@ -32,4 +34,5 @@ Map _$SettingsToJson(Settings instance) => {
'fileSettings': instance.fileSettings.toJson(),
'holidaysSettings': instance.holidaysSettings.toJson(),
'fileViewSettings': instance.fileViewSettings.toJson(),
+ 'notificationSettings': instance.notificationSettings.toJson(),
};
diff --git a/lib/storage/notification/notificationSettings.dart b/lib/storage/notification/notificationSettings.dart
new file mode 100644
index 0000000..81607ba
--- /dev/null
+++ b/lib/storage/notification/notificationSettings.dart
@@ -0,0 +1,14 @@
+import 'package:json_annotation/json_annotation.dart';
+
+part 'notificationSettings.g.dart';
+
+@JsonSerializable()
+class NotificationSettings {
+ bool askUsageDismissed;
+ bool enabled;
+
+ NotificationSettings({required this.askUsageDismissed, required this.enabled});
+
+ factory NotificationSettings.fromJson(Map json) => _$NotificationSettingsFromJson(json);
+ Map toJson() => _$NotificationSettingsToJson(this);
+}
\ No newline at end of file
diff --git a/lib/storage/notification/notificationSettings.g.dart b/lib/storage/notification/notificationSettings.g.dart
new file mode 100644
index 0000000..d46c15d
--- /dev/null
+++ b/lib/storage/notification/notificationSettings.g.dart
@@ -0,0 +1,21 @@
+// GENERATED CODE - DO NOT MODIFY BY HAND
+
+part of 'notificationSettings.dart';
+
+// **************************************************************************
+// JsonSerializableGenerator
+// **************************************************************************
+
+NotificationSettings _$NotificationSettingsFromJson(
+ Map json) =>
+ NotificationSettings(
+ askUsageDismissed: json['askUsageDismissed'] as bool,
+ enabled: json['enabled'] as bool,
+ );
+
+Map _$NotificationSettingsToJson(
+ NotificationSettings instance) =>
+ {
+ 'askUsageDismissed': instance.askUsageDismissed,
+ 'enabled': instance.enabled,
+ };
diff --git a/lib/view/pages/more/overhang.dart b/lib/view/pages/more/overhang.dart
index 5f9be9d..7b501a6 100644
--- a/lib/view/pages/more/overhang.dart
+++ b/lib/view/pages/more/overhang.dart
@@ -1,5 +1,4 @@
-import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:persistent_bottom_nav_bar/persistent_tab_view.dart';
diff --git a/lib/view/pages/talk/chatList.dart b/lib/view/pages/talk/chatList.dart
index 0760f9f..acd879d 100644
--- a/lib/view/pages/talk/chatList.dart
+++ b/lib/view/pages/talk/chatList.dart
@@ -1,5 +1,6 @@
import 'dart:async';
+import 'package:firebase_messaging/firebase_messaging.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
@@ -26,6 +27,8 @@ class _ChatListState extends State {
void initState() {
super.initState();
+ FirebaseMessaging.instance.requestPermission();
+
WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
_query();
});
diff --git a/lib/view/settings/defaultSettings.dart b/lib/view/settings/defaultSettings.dart
index 44d9494..1c6bc88 100644
--- a/lib/view/settings/defaultSettings.dart
+++ b/lib/view/settings/defaultSettings.dart
@@ -7,6 +7,7 @@ import '../../storage/file/fileSettings.dart';
import '../../storage/fileView/fileViewSettings.dart';
import '../../storage/gradeAverages/gradeAveragesSettings.dart';
import '../../storage/holidays/holidaysSettings.dart';
+import '../../storage/notification/notificationSettings.dart';
import '../../storage/talk/talkSettings.dart';
import '../../storage/timetable/timetableSettings.dart';
import '../pages/files/files.dart';
@@ -39,6 +40,10 @@ class DefaultSettings {
fileViewSettings: FileViewSettings(
alwaysOpenExternally: Platform.isIOS,
),
+ notificationSettings: NotificationSettings(
+ askUsageDismissed: false,
+ enabled: false,
+ )
);
}
}
\ No newline at end of file
diff --git a/lib/view/settings/settings.dart b/lib/view/settings/settings.dart
index 6c221ae..f106da4 100644
--- a/lib/view/settings/settings.dart
+++ b/lib/view/settings/settings.dart
@@ -7,6 +7,7 @@ import 'package:provider/provider.dart';
import 'package:shared_preferences/shared_preferences.dart';
import '../../model/accountData.dart';
+import '../../notification/notifyUpdater.dart';
import '../../storage/base/settingsProvider.dart';
import '../../theming/appTheme.dart';
import '../../widget/centeredLeading.dart';
@@ -87,9 +88,7 @@ class _SettingsState extends State {
),
)).toList(),
onChanged: (e) {
- setState(() {
- settings.val(write: true).appTheme = e!;
- });
+ settings.val(write: true).appTheme = e!;
},
),
),
@@ -102,9 +101,7 @@ class _SettingsState extends State {
trailing: Checkbox(
value: settings.val().talkSettings.sortFavoritesToTop,
onChanged: (e) {
- setState(() {
- settings.val(write: true).talkSettings.sortFavoritesToTop = e!;
- });
+ settings.val(write: true).talkSettings.sortFavoritesToTop = e!;
},
),
),
@@ -115,9 +112,7 @@ class _SettingsState extends State {
trailing: Checkbox(
value: settings.val().talkSettings.sortUnreadToTop,
onChanged: (e) {
- setState(() {
- settings.val(write: true).talkSettings.sortUnreadToTop = e!;
- });
+ settings.val(write: true).talkSettings.sortUnreadToTop = e!;
},
),
),
@@ -130,9 +125,7 @@ class _SettingsState extends State {
trailing: Checkbox(
value: settings.val().fileSettings.sortFoldersToTop,
onChanged: (e) {
- setState(() {
- settings.val(write: true).fileSettings.sortFoldersToTop = e!;
- });
+ settings.val(write: true).fileSettings.sortFoldersToTop = e!;
},
),
),
@@ -152,6 +145,49 @@ class _SettingsState extends State {
const Divider(),
+ ListTile(
+ leading: const CenteredLeading(Icon(Icons.notifications_active_outlined)),
+ title: const Text("Push-Benachrichtigungen aktivieren"),
+ subtitle: const Text("Lange tippen für mehr Informationen"),
+ trailing: Checkbox(
+ value: settings.val().notificationSettings.enabled,
+ onChanged: (e) {
+ if(e!) {
+ ConfirmDialog(
+ title: "Warnung",
+ icon: Icons.warning_amber,
+ content: ""
+ "Die Push-Benachrichtigungen werden durch mhsl.eu versendet.\n\n"
+ "Durch das aktivieren dieser Funktion wird dein Nutzername, dein Password und eine Geräte-ID von mhsl dauerhaft gespeichert und verarbeitet.\n\n"
+ "Für mehr Informationen drücke lange auf die Einstellungsoption!",
+ confirmButton: "Aktivieren",
+ onConfirm: () {
+ settings.val(write: true).notificationSettings.enabled = e;
+ NotifyUpdater.registerToServer();
+ },
+ ).asDialog(context);
+ } else {
+ settings.val(write: true).notificationSettings.enabled = e;
+ }
+ },
+ ),
+ onLongPress: () => showDialog(context: context, builder: (context) => AlertDialog(
+ title: const Text("Info über Push"),
+ content: const SingleChildScrollView(child: Text(""
+ "Aufgrund technischer Limitationen müssen Push-nachrichten über einen Externen Server - hier 'mhsl.eu' (Author dieser App) - erfolgen.\n\n"
+ "Wenn Push aktiviert wird, werden deine Zugangsdaten und ein Token verschlüsselt an den Betreiber gesendet und von ihm unverschlüsselt gespeichert.\n\n"
+ "Der extene Server verwendet die Zugangsdaten um sich maschinell in Nextcloud Talk anzumelden und via Websockets auf neue Nachrichten zu warten.\n\n"
+ "Wenn eine neue Nachricht eintrifft wird dein Telefon via FBC-Messaging (Google Firebase Push) vom Externen Server benachrichtigt.\n\n"
+ "Behalte im Hinterkopf, dass deine Zugangsdaten auf einem Externen Server gespeichert werden und dies trots bester Absichten ein Sicherheitsrisiko sein kann!"
+ )),
+ actions: [
+ TextButton(onPressed: () => Navigator.of(context).pop(), child: const Text("Zurück"))
+ ],
+ )),
+ ),
+
+ const Divider(),
+
ListTile(
leading: const Icon(Icons.live_help_outlined),
title: const Text("Informationen und Lizenzen"),
@@ -215,9 +251,7 @@ class _SettingsState extends State {
trailing: Checkbox(
value: settings.val().devToolsEnabled,
onChanged: (state) {
- changeView() => setState(() {
- settings.val(write: true).devToolsEnabled = state ?? false;
- });
+ changeView() => settings.val(write: true).devToolsEnabled = state ?? false;
if(!state!) {
changeView();
@@ -284,9 +318,7 @@ class _SettingsState extends State {
content: "Alle Einstellungen gehen verloren! Accountdaten sowie App-Daten sind nicht betroffen.",
confirmButton: "Unwiederruflich Löschen",
onConfirm: () {
- setState(() {
- Provider.of(context, listen: false).reset();
- });
+ Provider.of(context, listen: false).reset();
},
).asDialog(context);
},
diff --git a/macos/Flutter/GeneratedPluginRegistrant.swift b/macos/Flutter/GeneratedPluginRegistrant.swift
index 9d5f78f..f9e6582 100644
--- a/macos/Flutter/GeneratedPluginRegistrant.swift
+++ b/macos/Flutter/GeneratedPluginRegistrant.swift
@@ -7,6 +7,9 @@ import Foundation
import device_info_plus
import file_selector_macos
+import firebase_core
+import firebase_messaging
+import flutter_local_notifications
import package_info
import path_provider_foundation
import shared_preferences_foundation
@@ -17,6 +20,9 @@ import url_launcher_macos
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
DeviceInfoPlusMacosPlugin.register(with: registry.registrar(forPlugin: "DeviceInfoPlusMacosPlugin"))
FileSelectorPlugin.register(with: registry.registrar(forPlugin: "FileSelectorPlugin"))
+ FLTFirebaseCorePlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseCorePlugin"))
+ FLTFirebaseMessagingPlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseMessagingPlugin"))
+ FlutterLocalNotificationsPlugin.register(with: registry.registrar(forPlugin: "FlutterLocalNotificationsPlugin"))
FLTPackageInfoPlugin.register(with: registry.registrar(forPlugin: "FLTPackageInfoPlugin"))
PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))
SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin"))
diff --git a/pubspec.yaml b/pubspec.yaml
index d124148..ca0a645 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -81,6 +81,11 @@ dependencies:
syncfusion_flutter_pdfviewer: ^21.2.8
photo_view: ^0.14.0
uuid: ^3.0.7
+ firebase_messaging: ^14.6.5
+ firebase_core: ^2.15.0
+ firebase_in_app_messaging: ^0.7.3+4
+ flutter_local_notifications: ^15.1.0+1
+ fluttertoast: ^8.2.2
dev_dependencies:
flutter_test:
diff --git a/windows/flutter/generated_plugin_registrant.cc b/windows/flutter/generated_plugin_registrant.cc
index 779f0ee..9f7d909 100644
--- a/windows/flutter/generated_plugin_registrant.cc
+++ b/windows/flutter/generated_plugin_registrant.cc
@@ -7,12 +7,15 @@
#include "generated_plugin_registrant.h"
#include
+#include
#include
#include
void RegisterPlugins(flutter::PluginRegistry* registry) {
FileSelectorWindowsRegisterWithRegistrar(
registry->GetRegistrarForPlugin("FileSelectorWindows"));
+ FirebaseCorePluginCApiRegisterWithRegistrar(
+ registry->GetRegistrarForPlugin("FirebaseCorePluginCApi"));
SyncfusionPdfviewerWindowsPluginRegisterWithRegistrar(
registry->GetRegistrarForPlugin("SyncfusionPdfviewerWindowsPlugin"));
UrlLauncherWindowsRegisterWithRegistrar(
diff --git a/windows/flutter/generated_plugins.cmake b/windows/flutter/generated_plugins.cmake
index 3757972..d9d5615 100644
--- a/windows/flutter/generated_plugins.cmake
+++ b/windows/flutter/generated_plugins.cmake
@@ -4,6 +4,7 @@
list(APPEND FLUTTER_PLUGIN_LIST
file_selector_windows
+ firebase_core
syncfusion_pdfviewer_windows
url_launcher_windows
)