Skip to content

Commit

Permalink
Merge pull request #105 from nucleus-ffm/addMoreNotificationSettings
Browse files Browse the repository at this point in the history
Add more notification settings
  • Loading branch information
nucleus-ffm committed Jan 2, 2024
2 parents 983e2c7 + f373fe5 commit c98831d
Show file tree
Hide file tree
Showing 27 changed files with 824 additions and 400 deletions.
69 changes: 44 additions & 25 deletions lib/class/abstract_Place.dart
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import 'package:flutter/material.dart';
import 'package:foss_warn/class/class_notificationPreferences.dart';
import 'package:foss_warn/enums/WarningSource.dart';
import 'package:foss_warn/services/saveAndLoadSharedPreferences.dart';
import 'package:provider/provider.dart';

import '../enums/Severity.dart';
import '../main.dart';
import '../services/listHandler.dart';
import '../services/updateProvider.dart';
import 'class_NotificationService.dart';
import 'class_WarnMessage.dart';
Expand All @@ -13,17 +15,23 @@ abstract class Place {
List<WarnMessage> _warnings = [];
String eTag = "";

Place({required String name, required List<WarnMessage> warnings, required String eTag}) : _warnings = warnings, _name = name {
Place(
{required String name,
required List<WarnMessage> warnings,
required String eTag})
: _warnings = warnings,
_name = name {
eTag = eTag;
}

String get name => _name;
int get countWarnings=> this.warnings.length;
int get countWarnings => this.warnings.length;
List<WarnMessage> get warnings => _warnings;

// control the list for warnings
void addWarningToList(WarnMessage warnMessage) => _warnings.add(warnMessage);
void removeWarningFromList(WarnMessage warnMessage) => _warnings.remove(warnMessage);
void removeWarningFromList(WarnMessage warnMessage) =>
_warnings.remove(warnMessage);

// check if all warnings in `warnings` are
// also in the alreadyReadWarnings list
Expand All @@ -43,7 +51,7 @@ abstract class Place {
bool checkIfThereIsAWarningToNotify() {
for (WarnMessage myWarning in _warnings) {
if (!myWarning.notified &&
notificationSettingsImportance.contains(myWarning.severity)) {
_checkIfEventShouldBeNotified(myWarning.source, myWarning.severity)) {
// there is min. one warning without notification
return true;
}
Expand All @@ -55,13 +63,16 @@ abstract class Place {
Future<void> sendNotificationForWarnings() async {
for (WarnMessage myWarnMessage in _warnings) {
print(myWarnMessage.headline);
print("Read: " + myWarnMessage.read.toString() + " notified " + myWarnMessage.notified.toString());
print("should notify? :" +
((!myWarnMessage.read && !myWarnMessage.notified) &&
_checkIfEventShouldBeNotified(myWarnMessage.event))
.toString());
//print("Read: " + myWarnMessage.read.toString() + " notified " + myWarnMessage.notified.toString());
/*print("should notify? :" +
(_checkIfEventShouldBeNotified(
myWarnMessage.source, myWarnMessage.severity))
.toString());c*/
//(!myWarnMessage.read && !myWarnMessage.notified) &&

if ((!myWarnMessage.read && !myWarnMessage.notified) &&
_checkIfEventShouldBeNotified(myWarnMessage.event)) {
_checkIfEventShouldBeNotified(
myWarnMessage.source, myWarnMessage.severity)) {
// Alert is not already read or shown as notification
// set notified to true to avoid sending notification twice
myWarnMessage.notified = true;
Expand All @@ -86,6 +97,7 @@ abstract class Place {
void markAllWarningsAsRead(BuildContext context) {
for (WarnMessage myWarnMessage in _warnings) {
myWarnMessage.read = true;
NotificationService.cancelOneNotification(myWarnMessage.identifier.hashCode);
}
final updater = Provider.of<Update>(context, listen: false);
updater.updateReadStatusInList();
Expand All @@ -94,7 +106,7 @@ abstract class Place {

/// set the read and notified status from all warnings to false
/// used for debug purpose
/// @context to update view
/// [@context] to update view
void resetReadAndNotificationStatusForAllWarnings(BuildContext context) {
for (WarnMessage myWarnMessage in _warnings) {
myWarnMessage.read = false;
Expand All @@ -105,18 +117,25 @@ abstract class Place {
saveMyPlacesList();
}

/// return [true] or false if the warning should be irgnored or not
/// The event could be listed in the map notificationEventsSettings.
/// if it is listed in the map, return the stored value for the event
/// If not return as default true
bool _checkIfEventShouldBeNotified(String event) {
if (userPreferences.notificationEventsSettings[event] != null) {
print(event + " " + userPreferences.notificationEventsSettings[event]!.toString());
return userPreferences.notificationEventsSettings[event]!;
} else {
return true;
}
}
/// Return [true] if the user wants a notification - [false] if not.
///
/// The source should be listed in the List notificationSourceSettings.
/// check if the user wants to be notified for
/// the given source and the given severity
///
/// example:
///
/// Warning severity | Notification setting | notification? <br>
/// Moderate (2) | Minor (3) | 3 >= 2 => true <br>
/// Minor (3) | Moderate (2) | 2 >= 3 => false
bool _checkIfEventShouldBeNotified(WarningSource source, Severity severity) {
NotificationPreferences notificationSourceSetting = userPreferences
.notificationSourceSettings
.firstWhere((element) => element.warningSource == source);

Map<String, dynamic> toJson();
return notificationSourceSetting.disabled == false &&
Severity.getIndexFromSeverity(
notificationSourceSetting.notificationLevel) >=
Severity.getIndexFromSeverity(severity);
}
}
103 changes: 77 additions & 26 deletions lib/class/class_NotificationService.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import 'package:flutter_local_notifications/flutter_local_notifications.dart';
import 'package:foss_warn/services/translateAndColorizeWarning.dart';
import 'package:rxdart/rxdart.dart';
import 'package:flutter/material.dart';

///
/// ID 2: Status notification
/// ID 3: No Places selected warning
Expand All @@ -14,16 +15,13 @@ class NotificationService {
static Future _notificationsDetails(String channel) async {
return NotificationDetails(
android: AndroidNotificationDetails(
'foss_warn_notifications_' + channel.trim().toLowerCase(),
'de.nucleus.foss_warn.notifications_' + channel.trim().toLowerCase(),
"Warnstufe: " + translateWarningSeverity(channel),
channelDescription:
'FOSS Warn notifications for ' + channel.trim().toLowerCase(),
groupKey: "FossWarnWarnings",
category: AndroidNotificationCategory.alarm,
importance: Importance.max,
category: AndroidNotificationCategory.message,
priority: Priority.max,

//enable multiline notification
// enable multiline notification
styleInformation: BigTextStyleInformation(''),
color: Colors.red, // makes the icon red,
ledColor: Colors.red,
Expand All @@ -36,7 +34,7 @@ class NotificationService {
static Future _statusNotificationsDetails() async {
return NotificationDetails(
android: AndroidNotificationDetails(
'foss_warn_status',
'de.nucleus.foss_warn.notifications_state',
'Statusanzeige',
channelDescription: 'Status der Hintergrund Updates',
groupKey: "FossWarnService",
Expand Down Expand Up @@ -141,21 +139,74 @@ class NotificationService {

// init the different notifications channels
try {
await androidNotificationPlugin.createNotificationChannel(
AndroidNotificationChannel(
"foss_warn_notifications_minor", "Warnstufe: Gering"));
await androidNotificationPlugin.createNotificationChannelGroup(
AndroidNotificationChannelGroup(
"de.nucleus.foss_warn.notifications_emergency_information",
"Gefahreninformationen",
description: "Benachrichtigungen zu Gefahrenmeldungen"));

await androidNotificationPlugin.createNotificationChannelGroup(
AndroidNotificationChannelGroup(
"de.nucleus.foss_warn.notifications_other", "Sonstiges",
description: "Sonstige Benachrichtigungen"));

await androidNotificationPlugin
.createNotificationChannel(AndroidNotificationChannel(
"de.nucleus.foss_warn.notifications_minor",
"Warnstufe: Gering",
description:
"Warnung vor einer Beeinträchtigung des normalen Tagesablaufs.",
groupId: "de.nucleus.foss_warn.notifications_emergency_information",
importance: Importance.max,
));

await androidNotificationPlugin.createNotificationChannel(
AndroidNotificationChannel(
"foss_warn_notifications_moderate", "Warnstufe: Mittel"));
await androidNotificationPlugin
.createNotificationChannel(AndroidNotificationChannel(
"de.nucleus.foss_warn.notifications_moderate",
"Warnstufe: Moderat",
description:
"Eine Warnung vor einer starken Beeinträchtigung des normalen Tagesablaufs.",
groupId: "de.nucleus.foss_warn.notifications_emergency_information",
importance: Importance.max,
));

await androidNotificationPlugin
.createNotificationChannel(AndroidNotificationChannel(
"de.nucleus.foss_warn.notifications_severe",
"Warnstufe: Schwer",
description:
"Eine Warnung vor einer Gefahr, die ihre Gesundheit, ihr Eigentum und/oder öffentliche Infrastruktur beeinträchtigen kann.",
groupId: "de.nucleus.foss_warn.notifications_emergency_information",
importance: Importance.max,
));

await androidNotificationPlugin.createNotificationChannel(
AndroidNotificationChannel(
"foss_warn_notifications_severe", "Warnstufe: Schwer"));
await androidNotificationPlugin
.createNotificationChannel(AndroidNotificationChannel(
"de.nucleus.foss_warn.notifications_extreme",
"Warnstufe: Extrem",
description:
"Eine Warnung vor einer Gefahr, die sich kurzfristig signifikant auf ihre Gesundheit, ihr Eigentum und/oder öffentliche Infrastruktur auswirken kann.",
groupId: "de.nucleus.foss_warn.notifications_emergency_information",
importance: Importance.max,
));

await androidNotificationPlugin
.createNotificationChannel(AndroidNotificationChannel(
"de.nucleus.foss_warn.notifications_state",
"Statusanzeige",
description: "Zeit den aktuellen Status der Hintergrundupdates an.",
groupId: "de.nucleus.foss_warn.notifications_other",
importance: Importance.low,
));

await androidNotificationPlugin.createNotificationChannel(
AndroidNotificationChannel(
"foss_warn_notifications_extreme", "Warnstufe: Extrem"));
await androidNotificationPlugin
.createNotificationChannel(AndroidNotificationChannel(
"de.nucleus.foss_warn.notifications_other",
"Sonstiges",
description: "Sonstige Benachrichtigungen",
groupId: "de.nucleus.foss_warn.notifications_other",
importance: Importance.defaultImportance,
));
} catch (e) {
print("Error while creating notification channels: " + e.toString());
}
Expand All @@ -179,12 +230,12 @@ class NotificationService {

Future<void> cleanUpNotificationChannels() async {
List<String> channelIds = [];
channelIds.add("foss_warn_notifications_minor");
channelIds.add("foss_warn_notifications_severe");
channelIds.add("foss_warn_notifications_moderate");
channelIds.add("foss_warn_notifications_extreme");
channelIds.add("foss_warn_status");
channelIds.add("foss_warn_notifications_other");
channelIds.add("de.nucleus.foss_warn.notifications_minor");
channelIds.add("de.nucleus.foss_warn.notifications_moderate");
channelIds.add("de.nucleus.foss_warn.notifications_severe");
channelIds.add("de.nucleus.foss_warn.notifications_extreme");
channelIds.add("de.nucleus.foss_warn.notifications_state");
channelIds.add("de.nucleus.foss_warn.notifications_other");

print("[android notification channels]");
List<AndroidNotificationChannel>? temp =
Expand Down Expand Up @@ -229,7 +280,7 @@ class NotificationService {

if (activeNotifications!.length == 2 &&
activeNotifications
.any((element) => element.channelId == "foss_warn_status")) {
.any((element) => element.channelId == "de.nucleus.foss_warn.notifications_state")) {
if (activeNotifications[0].id == 0) {
// summery notification has id 0
cancelOneNotification(0);
Expand Down
20 changes: 11 additions & 9 deletions lib/class/class_WarnMessage.dart
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import 'package:foss_warn/enums/WarningSource.dart';

import '../enums/Certainty.dart';
import '../enums/Severity.dart';
import 'class_Area.dart';
Expand All @@ -6,7 +8,7 @@ import '../services/createAreaListFromJson.dart';
class WarnMessage {
final String identifier;
final String publisher;
final String source;
final WarningSource source;
final String sender;
final String sent;
final String status;
Expand Down Expand Up @@ -62,7 +64,7 @@ class WarnMessage {
return WarnMessage(
identifier: json['identifier'],
publisher: json['publisher'],
source: json['source'],
source: WarningSource.fromString(json['source'].toString()),
sender: json['sender'],
sent: json['sent'],
status: json['status'],
Expand Down Expand Up @@ -94,7 +96,7 @@ class WarnMessage {
String publisher, List<Area> areaList) {
print("Neue WarnMessage wird angelegt...");
return WarnMessage(
source: provider,
source: WarningSource.fromString(provider),
identifier: json["identifier"] ?? "?",
sender: json["sender"] ?? "?",
sent: json["sent"] ?? "?",
Expand All @@ -104,7 +106,7 @@ class WarnMessage {
category: json["info"][0]["category"][0] ?? "?",
event: json["info"][0]["event"] ?? "?",
urgency: json["info"][0]["urgency"] ?? "?",
severity: getSeverity(json["info"][0]["severity"].toString().toLowerCase()),
severity: Severity.fromString(json["info"][0]["severity"].toString().toLowerCase()),
certainty: getCertainty(json["info"][0]["certainty"].toString().toLowerCase()),
effective: json["info"][0]["effective"] ?? "",
onset: json["info"][0]["onset"] ?? "",
Expand All @@ -126,7 +128,7 @@ class WarnMessage {
factory WarnMessage.fromJsonAlertSwiss(Map<String, dynamic> json,
List<Area> areaList, String instructions, String license) {
return WarnMessage(
source: "Alert Swiss",
source: WarningSource.alertSwiss,
identifier: json["identifier"] ?? "?",
sender: json["sender"] ?? "?",
sent: json["sent"] ?? "?",
Expand All @@ -136,16 +138,16 @@ class WarnMessage {
category: json["event"] ?? "?", // missing
event: json["event"] ?? "?",
urgency: "?",
severity: getSeverity(json["severity"]),
severity: Severity.fromString(json["severity"]),
certainty: getCertainty(""), // missing
effective: "", // missing
onset: json["onset"] ?? "", // m
expires: json["expires"] ?? "", // m
headline: json["title"] ?? "?",
description: json["description"] ?? "",
headline: json["title"]["title"] ?? "?",
description: json["description"]["description"] ?? "",
instruction: instructions,
publisher: license,
contact: json["contact"] ?? "",
contact: json["contact"]["contact"] ?? "",
web: json["link"] ?? "",
areaList: areaList,
notified: false,
Expand Down
2 changes: 1 addition & 1 deletion lib/class/class_alarmManager.dart
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ class AlarmManager {
final int isolateId = Isolate.current.hashCode;
print("[$now] Call APIs! isolate=$isolateId function='$callback'");

await checkForMyPlacesWarnings(true, true);
await checkForMyPlacesWarnings(true);
print("Call APIs executed");
}

Expand Down
26 changes: 26 additions & 0 deletions lib/class/class_notificationPreferences.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import 'package:foss_warn/enums/Severity.dart';
import 'package:foss_warn/enums/WarningSource.dart';

/// to store the chosen notificationLevel for a warningSource
class NotificationPreferences {
Severity notificationLevel;
bool disabled;
WarningSource warningSource;

NotificationPreferences(
{required this.warningSource, required this.notificationLevel, this.disabled = false});

factory NotificationPreferences.fromJson(Map<String, dynamic> json) {
return NotificationPreferences(
warningSource: WarningSource.fromString(json['warningSource'].toString()),
disabled: json['disabled'],
notificationLevel:
Severity.fromJson(json['notificationLevel']));
}

Map<String, dynamic> toJson() => {
'notificationLevel': notificationLevel.toJson(),
'disabled': disabled,
'warningSource': warningSource.toJson(),
};
}
Loading

0 comments on commit c98831d

Please sign in to comment.