diff --git a/flutter_local_notifications/README.md b/flutter_local_notifications/README.md index 6e94de977..46fbce92c 100644 --- a/flutter_local_notifications/README.md +++ b/flutter_local_notifications/README.md @@ -17,6 +17,10 @@ This is fork of [flutter_local_notifications](https://github.com/MaikuB/flutter_ * Ability to set your own custom layout for notifications. * More detailed exceptions in `ScheduledNotificationReceiver.onReceive()`. * Ability to set notification window for inexact alarm. See `AndroidNotificationDetails.inexactWindowLengthMillis` +* Ability to store info about shown notifications. See `AndroidNotificationDetails.shownNotificationsInfo`. If you pass this param + it will save in local store if notification show. + * You can load this stored information by `FlutterLocalNotificationsPlugin.getShownNotificationsInfo()` + method and clear stored info by `FlutterLocalNotificationsPlugin.cleanShownNotificationsInfo()` method. ### iOS diff --git a/flutter_local_notifications/android/src/main/java/com/dexterous/flutterlocalnotifications/ScheduledNotificationReceiver.java b/flutter_local_notifications/android/src/main/java/com/dexterous/flutterlocalnotifications/ScheduledNotificationReceiver.java index 74b9f6d51..6b07329b4 100644 --- a/flutter_local_notifications/android/src/main/java/com/dexterous/flutterlocalnotifications/ScheduledNotificationReceiver.java +++ b/flutter_local_notifications/android/src/main/java/com/dexterous/flutterlocalnotifications/ScheduledNotificationReceiver.java @@ -21,130 +21,131 @@ import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; -/** Created by michaelbui on 24/3/18. */ +/** + * Created by michaelbui on 24/3/18. + */ @Keep public class ScheduledNotificationReceiver extends BroadcastReceiver { - - private ShownNotificationsPreferences preferences; - private static final String TAG = "ScheduledNotifReceiver"; - - @Override - @SuppressWarnings("deprecation") - public void onReceive(final Context context, Intent intent) { - preferences = preferences == null ? new ShownNotificationsPreferences(context) : preferences; - - String notificationDetailsJson = - intent.getStringExtra(FlutterLocalNotificationsPlugin.NOTIFICATION_DETAILS); - if (StringUtils.isNullOrEmpty(notificationDetailsJson)) { - // This logic is needed for apps that used the plugin prior to 0.3.4 - - Notification notification; - int notificationId = intent.getIntExtra("notification_id", 0); - - if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.TIRAMISU) { - notification = intent.getParcelableExtra("notification", Notification.class); - } else { - notification = intent.getParcelableExtra("notification"); - } - - if (notification == null) { - // This means the notification is corrupt - FlutterLocalNotificationsPlugin.removeNotificationFromCache(context, notificationId); - Log.e(TAG, "Failed to parse a notification from Intent. ID: " + notificationId); - fault("Notification is null - invalid data.", intent); - return; - } - - notification.when = System.currentTimeMillis(); - NotificationManagerCompat notificationManager = NotificationManagerCompat.from(context); - notificationManager.notify(notificationId, notification); - boolean repeat = intent.getBooleanExtra("repeat", false); - if (!repeat) { - FlutterLocalNotificationsPlugin.removeNotificationFromCache(context, notificationId); - } - } else { - Gson gson = FlutterLocalNotificationsPlugin.buildGson(); - Type type = new TypeToken() {}.getType(); - NotificationDetails notificationDetails = gson.fromJson(notificationDetailsJson, type); - - if (notificationDetails == null) { - fault("NotificationDetails is null - gson.fromJson can't parse it.", intent); - return; - } - - try { - FlutterLocalNotificationsPlugin.showNotification(context, notificationDetails); - } catch (Exception e) { - - DateTimeFormatter formatter = DateTimeFormatter.ISO_LOCAL_DATE_TIME; - - LocalDateTime scheduledDateTime = - LocalDateTime.parse(notificationDetails.scheduledDateTime, formatter); - - LocalDateTime currentDateTime = LocalDateTime.now(); - - // TODO: временное решение, удалить как все починим! - // Проблема: у части давних пользователей остались очень древние оповещения, - // которые планировались по старому методу, который сейчас вызывает ошибку при показе. - // Если мы ловим ошибку при показе именно такого старого оповещения, то отменяем его. - if (scheduledDateTime.isBefore(currentDateTime.minusYears(1))) { - FlutterLocalNotificationsPlugin.cancelByNotificationDetails(context, notificationDetails); - fault("Wrong notification! Date older than a year.", intent); + private static final String TAG = "ScheduledNotifReceiver"; + + @Override + @SuppressWarnings("deprecation") + public void onReceive(final Context context, Intent intent) { + + String notificationDetailsJson = + intent.getStringExtra(FlutterLocalNotificationsPlugin.NOTIFICATION_DETAILS); + if (StringUtils.isNullOrEmpty(notificationDetailsJson)) { + // This logic is needed for apps that used the plugin prior to 0.3.4 + + Notification notification; + int notificationId = intent.getIntExtra("notification_id", 0); + + if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.TIRAMISU) { + notification = intent.getParcelableExtra("notification", Notification.class); + } else { + notification = intent.getParcelableExtra("notification"); + } + + if (notification == null) { + // This means the notification is corrupt + FlutterLocalNotificationsPlugin.removeNotificationFromCache(context, notificationId); + Log.e(TAG, "Failed to parse a notification from Intent. ID: " + notificationId); + fault("Notification is null - invalid data.", intent); + return; + } + + notification.when = System.currentTimeMillis(); + NotificationManagerCompat notificationManager = NotificationManagerCompat.from(context); + notificationManager.notify(notificationId, notification); + boolean repeat = intent.getBooleanExtra("repeat", false); + if (!repeat) { + FlutterLocalNotificationsPlugin.removeNotificationFromCache(context, notificationId); + } } else { - fault("Exception while showing notification.", e, intent); + Gson gson = FlutterLocalNotificationsPlugin.buildGson(); + Type type = new TypeToken() { + }.getType(); + NotificationDetails notificationDetails = gson.fromJson(notificationDetailsJson, type); + + if (notificationDetails == null) { + fault("NotificationDetails is null - gson.fromJson can't parse it.", intent); + return; + } + + try { + FlutterLocalNotificationsPlugin.showNotification(context, notificationDetails); + } catch (Exception e) { + + DateTimeFormatter formatter = DateTimeFormatter.ISO_LOCAL_DATE_TIME; + + LocalDateTime scheduledDateTime = + LocalDateTime.parse(notificationDetails.scheduledDateTime, formatter); + + LocalDateTime currentDateTime = LocalDateTime.now(); + + // TODO: временное решение, удалить как все починим! + // Проблема: у части давних пользователей остались очень древние оповещения, + // которые планировались по старому методу, который сейчас вызывает ошибку при показе. + // Если мы ловим ошибку при показе именно такого старого оповещения, то отменяем его. + if (scheduledDateTime.isBefore(currentDateTime.minusYears(1))) { + FlutterLocalNotificationsPlugin.cancelByNotificationDetails(context, notificationDetails); + fault("Wrong notification! Date older than a year.", intent); + } else { + fault("Exception while showing notification.", e, intent); + } + return; + } + + final String info = notificationDetails.shownNotificationsInfo; + if (!StringUtils.isNullOrEmpty(info)) { + final ShownNotificationsPreferences preferences = new ShownNotificationsPreferences(context); + preferences.saveShownNotificationInfo(info); + } + + try { + FlutterLocalNotificationsPlugin.scheduleNextNotification(context, notificationDetails); + } catch (Exception e) { + fault("Exception while preparing next notification.", e, intent); + return; + } } - return; - } - - final String info = notificationDetails.shownNotificationsInfo; - if (!StringUtils.isNullOrEmpty(info)) { - preferences.saveShownNotificationInfo(info); - } - - try { - FlutterLocalNotificationsPlugin.scheduleNextNotification(context, notificationDetails); - } catch (Exception e) { - fault("Exception while preparing next notification.", e, intent); - return; - } - } - } - - private void fault(String message, Intent intent) { - fault(message, null, intent); - } - - private void fault(String message, Exception e, Intent intent) { - Bundle bundle = intent.getExtras(); - StringBuilder sb = new StringBuilder(); - if (bundle != null) { - sb.append("{\n"); - for (String key : bundle.keySet()) { - sb.append("\t") - .append(key) - .append(" : ") - .append(bundle.get(key) != null ? bundle.get(key) : "NULL"); - sb.append("\n"); - } - sb.append("}"); - } else { - sb.append("NULL"); } - StringBuilder msg = new StringBuilder(message); - msg.append("\n"); - if (e != null) { - msg.append("Exception: ").append(e).append("\n"); + private void fault(String message, Intent intent) { + fault(message, null, intent); } - msg.append("Intent extras: "); - msg.append(sb); - if (e != null) { - StringWriter errors = new StringWriter(); - e.printStackTrace(new PrintWriter(errors)); - msg.append("\n").append("Exception Stack trace:\n").append(errors); - } + private void fault(String message, Exception e, Intent intent) { + Bundle bundle = intent.getExtras(); + StringBuilder sb = new StringBuilder(); + if (bundle != null) { + sb.append("{\n"); + for (String key : bundle.keySet()) { + sb.append("\t") + .append(key) + .append(" : ") + .append(bundle.get(key) != null ? bundle.get(key) : "NULL"); + sb.append("\n"); + } + sb.append("}"); + } else { + sb.append("NULL"); + } - throw new RuntimeException(msg.toString()); - } + StringBuilder msg = new StringBuilder(message); + msg.append("\n"); + if (e != null) { + msg.append("Exception: ").append(e).append("\n"); + } + msg.append("Intent extras: "); + msg.append(sb); + + if (e != null) { + StringWriter errors = new StringWriter(); + e.printStackTrace(new PrintWriter(errors)); + msg.append("\n").append("Exception Stack trace:\n").append(errors); + } + + throw new RuntimeException(msg.toString()); + } }