From f51c2f7449ec094240bd8bef99d9f6a6909d6545 Mon Sep 17 00:00:00 2001 From: Valentin REVERSAT Date: Mon, 8 May 2023 21:08:30 +0200 Subject: [PATCH] feat(notifications): display a message if the notifications are disabled --- lib/const.dart | 2 +- lib/l10n/app_en.arb | 4 +- lib/l10n/app_es.arb | 4 +- lib/l10n/app_fr.arb | 4 +- lib/screens/notification_screen.dart | 58 +++++++++ lib/service/notification_service.dart | 167 ++++++++++++++------------ pubspec.lock | 8 ++ pubspec.yaml | 1 + 8 files changed, 168 insertions(+), 80 deletions(-) diff --git a/lib/const.dart b/lib/const.dart index a94068c2..f730c99b 100644 --- a/lib/const.dart +++ b/lib/const.dart @@ -83,7 +83,7 @@ class Const { '@mipmap/ic_slice_launcher_adaptive_fore'; static const Duration notificationDurationValueDefaultValue = Duration(minutes: 60); - static const bool notificationDurationEnabledDefaultValue = true; + static const bool notificationDurationEnabledDefaultValue = false; static TimeOfDay notificationTimeValueDefaultValue = const TimeOfDay(hour: 6, minute: 0); static const bool notificationTimeEnabledDefaultValue = false; diff --git a/lib/l10n/app_en.arb b/lib/l10n/app_en.arb index 3033c98c..cc14bdac 100644 --- a/lib/l10n/app_en.arb +++ b/lib/l10n/app_en.arb @@ -176,5 +176,7 @@ } }, "favoriteSlotsInterferenceWarning": "This schedule interferes with one or more time slots", - "favoriteTimeSlotEnabledWarning": "Attention, by activating this parameter you will only receive notifications when an event occurs during one of your time slots" + "favoriteTimeSlotEnabledWarning": "Attention, by activating this parameter you will only receive notifications when an event occurs during one of your time slots", + "notificationNotEnabledMessage": "Please note that notifications are not allowed. Please go to the application settings to authorize the sending of notifications", + "notificationNotEnabledOpenSettings": "Open notification settings" } \ No newline at end of file diff --git a/lib/l10n/app_es.arb b/lib/l10n/app_es.arb index ad9a7c3b..cbe7c664 100644 --- a/lib/l10n/app_es.arb +++ b/lib/l10n/app_es.arb @@ -176,5 +176,7 @@ } }, "favoriteSlotsInterferenceWarning": "Este horario interfiere con uno o más intervalos de tiempo", - "favoriteTimeSlotEnabledWarning": "Atención, al activar este parámetro solo recibirás notificaciones cuando ocurra un evento durante una de tus franjas horarias" + "favoriteTimeSlotEnabledWarning": "Atención, al activar este parámetro solo recibirás notificaciones cuando ocurra un evento durante una de tus franjas horarias", + "notificationNotEnabledMessage": "Tenga en cuenta que las notificaciones no están permitidas. Vaya a la configuración de la aplicación para autorizar el envío de notificaciones.", + "notificationNotEnabledOpenSettings": "Abrir configuración de notificaciones" } \ No newline at end of file diff --git a/lib/l10n/app_fr.arb b/lib/l10n/app_fr.arb index 8fd70674..7312e65d 100644 --- a/lib/l10n/app_fr.arb +++ b/lib/l10n/app_fr.arb @@ -176,5 +176,7 @@ } }, "favoriteSlotsInterferenceWarning": "Cette prévision interfère avec un ou plusieurs créneaux", - "favoriteTimeSlotEnabledWarning": "Attention, en activant ce paramètres vous allez uniquement recevoir les notifications lorsqu'un évèmenent se produit durant l'un de vos créneaux" + "favoriteTimeSlotEnabledWarning": "Attention, en activant ce paramètres vous allez uniquement recevoir les notifications lorsqu'un évèmenent se produit durant l'un de vos créneaux", + "notificationNotEnabledMessage": "Attention, les notifications ne sont pas autorisées. Veuillez vous rendre dans les paramètres de l'application pour autoriser l'envoi de notification", + "notificationNotEnabledOpenSettings": "Ouvrir les paramètres de notification" } \ No newline at end of file diff --git a/lib/screens/notification_screen.dart b/lib/screens/notification_screen.dart index f17a2dc6..90d210e2 100644 --- a/lib/screens/notification_screen.dart +++ b/lib/screens/notification_screen.dart @@ -1,7 +1,9 @@ import 'dart:ui'; +import 'package:app_settings/app_settings.dart'; import 'package:chabo/bloc/notification/notification_bloc.dart'; import 'package:chabo/cubits/floating_actions_cubit.dart'; +import 'package:chabo/cubits/notification_service_cubit.dart'; import 'package:chabo/custom_properties.dart'; import 'package:chabo/custom_widget_state.dart'; import 'package:chabo/dialogs/days_of_the_week_dialog.dart'; @@ -64,6 +66,62 @@ class _NotificationScreenState extends CustomWidgetState { builder: (context, notificationState) { return Column( children: [ + FutureBuilder( + future: context + .read() + .state + .areNotificationsEnabled(), + builder: ( + BuildContext context, + AsyncSnapshot snapshot, + ) { + if (snapshot.hasData && + snapshot.data != null && + !snapshot.data!) { + WidgetsBinding.instance.addPostFrameCallback((_) { + ScaffoldMessenger.of(context).hideCurrentSnackBar(); + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + duration: const Duration(seconds: 10), + showCloseIcon: true, + backgroundColor: + Theme.of(context).colorScheme.error, + content: Column( + children: [ + Text( + AppLocalizations.of(context)! + .notificationNotEnabledMessage, + style: Theme.of(context) + .textTheme + .labelLarge + ?.copyWith( + color: Theme.of(context) + .colorScheme + .onError, + ), + overflow: TextOverflow.visible, + ), + const SizedBox( + height: 20, + ), + ElevatedButton( + onPressed: () => AppSettings + .openNotificationSettings(), + child: Text( + AppLocalizations.of(context)! + .notificationNotEnabledOpenSettings, + ), + ), + ], + ), + ), + ); + }); + } + + return const SizedBox.shrink(); + }, + ), Column( children: [ _CustomListTile( diff --git a/lib/service/notification_service.dart b/lib/service/notification_service.dart index 2285f026..7a92aeea 100644 --- a/lib/service/notification_service.dart +++ b/lib/service/notification_service.dart @@ -85,6 +85,18 @@ class NotificationService { return false; } + Future areNotificationsEnabled() async { + if (Platform.isAndroid) { + final AndroidFlutterLocalNotificationsPlugin? androidImplementation = + localNotifications.resolvePlatformSpecificImplementation< + AndroidFlutterLocalNotificationsPlugin>(); + + return await androidImplementation?.areNotificationsEnabled() ?? false; + } + + return false; + } + Future computeNotifications( List chabanBridgeForecasts, NotificationState notificationSate, @@ -92,86 +104,90 @@ class NotificationService { ) async { tz.initializeTimeZones(); int index = 0; - localNotifications.cancelAll().then((value) => null); + localNotifications.cancelAll(); List weekSeparatedChabanBridgeForecast = []; - for (final chabanBridgeForecast in chabanBridgeForecasts) { - /// Compute the slot time linked to a forecast before starting the notification computation - chabanBridgeForecast - .computeSlotInterference(notificationSate.timeSlotsValue); - final hasTimeSlots = chabanBridgeForecast.interferingTimeSlots.isNotEmpty; - if ((notificationSate.openingNotificationEnabled && - !notificationSate.timeSlotsEnabledForNotifications) || - (notificationSate.openingNotificationEnabled && - notificationSate.timeSlotsEnabledForNotifications && - hasTimeSlots)) { - index += 1; - await _createOpeningScheduledNotifications( - index, - chabanBridgeForecast, - context, - ); - } - if ((notificationSate.closingNotificationEnabled && - !notificationSate.timeSlotsEnabledForNotifications) || - (notificationSate.closingNotificationEnabled && - notificationSate.timeSlotsEnabledForNotifications && - hasTimeSlots)) { - index += 1; - await _createClosingScheduledNotifications( - index, - chabanBridgeForecast, - context, - ); - } - if ((notificationSate.timeNotificationEnabled && - !notificationSate.timeSlotsEnabledForNotifications) || - (notificationSate.timeNotificationEnabled && - notificationSate.timeSlotsEnabledForNotifications && - hasTimeSlots)) { - index += 1; - await _createTimeScheduledNotifications( - index, - chabanBridgeForecast, - context, - notificationSate.timeNotificationValue, - ); - } - if ((notificationSate.dayNotificationEnabled && - !notificationSate.timeSlotsEnabledForNotifications) || - (notificationSate.dayNotificationEnabled && - notificationSate.timeSlotsEnabledForNotifications && - hasTimeSlots)) { - var last = chabanBridgeForecast.circulationClosingDate - .previous(notificationSate.dayNotificationValue.weekPosition); - if (weekSeparatedChabanBridgeForecast.isEmpty || - weekSeparatedChabanBridgeForecast.last == last) { - weekSeparatedChabanBridgeForecast.add(last); - } else { + if (await _requestPermissions()) { + for (final chabanBridgeForecast in chabanBridgeForecasts) { + /// Compute the slot time linked to a forecast before starting the notification computation + chabanBridgeForecast + .computeSlotInterference(notificationSate.timeSlotsValue); + final hasTimeSlots = + chabanBridgeForecast.interferingTimeSlots.isNotEmpty; + if ((notificationSate.openingNotificationEnabled && + !notificationSate.timeSlotsEnabledForNotifications) || + (notificationSate.openingNotificationEnabled && + notificationSate.timeSlotsEnabledForNotifications && + hasTimeSlots)) { index += 1; - await _createDayScheduledNotifications( + await _createOpeningScheduledNotifications( index, - weekSeparatedChabanBridgeForecast.length, - weekSeparatedChabanBridgeForecast.last, - notificationSate.dayNotificationTimeValue, + chabanBridgeForecast, context, ); - weekSeparatedChabanBridgeForecast.clear(); - weekSeparatedChabanBridgeForecast.add(last); } - } - if ((notificationSate.durationNotificationEnabled && - !notificationSate.timeSlotsEnabledForNotifications) || - (notificationSate.durationNotificationEnabled && - notificationSate.timeSlotsEnabledForNotifications && - hasTimeSlots)) { - index += 1; - await _createDurationScheduledNotifications( - index, - chabanBridgeForecast, - context, - notificationSate.durationNotificationValue, - notificationSate.durationNotificationValue.durationToString(context), - ); + if ((notificationSate.closingNotificationEnabled && + !notificationSate.timeSlotsEnabledForNotifications) || + (notificationSate.closingNotificationEnabled && + notificationSate.timeSlotsEnabledForNotifications && + hasTimeSlots)) { + index += 1; + await _createClosingScheduledNotifications( + index, + chabanBridgeForecast, + context, + ); + } + if ((notificationSate.timeNotificationEnabled && + !notificationSate.timeSlotsEnabledForNotifications) || + (notificationSate.timeNotificationEnabled && + notificationSate.timeSlotsEnabledForNotifications && + hasTimeSlots)) { + index += 1; + await _createTimeScheduledNotifications( + index, + chabanBridgeForecast, + context, + notificationSate.timeNotificationValue, + ); + } + if ((notificationSate.dayNotificationEnabled && + !notificationSate.timeSlotsEnabledForNotifications) || + (notificationSate.dayNotificationEnabled && + notificationSate.timeSlotsEnabledForNotifications && + hasTimeSlots)) { + var last = chabanBridgeForecast.circulationClosingDate + .previous(notificationSate.dayNotificationValue.weekPosition); + if (weekSeparatedChabanBridgeForecast.isEmpty || + weekSeparatedChabanBridgeForecast.last == last) { + weekSeparatedChabanBridgeForecast.add(last); + } else { + index += 1; + await _createDayScheduledNotifications( + index, + weekSeparatedChabanBridgeForecast.length, + weekSeparatedChabanBridgeForecast.last, + notificationSate.dayNotificationTimeValue, + context, + ); + weekSeparatedChabanBridgeForecast.clear(); + weekSeparatedChabanBridgeForecast.add(last); + } + } + if ((notificationSate.durationNotificationEnabled && + !notificationSate.timeSlotsEnabledForNotifications) || + (notificationSate.durationNotificationEnabled && + notificationSate.timeSlotsEnabledForNotifications && + hasTimeSlots)) { + index += 1; + await _createDurationScheduledNotifications( + index, + chabanBridgeForecast, + context, + notificationSate.durationNotificationValue, + notificationSate.durationNotificationValue + .durationToString(context), + ); + } } } } @@ -318,8 +334,7 @@ class NotificationService { NotificationDetails notificationDetails, ) async { /// Prevent from creating notification in the past AND make sure that the user enable the notification - if (notificationScheduleTime.isAfter(DateTime.now()) && - await _requestPermissions()) { + if (notificationScheduleTime.isAfter(DateTime.now())) { developer.log( 'Creating a notification on channel ${notificationDetails.android!.channelId} with ID $notificationId scheduled at $notificationScheduleTime', name: 'notification-service.on.scheduleNotification', diff --git a/pubspec.lock b/pubspec.lock index 266df46d..a0e95557 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -41,6 +41,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.0.1" + app_settings: + dependency: "direct main" + description: + name: app_settings + sha256: "66715a323ac36d6c8201035ba678777c0d2ea869e4d7064300d95af10c3bb8cb" + url: "https://pub.dev" + source: hosted + version: "4.2.0" archive: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index ba015aec..c5ed75b9 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -33,6 +33,7 @@ dependencies: stream_transform: ^2.0.0 timezone: ^0.9.1 url_launcher: ^6.1.7 + app_settings: ^4.2.0 dev_dependencies: