Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add more notification settings #105

Merged
merged 64 commits into from
Jan 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
64 commits
Select commit Hold shift + click to select a range
d35ef81
replace the old notification settings with improved one. You can now …
nucleus-ffm Dec 18, 2023
c77e28c
add a check if the text field is in focus because the onTapOutside me…
nucleus-ffm Dec 18, 2023
c6c12a9
migrate WarningSource to enum
nucleus-ffm Dec 18, 2023
9451fc1
save new notification settings and mark old methode as deprecated
nucleus-ffm Dec 18, 2023
f558179
new notification settings view
nucleus-ffm Dec 18, 2023
d1ed811
migrate WarningSource to enum
nucleus-ffm Dec 18, 2023
d5d4867
migrate to new notification settings
nucleus-ffm Dec 18, 2023
b3bc5ce
remove unused parameter
nucleus-ffm Dec 18, 2023
08a343f
migrate WarningSource to enum
nucleus-ffm Dec 18, 2023
d09145b
update dependencies
nucleus-ffm Dec 18, 2023
5ee10ba
fix spelling
nucleus-ffm Dec 18, 2023
fe8a957
Fix logic error in the notification logic
nucleus-ffm Dec 19, 2023
fff69ff
display the snackbar if there is no warning and not if there is a war…
nucleus-ffm Dec 19, 2023
2ca2d91
remove deprecated notification settings
nucleus-ffm Dec 19, 2023
79ca698
hide alert swiss settings, if disabled
nucleus-ffm Dec 20, 2023
d14b1a3
hide frequency update slider if background updates are deactivated
nucleus-ffm Dec 20, 2023
a0bda26
Simplified code
MatsG23 Dec 23, 2023
17c1206
Remove duplicated code and restore old Severity values order
MatsG23 Dec 23, 2023
c703f5a
extend documentation
nucleus-ffm Dec 27, 2023
e7d5967
move getSeverity() to Severity enum
nucleus-ffm Dec 27, 2023
1c53c2f
Change the label of the slider to obtain a descending order of the no…
nucleus-ffm Dec 27, 2023
11fb26b
move getIndexFromWarningSource to enum
nucleus-ffm Dec 27, 2023
f6b08c4
Reorder WarningSource enum and fix default index
MatsG23 Dec 28, 2023
730544a
Fix default index in Severity
MatsG23 Dec 28, 2023
2b36251
use unique IDs, use groups and add description to channels
nucleus-ffm Dec 28, 2023
82a9d9f
rename methode from getSeverity to fromString
nucleus-ffm Dec 28, 2023
d22f0b4
rename methode from getSeverity to fromString
nucleus-ffm Dec 28, 2023
744650c
rename methode from getSeverity to fromString
nucleus-ffm Dec 28, 2023
421c88b
add missing biwapp source
nucleus-ffm Dec 28, 2023
2580b90
Merge remote-tracking branch 'origin/addMoreNotificationSettings' int…
nucleus-ffm Dec 28, 2023
b361248
return index 6 as default
nucleus-ffm Dec 28, 2023
fecb5bb
Reorder WarningSource values alphabetically
MatsG23 Dec 28, 2023
c1e9074
fix wrong channel importance
nucleus-ffm Dec 28, 2023
9df4843
Translated using Weblate (English)
weblate Dec 29, 2023
055294e
Translated using Weblate (German)
nucleus-ffm Dec 28, 2023
835d6ff
Translated using Weblate (French)
nucleus-ffm Dec 28, 2023
fb18f39
Translated using Weblate (English)
weblate Jan 1, 2024
767313d
Translated using Weblate (English)
nucleus-ffm Jan 1, 2024
ddc3bc1
Translated using Weblate (English)
weblate Jan 1, 2024
54c6968
Translated using Weblate (English)
nucleus-ffm Jan 1, 2024
0e909b6
Add information about the different severity levels
nucleus-ffm Jan 1, 2024
6785572
open severity explanation dialog when pressed on the severity tag
nucleus-ffm Jan 1, 2024
473370d
add button to open the severity explanation dialog and prepare transl…
nucleus-ffm Jan 1, 2024
dc0b3c9
prepare translation
nucleus-ffm Jan 1, 2024
834610f
Translated using Weblate (English)
weblate Jan 1, 2024
591428e
fix double Other entry in notificationSettings because of wrong json …
nucleus-ffm Jan 2, 2024
50bafd5
fix broken json parsing of alert swiss due to a changed structure
nucleus-ffm Jan 2, 2024
f7f9e90
fix json parsing
nucleus-ffm Jan 2, 2024
d138542
fix json parsing. The boolean value is not stored as string.
nucleus-ffm Jan 2, 2024
6bf002e
cancel notification if warning is read or marked as read
nucleus-ffm Jan 2, 2024
99e587f
Translated using Weblate (English)
nucleus-ffm Jan 2, 2024
76a29f0
Translated using Weblate (German)
nucleus-ffm Jan 2, 2024
013c952
Translated using Weblate (French)
nucleus-ffm Jan 2, 2024
fda0506
Merge pull request #109 from nucleus-ffm/weblate
nucleus-ffm Jan 2, 2024
6397a5f
Translated using Weblate (German)
nucleus-ffm Jan 2, 2024
5948c34
Translated using Weblate (French)
nucleus-ffm Jan 2, 2024
80a95af
Translated using Weblate (English)
nucleus-ffm Jan 2, 2024
511909a
Translated using Weblate (English)
weblate Jan 2, 2024
3c3da61
Translated using Weblate (German)
nucleus-ffm Jan 2, 2024
990efa3
Translated using Weblate (French)
nucleus-ffm Jan 2, 2024
0d08cba
Merge pull request #110 from nucleus-ffm/weblate
nucleus-ffm Jan 2, 2024
7ce44fd
add translation
nucleus-ffm Jan 2, 2024
05aeb8a
Changing the translation of moderate for a uniform name
nucleus-ffm Jan 2, 2024
f373fe5
bump version number and add changelog
nucleus-ffm Jan 2, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading