From 9267688d51a3b7548dad59211f105376befd1fa9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tautvydas=20=C5=A0idlauskas?= <4973296+sidlatau@users.noreply.github.com> Date: Sun, 10 Jan 2021 11:56:23 +0200 Subject: [PATCH 01/63] [flutter_local_notifications] Add ability to set "subText" property in Android notifications (#963) * Add ability to set "subText" property in Android notifications * fixup! Add ability to set "subText" property in Android notifications * fixup! Add ability to set "subText" property in Android notifications --- flutter_local_notifications/CHANGELOG.md | 4 ++ .../FlutterLocalNotificationsPlugin.java | 4 ++ .../models/NotificationDetails.java | 3 ++ .../example/lib/main.dart | 28 ++++++++++++- .../android/method_channel_mappers.dart | 1 + .../android/notification_details.dart | 17 ++++++++ flutter_local_notifications/pubspec.yaml | 4 +- ...form_flutter_local_notifications_test.dart | 40 ++++++++++++++----- 8 files changed, 86 insertions(+), 15 deletions(-) diff --git a/flutter_local_notifications/CHANGELOG.md b/flutter_local_notifications/CHANGELOG.md index 1d1b1bbe5..078a78bd6 100644 --- a/flutter_local_notifications/CHANGELOG.md +++ b/flutter_local_notifications/CHANGELOG.md @@ -1,3 +1,7 @@ +# [3.0.3] + +* [Android] added support for showing subtext in the notification. Thanks to the PR from [sidlatau](https://github.com/sidlatau) + # [3.0.2] * [Android] added support for showing the notification timestamp as a stopwatch instead via the `usesChronometer` argument added to the constructor of the `AndroidNotificationDetails` class. Thanks to the PR from [andymstone](https://github.com/andymstone) diff --git a/flutter_local_notifications/android/src/main/java/com/dexterous/flutterlocalnotifications/FlutterLocalNotificationsPlugin.java b/flutter_local_notifications/android/src/main/java/com/dexterous/flutterlocalnotifications/FlutterLocalNotificationsPlugin.java index bb20bfef8..f720a6afe 100644 --- a/flutter_local_notifications/android/src/main/java/com/dexterous/flutterlocalnotifications/FlutterLocalNotificationsPlugin.java +++ b/flutter_local_notifications/android/src/main/java/com/dexterous/flutterlocalnotifications/FlutterLocalNotificationsPlugin.java @@ -199,6 +199,10 @@ private static Notification createNotification(Context context, NotificationDeta builder.setShortcutId(notificationDetails.shortcutId); } + if (!StringUtils.isNullOrEmpty(notificationDetails.subText)) { + builder.setSubText(notificationDetails.subText); + } + setVisibility(notificationDetails, builder); applyGrouping(notificationDetails, builder); setSound(context, notificationDetails, builder); diff --git a/flutter_local_notifications/android/src/main/java/com/dexterous/flutterlocalnotifications/models/NotificationDetails.java b/flutter_local_notifications/android/src/main/java/com/dexterous/flutterlocalnotifications/models/NotificationDetails.java index 59bdfb506..8c01030bf 100644 --- a/flutter_local_notifications/android/src/main/java/com/dexterous/flutterlocalnotifications/models/NotificationDetails.java +++ b/flutter_local_notifications/android/src/main/java/com/dexterous/flutterlocalnotifications/models/NotificationDetails.java @@ -116,6 +116,7 @@ public class NotificationDetails { private static final String FULL_SCREEN_INTENT = "fullScreenIntent"; private static final String SHORTCUT_ID = "shortcutId"; + private static final String SUB_TEXT = "subText"; public Integer id; @@ -174,6 +175,7 @@ public class NotificationDetails { public Long when; public Boolean fullScreenIntent; public String shortcutId; + public String subText; @@ -247,6 +249,7 @@ private static void readPlatformSpecifics(Map arguments, Notific notificationDetails.fullScreenIntent = (Boolean) platformChannelSpecifics.get((FULL_SCREEN_INTENT)); notificationDetails.shortcutId = (String) platformChannelSpecifics.get(SHORTCUT_ID); notificationDetails.additionalFlags = (int[]) platformChannelSpecifics.get(ADDITIONAL_FLAGS); + notificationDetails.subText = (String) platformChannelSpecifics.get(SUB_TEXT); } } diff --git a/flutter_local_notifications/example/lib/main.dart b/flutter_local_notifications/example/lib/main.dart index 639d519b3..db61aab05 100644 --- a/flutter_local_notifications/example/lib/main.dart +++ b/flutter_local_notifications/example/lib/main.dart @@ -486,6 +486,12 @@ class _HomePageState extends State { await _showNotificationWithCustomTimestamp(); }, ), + PaddedRaisedButton( + buttonText: 'Show notification with custom sub-text', + onPressed: () async { + await _showNotificationWithCustomSubText(); + }, + ), PaddedRaisedButton( buttonText: 'Show notification with chronometer', onPressed: () async { @@ -1286,9 +1292,27 @@ class _HomePageState extends State { payload: 'item x'); } + Future _showNotificationWithCustomSubText() async { + const AndroidNotificationDetails androidPlatformChannelSpecifics = + AndroidNotificationDetails( + 'your channel id', + 'your channel name', + 'your channel description', + importance: Importance.max, + priority: Priority.high, + showWhen: false, + subText: 'custom subtext', + ); + const NotificationDetails platformChannelSpecifics = + NotificationDetails(android: androidPlatformChannelSpecifics); + await flutterLocalNotificationsPlugin.show( + 0, 'plain title', 'plain body', platformChannelSpecifics, + payload: 'item x'); + } + Future _showNotificationWithChronometer() async { final AndroidNotificationDetails androidPlatformChannelSpecifics = - AndroidNotificationDetails( + AndroidNotificationDetails( 'your channel id', 'your channel name', 'your channel description', @@ -1298,7 +1322,7 @@ class _HomePageState extends State { usesChronometer: true, ); final NotificationDetails platformChannelSpecifics = - NotificationDetails(android: androidPlatformChannelSpecifics); + NotificationDetails(android: androidPlatformChannelSpecifics); await flutterLocalNotificationsPlugin.show( 0, 'plain title', 'plain body', platformChannelSpecifics, payload: 'item x'); diff --git a/flutter_local_notifications/lib/src/platform_specifics/android/method_channel_mappers.dart b/flutter_local_notifications/lib/src/platform_specifics/android/method_channel_mappers.dart index 837887138..a81fc43ce 100644 --- a/flutter_local_notifications/lib/src/platform_specifics/android/method_channel_mappers.dart +++ b/flutter_local_notifications/lib/src/platform_specifics/android/method_channel_mappers.dart @@ -249,6 +249,7 @@ extension AndroidNotificationDetailsMapper on AndroidNotificationDetails { 'fullScreenIntent': fullScreenIntent, 'shortcutId': shortcutId, 'additionalFlags': additionalFlags, + 'subText': subText, } ..addAll(_convertStyleInformationToMap()) ..addAll(_convertNotificationSoundToMap(sound)) diff --git a/flutter_local_notifications/lib/src/platform_specifics/android/notification_details.dart b/flutter_local_notifications/lib/src/platform_specifics/android/notification_details.dart index 1fdc6011e..e4fe9f0c2 100644 --- a/flutter_local_notifications/lib/src/platform_specifics/android/notification_details.dart +++ b/flutter_local_notifications/lib/src/platform_specifics/android/notification_details.dart @@ -49,6 +49,7 @@ class AndroidNotificationDetails { this.fullScreenIntent = false, this.shortcutId, this.additionalFlags, + this.subText, }); /// The icon that should be used when displaying the notification. @@ -251,4 +252,20 @@ class AndroidNotificationDetails { /// For a list of a values, refer to the documented constants prefixed with "FLAG_" (without the quotes) at https://developer.android.com/reference/android/app/Notification.html#constants_1. /// For example, use a value of 4 to allow the audio to repeat as documented at https://developer.android.com/reference/android/app/Notification.html#FLAG_INSISTEN final Int32List additionalFlags; + + /// Provides some additional information that is displayed in the + /// notification. + /// + /// No guarantees are given where exactly it is displayed. This information + /// should only be provided if it provides an essential benefit to the + /// understanding of the notification. The more text you provide the less + /// readable it becomes. For example, an email client should only provide the + /// account name here if more than one email account has been added. + /// + /// As of Android 7.0 this information is displayed in the notification header + /// area. On Android versions before 7.0 this will be shown in the third line + /// of text in the platform notification template. You should not be using + /// setProgress(int, int, boolean) at the same time on those versions; they + /// occupy the same place. + final String subText; } diff --git a/flutter_local_notifications/pubspec.yaml b/flutter_local_notifications/pubspec.yaml index e86b734e5..4ff0e57a6 100644 --- a/flutter_local_notifications/pubspec.yaml +++ b/flutter_local_notifications/pubspec.yaml @@ -1,6 +1,6 @@ name: flutter_local_notifications description: A cross platform plugin for displaying and scheduling local notifications for Flutter applications with the ability to customise for each platform. -version: 3.0.2 +version: 3.0.3 homepage: https://github.com/MaikuB/flutter_local_notifications/tree/master/flutter_local_notifications dependencies: @@ -32,4 +32,4 @@ flutter: environment: sdk: ">=2.6.0 <3.0.0" - flutter: ">=1.12.13+hotfix.5" \ No newline at end of file + flutter: ">=1.12.13+hotfix.5" diff --git a/flutter_local_notifications/test/platform_flutter_local_notifications_test.dart b/flutter_local_notifications/test/platform_flutter_local_notifications_test.dart index 0e7474c85..612a56cd4 100644 --- a/flutter_local_notifications/test/platform_flutter_local_notifications_test.dart +++ b/flutter_local_notifications/test/platform_flutter_local_notifications_test.dart @@ -140,6 +140,7 @@ void main() { 'additionalFlags': null, 'fullScreenIntent': false, 'shortcutId': null, + 'subText': null, 'style': AndroidNotificationStyle.defaultStyle.index, 'styleInformation': { 'htmlFormatContent': false, @@ -216,6 +217,7 @@ void main() { 'category': null, 'fullScreenIntent': false, 'shortcutId': null, + 'subText': null, 'additionalFlags': [4, 32], 'style': AndroidNotificationStyle.defaultStyle.index, 'styleInformation': { @@ -296,6 +298,7 @@ void main() { 'additionalFlags': null, 'fullScreenIntent': false, 'shortcutId': null, + 'subText': null, 'style': AndroidNotificationStyle.defaultStyle.index, 'styleInformation': { 'htmlFormatContent': false, @@ -305,21 +308,19 @@ void main() { })); }); - test( - 'show with default Android-specific details with a chronometer', - () async { + test('show with default Android-specific details with a chronometer', + () async { const AndroidInitializationSettings androidInitializationSettings = - AndroidInitializationSettings('app_icon'); + AndroidInitializationSettings('app_icon'); const InitializationSettings initializationSettings = - InitializationSettings(android: androidInitializationSettings); + InitializationSettings(android: androidInitializationSettings); await flutterLocalNotificationsPlugin.initialize(initializationSettings); final int timestamp = DateTime.now().millisecondsSinceEpoch; final AndroidNotificationDetails androidNotificationDetails = - AndroidNotificationDetails( - 'channelId', 'channelName', 'channelDescription', - when: timestamp, - usesChronometer: true); + AndroidNotificationDetails( + 'channelId', 'channelName', 'channelDescription', + when: timestamp, usesChronometer: true); await flutterLocalNotificationsPlugin.show( 1, 'notification title', @@ -339,7 +340,7 @@ void main() { 'channelDescription': 'channelDescription', 'channelShowBadge': true, 'channelAction': - AndroidNotificationChannelAction.createIfNotExists.index, + AndroidNotificationChannelAction.createIfNotExists.index, 'importance': Importance.defaultImportance.value, 'priority': Priority.defaultPriority.value, 'playSound': true, @@ -376,6 +377,7 @@ void main() { 'additionalFlags': null, 'fullScreenIntent': false, 'shortcutId': null, + 'subText': null, 'style': AndroidNotificationStyle.defaultStyle.index, 'styleInformation': { 'htmlFormatContent': false, @@ -383,7 +385,7 @@ void main() { }, }, })); - }); + }); test( 'show with default Android-specific details and custom sound from raw ' @@ -459,6 +461,7 @@ void main() { 'additionalFlags': null, 'fullScreenIntent': false, 'shortcutId': null, + 'subText': null, 'style': AndroidNotificationStyle.defaultStyle.index, 'styleInformation': { 'htmlFormatContent': false, @@ -541,6 +544,7 @@ void main() { 'additionalFlags': null, 'fullScreenIntent': false, 'shortcutId': null, + 'subText': null, 'style': AndroidNotificationStyle.defaultStyle.index, 'styleInformation': { 'htmlFormatContent': false, @@ -622,6 +626,7 @@ void main() { 'additionalFlags': null, 'fullScreenIntent': false, 'shortcutId': null, + 'subText': null, 'style': AndroidNotificationStyle.defaultStyle.index, 'styleInformation': { 'htmlFormatContent': true, @@ -705,6 +710,7 @@ void main() { 'additionalFlags': null, 'fullScreenIntent': false, 'shortcutId': null, + 'subText': null, 'style': AndroidNotificationStyle.bigPicture.index, 'styleInformation': { 'htmlFormatContent': false, @@ -803,6 +809,7 @@ void main() { 'additionalFlags': null, 'fullScreenIntent': false, 'shortcutId': null, + 'subText': null, 'style': AndroidNotificationStyle.bigPicture.index, 'styleInformation': { 'htmlFormatContent': true, @@ -895,6 +902,7 @@ void main() { 'additionalFlags': null, 'fullScreenIntent': false, 'shortcutId': null, + 'subText': null, 'style': AndroidNotificationStyle.bigPicture.index, 'styleInformation': { 'htmlFormatContent': false, @@ -993,6 +1001,7 @@ void main() { 'additionalFlags': null, 'fullScreenIntent': false, 'shortcutId': null, + 'subText': null, 'style': AndroidNotificationStyle.bigPicture.index, 'styleInformation': { 'htmlFormatContent': true, @@ -1083,6 +1092,7 @@ void main() { 'additionalFlags': null, 'fullScreenIntent': false, 'shortcutId': null, + 'subText': null, 'style': AndroidNotificationStyle.inbox.index, 'styleInformation': { 'htmlFormatContent': false, @@ -1177,6 +1187,7 @@ void main() { 'additionalFlags': null, 'fullScreenIntent': false, 'shortcutId': null, + 'subText': null, 'style': AndroidNotificationStyle.inbox.index, 'styleInformation': { 'htmlFormatContent': true, @@ -1262,6 +1273,7 @@ void main() { 'additionalFlags': null, 'fullScreenIntent': false, 'shortcutId': null, + 'subText': null, 'style': AndroidNotificationStyle.media.index, 'styleInformation': { 'htmlFormatContent': false, @@ -1344,6 +1356,7 @@ void main() { 'additionalFlags': null, 'fullScreenIntent': false, 'shortcutId': null, + 'subText': null, 'style': AndroidNotificationStyle.media.index, 'styleInformation': { 'htmlFormatContent': true, @@ -1433,6 +1446,7 @@ void main() { 'additionalFlags': null, 'fullScreenIntent': false, 'shortcutId': null, + 'subText': null, 'style': AndroidNotificationStyle.messaging.index, 'styleInformation': { 'htmlFormatContent': false, @@ -1551,6 +1565,7 @@ void main() { 'additionalFlags': null, 'fullScreenIntent': false, 'shortcutId': null, + 'subText': null, 'style': AndroidNotificationStyle.messaging.index, 'styleInformation': { 'htmlFormatContent': false, @@ -1658,6 +1673,7 @@ void main() { 'additionalFlags': null, 'fullScreenIntent': false, 'shortcutId': null, + 'subText': null, 'style': AndroidNotificationStyle.defaultStyle.index, 'styleInformation': { 'htmlFormatContent': false, @@ -1747,6 +1763,7 @@ void main() { 'additionalFlags': null, 'fullScreenIntent': false, 'shortcutId': null, + 'subText': null, 'style': AndroidNotificationStyle.defaultStyle.index, 'styleInformation': { 'htmlFormatContent': false, @@ -1837,6 +1854,7 @@ void main() { 'additionalFlags': null, 'fullScreenIntent': false, 'shortcutId': null, + 'subText': null, 'style': AndroidNotificationStyle.defaultStyle.index, 'styleInformation': { 'htmlFormatContent': false, From c7c162bc14bb197ff65c863794a97b7ae0819bab Mon Sep 17 00:00:00 2001 From: Erlend Date: Sun, 17 Jan 2021 02:44:15 +0100 Subject: [PATCH 02/63] [flutter_local_notifications] Allows past dates when using matchDateComponents (#978) * add vscode to gitignore * have validateDateIsInTheFuture take matchDateComponents and return if its not null * add example to test daily notification given next 5 second last year as date * helpers: use relative imports * specify type for tz.TZDateTime * fix last year example to use 10 am * rename arg matchDateTimeComponents in validateDateIsInTheFuture * fix example grammar "last year's date" * update showDialog call to use builder prop instead of child * recreated macOS project Co-authored-by: Michael Bui <25263378+MaikuB@users.noreply.github.com> --- .gitignore | 3 +- .../example/lib/main.dart | 109 ++++++++++++------ .../macos/Runner.xcodeproj/project.pbxproj | 96 ++++++--------- .../xcshareddata/xcschemes/Runner.xcscheme | 14 +-- .../macos/Runner/Configs/AppInfo.xcconfig | 4 +- .../macos/Runner/DebugProfile.entitlements | 2 - .../macos/Runner/MainFlutterWindow.swift | 2 +- .../example/macos/Runner/Release.entitlements | 2 - .../macos/{Runner => }/slow_spring_board.aiff | Bin .../flutter_local_notifications_plugin.dart | 2 +- .../lib/src/helpers.dart | 14 ++- .../platform_flutter_local_notifications.dart | 12 +- 12 files changed, 132 insertions(+), 128 deletions(-) rename flutter_local_notifications/example/macos/{Runner => }/slow_spring_board.aiff (100%) diff --git a/.gitignore b/.gitignore index 34c62e31c..1a2736df2 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ -pubspec.lock \ No newline at end of file +pubspec.lock +.vscode diff --git a/flutter_local_notifications/example/lib/main.dart b/flutter_local_notifications/example/lib/main.dart index db61aab05..62bbc2202 100644 --- a/flutter_local_notifications/example/lib/main.dart +++ b/flutter_local_notifications/example/lib/main.dart @@ -316,6 +316,14 @@ class _HomePageState extends State { await _scheduleDailyTenAMNotification(); }, ), + PaddedRaisedButton( + buttonText: + 'Schedule daily 10:00:00 am notification in your ' + "local time zone using last year's date", + onPressed: () async { + await _scheduleDailyTenAMLastYearNotification(); + }, + ), PaddedRaisedButton( buttonText: 'Schedule weekly 10:00:00 am notification in your ' @@ -583,44 +591,45 @@ class _HomePageState extends State { Future _showFullScreenNotification() async { await showDialog( - context: context, - child: AlertDialog( - title: const Text('Turn off your screen'), - content: const Text( - 'to see the full-screen intent in 5 seconds, press OK and TURN ' - 'OFF your screen'), - actions: [ - FlatButton( - onPressed: () { - Navigator.pop(context); - }, - child: const Text('Cancel'), - ), - FlatButton( - onPressed: () async { - await flutterLocalNotificationsPlugin.zonedSchedule( - 0, - 'scheduled title', - 'scheduled body', - tz.TZDateTime.now(tz.local).add(const Duration(seconds: 5)), - const NotificationDetails( - android: AndroidNotificationDetails( - 'full screen channel id', - 'full screen channel name', - 'full screen channel description', - priority: Priority.high, - importance: Importance.high, - fullScreenIntent: true)), - androidAllowWhileIdle: true, - uiLocalNotificationDateInterpretation: - UILocalNotificationDateInterpretation.absoluteTime); - - Navigator.pop(context); - }, - child: const Text('OK'), - ) - ], - )); + context: context, + builder: (_) => AlertDialog( + title: const Text('Turn off your screen'), + content: const Text( + 'to see the full-screen intent in 5 seconds, press OK and TURN ' + 'OFF your screen'), + actions: [ + FlatButton( + onPressed: () { + Navigator.pop(context); + }, + child: const Text('Cancel'), + ), + FlatButton( + onPressed: () async { + await flutterLocalNotificationsPlugin.zonedSchedule( + 0, + 'scheduled title', + 'scheduled body', + tz.TZDateTime.now(tz.local).add(const Duration(seconds: 5)), + const NotificationDetails( + android: AndroidNotificationDetails( + 'full screen channel id', + 'full screen channel name', + 'full screen channel description', + priority: Priority.high, + importance: Importance.high, + fullScreenIntent: true)), + androidAllowWhileIdle: true, + uiLocalNotificationDateInterpretation: + UILocalNotificationDateInterpretation.absoluteTime); + + Navigator.pop(context); + }, + child: const Text('OK'), + ) + ], + ), + ); } Future _showNotificationWithNoBody() async { @@ -1085,6 +1094,25 @@ class _HomePageState extends State { matchDateTimeComponents: DateTimeComponents.time); } + /// To test we don't validate past dates when using `matchDateTimeComponents` + Future _scheduleDailyTenAMLastYearNotification() async { + await flutterLocalNotificationsPlugin.zonedSchedule( + 0, + 'daily scheduled notification title', + 'daily scheduled notification body', + _nextInstanceOfTenAMLastYear(), + const NotificationDetails( + android: AndroidNotificationDetails( + 'daily notification channel id', + 'daily notification channel name', + 'daily notification description'), + ), + androidAllowWhileIdle: true, + uiLocalNotificationDateInterpretation: + UILocalNotificationDateInterpretation.absoluteTime, + matchDateTimeComponents: DateTimeComponents.time); + } + Future _scheduleWeeklyTenAMNotification() async { await flutterLocalNotificationsPlugin.zonedSchedule( 0, @@ -1131,6 +1159,11 @@ class _HomePageState extends State { return scheduledDate; } + tz.TZDateTime _nextInstanceOfTenAMLastYear() { + final tz.TZDateTime now = tz.TZDateTime.now(tz.local); + return tz.TZDateTime(tz.local, now.year - 1, now.month, now.day, 10); + } + tz.TZDateTime _nextInstanceOfMondayTenAM() { tz.TZDateTime scheduledDate = _nextInstanceOfTenAM(); while (scheduledDate.weekday != DateTime.monday) { diff --git a/flutter_local_notifications/example/macos/Runner.xcodeproj/project.pbxproj b/flutter_local_notifications/example/macos/Runner.xcodeproj/project.pbxproj index fd824535a..b7a623f26 100644 --- a/flutter_local_notifications/example/macos/Runner.xcodeproj/project.pbxproj +++ b/flutter_local_notifications/example/macos/Runner.xcodeproj/project.pbxproj @@ -26,12 +26,8 @@ 33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F22044A3C60003C045 /* Assets.xcassets */; }; 33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F42044A3C60003C045 /* MainMenu.xib */; }; 33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */; }; - 33D1A10422148B71006C7A3E /* FlutterMacOS.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 33D1A10322148B71006C7A3E /* FlutterMacOS.framework */; }; - 33D1A10522148B93006C7A3E /* FlutterMacOS.framework in Bundle Framework */ = {isa = PBXBuildFile; fileRef = 33D1A10322148B71006C7A3E /* FlutterMacOS.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; - 5153E5A3247A426900B646BC /* slow_spring_board.aiff in Resources */ = {isa = PBXBuildFile; fileRef = 5153E5A2247A426900B646BC /* slow_spring_board.aiff */; }; - 6B54059AE2FB4C90D9D99DAC /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 762D02E73EA6ABDB0FAFD091 /* Pods_Runner.framework */; }; - D73912F022F37F9E000D13A0 /* App.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D73912EF22F37F9E000D13A0 /* App.framework */; }; - D73912F222F3801D000D13A0 /* App.framework in Bundle Framework */ = {isa = PBXBuildFile; fileRef = D73912EF22F37F9E000D13A0 /* App.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 51644AFE25B3BDAD00A1F97B /* slow_spring_board.aiff in Resources */ = {isa = PBXBuildFile; fileRef = 51644AFD25B3BDAD00A1F97B /* slow_spring_board.aiff */; }; + 6DEC1E4EF7EB33B65651F050 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = AF459FAC079661014F754FE6 /* Pods_Runner.framework */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -51,8 +47,6 @@ dstPath = ""; dstSubfolderSpec = 10; files = ( - D73912F222F3801D000D13A0 /* App.framework in Bundle Framework */, - 33D1A10522148B93006C7A3E /* FlutterMacOS.framework in Bundle Framework */, ); name = "Bundle Framework"; runOnlyForDeploymentPostprocessing = 0; @@ -60,6 +54,7 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ + 13FC150C898181695ADDEFFB /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; 333000ED22D3DE5D00554162 /* Warnings.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Warnings.xcconfig; sourceTree = ""; }; 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GeneratedPluginRegistrant.swift; sourceTree = ""; }; 33CC10ED2044A3C60003C045 /* flutter_local_notifications_example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = flutter_local_notifications_example.app; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -71,18 +66,15 @@ 33CEB47222A05771004F2AC0 /* Flutter-Debug.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Flutter-Debug.xcconfig"; sourceTree = ""; }; 33CEB47422A05771004F2AC0 /* Flutter-Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Flutter-Release.xcconfig"; sourceTree = ""; }; 33CEB47722A0578A004F2AC0 /* Flutter-Generated.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = "Flutter-Generated.xcconfig"; path = "ephemeral/Flutter-Generated.xcconfig"; sourceTree = ""; }; - 33D1A10322148B71006C7A3E /* FlutterMacOS.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = FlutterMacOS.framework; path = Flutter/ephemeral/FlutterMacOS.framework; sourceTree = SOURCE_ROOT; }; 33E51913231747F40026EE4D /* DebugProfile.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = DebugProfile.entitlements; sourceTree = ""; }; 33E51914231749380026EE4D /* Release.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.entitlements; path = Release.entitlements; sourceTree = ""; }; 33E5194F232828860026EE4D /* AppInfo.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = AppInfo.xcconfig; sourceTree = ""; }; - 5153E5A2247A426900B646BC /* slow_spring_board.aiff */ = {isa = PBXFileReference; lastKnownFileType = audio.aiff; path = slow_spring_board.aiff; sourceTree = ""; }; - 615743662875AFFEF6D07C7A /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; - 64D6E94E2E5B847AAB992EF7 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; }; - 762D02E73EA6ABDB0FAFD091 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 4A046A0F9B68C79AE0BC8AEB /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; }; + 51644AFD25B3BDAD00A1F97B /* slow_spring_board.aiff */ = {isa = PBXFileReference; lastKnownFileType = audio.aiff; path = slow_spring_board.aiff; sourceTree = SOURCE_ROOT; }; + 5E9C0662C4DB5505A8130C5C /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Release.xcconfig; sourceTree = ""; }; 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Debug.xcconfig; sourceTree = ""; }; - D73912EF22F37F9E000D13A0 /* App.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = App.framework; path = Flutter/ephemeral/App.framework; sourceTree = SOURCE_ROOT; }; - EBF76761195F0D831D5259D6 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; + AF459FAC079661014F754FE6 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -90,9 +82,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - D73912F022F37F9E000D13A0 /* App.framework in Frameworks */, - 33D1A10422148B71006C7A3E /* FlutterMacOS.framework in Frameworks */, - 6B54059AE2FB4C90D9D99DAC /* Pods_Runner.framework in Frameworks */, + 6DEC1E4EF7EB33B65651F050 /* Pods_Runner.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -117,7 +107,7 @@ 33CEB47122A05771004F2AC0 /* Flutter */, 33CC10EE2044A3C60003C045 /* Products */, D73912EC22F37F3D000D13A0 /* Frameworks */, - 4E06C161AA1A5BA26DB88724 /* Pods */, + 45C6A21E4A86F545DE32BA57 /* Pods */, ); sourceTree = ""; }; @@ -147,8 +137,6 @@ 33CEB47222A05771004F2AC0 /* Flutter-Debug.xcconfig */, 33CEB47422A05771004F2AC0 /* Flutter-Release.xcconfig */, 33CEB47722A0578A004F2AC0 /* Flutter-Generated.xcconfig */, - D73912EF22F37F9E000D13A0 /* App.framework */, - 33D1A10322148B71006C7A3E /* FlutterMacOS.framework */, ); path = Flutter; sourceTree = ""; @@ -156,23 +144,23 @@ 33FAB671232836740065AC1E /* Runner */ = { isa = PBXGroup; children = ( - 5153E5A2247A426900B646BC /* slow_spring_board.aiff */, 33CC10F02044A3C60003C045 /* AppDelegate.swift */, 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */, 33E51913231747F40026EE4D /* DebugProfile.entitlements */, 33E51914231749380026EE4D /* Release.entitlements */, + 51644AFD25B3BDAD00A1F97B /* slow_spring_board.aiff */, 33CC11242044D66E0003C045 /* Resources */, 33BA886A226E78AF003329D5 /* Configs */, ); path = Runner; sourceTree = ""; }; - 4E06C161AA1A5BA26DB88724 /* Pods */ = { + 45C6A21E4A86F545DE32BA57 /* Pods */ = { isa = PBXGroup; children = ( - EBF76761195F0D831D5259D6 /* Pods-Runner.debug.xcconfig */, - 615743662875AFFEF6D07C7A /* Pods-Runner.release.xcconfig */, - 64D6E94E2E5B847AAB992EF7 /* Pods-Runner.profile.xcconfig */, + 5E9C0662C4DB5505A8130C5C /* Pods-Runner.debug.xcconfig */, + 13FC150C898181695ADDEFFB /* Pods-Runner.release.xcconfig */, + 4A046A0F9B68C79AE0BC8AEB /* Pods-Runner.profile.xcconfig */, ); path = Pods; sourceTree = ""; @@ -180,7 +168,7 @@ D73912EC22F37F3D000D13A0 /* Frameworks */ = { isa = PBXGroup; children = ( - 762D02E73EA6ABDB0FAFD091 /* Pods_Runner.framework */, + AF459FAC079661014F754FE6 /* Pods_Runner.framework */, ); name = Frameworks; sourceTree = ""; @@ -192,13 +180,13 @@ isa = PBXNativeTarget; buildConfigurationList = 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */; buildPhases = ( - D6F9983C62DED65726A9AFE4 /* [CP] Check Pods Manifest.lock */, + CB7C7AA6F6C20C41B181EB5D /* [CP] Check Pods Manifest.lock */, 33CC10E92044A3C60003C045 /* Sources */, 33CC10EA2044A3C60003C045 /* Frameworks */, 33CC10EB2044A3C60003C045 /* Resources */, 33CC110E2044A8840003C045 /* Bundle Framework */, 3399D490228B24CF009A79C7 /* ShellScript */, - 6CF284EFDB7F66625505BC1E /* [CP] Embed Pods Frameworks */, + FF9D5ADF2F93716F0052AA35 /* [CP] Embed Pods Frameworks */, ); buildRules = ( ); @@ -218,7 +206,7 @@ attributes = { LastSwiftUpdateCheck = 0920; LastUpgradeCheck = 0930; - ORGANIZATIONNAME = "The Flutter Authors"; + ORGANIZATIONNAME = ""; TargetAttributes = { 33CC10EC2044A3C60003C045 = { CreatedOnToolsVersion = 9.2; @@ -237,7 +225,7 @@ }; }; buildConfigurationList = 33CC10E82044A3C60003C045 /* Build configuration list for PBXProject "Runner" */; - compatibilityVersion = "Xcode 8.0"; + compatibilityVersion = "Xcode 9.3"; developmentRegion = en; hasScannedForEncodings = 0; knownRegions = ( @@ -260,7 +248,7 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( - 5153E5A3247A426900B646BC /* slow_spring_board.aiff in Resources */, + 51644AFE25B3BDAD00A1F97B /* slow_spring_board.aiff in Resources */, 33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */, 33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */, ); @@ -284,7 +272,7 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "echo \"$PRODUCT_NAME.app\" > \"$PROJECT_DIR\"/Flutter/ephemeral/.app_filename\n"; + shellScript = "echo \"$PRODUCT_NAME.app\" > \"$PROJECT_DIR\"/Flutter/ephemeral/.app_filename && \"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh embed\n"; }; 33CC111E2044C6BF0003C045 /* ShellScript */ = { isa = PBXShellScriptBuildPhase; @@ -304,43 +292,45 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "\"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh\ntouch Flutter/ephemeral/tripwire\n"; + shellScript = "\"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh && touch Flutter/ephemeral/tripwire"; }; - 6CF284EFDB7F66625505BC1E /* [CP] Embed Pods Frameworks */ = { + CB7C7AA6F6C20C41B181EB5D /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputFileListPaths = ( ); - name = "[CP] Embed Pods Frameworks"; + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; outputFileListPaths = ( ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt", + ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; showEnvVarsInLog = 0; }; - D6F9983C62DED65726A9AFE4 /* [CP] Check Pods Manifest.lock */ = { + FF9D5ADF2F93716F0052AA35 /* [CP] Embed Pods Frameworks */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist", ); - inputPaths = ( - "${PODS_PODFILE_DIR_PATH}/Podfile.lock", - "${PODS_ROOT}/Manifest.lock", - ); - name = "[CP] Check Pods Manifest.lock"; + name = "[CP] Embed Pods Frameworks"; outputFileListPaths = ( - ); - outputPaths = ( - "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt", + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; showEnvVarsInLog = 0; }; /* End PBXShellScriptBuildPhase section */ @@ -434,10 +424,6 @@ CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; - FRAMEWORK_SEARCH_PATHS = ( - "$(inherited)", - "$(PROJECT_DIR)/Flutter/ephemeral", - ); INFOPLIST_FILE = Runner/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", @@ -564,10 +550,6 @@ CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; - FRAMEWORK_SEARCH_PATHS = ( - "$(inherited)", - "$(PROJECT_DIR)/Flutter/ephemeral", - ); INFOPLIST_FILE = Runner/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", @@ -588,10 +570,6 @@ CODE_SIGN_ENTITLEMENTS = Runner/Release.entitlements; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; - FRAMEWORK_SEARCH_PATHS = ( - "$(inherited)", - "$(PROJECT_DIR)/Flutter/ephemeral", - ); INFOPLIST_FILE = Runner/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", diff --git a/flutter_local_notifications/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/flutter_local_notifications/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme index 03aefde57..f64829ccb 100644 --- a/flutter_local_notifications/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ b/flutter_local_notifications/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -27,18 +27,6 @@ selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" shouldUseLaunchSchemeArgsEnv = "YES"> - - - - - - com.apple.security.network.server - com.apple.security.network.client - diff --git a/flutter_local_notifications/example/macos/Runner/MainFlutterWindow.swift b/flutter_local_notifications/example/macos/Runner/MainFlutterWindow.swift index ca448e100..b5c45b2b3 100644 --- a/flutter_local_notifications/example/macos/Runner/MainFlutterWindow.swift +++ b/flutter_local_notifications/example/macos/Runner/MainFlutterWindow.swift @@ -20,4 +20,4 @@ class MainFlutterWindow: NSWindow { super.awakeFromNib() } -} +} \ No newline at end of file diff --git a/flutter_local_notifications/example/macos/Runner/Release.entitlements b/flutter_local_notifications/example/macos/Runner/Release.entitlements index ee95ab7e5..852fa1a47 100644 --- a/flutter_local_notifications/example/macos/Runner/Release.entitlements +++ b/flutter_local_notifications/example/macos/Runner/Release.entitlements @@ -4,7 +4,5 @@ com.apple.security.app-sandbox - com.apple.security.network.client - diff --git a/flutter_local_notifications/example/macos/Runner/slow_spring_board.aiff b/flutter_local_notifications/example/macos/slow_spring_board.aiff similarity index 100% rename from flutter_local_notifications/example/macos/Runner/slow_spring_board.aiff rename to flutter_local_notifications/example/macos/slow_spring_board.aiff diff --git a/flutter_local_notifications/lib/src/flutter_local_notifications_plugin.dart b/flutter_local_notifications/lib/src/flutter_local_notifications_plugin.dart index 71b4bad44..503e95b8b 100644 --- a/flutter_local_notifications/lib/src/flutter_local_notifications_plugin.dart +++ b/flutter_local_notifications/lib/src/flutter_local_notifications_plugin.dart @@ -298,7 +298,7 @@ class FlutterLocalNotificationsPlugin { id, title, body, scheduledDate, notificationDetails?.android, payload: payload, androidAllowWhileIdle: androidAllowWhileIdle, - matchDateComponents: matchDateTimeComponents); + matchDateTimeComponents: matchDateTimeComponents); } else if (_platform.isIOS) { await resolvePlatformSpecificImplementation< IOSFlutterLocalNotificationsPlugin>() diff --git a/flutter_local_notifications/lib/src/helpers.dart b/flutter_local_notifications/lib/src/helpers.dart index 3f67d9ecd..aec75be0e 100644 --- a/flutter_local_notifications/lib/src/helpers.dart +++ b/flutter_local_notifications/lib/src/helpers.dart @@ -1,5 +1,7 @@ import 'package:timezone/timezone.dart'; +import 'types.dart'; + /// Helper method for validating notification IDs. /// Ensures IDs are valid 32-bit integers. void validateId(int id) { @@ -10,9 +12,15 @@ void validateId(int id) { } } -/// Helper method for validating a date/time value represents a -/// future point in time. -void validateDateIsInTheFuture(TZDateTime scheduledDate) { +/// Helper method for validating a date/time value represents a future point in +/// time where `matchDateTimeComponents` is null. +void validateDateIsInTheFuture( + TZDateTime scheduledDate, + DateTimeComponents matchDateTimeComponents, +) { + if (matchDateTimeComponents != null) { + return; + } if (scheduledDate.isBefore(DateTime.now())) { throw ArgumentError.value( scheduledDate, 'scheduledDate', 'Must be a date in the future'); diff --git a/flutter_local_notifications/lib/src/platform_flutter_local_notifications.dart b/flutter_local_notifications/lib/src/platform_flutter_local_notifications.dart index d088f68a8..c0284c204 100644 --- a/flutter_local_notifications/lib/src/platform_flutter_local_notifications.dart +++ b/flutter_local_notifications/lib/src/platform_flutter_local_notifications.dart @@ -122,10 +122,10 @@ class AndroidFlutterLocalNotificationsPlugin AndroidNotificationDetails notificationDetails, { @required bool androidAllowWhileIdle, String payload, - DateTimeComponents matchDateComponents, + DateTimeComponents matchDateTimeComponents, }) async { validateId(id); - validateDateIsInTheFuture(scheduledDate); + validateDateIsInTheFuture(scheduledDate, matchDateTimeComponents); ArgumentError.checkNotNull(androidAllowWhileIdle, 'androidAllowWhileIdle'); final Map serializedPlatformSpecifics = notificationDetails?.toMap() ?? {}; @@ -140,10 +140,10 @@ class AndroidFlutterLocalNotificationsPlugin 'payload': payload ?? '' } ..addAll(scheduledDate.toMap()) - ..addAll(matchDateComponents == null + ..addAll(matchDateTimeComponents == null ? {} : { - 'matchDateTimeComponents': matchDateComponents.index + 'matchDateTimeComponents': matchDateTimeComponents.index })); } @@ -400,7 +400,7 @@ class IOSFlutterLocalNotificationsPlugin DateTimeComponents matchDateTimeComponents, }) async { validateId(id); - validateDateIsInTheFuture(scheduledDate); + validateDateIsInTheFuture(scheduledDate, matchDateTimeComponents); ArgumentError.checkNotNull(uiLocalNotificationDateInterpretation, 'uiLocalNotificationDateInterpretation'); final Map serializedPlatformSpecifics = @@ -589,7 +589,7 @@ class MacOSFlutterLocalNotificationsPlugin DateTimeComponents matchDateTimeComponents, }) async { validateId(id); - validateDateIsInTheFuture(scheduledDate); + validateDateIsInTheFuture(scheduledDate, matchDateTimeComponents); final Map serializedPlatformSpecifics = notificationDetails?.toMap() ?? {}; await _channel.invokeMethod( From 5a4a0b1a7b6a3ffe1735492083bba804a13333a9 Mon Sep 17 00:00:00 2001 From: davidlepilote Date: Sun, 17 Jan 2021 07:28:45 +0100 Subject: [PATCH 03/63] [flutter_local_notifications] Replace commit() by apply() in Android implementation to avoid blocking UI thread (#980) * Update FlutterLocalNotificationsPlugin.java Modify commit() to apply() to avoid framerate falls when scheduling many notifications. commit() is called on the main thread and is blocking to the contrary of apply(), and it should lead to no bug due to asynchronism whatsoever. * Make commit asynchronous using a Thread and retry if failing Co-authored-by: David Fournier --- .../FlutterLocalNotificationsPlugin.java | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/flutter_local_notifications/android/src/main/java/com/dexterous/flutterlocalnotifications/FlutterLocalNotificationsPlugin.java b/flutter_local_notifications/android/src/main/java/com/dexterous/flutterlocalnotifications/FlutterLocalNotificationsPlugin.java index f720a6afe..7fa2af507 100644 --- a/flutter_local_notifications/android/src/main/java/com/dexterous/flutterlocalnotifications/FlutterLocalNotificationsPlugin.java +++ b/flutter_local_notifications/android/src/main/java/com/dexterous/flutterlocalnotifications/FlutterLocalNotificationsPlugin.java @@ -273,7 +273,22 @@ private static void saveScheduledNotifications(Context context, ArrayList Date: Mon, 18 Jan 2021 12:50:51 +0100 Subject: [PATCH 04/63] [flutter_local_notifications] Add tag and threadIdentifier platform-specific properties (#983) * Add tag property to AndroidNotificationDetails * Add tag property to the details mapper * Add threadIdentifier for iOS and macOS * Fix tests and update README * Add example for tag and threadIdentifier * Update README * Add tag property to the cancel method * Fix tests * Update example to show only one notification with tag * Update docs for the cancel method in Android plugin --- flutter_local_notifications/README.md | 20 ++++-- .../FlutterLocalNotificationsPlugin.java | 23 +++++-- .../models/NotificationDetails.java | 3 + .../example/lib/main.dart | 67 +++++++++++++++++++ .../Classes/FlutterLocalNotificationsPlugin.m | 4 ++ .../flutter_local_notifications_plugin.dart | 14 +++- .../platform_flutter_local_notifications.dart | 18 +++++ .../android/method_channel_mappers.dart | 1 + .../android/notification_details.dart | 7 ++ .../ios/method_channel_mappers.dart | 1 + .../ios/notification_details.dart | 7 ++ .../macos/method_channel_mappers.dart | 1 + .../macos/notification_details.dart | 7 ++ .../FlutterLocalNotificationsPlugin.swift | 4 ++ ...form_flutter_local_notifications_test.dart | 46 ++++++++++++- 15 files changed, 210 insertions(+), 13 deletions(-) diff --git a/flutter_local_notifications/README.md b/flutter_local_notifications/README.md index 782ed962c..6f8f36234 100644 --- a/flutter_local_notifications/README.md +++ b/flutter_local_notifications/README.md @@ -35,9 +35,9 @@ A cross platform plugin for displaying local notifications. - [Displaying a notification](#displaying-a-notification) - [Scheduling a notification](#scheduling-a-notification) - [Periodically show a notification with a specified interval](#periodically-show-a-notification-with-a-specified-interval) - - [Retrieveing pending notification requests](#retrieveing-pending-notification-requests) + - [Retrieving pending notification requests](#retrieving-pending-notification-requests) - [[Android only] Retrieving active notifications](#android-only-retrieving-active-notifications) - - [[Android only] Grouping notifications](#android-only-grouping-notifications) + - [Grouping notifications](#grouping-notifications) - [Cancelling/deleting a notification](#cancellingdeleting-a-notification) - [Cancelling/deleting all notifications](#cancellingdeleting-all-notifications) - [Getting details on if the app was launched via a notification created by this plugin](#getting-details-on-if-the-app-was-launched-via-a-notification-created-by-this-plugin) @@ -448,7 +448,7 @@ await flutterLocalNotificationsPlugin.periodicallyShow(0, 'repeating title', androidAllowWhileIdle: true); ``` -### Retrieveing pending notification requests +### Retrieving pending notification requests ```dart final List pendingNotificationRequests = @@ -465,10 +465,20 @@ final List activeNotifications = ?.getActiveNotifications(); ``` -### [Android only] Grouping notifications +### Grouping notifications + +#### iOS + +For iOS, you can specify `threadIdentifier` in `IOSNotificationDetails`. Notifications with the same `threadIdentifier` will get grouped together automatically. + +```dart +const IOSNotificationDetails iOSPlatformChannelSpecifics = + IOSNotificationDetails(threadIdentifier: 'thread_id'); +``` + +#### Android This is a "translation" of the sample available at https://developer.android.com/training/notify-user/group.html -For iOS, you could just display the summary notification (not shown in the example) as otherwise the following code would show three notifications ```dart const String groupKey = 'com.android.example.WORK_EMAIL'; diff --git a/flutter_local_notifications/android/src/main/java/com/dexterous/flutterlocalnotifications/FlutterLocalNotificationsPlugin.java b/flutter_local_notifications/android/src/main/java/com/dexterous/flutterlocalnotifications/FlutterLocalNotificationsPlugin.java index 7fa2af507..cb6ec935b 100644 --- a/flutter_local_notifications/android/src/main/java/com/dexterous/flutterlocalnotifications/FlutterLocalNotificationsPlugin.java +++ b/flutter_local_notifications/android/src/main/java/com/dexterous/flutterlocalnotifications/FlutterLocalNotificationsPlugin.java @@ -118,6 +118,8 @@ public class FlutterLocalNotificationsPlugin implements MethodCallHandler, Plugi private static final String NOTIFICATION_LAUNCHED_APP = "notificationLaunchedApp"; private static final String INVALID_DRAWABLE_RESOURCE_ERROR_MESSAGE = "The resource %s could not be found. Please make sure it has been added as a drawable resource to your Android head project."; private static final String INVALID_RAW_RESOURCE_ERROR_MESSAGE = "The resource %s could not be found. Please make sure it has been added as a raw resource to your Android head project."; + private static final String CANCEL_ID = "id"; + private static final String CANCEL_TAG = "tag"; static String NOTIFICATION_DETAILS = "notificationDetails"; static Gson gson; private MethodChannel channel; @@ -785,7 +787,12 @@ private static boolean isValidDrawableResource(Context context, String name, Res static void showNotification(Context context, NotificationDetails notificationDetails) { Notification notification = createNotification(context, notificationDetails); NotificationManagerCompat notificationManagerCompat = getNotificationManager(context); - notificationManagerCompat.notify(notificationDetails.id, notification); + + if (notificationDetails.tag != null) { + notificationManagerCompat.notify(notificationDetails.tag, notificationDetails.id, notification); + } else { + notificationManagerCompat.notify(notificationDetails.id, notification); + } } static void zonedScheduleNextNotification(Context context, NotificationDetails notificationDetails) { @@ -993,8 +1000,10 @@ private void pendingNotificationRequests(Result result) { } private void cancel(MethodCall call, Result result) { - Integer id = call.arguments(); - cancelNotification(id); + Map arguments = call.arguments(); + Integer id = (Integer) arguments.get(CANCEL_ID); + String tag = (String) arguments.get(CANCEL_TAG); + cancelNotification(id, tag); result.success(null); } @@ -1125,13 +1134,17 @@ private boolean hasInvalidIcon(Result result, String icon) { return !StringUtils.isNullOrEmpty(icon) && !isValidDrawableResource(applicationContext, icon, result, INVALID_ICON_ERROR_CODE); } - private void cancelNotification(Integer id) { + private void cancelNotification(Integer id, String tag) { Intent intent = new Intent(applicationContext, ScheduledNotificationReceiver.class); PendingIntent pendingIntent = PendingIntent.getBroadcast(applicationContext, id, intent, PendingIntent.FLAG_UPDATE_CURRENT); AlarmManager alarmManager = getAlarmManager(applicationContext); alarmManager.cancel(pendingIntent); NotificationManagerCompat notificationManager = getNotificationManager(applicationContext); - notificationManager.cancel(id); + if (tag == null) { + notificationManager.cancel(id); + } else { + notificationManager.cancel(tag, id); + } removeNotificationFromCache(applicationContext, id); } diff --git a/flutter_local_notifications/android/src/main/java/com/dexterous/flutterlocalnotifications/models/NotificationDetails.java b/flutter_local_notifications/android/src/main/java/com/dexterous/flutterlocalnotifications/models/NotificationDetails.java index 8c01030bf..6873ac810 100644 --- a/flutter_local_notifications/android/src/main/java/com/dexterous/flutterlocalnotifications/models/NotificationDetails.java +++ b/flutter_local_notifications/android/src/main/java/com/dexterous/flutterlocalnotifications/models/NotificationDetails.java @@ -41,6 +41,7 @@ public class NotificationDetails { private static final String SOUND_SOURCE = "soundSource"; private static final String ENABLE_VIBRATION = "enableVibration"; private static final String VIBRATION_PATTERN = "vibrationPattern"; + private static final String TAG = "tag"; private static final String GROUP_KEY = "groupKey"; private static final String SET_AS_GROUP_SUMMARY = "setAsGroupSummary"; private static final String GROUP_ALERT_BEHAVIOR = "groupAlertBehavior"; @@ -176,6 +177,7 @@ public class NotificationDetails { public Boolean fullScreenIntent; public String shortcutId; public String subText; + public String tag; @@ -250,6 +252,7 @@ private static void readPlatformSpecifics(Map arguments, Notific notificationDetails.shortcutId = (String) platformChannelSpecifics.get(SHORTCUT_ID); notificationDetails.additionalFlags = (int[]) platformChannelSpecifics.get(ADDITIONAL_FLAGS); notificationDetails.subText = (String) platformChannelSpecifics.get(SUB_TEXT); + notificationDetails.tag = (String) platformChannelSpecifics.get(TAG); } } diff --git a/flutter_local_notifications/example/lib/main.dart b/flutter_local_notifications/example/lib/main.dart index 62bbc2202..f95378f3a 100644 --- a/flutter_local_notifications/example/lib/main.dart +++ b/flutter_local_notifications/example/lib/main.dart @@ -456,6 +456,18 @@ class _HomePageState extends State { await _showGroupedNotifications(); }, ), + PaddedRaisedButton( + buttonText: 'Show notification with tag', + onPressed: () async { + await _showNotificationWithTag(); + }, + ), + PaddedRaisedButton( + buttonText: 'Cancel notification with tag', + onPressed: () async { + await _cancelNotificationWithTag(); + }, + ), PaddedRaisedButton( buttonText: 'Show ongoing notification', onPressed: () async { @@ -566,6 +578,12 @@ class _HomePageState extends State { await _showNotificationWithAttachment(); }, ), + PaddedRaisedButton( + buttonText: 'Show notifications with thread identifier', + onPressed: () async { + await _showNotificationsWithThreadIdentifier(); + }, + ), ], ], ), @@ -666,6 +684,10 @@ class _HomePageState extends State { await flutterLocalNotificationsPlugin.cancel(0); } + Future _cancelNotificationWithTag() async { + await flutterLocalNotificationsPlugin.cancel(0, tag: 'tag'); + } + Future _showNotificationCustomSound() async { const AndroidNotificationDetails androidPlatformChannelSpecifics = AndroidNotificationDetails( @@ -1026,6 +1048,18 @@ class _HomePageState extends State { 3, 'Attention', 'Two messages', platformChannelSpecifics); } + Future _showNotificationWithTag() async { + const AndroidNotificationDetails androidPlatformChannelSpecifics = + AndroidNotificationDetails( + 'your channel id', 'your channel name', 'your channel description', + importance: Importance.max, priority: Priority.high, tag: 'tag'); + const NotificationDetails platformChannelSpecifics = NotificationDetails( + android: androidPlatformChannelSpecifics, + ); + await flutterLocalNotificationsPlugin.show( + 0, 'first notification', null, platformChannelSpecifics); + } + Future _checkPendingNotificationRequests() async { final List pendingNotificationRequests = await flutterLocalNotificationsPlugin.pendingNotificationRequests(); @@ -1294,6 +1328,39 @@ class _HomePageState extends State { payload: 'item x'); } + Future _showNotificationsWithThreadIdentifier() async { + NotificationDetails buildNotificationDetailsForThread( + String threadIdentifier, + ) { + final IOSNotificationDetails iOSPlatformChannelSpecifics = + IOSNotificationDetails(threadIdentifier: threadIdentifier); + final MacOSNotificationDetails macOSPlatformChannelSpecifics = + MacOSNotificationDetails(threadIdentifier: threadIdentifier); + return NotificationDetails( + iOS: iOSPlatformChannelSpecifics, + macOS: macOSPlatformChannelSpecifics); + } + + final NotificationDetails thread1PlatformChannelSpecifics = + buildNotificationDetailsForThread('thread1'); + final NotificationDetails thread2PlatformChannelSpecifics = + buildNotificationDetailsForThread('thread2'); + + await flutterLocalNotificationsPlugin.show( + 0, 'thread 1', 'first notification', thread1PlatformChannelSpecifics); + await flutterLocalNotificationsPlugin.show( + 1, 'thread 1', 'second notification', thread1PlatformChannelSpecifics); + await flutterLocalNotificationsPlugin.show( + 2, 'thread 1', 'third notification', thread1PlatformChannelSpecifics); + + await flutterLocalNotificationsPlugin.show( + 3, 'thread 2', 'first notification', thread2PlatformChannelSpecifics); + await flutterLocalNotificationsPlugin.show( + 4, 'thread 2', 'second notification', thread2PlatformChannelSpecifics); + await flutterLocalNotificationsPlugin.show( + 5, 'thread 2', 'third notification', thread2PlatformChannelSpecifics); + } + Future _showNotificationWithoutTimestamp() async { const AndroidNotificationDetails androidPlatformChannelSpecifics = AndroidNotificationDetails( diff --git a/flutter_local_notifications/ios/Classes/FlutterLocalNotificationsPlugin.m b/flutter_local_notifications/ios/Classes/FlutterLocalNotificationsPlugin.m index f63d2423c..d4ac01af5 100644 --- a/flutter_local_notifications/ios/Classes/FlutterLocalNotificationsPlugin.m +++ b/flutter_local_notifications/ios/Classes/FlutterLocalNotificationsPlugin.m @@ -52,6 +52,7 @@ @implementation FlutterLocalNotificationsPlugin{ NSString *const ATTACHMENTS = @"attachments"; NSString *const ATTACHMENT_IDENTIFIER = @"identifier"; NSString *const ATTACHMENT_FILE_PATH = @"filePath"; +NSString *const THREAD_IDENTIFIER = @"threadIdentifier"; NSString *const PRESENT_ALERT = @"presentAlert"; NSString *const PRESENT_SOUND = @"presentSound"; NSString *const PRESENT_BADGE = @"presentBadge"; @@ -558,6 +559,9 @@ - (UNMutableNotificationContent *) buildStandardNotificationContent:(NSDictionar if([self containsKey:BADGE_NUMBER forDictionary:platformSpecifics]) { content.badge = [platformSpecifics objectForKey:BADGE_NUMBER]; } + if([self containsKey:THREAD_IDENTIFIER forDictionary:platformSpecifics]) { + content.threadIdentifier = platformSpecifics[THREAD_IDENTIFIER]; + } if([self containsKey:ATTACHMENTS forDictionary:platformSpecifics]) { NSArray *attachments = platformSpecifics[ATTACHMENTS]; if(attachments.count > 0) { diff --git a/flutter_local_notifications/lib/src/flutter_local_notifications_plugin.dart b/flutter_local_notifications/lib/src/flutter_local_notifications_plugin.dart index 503e95b8b..1d371a5f6 100644 --- a/flutter_local_notifications/lib/src/flutter_local_notifications_plugin.dart +++ b/flutter_local_notifications/lib/src/flutter_local_notifications_plugin.dart @@ -203,8 +203,18 @@ class FlutterLocalNotificationsPlugin { /// /// This applies to notifications that have been scheduled and those that /// have already been presented. - Future cancel(int id) async { - await FlutterLocalNotificationsPlatform.instance?.cancel(id); + /// + /// The `tag` parameter specifies the Android tag. If it is provided, + /// then the notification that matches both the id and the tag will + /// be canceled. `tag` has no effect on other platforms. + Future cancel(int id, {String tag}) async { + if (_platform.isAndroid) { + await resolvePlatformSpecificImplementation< + AndroidFlutterLocalNotificationsPlugin>() + ?.cancel(id, tag: tag); + } else { + await FlutterLocalNotificationsPlatform.instance?.cancel(id); + } } /// Cancels/removes all notifications. diff --git a/flutter_local_notifications/lib/src/platform_flutter_local_notifications.dart b/flutter_local_notifications/lib/src/platform_flutter_local_notifications.dart index c0284c204..58d21093c 100644 --- a/flutter_local_notifications/lib/src/platform_flutter_local_notifications.dart +++ b/flutter_local_notifications/lib/src/platform_flutter_local_notifications.dart @@ -244,6 +244,24 @@ class AndroidFlutterLocalNotificationsPlugin }); } + /// Cancel/remove the notification with the specified id. + /// + /// This applies to notifications that have been scheduled and those that + /// have already been presented. + /// + /// The `tag` parameter specifies the Android tag. If it is provided, + /// then the notification that matches both the id and the tag will + /// be canceled. `tag` has no effect on other platforms. + @override + Future cancel(int id, {String tag}) async { + validateId(id); + + return _channel.invokeMethod('cancel', { + 'id': id, + 'tag': tag, + }); + } + /// Creates a notification channel group. /// /// This method is only applicable to Android versions 8.0 or newer. diff --git a/flutter_local_notifications/lib/src/platform_specifics/android/method_channel_mappers.dart b/flutter_local_notifications/lib/src/platform_specifics/android/method_channel_mappers.dart index a81fc43ce..2815cd375 100644 --- a/flutter_local_notifications/lib/src/platform_specifics/android/method_channel_mappers.dart +++ b/flutter_local_notifications/lib/src/platform_specifics/android/method_channel_mappers.dart @@ -250,6 +250,7 @@ extension AndroidNotificationDetailsMapper on AndroidNotificationDetails { 'shortcutId': shortcutId, 'additionalFlags': additionalFlags, 'subText': subText, + 'tag': tag, } ..addAll(_convertStyleInformationToMap()) ..addAll(_convertNotificationSoundToMap(sound)) diff --git a/flutter_local_notifications/lib/src/platform_specifics/android/notification_details.dart b/flutter_local_notifications/lib/src/platform_specifics/android/notification_details.dart index e4fe9f0c2..a3544fba2 100644 --- a/flutter_local_notifications/lib/src/platform_specifics/android/notification_details.dart +++ b/flutter_local_notifications/lib/src/platform_specifics/android/notification_details.dart @@ -50,6 +50,7 @@ class AndroidNotificationDetails { this.shortcutId, this.additionalFlags, this.subText, + this.tag, }); /// The icon that should be used when displaying the notification. @@ -268,4 +269,10 @@ class AndroidNotificationDetails { /// setProgress(int, int, boolean) at the same time on those versions; they /// occupy the same place. final String subText; + + /// The notification tag. + /// + /// Showing notification with the same (tag, id) pair as a currently visible + /// notification will replace the old notification with the new one. + final String tag; } diff --git a/flutter_local_notifications/lib/src/platform_specifics/ios/method_channel_mappers.dart b/flutter_local_notifications/lib/src/platform_specifics/ios/method_channel_mappers.dart index 10ca2f28d..3dd60e33c 100644 --- a/flutter_local_notifications/lib/src/platform_specifics/ios/method_channel_mappers.dart +++ b/flutter_local_notifications/lib/src/platform_specifics/ios/method_channel_mappers.dart @@ -29,6 +29,7 @@ extension IOSNotificationDetailsMapper on IOSNotificationDetails { 'subtitle': subtitle, 'sound': sound, 'badgeNumber': badgeNumber, + 'threadIdentifier': threadIdentifier, 'attachments': attachments ?.map((a) => a.toMap()) // ignore: always_specify_types ?.toList() diff --git a/flutter_local_notifications/lib/src/platform_specifics/ios/notification_details.dart b/flutter_local_notifications/lib/src/platform_specifics/ios/notification_details.dart index 8bde29c2f..5d5d2dfe8 100644 --- a/flutter_local_notifications/lib/src/platform_specifics/ios/notification_details.dart +++ b/flutter_local_notifications/lib/src/platform_specifics/ios/notification_details.dart @@ -11,6 +11,7 @@ class IOSNotificationDetails { this.badgeNumber, this.attachments, this.subtitle, + this.threadIdentifier, }); /// Display an alert when the notification is triggered while app is @@ -64,4 +65,10 @@ class IOSNotificationDetails { /// /// This property is only applicable to iOS 10 or newer. final String subtitle; + + /// Specifies the thread identifier that can be used to group + /// notifications together. + /// + /// This property is only applicable to iOS 10 or newer. + final String threadIdentifier; } diff --git a/flutter_local_notifications/lib/src/platform_specifics/macos/method_channel_mappers.dart b/flutter_local_notifications/lib/src/platform_specifics/macos/method_channel_mappers.dart index 227c5532e..6fddd3e18 100644 --- a/flutter_local_notifications/lib/src/platform_specifics/macos/method_channel_mappers.dart +++ b/flutter_local_notifications/lib/src/platform_specifics/macos/method_channel_mappers.dart @@ -29,6 +29,7 @@ extension MacOSNotificationDetailsMapper on MacOSNotificationDetails { 'subtitle': subtitle, 'sound': sound, 'badgeNumber': badgeNumber, + 'threadIdentifier': threadIdentifier, 'attachments': attachments ?.map((a) => a.toMap()) // ignore: always_specify_types ?.toList() diff --git a/flutter_local_notifications/lib/src/platform_specifics/macos/notification_details.dart b/flutter_local_notifications/lib/src/platform_specifics/macos/notification_details.dart index d750a2d9c..754eab280 100644 --- a/flutter_local_notifications/lib/src/platform_specifics/macos/notification_details.dart +++ b/flutter_local_notifications/lib/src/platform_specifics/macos/notification_details.dart @@ -11,6 +11,7 @@ class MacOSNotificationDetails { this.badgeNumber, this.attachments, this.subtitle, + this.threadIdentifier, }); /// Display an alert when the notification is triggered while app is @@ -62,4 +63,10 @@ class MacOSNotificationDetails { /// Specifies the secondary description. final String subtitle; + + /// Specifies the thread identifier that can be used to group + /// notifications together. + /// + /// This property is only applicable to macOS 10.14 or newer. + final String threadIdentifier; } diff --git a/flutter_local_notifications/macos/Classes/FlutterLocalNotificationsPlugin.swift b/flutter_local_notifications/macos/Classes/FlutterLocalNotificationsPlugin.swift index 64b5544fb..14b4a39bf 100644 --- a/flutter_local_notifications/macos/Classes/FlutterLocalNotificationsPlugin.swift +++ b/flutter_local_notifications/macos/Classes/FlutterLocalNotificationsPlugin.swift @@ -32,6 +32,7 @@ public class FlutterLocalNotificationsPlugin: NSObject, FlutterPlugin, UNUserNot static let attachments = "attachments" static let identifier = "identifier" static let filePath = "filePath" + static let threadIdentifier = "threadIdentifier" } struct DateFormatStrings { @@ -372,6 +373,9 @@ public class FlutterLocalNotificationsPlugin: NSObject, FlutterPlugin, UNUserNot if !(platformSpecifics[MethodCallArguments.presentBadge] is NSNull) && platformSpecifics[MethodCallArguments.presentBadge] != nil { presentBadge = platformSpecifics[MethodCallArguments.presentBadge] as! Bool } + if let threadIdentifier = platformSpecifics[MethodCallArguments.threadIdentifier] as? String { + content.threadIdentifier = threadIdentifier + } if let attachments = platformSpecifics[MethodCallArguments.attachments] as? [Dictionary] { content.attachments = [] for attachment in attachments { diff --git a/flutter_local_notifications/test/platform_flutter_local_notifications_test.dart b/flutter_local_notifications/test/platform_flutter_local_notifications_test.dart index 612a56cd4..cb2375b74 100644 --- a/flutter_local_notifications/test/platform_flutter_local_notifications_test.dart +++ b/flutter_local_notifications/test/platform_flutter_local_notifications_test.dart @@ -146,6 +146,7 @@ void main() { 'htmlFormatContent': false, 'htmlFormatTitle': false, }, + 'tag': null, }, })); }); @@ -224,6 +225,7 @@ void main() { 'htmlFormatContent': false, 'htmlFormatTitle': false, }, + 'tag': null, }, })); }); @@ -304,6 +306,7 @@ void main() { 'htmlFormatContent': false, 'htmlFormatTitle': false, }, + 'tag': null, }, })); }); @@ -383,6 +386,7 @@ void main() { 'htmlFormatContent': false, 'htmlFormatTitle': false, }, + 'tag': null, }, })); }); @@ -467,6 +471,7 @@ void main() { 'htmlFormatContent': false, 'htmlFormatTitle': false, }, + 'tag': null, }, })); }); @@ -550,6 +555,7 @@ void main() { 'htmlFormatContent': false, 'htmlFormatTitle': false, }, + 'tag': null, }, })); }); @@ -632,6 +638,7 @@ void main() { 'htmlFormatContent': true, 'htmlFormatTitle': true, }, + 'tag': null, }, })); }); @@ -723,6 +730,7 @@ void main() { 'htmlFormatSummaryText': false, 'hideExpandedLargeIcon': false, }, + 'tag': null, }, })); }); @@ -824,6 +832,7 @@ void main() { 'htmlFormatSummaryText': true, 'hideExpandedLargeIcon': true, }, + 'tag': null, }, })); }); @@ -915,6 +924,7 @@ void main() { 'htmlFormatSummaryText': false, 'hideExpandedLargeIcon': false, }, + 'tag': null, }, })); }); @@ -1016,6 +1026,7 @@ void main() { 'htmlFormatSummaryText': true, 'hideExpandedLargeIcon': true, }, + 'tag': null, }, })); }); @@ -1104,6 +1115,7 @@ void main() { 'htmlFormatSummaryText': false, 'htmlFormatLines': false, }, + 'tag': null, }, })); }); @@ -1199,6 +1211,7 @@ void main() { 'htmlFormatSummaryText': true, 'htmlFormatLines': true, }, + 'tag': null, }, })); }); @@ -1279,6 +1292,7 @@ void main() { 'htmlFormatContent': false, 'htmlFormatTitle': false, }, + 'tag': null, }, })); }); @@ -1362,6 +1376,7 @@ void main() { 'htmlFormatContent': true, 'htmlFormatTitle': true, }, + 'tag': null, }, })); }); @@ -1470,6 +1485,7 @@ void main() { } ], }, + 'tag': null, }, })); }); @@ -1591,6 +1607,7 @@ void main() { } ], }, + 'tag': null, }, })); }); @@ -1679,6 +1696,7 @@ void main() { 'htmlFormatContent': false, 'htmlFormatTitle': false, }, + 'tag': null, }, })); }); @@ -1769,6 +1787,7 @@ void main() { 'htmlFormatContent': false, 'htmlFormatTitle': false, }, + 'tag': null, }, })); }); @@ -1860,6 +1879,7 @@ void main() { 'htmlFormatContent': false, 'htmlFormatTitle': false, }, + 'tag': null, }, })); }); @@ -1986,7 +2006,22 @@ void main() { test('cancel', () async { await flutterLocalNotificationsPlugin.cancel(1); - expect(log, [isMethodCall('cancel', arguments: 1)]); + expect(log, [ + isMethodCall('cancel', arguments: { + 'id': 1, + 'tag': null, + }) + ]); + }); + + test('cancel with tag', () async { + await flutterLocalNotificationsPlugin.cancel(1, tag: 'tag'); + expect(log, [ + isMethodCall('cancel', arguments: { + 'id': 1, + 'tag': 'tag', + }) + ]); }); test('cancelAll', () async { @@ -2141,6 +2176,7 @@ void main() { 'subtitle': 'a subtitle', 'sound': 'sound.mp3', 'badgeNumber': 1, + 'threadIdentifier': null, 'attachments': >[ { 'filePath': 'video.mp4', @@ -2203,6 +2239,7 @@ void main() { 'subtitle': null, 'sound': 'sound.mp3', 'badgeNumber': 1, + 'threadIdentifier': null, 'attachments': >[ { 'filePath': 'video.mp4', @@ -2266,6 +2303,7 @@ void main() { 'subtitle': null, 'sound': 'sound.mp3', 'badgeNumber': 1, + 'threadIdentifier': null, 'attachments': >[ { 'filePath': 'video.mp4', @@ -2330,6 +2368,7 @@ void main() { 'subtitle': null, 'sound': 'sound.mp3', 'badgeNumber': 1, + 'threadIdentifier': null, 'attachments': >[ { 'filePath': 'video.mp4', @@ -2491,6 +2530,7 @@ void main() { presentSound: true, sound: 'sound.mp3', badgeNumber: 1, + threadIdentifier: 'thread', attachments: [ MacOSNotificationAttachment('video.mp4', identifier: '2b3f705f-a680-4c9f-8075-a46a70e28373'), @@ -2513,6 +2553,7 @@ void main() { 'presentSound': true, 'sound': 'sound.mp3', 'badgeNumber': 1, + 'threadIdentifier': 'thread', 'attachments': >[ { 'filePath': 'video.mp4', @@ -2573,6 +2614,7 @@ void main() { 'presentSound': true, 'sound': 'sound.mp3', 'badgeNumber': 1, + 'threadIdentifier': null, 'attachments': >[ { 'filePath': 'video.mp4', @@ -2634,6 +2676,7 @@ void main() { 'presentSound': true, 'sound': 'sound.mp3', 'badgeNumber': 1, + 'threadIdentifier': null, 'attachments': >[ { 'filePath': 'video.mp4', @@ -2696,6 +2739,7 @@ void main() { 'presentSound': true, 'sound': 'sound.mp3', 'badgeNumber': 1, + 'threadIdentifier': null, 'attachments': >[ { 'filePath': 'video.mp4', From 41ae0b1f7af9e32301c5b2a831ad91046d4ee18b Mon Sep 17 00:00:00 2001 From: Michael Bui <25263378+MaikuB@users.noreply.github.com> Date: Sat, 30 Jan 2021 10:47:48 +1100 Subject: [PATCH 05/63] [flutter_local_notifications] recreate example apps and change initialize to not trigger onSelectNotification for launch notifications (#1000) * recreate iOS example app in Swift * recreate Android example app using Kotlin without MainActivityTest file * switched from deprecated e2e package to integration_test * change initialize method to no longer trigger onSelectNotification for a launch notification and update related docs * bump plugin version to 4.0.0-dev.1 * add acknowledge in changelog and add entry on example apps being recreated * update integration test task in cirrus script * add changelog entry on using integration_test and instead of e2e * rename variable for the name of the route associated the pages in the example app * remove Proguard rules related to Flutter as they are no longer needed * bump plugin dependencies * update changelog and bump plugin to 4.0.0-dev.2 * update changelog and plugin version for 4.0.0 release --- .cirrus.yml | 2 +- flutter_local_notifications/CHANGELOG.md | 14 + flutter_local_notifications/README.md | 6 +- .../FlutterLocalNotificationsPlugin.java | 4 - .../example/android/.gitignore | 17 +- .../example/android/app/build.gradle | 52 ++-- .../example/android/app/proguard-rules.pro | 9 - .../MainActivityTest.java | 9 +- .../android/app/src/debug/AndroidManifest.xml | 7 + .../android/app/src/main/AndroidManifest.xml | 42 +-- .../MainActivity.java | 44 ---- .../MainActivity.kt | 37 +++ .../res/drawable-v21/launch_background.xml | 12 + .../app/src/main/res/values-night/styles.xml | 18 ++ .../app/src/main/res/values/styles.xml | 12 +- .../app/src/profile/AndroidManifest.xml | 7 + .../example/android/build.gradle | 9 +- .../gradle/wrapper/gradle-wrapper.properties | 4 +- .../example/android/settings.gradle | 18 +- .../flutter_local_notifications_test.dart} | 4 +- .../ios/Runner.xcodeproj/project.pbxproj | 249 +++++++++++------- .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../xcshareddata/WorkspaceSettings.xcsettings | 8 + .../xcshareddata/xcschemes/Runner.xcscheme | 6 +- .../xcshareddata/WorkspaceSettings.xcsettings | 8 + .../example/ios/Runner/AppDelegate.h | 6 - .../example/ios/Runner/AppDelegate.m | 35 --- .../example/ios/Runner/AppDelegate.swift | 27 ++ .../Icon-App-1024x1024@1x.png | Bin 11112 -> 10932 bytes .../example/ios/Runner/Info.plist | 6 +- .../ios/Runner/Runner-Bridging-Header.h | 1 + .../example/ios/Runner/main.m | 9 - .../example/lib/main.dart | 37 ++- .../example/pubspec.yaml | 8 +- .../flutter_local_notifications_e2e_test.dart | 5 - .../example/test_driver/integration_test.dart | 3 + .../Classes/FlutterLocalNotificationsPlugin.m | 21 +- .../flutter_local_notifications_plugin.dart | 3 - .../FlutterLocalNotificationsPlugin.swift | 3 - flutter_local_notifications/pubspec.yaml | 15 +- 40 files changed, 448 insertions(+), 337 deletions(-) rename flutter_local_notifications/example/android/app/src/androidTest/java/com/dexterous/{flutterlocalnotifications => flutter_local_notifications_example}/MainActivityTest.java (62%) create mode 100644 flutter_local_notifications/example/android/app/src/debug/AndroidManifest.xml delete mode 100644 flutter_local_notifications/example/android/app/src/main/java/com/dexterous/flutterlocalnotificationsexample/MainActivity.java create mode 100644 flutter_local_notifications/example/android/app/src/main/kotlin/com/dexterous/flutter_local_notifications_example/MainActivity.kt create mode 100644 flutter_local_notifications/example/android/app/src/main/res/drawable-v21/launch_background.xml create mode 100644 flutter_local_notifications/example/android/app/src/main/res/values-night/styles.xml create mode 100644 flutter_local_notifications/example/android/app/src/profile/AndroidManifest.xml rename flutter_local_notifications/example/{test_driver/flutter_local_notifications_e2e.dart => integration_test/flutter_local_notifications_test.dart} (96%) create mode 100644 flutter_local_notifications/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 flutter_local_notifications/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings create mode 100644 flutter_local_notifications/example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings delete mode 100644 flutter_local_notifications/example/ios/Runner/AppDelegate.h delete mode 100644 flutter_local_notifications/example/ios/Runner/AppDelegate.m create mode 100644 flutter_local_notifications/example/ios/Runner/AppDelegate.swift create mode 100644 flutter_local_notifications/example/ios/Runner/Runner-Bridging-Header.h delete mode 100644 flutter_local_notifications/example/ios/Runner/main.m delete mode 100644 flutter_local_notifications/example/test_driver/flutter_local_notifications_e2e_test.dart create mode 100644 flutter_local_notifications/example/test_driver/integration_test.dart diff --git a/.cirrus.yml b/.cirrus.yml index b0b47f219..3ab328339 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -73,4 +73,4 @@ task: test_script: - cd flutter_local_notifications - cd example - - flutter drive --driver=test_driver/flutter_local_notifications_e2e_test.dart test_driver/flutter_local_notifications_e2e.dart + - flutter drive --driver=test_driver/integration_test.dart --target=integration_test/flutter_local_notifications_test.dart diff --git a/flutter_local_notifications/CHANGELOG.md b/flutter_local_notifications/CHANGELOG.md index 078a78bd6..09eda086d 100644 --- a/flutter_local_notifications/CHANGELOG.md +++ b/flutter_local_notifications/CHANGELOG.md @@ -1,3 +1,17 @@ +# [4.0.0] + +* **Breaking change** calling `initialize` will no longer trigger the `onSelectNotification` if a notification was tapped on prior to calling `initialize`. This was done as the `getNotificationAppLaunchDetails` method already provided a way to handle when an application was launched by a notification. Furthermore, calling `initialize` multiple times (e.g. on different pages) would have previously caused the `onSelectNotification` callback multiples times as well. This potentially results in the same notification being processed again +* **Breaking change** the `matchDateComponents` parameter has been renamed to `matchDateTimeComponents` +* Dates in the past can now be used with `zonedSchedule` when a value for the `matchDateTimeComponents` parameter has been specified to create recurring notifications. Thanks to the PR from [Erlend](https://github.com/erf) for implementing this and the previous change +* [Android] notification data is now saved to shared preferences in a background thread to minimise jank. Thanks to the PR from [davidlepilote](https://github.com/davidlepilote) +* [Android] the `tag` property has been added to the `AndroidNotificationDetails` class. This allows notifications on Android to be uniquely identifier through the use of the value of the `tag` and the `id` passed to the method for show/schedule the notification +* [Android] the optional `tag` argument has been added to the `cancel` method for the `FlutterLocalNotificationsPlugin` and `AndroidFlutterLocalNotificationsPlugin` classes. This can be used to cancel notifications where the `tag` has been specified +* [iOS][macOS] the `threadIdentifier` property has been added to the `IOSNotificationDetails` and `MacOSNotificationDetails` classes. This can be used to group notifications on iOS 10.0 or newer, and macOS 10.14 or newer. Thanks to the PR from [Marcin Chudy](https://github.com/mchudy) for adding this and the `tag` property for Android notifications +* The Android and iOS example applications have been recreated in Kotlin and Swift respectively +* Updated example application's dev dependency on the deprecated `e2e` for integration tests to use `integration_test` instead +* Bumped Flutter dependencies +* Example app cleanup including updating Proguard rules as specifying the rules for Flutter were no longer needed + # [3.0.3] * [Android] added support for showing subtext in the notification. Thanks to the PR from [sidlatau](https://github.com/sidlatau) diff --git a/flutter_local_notifications/README.md b/flutter_local_notifications/README.md index 6f8f36234..d400aff40 100644 --- a/flutter_local_notifications/README.md +++ b/flutter_local_notifications/README.md @@ -282,7 +282,7 @@ await flutterLocalNotificationsPlugin.initialize(initializationSettings, onSelectNotification: selectNotification); ``` -Initialisation should only be done **once**, and this can be done is in the `main` function of your application. Alternatively, this can be done within the first page shown in your app. Developers can refer to the example app that has code for the initialising within the `main` function. The code above has been simplified for explaining the concepts. Here we have specified the default icon to use for notifications on Android (refer to the *Android setup* section) and designated the function (`selectNotification`) that should fire when a notification has been tapped on via the `onSelectNotification` callback. Specifying this callback is entirely optional but here it will trigger navigation to another page and display the payload associated with the notification. +Initialisation can be done is in the `main` function of your application or can be done within the first page shown in your app. Developers can refer to the example app that has code for the initialising within the `main` function. The code above has been simplified for explaining the concepts. Here we have specified the default icon to use for notifications on Android (refer to the *Android setup* section) and designated the function (`selectNotification`) that should fire when a notification has been tapped on via the `onSelectNotification` callback. Specifying this callback is entirely optional but here it will trigger navigation to another page and display the payload associated with the notification. ```dart Future selectNotification(String payload) async { @@ -302,9 +302,7 @@ The `IOSInitializationSettings` and `MacOSInitializationSettings` provides defau On iOS and macOS, initialisation may show a prompt to requires users to give the application permission to display notifications (note: permissions don't need to be requested on Android). Depending on when this happens, this may not be the ideal user experience for your application. If so, please refer to the next section on how to work around this. - - -⚠ If the app has been launched by tapping on a notification created by this plugin, calling `initialize` is what will trigger the `onSelectNotification` to trigger to handle the notification that the user tapped on. An alternative to handling the "launch notification" is to call the `getNotificationAppLaunchDetails` method that is available in the plugin. This could be used, for example, to change the home route of the app for deep-linking. Calling `initialize` will still cause the `onSelectNotification` callback to fire for the launch notification. It will be up to developers to ensure that they don't process the same notification twice (e.g. by storing and comparing the notification id). +*Note*: from version 4.0 of the plugin, calling `initialize` will now trigger the `onSelectNotification` callback when the application was started by tapping on a notification to trigger. Use the `getNotificationAppLaunchDetails` method that is available in the plugin if you need to handle a notification triggering the launch for an app e.g. change the home route of the app for deep-linking. ### [iOS (all supported versions) and macOS 10.14+] Requesting notification permissions diff --git a/flutter_local_notifications/android/src/main/java/com/dexterous/flutterlocalnotifications/FlutterLocalNotificationsPlugin.java b/flutter_local_notifications/android/src/main/java/com/dexterous/flutterlocalnotifications/FlutterLocalNotificationsPlugin.java index cb6ec935b..6a699e984 100644 --- a/flutter_local_notifications/android/src/main/java/com/dexterous/flutterlocalnotifications/FlutterLocalNotificationsPlugin.java +++ b/flutter_local_notifications/android/src/main/java/com/dexterous/flutterlocalnotifications/FlutterLocalNotificationsPlugin.java @@ -1071,10 +1071,6 @@ private void initialize(MethodCall call, Result result) { SharedPreferences.Editor editor = sharedPreferences.edit(); editor.putString(DEFAULT_ICON, defaultIcon); tryCommittingInBackground(editor, 3); - - if (mainActivity != null && !launchedActivityFromHistory(mainActivity.getIntent())) { - sendNotificationPayloadMessage(mainActivity.getIntent()); - } result.success(true); } diff --git a/flutter_local_notifications/example/android/.gitignore b/flutter_local_notifications/example/android/.gitignore index 65b7315af..0a741cb43 100644 --- a/flutter_local_notifications/example/android/.gitignore +++ b/flutter_local_notifications/example/android/.gitignore @@ -1,10 +1,11 @@ -*.iml -*.class -.gradle +gradle-wrapper.jar +/.gradle +/captures/ +/gradlew +/gradlew.bat /local.properties -/.idea/workspace.xml -/.idea/libraries -.DS_Store -/build -/captures GeneratedPluginRegistrant.java + +# Remember to never publicly share your keystore. +# See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app +key.properties diff --git a/flutter_local_notifications/example/android/app/build.gradle b/flutter_local_notifications/example/android/app/build.gradle index 5ba544458..2bae4d49b 100644 --- a/flutter_local_notifications/example/android/app/build.gradle +++ b/flutter_local_notifications/example/android/app/build.gradle @@ -11,22 +11,34 @@ if (flutterRoot == null) { throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.") } +def flutterVersionCode = localProperties.getProperty('flutter.versionCode') +if (flutterVersionCode == null) { + flutterVersionCode = '1' +} + +def flutterVersionName = localProperties.getProperty('flutter.versionName') +if (flutterVersionName == null) { + flutterVersionName = '1.0' +} + apply plugin: 'com.android.application' +apply plugin: 'kotlin-android' apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" android { - compileSdkVersion 29 + compileSdkVersion 30 - lintOptions { - disable 'InvalidPackage' + sourceSets { + main.java.srcDirs += 'src/main/kotlin' } defaultConfig { - applicationId "com.dexterous.flutterlocalnotificationsexample" + // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). + applicationId "com.dexterous.flutter_local_notifications_example" minSdkVersion 16 - targetSdkVersion 29 - versionCode 1 - versionName "1.0" + targetSdkVersion 30 + versionCode flutterVersionCode.toInteger() + versionName flutterVersionName testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" } @@ -35,23 +47,13 @@ android { // TODO: Add your own signing config for the release build. // Signing with the debug keys for now, so `flutter run --release` works. signingConfig signingConfigs.debug - - // Enables code shrinking, obfuscation, and optimization for only - // your project's release build type. - minifyEnabled true - - // Enables resource shrinking, which is performed by the - // Android Gradle plugin. - shrinkResources true - - // Includes the default ProGuard rules files that are packaged with - // the Android Gradle plugin. To learn more, go to the section about - // R8 configuration files. - proguardFiles getDefaultProguardFile( - 'proguard-android-optimize.txt'), - 'proguard-rules.pro' } } + + // Temporary workaround as per https://issuetracker.google.com/issues/158060799 + lintOptions { + checkReleaseBuilds false + } } flutter { @@ -59,10 +61,10 @@ flutter { } dependencies { + implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" + implementation "com.jakewharton.threetenabp:threetenabp:1.2.3" + testImplementation 'junit:junit:4.12' androidTestImplementation 'androidx.test:runner:1.2.0' - androidTestImplementation 'androidx.test:rules:1.2.0' androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0' - implementation "androidx.core:core:1.2.0" - implementation "com.jakewharton.threetenabp:threetenabp:1.2.3" } diff --git a/flutter_local_notifications/example/android/app/proguard-rules.pro b/flutter_local_notifications/example/android/app/proguard-rules.pro index fd1b02bdb..2d6b4a959 100644 --- a/flutter_local_notifications/example/android/app/proguard-rules.pro +++ b/flutter_local_notifications/example/android/app/proguard-rules.pro @@ -1,12 +1,3 @@ -## Flutter wrapper --keep class io.flutter.app.** { *; } --keep class io.flutter.plugin.** { *; } --keep class io.flutter.util.** { *; } --keep class io.flutter.view.** { *; } --keep class io.flutter.** { *; } --keep class io.flutter.plugins.** { *; } --dontwarn io.flutter.embedding.** - ## Gson rules # Gson uses generic type information stored in a class file when working with fields. Proguard # removes such information by default, so configure it to keep all of it. diff --git a/flutter_local_notifications/example/android/app/src/androidTest/java/com/dexterous/flutterlocalnotifications/MainActivityTest.java b/flutter_local_notifications/example/android/app/src/androidTest/java/com/dexterous/flutter_local_notifications_example/MainActivityTest.java similarity index 62% rename from flutter_local_notifications/example/android/app/src/androidTest/java/com/dexterous/flutterlocalnotifications/MainActivityTest.java rename to flutter_local_notifications/example/android/app/src/androidTest/java/com/dexterous/flutter_local_notifications_example/MainActivityTest.java index 5c60399f6..a9670a26f 100644 --- a/flutter_local_notifications/example/android/app/src/androidTest/java/com/dexterous/flutterlocalnotifications/MainActivityTest.java +++ b/flutter_local_notifications/example/android/app/src/androidTest/java/com/dexterous/flutter_local_notifications_example/MainActivityTest.java @@ -1,13 +1,12 @@ -package com.dexterous.flutterlocalnotifications; +package com.dexterous.flutter_local_notifications_example; import androidx.test.rule.ActivityTestRule; - -import com.dexterous.flutterlocalnotificationsexample.MainActivity; - -import dev.flutter.plugins.e2e.FlutterTestRunner; import org.junit.Rule; import org.junit.runner.RunWith; +import dev.flutter.plugins.integration_test.FlutterTestRunner; + + @RunWith(FlutterTestRunner.class) public class MainActivityTest { @Rule diff --git a/flutter_local_notifications/example/android/app/src/debug/AndroidManifest.xml b/flutter_local_notifications/example/android/app/src/debug/AndroidManifest.xml new file mode 100644 index 000000000..c1569a936 --- /dev/null +++ b/flutter_local_notifications/example/android/app/src/debug/AndroidManifest.xml @@ -0,0 +1,7 @@ + + + + diff --git a/flutter_local_notifications/example/android/app/src/main/AndroidManifest.xml b/flutter_local_notifications/example/android/app/src/main/AndroidManifest.xml index 538e279e7..bf3ee1689 100644 --- a/flutter_local_notifications/example/android/app/src/main/AndroidManifest.xml +++ b/flutter_local_notifications/example/android/app/src/main/AndroidManifest.xml @@ -1,37 +1,39 @@ - - - - - - + + android:windowSoftInputMode="adjustResize"> + + + + + This is used by the Flutter tool to generate GeneratedPluginRegistrant.java --> diff --git a/flutter_local_notifications/example/android/app/src/main/java/com/dexterous/flutterlocalnotificationsexample/MainActivity.java b/flutter_local_notifications/example/android/app/src/main/java/com/dexterous/flutterlocalnotificationsexample/MainActivity.java deleted file mode 100644 index 5a5ab53fb..000000000 --- a/flutter_local_notifications/example/android/app/src/main/java/com/dexterous/flutterlocalnotificationsexample/MainActivity.java +++ /dev/null @@ -1,44 +0,0 @@ -package com.dexterous.flutterlocalnotificationsexample; - -import android.content.ContentResolver; -import android.content.Context; -import android.media.RingtoneManager; -import android.os.Bundle; - -import java.util.TimeZone; - -import io.flutter.embedding.android.FlutterActivity; -import io.flutter.embedding.engine.FlutterEngine; -import io.flutter.plugin.common.MethodChannel; -import io.flutter.plugins.GeneratedPluginRegistrant; - -public class MainActivity extends FlutterActivity { - private static String resourceToUriString(Context context, int resId) { - return - ContentResolver.SCHEME_ANDROID_RESOURCE - + "://" - + context.getResources().getResourcePackageName(resId) - + "/" - + context.getResources().getResourceTypeName(resId) - + "/" - + context.getResources().getResourceEntryName(resId); - } - - @Override - public void configureFlutterEngine(FlutterEngine flutterEngine) { - GeneratedPluginRegistrant.registerWith(flutterEngine); - new MethodChannel(flutterEngine.getDartExecutor(), "dexterx.dev/flutter_local_notifications_example").setMethodCallHandler( - (call, result) -> { - if ("drawableToUri".equals(call.method)) { - int resourceId = MainActivity.this.getResources().getIdentifier((String) call.arguments, "drawable", MainActivity.this.getPackageName()); - result.success(resourceToUriString(MainActivity.this.getApplicationContext(), resourceId)); - } - if("getAlarmUri".equals(call.method)) { - result.success(RingtoneManager.getDefaultUri(RingtoneManager.TYPE_ALARM).toString()); - } - if("getTimeZoneName".equals(call.method)) { - result.success(TimeZone.getDefault().getID()); - } - }); - } -} \ No newline at end of file diff --git a/flutter_local_notifications/example/android/app/src/main/kotlin/com/dexterous/flutter_local_notifications_example/MainActivity.kt b/flutter_local_notifications/example/android/app/src/main/kotlin/com/dexterous/flutter_local_notifications_example/MainActivity.kt new file mode 100644 index 000000000..c260cace1 --- /dev/null +++ b/flutter_local_notifications/example/android/app/src/main/kotlin/com/dexterous/flutter_local_notifications_example/MainActivity.kt @@ -0,0 +1,37 @@ +package com.dexterous.flutter_local_notifications_example + +import android.content.ContentResolver +import android.content.Context +import android.media.RingtoneManager +import io.flutter.embedding.android.FlutterActivity +import io.flutter.embedding.engine.FlutterEngine +import io.flutter.plugin.common.MethodChannel +import java.util.* + + +class MainActivity: FlutterActivity() { + override fun configureFlutterEngine(flutterEngine: FlutterEngine) { + super.configureFlutterEngine(flutterEngine) + MethodChannel(flutterEngine.dartExecutor.binaryMessenger, "dexterx.dev/flutter_local_notifications_example").setMethodCallHandler { call, result -> + if ("drawableToUri" == call.method) { + val resourceId = this@MainActivity.resources.getIdentifier(call.arguments as String, "drawable", this@MainActivity.packageName) + result.success(resourceToUriString(this@MainActivity.applicationContext, resourceId)) + } + if ("getAlarmUri" == call.method) { + result.success(RingtoneManager.getDefaultUri(RingtoneManager.TYPE_ALARM).toString()) + } + if ("getTimeZoneName" == call.method) { + result.success(TimeZone.getDefault().id) + } + } + } + + private fun resourceToUriString(context: Context, resId: Int): String? { + return (ContentResolver.SCHEME_ANDROID_RESOURCE + "://" + + context.resources.getResourcePackageName(resId) + + "/" + + context.resources.getResourceTypeName(resId) + + "/" + + context.resources.getResourceEntryName(resId)) + } +} diff --git a/flutter_local_notifications/example/android/app/src/main/res/drawable-v21/launch_background.xml b/flutter_local_notifications/example/android/app/src/main/res/drawable-v21/launch_background.xml new file mode 100644 index 000000000..f74085f3f --- /dev/null +++ b/flutter_local_notifications/example/android/app/src/main/res/drawable-v21/launch_background.xml @@ -0,0 +1,12 @@ + + + + + + + + diff --git a/flutter_local_notifications/example/android/app/src/main/res/values-night/styles.xml b/flutter_local_notifications/example/android/app/src/main/res/values-night/styles.xml new file mode 100644 index 000000000..449a9f930 --- /dev/null +++ b/flutter_local_notifications/example/android/app/src/main/res/values-night/styles.xml @@ -0,0 +1,18 @@ + + + + + + + diff --git a/flutter_local_notifications/example/android/app/src/main/res/values/styles.xml b/flutter_local_notifications/example/android/app/src/main/res/values/styles.xml index 00fa4417c..d74aa35c2 100644 --- a/flutter_local_notifications/example/android/app/src/main/res/values/styles.xml +++ b/flutter_local_notifications/example/android/app/src/main/res/values/styles.xml @@ -1,8 +1,18 @@ - + + diff --git a/flutter_local_notifications/example/android/app/src/profile/AndroidManifest.xml b/flutter_local_notifications/example/android/app/src/profile/AndroidManifest.xml new file mode 100644 index 000000000..c1569a936 --- /dev/null +++ b/flutter_local_notifications/example/android/app/src/profile/AndroidManifest.xml @@ -0,0 +1,7 @@ + + + + diff --git a/flutter_local_notifications/example/android/build.gradle b/flutter_local_notifications/example/android/build.gradle index 1f45b28b5..c505a8635 100644 --- a/flutter_local_notifications/example/android/build.gradle +++ b/flutter_local_notifications/example/android/build.gradle @@ -1,11 +1,13 @@ buildscript { + ext.kotlin_version = '1.3.50' repositories { google() jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:3.6.3' + classpath 'com.android.tools.build:gradle:4.1.0' + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" } } @@ -14,11 +16,6 @@ allprojects { google() jcenter() } - gradle.projectsEvaluated { - tasks.withType(JavaCompile) { - options.compilerArgs << "-Xlint:unchecked" << "-Xlint:deprecation" - } - } } rootProject.buildDir = '../build' diff --git a/flutter_local_notifications/example/android/gradle/wrapper/gradle-wrapper.properties b/flutter_local_notifications/example/android/gradle/wrapper/gradle-wrapper.properties index 2d0da59ad..bc6a58afd 100644 --- a/flutter_local_notifications/example/android/gradle/wrapper/gradle-wrapper.properties +++ b/flutter_local_notifications/example/android/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ -#Sun May 03 15:09:14 AEST 2020 +#Fri Jun 23 08:50:38 CEST 2017 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.4-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-6.7-all.zip diff --git a/flutter_local_notifications/example/android/settings.gradle b/flutter_local_notifications/example/android/settings.gradle index 5a2f14fb1..44e62bcf0 100644 --- a/flutter_local_notifications/example/android/settings.gradle +++ b/flutter_local_notifications/example/android/settings.gradle @@ -1,15 +1,11 @@ include ':app' -def flutterProjectRoot = rootProject.projectDir.parentFile.toPath() +def localPropertiesFile = new File(rootProject.projectDir, "local.properties") +def properties = new Properties() -def plugins = new Properties() -def pluginsFile = new File(flutterProjectRoot.toFile(), '.flutter-plugins') -if (pluginsFile.exists()) { - pluginsFile.withReader('UTF-8') { reader -> plugins.load(reader) } -} +assert localPropertiesFile.exists() +localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) } -plugins.each { name, path -> - def pluginDirectory = flutterProjectRoot.resolve(path).resolve('android').toFile() - include ":$name" - project(":$name").projectDir = pluginDirectory -} +def flutterSdkPath = properties.getProperty("flutter.sdk") +assert flutterSdkPath != null, "flutter.sdk not set in local.properties" +apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle" diff --git a/flutter_local_notifications/example/test_driver/flutter_local_notifications_e2e.dart b/flutter_local_notifications/example/integration_test/flutter_local_notifications_test.dart similarity index 96% rename from flutter_local_notifications/example/test_driver/flutter_local_notifications_e2e.dart rename to flutter_local_notifications/example/integration_test/flutter_local_notifications_test.dart index e59f77888..5ea363afb 100644 --- a/flutter_local_notifications/example/test_driver/flutter_local_notifications_e2e.dart +++ b/flutter_local_notifications/example/integration_test/flutter_local_notifications_test.dart @@ -1,11 +1,11 @@ import 'dart:io'; -import 'package:e2e/e2e.dart'; import 'package:flutter_local_notifications/flutter_local_notifications.dart'; import 'package:flutter_test/flutter_test.dart'; +import 'package:integration_test/integration_test.dart'; void main() { - E2EWidgetsFlutterBinding.ensureInitialized(); + IntegrationTestWidgetsFlutterBinding.ensureInitialized(); FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin; group('initialize()', () { setUpAll(() async { diff --git a/flutter_local_notifications/example/ios/Runner.xcodeproj/project.pbxproj b/flutter_local_notifications/example/ios/Runner.xcodeproj/project.pbxproj index e4fc93a23..b1317b56b 100644 --- a/flutter_local_notifications/example/ios/Runner.xcodeproj/project.pbxproj +++ b/flutter_local_notifications/example/ios/Runner.xcodeproj/project.pbxproj @@ -3,21 +3,18 @@ archiveVersion = 1; classes = { }; - objectVersion = 46; + objectVersion = 50; objects = { /* Begin PBXBuildFile section */ 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; - 51AC6CD32084C6F70042C077 /* slow_spring_board.aiff in Resources */ = {isa = PBXBuildFile; fileRef = 51AC6CD22084C6F70042C077 /* slow_spring_board.aiff */; }; - 9740EEB41CF90195004384FC /* Debug.xcconfig in Resources */ = {isa = PBXBuildFile; fileRef = 9740EEB21CF90195004384FC /* Debug.xcconfig */; }; - 9740EEB51CF90195004384FC /* Generated.xcconfig in Resources */ = {isa = PBXBuildFile; fileRef = 9740EEB31CF90195004384FC /* Generated.xcconfig */; }; - 978B8F6F1D3862AE00F588F7 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */; }; - 97C146F31CF9000F007C117D /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 97C146F21CF9000F007C117D /* main.m */; }; + 51155F9225B5B31600CEBA3A /* slow_spring_board.aiff in Resources */ = {isa = PBXBuildFile; fileRef = 51155F9125B5B31600CEBA3A /* slow_spring_board.aiff */; }; + 6C09322FA59197E669E2EE5E /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 59531C21662011537870918B /* Pods_Runner.framework */; }; + 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; - B6BC096867E2CDC7D72158C1 /* libPods-Runner.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5FDAE19F7270673E2DA66D4E /* libPods-Runner.a */; }; /* End PBXBuildFile section */ /* Begin PBXCopyFilesBuildPhase section */ @@ -37,21 +34,21 @@ 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; - 51AC6CD22084C6F70042C077 /* slow_spring_board.aiff */ = {isa = PBXFileReference; lastKnownFileType = audio.aiff; path = slow_spring_board.aiff; sourceTree = SOURCE_ROOT; }; - 534C29A22B3DE51E7D11AAB7 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; - 5FDAE19F7270673E2DA66D4E /* libPods-Runner.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Runner.a"; sourceTree = BUILT_PRODUCTS_DIR; }; - 68ABB3A076FEC4EC3F71EBB4 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; + 40C5B899F98F1C3C5F54F083 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; + 51155F9125B5B31600CEBA3A /* slow_spring_board.aiff */ = {isa = PBXFileReference; lastKnownFileType = audio.aiff; path = slow_spring_board.aiff; sourceTree = SOURCE_ROOT; }; + 59531C21662011537870918B /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; }; + 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; - 7AFFD8ED1D35381100E5BB4D /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; - 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; - 97C146F21CF9000F007C117D /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + D08F0A5F4043804786EAECD7 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; }; + F2ADC50DEEB76E0B5C93F37C /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -59,13 +56,21 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - B6BC096867E2CDC7D72158C1 /* libPods-Runner.a in Frameworks */, + 6C09322FA59197E669E2EE5E /* Pods_Runner.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ + 05167A11411FF5D8B2E37DB8 /* Frameworks */ = { + isa = PBXGroup; + children = ( + 59531C21662011537870918B /* Pods_Runner.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; 9740EEB11CF90186004384FC /* Flutter */ = { isa = PBXGroup; children = ( @@ -83,8 +88,8 @@ 9740EEB11CF90186004384FC /* Flutter */, 97C146F01CF9000F007C117D /* Runner */, 97C146EF1CF9000F007C117D /* Products */, - BFDDF33B560D99A1F1EB205D /* Pods */, - FEB620C7FF7840C2297690B8 /* Frameworks */, + CC9CABD983DC0B414464B379 /* Pods */, + 05167A11411FF5D8B2E37DB8 /* Frameworks */, ); sourceTree = ""; }; @@ -99,43 +104,28 @@ 97C146F01CF9000F007C117D /* Runner */ = { isa = PBXGroup; children = ( - 51AC6CD22084C6F70042C077 /* slow_spring_board.aiff */, - 7AFFD8ED1D35381100E5BB4D /* AppDelegate.h */, - 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */, 97C146FA1CF9000F007C117D /* Main.storyboard */, 97C146FD1CF9000F007C117D /* Assets.xcassets */, 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */, 97C147021CF9000F007C117D /* Info.plist */, - 97C146F11CF9000F007C117D /* Supporting Files */, 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */, 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */, + 74858FAE1ED2DC5600515810 /* AppDelegate.swift */, + 51155F9125B5B31600CEBA3A /* slow_spring_board.aiff */, + 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */, ); path = Runner; sourceTree = ""; }; - 97C146F11CF9000F007C117D /* Supporting Files */ = { - isa = PBXGroup; - children = ( - 97C146F21CF9000F007C117D /* main.m */, - ); - name = "Supporting Files"; - sourceTree = ""; - }; - BFDDF33B560D99A1F1EB205D /* Pods */ = { + CC9CABD983DC0B414464B379 /* Pods */ = { isa = PBXGroup; children = ( - 68ABB3A076FEC4EC3F71EBB4 /* Pods-Runner.debug.xcconfig */, - 534C29A22B3DE51E7D11AAB7 /* Pods-Runner.release.xcconfig */, + 40C5B899F98F1C3C5F54F083 /* Pods-Runner.debug.xcconfig */, + F2ADC50DEEB76E0B5C93F37C /* Pods-Runner.release.xcconfig */, + D08F0A5F4043804786EAECD7 /* Pods-Runner.profile.xcconfig */, ); name = Pods; - sourceTree = ""; - }; - FEB620C7FF7840C2297690B8 /* Frameworks */ = { - isa = PBXGroup; - children = ( - 5FDAE19F7270673E2DA66D4E /* libPods-Runner.a */, - ); - name = Frameworks; + path = Pods; sourceTree = ""; }; /* End PBXGroup section */ @@ -145,14 +135,14 @@ isa = PBXNativeTarget; buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; buildPhases = ( - B5B2A146A868A8661960FBBA /* [CP] Check Pods Manifest.lock */, + 11ABBB00F6B4C56238928421 /* [CP] Check Pods Manifest.lock */, 9740EEB61CF901F6004384FC /* Run Script */, 97C146EA1CF9000F007C117D /* Sources */, 97C146EB1CF9000F007C117D /* Frameworks */, 97C146EC1CF9000F007C117D /* Resources */, 9705A1C41CF9048500538489 /* Embed Frameworks */, 3B06AD1E1E4923F5004D2608 /* Thin Binary */, - AED31D55386FBAF66E81FAA8 /* [CP] Embed Pods Frameworks */, + B18758020F275E3A9128BDE4 /* [CP] Embed Pods Frameworks */, ); buildRules = ( ); @@ -169,20 +159,20 @@ 97C146E61CF9000F007C117D /* Project object */ = { isa = PBXProject; attributes = { - LastUpgradeCheck = 0910; - ORGANIZATIONNAME = "The Chromium Authors"; + LastUpgradeCheck = 1020; + ORGANIZATIONNAME = ""; TargetAttributes = { 97C146ED1CF9000F007C117D = { CreatedOnToolsVersion = 7.3.1; + LastSwiftMigration = 1100; }; }; }; buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */; - compatibilityVersion = "Xcode 3.2"; - developmentRegion = English; + compatibilityVersion = "Xcode 9.3"; + developmentRegion = en; hasScannedForEncodings = 0; knownRegions = ( - English, en, Base, ); @@ -201,11 +191,9 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( - 51AC6CD32084C6F70042C077 /* slow_spring_board.aiff in Resources */, + 51155F9225B5B31600CEBA3A /* slow_spring_board.aiff in Resources */, 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */, - 9740EEB51CF90195004384FC /* Generated.xcconfig in Resources */, 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */, - 9740EEB41CF90195004384FC /* Debug.xcconfig in Resources */, 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */, 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */, ); @@ -214,68 +202,71 @@ /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ - 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { + 11ABBB00F6B4C56238928421 /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); + inputFileListPaths = ( + ); inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( ); - name = "Thin Binary"; outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; }; - 9740EEB61CF901F6004384FC /* Run Script */ = { + 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputPaths = ( ); - name = "Run Script"; + name = "Thin Binary"; outputPaths = ( ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; + shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; }; - AED31D55386FBAF66E81FAA8 /* [CP] Embed Pods Frameworks */ = { + 9740EEB61CF901F6004384FC /* Run Script */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh", - "${PODS_ROOT}/../Flutter/Flutter.framework", ); - name = "[CP] Embed Pods Frameworks"; + name = "Run Script"; outputPaths = ( - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Flutter.framework", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; - showEnvVarsInLog = 0; + shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; }; - B5B2A146A868A8661960FBBA /* [CP] Check Pods Manifest.lock */ = { + B18758020F275E3A9128BDE4 /* [CP] Embed Pods Frameworks */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); - inputPaths = ( - "${PODS_PODFILE_DIR_PATH}/Podfile.lock", - "${PODS_ROOT}/Manifest.lock", + inputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist", ); - name = "[CP] Check Pods Manifest.lock"; - outputPaths = ( - "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt", + name = "[CP] Embed Pods Frameworks"; + outputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; showEnvVarsInLog = 0; }; /* End PBXShellScriptBuildPhase section */ @@ -285,8 +276,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 978B8F6F1D3862AE00F588F7 /* AppDelegate.m in Sources */, - 97C146F31CF9000F007C117D /* main.m in Sources */, + 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */, 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -313,6 +303,77 @@ /* End PBXVariantGroup section */ /* Begin XCBuildConfiguration section */ + 249021D3217E4FDB00AE95B9 /* Profile */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + SUPPORTED_PLATFORMS = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Profile; + }; + 249021D4217E4FDB00AE95B9 /* Profile */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + ENABLE_BITCODE = NO; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.dexterous.flutterLocalNotificationsExample; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; + SWIFT_VERSION = 5.0; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Profile; + }; 97C147031CF9000F007C117D /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { @@ -326,12 +387,14 @@ CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; @@ -358,7 +421,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; @@ -379,12 +442,14 @@ CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; @@ -405,9 +470,12 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; + SUPPORTED_PLATFORMS = iphoneos; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; TARGETED_DEVICE_FAMILY = "1,2"; VALIDATE_PRODUCT = YES; }; @@ -418,19 +486,20 @@ baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; ENABLE_BITCODE = NO; - FRAMEWORK_SEARCH_PATHS = ( - "$(inherited)", - "$(PROJECT_DIR)/Flutter", - ); INFOPLIST_FILE = Runner/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; - LIBRARY_SEARCH_PATHS = ( + LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", - "$(PROJECT_DIR)/Flutter", + "@executable_path/Frameworks", ); PRODUCT_BUNDLE_IDENTIFIER = com.dexterous.flutterLocalNotificationsExample; PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + VERSIONING_SYSTEM = "apple-generic"; }; name = Debug; }; @@ -439,19 +508,19 @@ baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; ENABLE_BITCODE = NO; - FRAMEWORK_SEARCH_PATHS = ( - "$(inherited)", - "$(PROJECT_DIR)/Flutter", - ); INFOPLIST_FILE = Runner/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; - LIBRARY_SEARCH_PATHS = ( + LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", - "$(PROJECT_DIR)/Flutter", + "@executable_path/Frameworks", ); PRODUCT_BUNDLE_IDENTIFIER = com.dexterous.flutterLocalNotificationsExample; PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; + SWIFT_VERSION = 5.0; + VERSIONING_SYSTEM = "apple-generic"; }; name = Release; }; @@ -463,6 +532,7 @@ buildConfigurations = ( 97C147031CF9000F007C117D /* Debug */, 97C147041CF9000F007C117D /* Release */, + 249021D3217E4FDB00AE95B9 /* Profile */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; @@ -472,6 +542,7 @@ buildConfigurations = ( 97C147061CF9000F007C117D /* Debug */, 97C147071CF9000F007C117D /* Release */, + 249021D4217E4FDB00AE95B9 /* Profile */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; diff --git a/flutter_local_notifications/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/flutter_local_notifications/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 000000000..18d981003 --- /dev/null +++ b/flutter_local_notifications/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/flutter_local_notifications/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/flutter_local_notifications/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings new file mode 100644 index 000000000..f9b0d7c5e --- /dev/null +++ b/flutter_local_notifications/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings @@ -0,0 +1,8 @@ + + + + + PreviewsEnabled + + + diff --git a/flutter_local_notifications/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/flutter_local_notifications/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme index 1263ac84b..a28140cfd 100644 --- a/flutter_local_notifications/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ b/flutter_local_notifications/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -1,6 +1,6 @@ @@ -46,7 +45,6 @@ buildConfiguration = "Debug" selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" - language = "" launchStyle = "0" useCustomWorkingDirectory = "NO" ignoresPersistentStateOnLaunch = "NO" @@ -67,7 +65,7 @@ + + + + PreviewsEnabled + + + diff --git a/flutter_local_notifications/example/ios/Runner/AppDelegate.h b/flutter_local_notifications/example/ios/Runner/AppDelegate.h deleted file mode 100644 index cf210d213..000000000 --- a/flutter_local_notifications/example/ios/Runner/AppDelegate.h +++ /dev/null @@ -1,6 +0,0 @@ -#import -#import - -@interface AppDelegate : FlutterAppDelegate - -@end diff --git a/flutter_local_notifications/example/ios/Runner/AppDelegate.m b/flutter_local_notifications/example/ios/Runner/AppDelegate.m deleted file mode 100644 index 61d82e65a..000000000 --- a/flutter_local_notifications/example/ios/Runner/AppDelegate.m +++ /dev/null @@ -1,35 +0,0 @@ -#include "AppDelegate.h" -#include "GeneratedPluginRegistrant.h" -#import -#import - -@implementation AppDelegate - -- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { - [GeneratedPluginRegistrant registerWithRegistry:self]; - // cancel old notifications that were scheduled to be periodically shown upon a reinstallation of the app - if(![[NSUserDefaults standardUserDefaults]objectForKey:@"Notification"]){ - [[UIApplication sharedApplication] cancelAllLocalNotifications]; - [[NSUserDefaults standardUserDefaults]setBool:YES forKey:@"Notification"]; - } - if(@available(iOS 10.0, *)) { - [UNUserNotificationCenter currentNotificationCenter].delegate = (id) self; - } - - FlutterViewController* controller = (FlutterViewController*)self.window.rootViewController; - - FlutterMethodChannel* channel = [FlutterMethodChannel - methodChannelWithName:@"dexterx.dev/flutter_local_notifications_example" - binaryMessenger:controller.binaryMessenger]; - - [channel setMethodCallHandler:^(FlutterMethodCall* call, FlutterResult result) { - if([@"getTimeZoneName" isEqualToString:call.method]) { - result([[NSTimeZone localTimeZone] name]); - } - }]; - - // Override point for customization after application launch. - return [super application:application didFinishLaunchingWithOptions:launchOptions]; -} - -@end diff --git a/flutter_local_notifications/example/ios/Runner/AppDelegate.swift b/flutter_local_notifications/example/ios/Runner/AppDelegate.swift new file mode 100644 index 000000000..d8d69ec06 --- /dev/null +++ b/flutter_local_notifications/example/ios/Runner/AppDelegate.swift @@ -0,0 +1,27 @@ +import UIKit +import Flutter + +@UIApplicationMain +@objc class AppDelegate: FlutterAppDelegate { + override func application( + _ application: UIApplication, + didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? + ) -> Bool { + if #available(iOS 10.0, *) { + UNUserNotificationCenter.current().delegate = self as UNUserNotificationCenterDelegate + } + + let controller : FlutterViewController = window?.rootViewController as! FlutterViewController + let channel = FlutterMethodChannel(name: "dexterx.dev/flutter_local_notifications_example", + binaryMessenger: controller.binaryMessenger) + channel.setMethodCallHandler({ + (call: FlutterMethodCall, result: @escaping FlutterResult) -> Void in + if ("getTimeZoneName" == call.method) { + result(TimeZone.current.identifier) + } + }) + + GeneratedPluginRegistrant.register(with: self) + return super.application(application, didFinishLaunchingWithOptions: launchOptions) + } +} diff --git a/flutter_local_notifications/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png b/flutter_local_notifications/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png index 3d43d11e66f4de3da27ed045ca4fe38ad8b48094..dc9ada4725e9b0ddb1deab583e5b5102493aa332 100644 GIT binary patch literal 10932 zcmeHN2~<R zh`|8`A_PQ1nSu(UMFx?8j8PC!!VDphaL#`F42fd#7Vlc`zIE4n%Y~eiz4y1j|NDpi z?<@|pSJ-HM`qifhf@m%MamgwK83`XpBA<+azdF#2QsT{X@z0A9Bq>~TVErigKH1~P zRX-!h-f0NJ4Mh++{D}J+K>~~rq}d%o%+4dogzXp7RxX4C>Km5XEI|PAFDmo;DFm6G zzjVoB`@qW98Yl0Kvc-9w09^PrsobmG*Eju^=3f?0o-t$U)TL1B3;sZ^!++3&bGZ!o-*6w?;oOhf z=A+Qb$scV5!RbG+&2S}BQ6YH!FKb0``VVX~T$dzzeSZ$&9=X$3)_7Z{SspSYJ!lGE z7yig_41zpQ)%5dr4ff0rh$@ky3-JLRk&DK)NEIHecf9c*?Z1bUB4%pZjQ7hD!A0r-@NF(^WKdr(LXj|=UE7?gBYGgGQV zidf2`ZT@pzXf7}!NH4q(0IMcxsUGDih(0{kRSez&z?CFA0RVXsVFw3^u=^KMtt95q z43q$b*6#uQDLoiCAF_{RFc{!H^moH_cmll#Fc^KXi{9GDl{>%+3qyfOE5;Zq|6#Hb zp^#1G+z^AXfRKaa9HK;%b3Ux~U@q?xg<2DXP%6k!3E)PA<#4$ui8eDy5|9hA5&{?v z(-;*1%(1~-NTQ`Is1_MGdQ{+i*ccd96ab$R$T3=% zw_KuNF@vI!A>>Y_2pl9L{9h1-C6H8<)J4gKI6{WzGBi<@u3P6hNsXG=bRq5c+z;Gc3VUCe;LIIFDmQAGy+=mRyF++u=drBWV8-^>0yE9N&*05XHZpPlE zxu@?8(ZNy7rm?|<+UNe0Vs6&o?l`Pt>P&WaL~M&#Eh%`rg@Mbb)J&@DA-wheQ>hRV z<(XhigZAT z>=M;URcdCaiO3d^?H<^EiEMDV+7HsTiOhoaMX%P65E<(5xMPJKxf!0u>U~uVqnPN7T!X!o@_gs3Ct1 zlZ_$5QXP4{Aj645wG_SNT&6m|O6~Tsl$q?nK*)(`{J4b=(yb^nOATtF1_aS978$x3 zx>Q@s4i3~IT*+l{@dx~Hst21fR*+5}S1@cf>&8*uLw-0^zK(+OpW?cS-YG1QBZ5q! zgTAgivzoF#`cSz&HL>Ti!!v#?36I1*l^mkrx7Y|K6L#n!-~5=d3;K<;Zqi|gpNUn_ z_^GaQDEQ*jfzh;`j&KXb66fWEk1K7vxQIMQ_#Wu_%3 z4Oeb7FJ`8I>Px;^S?)}2+4D_83gHEq>8qSQY0PVP?o)zAv3K~;R$fnwTmI-=ZLK`= zTm+0h*e+Yfr(IlH3i7gUclNH^!MU>id$Jw>O?2i0Cila#v|twub21@e{S2v}8Z13( zNDrTXZVgris|qYm<0NU(tAPouG!QF4ZNpZPkX~{tVf8xY690JqY1NVdiTtW+NqyRP zZ&;T0ikb8V{wxmFhlLTQ&?OP7 z;(z*<+?J2~z*6asSe7h`$8~Se(@t(#%?BGLVs$p``;CyvcT?7Y!{tIPva$LxCQ&4W z6v#F*);|RXvI%qnoOY&i4S*EL&h%hP3O zLsrFZhv&Hu5tF$Lx!8(hs&?!Kx5&L(fdu}UI5d*wn~A`nPUhG&Rv z2#ixiJdhSF-K2tpVL=)5UkXRuPAFrEW}7mW=uAmtVQ&pGE-&az6@#-(Te^n*lrH^m@X-ftVcwO_#7{WI)5v(?>uC9GG{lcGXYJ~Q8q zbMFl7;t+kV;|;KkBW2!P_o%Czhw&Q(nXlxK9ak&6r5t_KH8#1Mr-*0}2h8R9XNkr zto5-b7P_auqTJb(TJlmJ9xreA=6d=d)CVbYP-r4$hDn5|TIhB>SReMfh&OVLkMk-T zYf%$taLF0OqYF?V{+6Xkn>iX@TuqQ?&cN6UjC9YF&%q{Ut3zv{U2)~$>-3;Dp)*(? zg*$mu8^i=-e#acaj*T$pNowo{xiGEk$%DusaQiS!KjJH96XZ-hXv+jk%ard#fu=@Q z$AM)YWvE^{%tDfK%nD49=PI|wYu}lYVbB#a7wtN^Nml@CE@{Gv7+jo{_V?I*jkdLD zJE|jfdrmVbkfS>rN*+`#l%ZUi5_bMS<>=MBDNlpiSb_tAF|Zy`K7kcp@|d?yaTmB^ zo?(vg;B$vxS|SszusORgDg-*Uitzdi{dUV+glA~R8V(?`3GZIl^egW{a919!j#>f` znL1o_^-b`}xnU0+~KIFLQ)$Q6#ym%)(GYC`^XM*{g zv3AM5$+TtDRs%`2TyR^$(hqE7Y1b&`Jd6dS6B#hDVbJlUXcG3y*439D8MrK!2D~6gn>UD4Imctb z+IvAt0iaW73Iq$K?4}H`7wq6YkTMm`tcktXgK0lKPmh=>h+l}Y+pDtvHnG>uqBA)l zAH6BV4F}v$(o$8Gfo*PB>IuaY1*^*`OTx4|hM8jZ?B6HY;F6p4{`OcZZ(us-RVwDx zUzJrCQlp@mz1ZFiSZ*$yX3c_#h9J;yBE$2g%xjmGF4ca z&yL`nGVs!Zxsh^j6i%$a*I3ZD2SoNT`{D%mU=LKaEwbN(_J5%i-6Va?@*>=3(dQy` zOv%$_9lcy9+(t>qohkuU4r_P=R^6ME+wFu&LA9tw9RA?azGhjrVJKy&8=*qZT5Dr8g--d+S8zAyJ$1HlW3Olryt`yE zFIph~Z6oF&o64rw{>lgZISC6p^CBer9C5G6yq%?8tC+)7*d+ib^?fU!JRFxynRLEZ zj;?PwtS}Ao#9whV@KEmwQgM0TVP{hs>dg(1*DiMUOKHdQGIqa0`yZnHk9mtbPfoLx zo;^V6pKUJ!5#n`w2D&381#5#_t}AlTGEgDz$^;u;-vxDN?^#5!zN9ngytY@oTv!nc zp1Xn8uR$1Z;7vY`-<*?DfPHB;x|GUi_fI9@I9SVRv1)qETbNU_8{5U|(>Du84qP#7 z*l9Y$SgA&wGbj>R1YeT9vYjZuC@|{rajTL0f%N@>3$DFU=`lSPl=Iv;EjuGjBa$Gw zHD-;%YOE@<-!7-Mn`0WuO3oWuL6tB2cpPw~Nvuj|KM@))ixuDK`9;jGMe2d)7gHin zS<>k@!x;!TJEc#HdL#RF(`|4W+H88d4V%zlh(7#{q2d0OQX9*FW^`^_<3r$kabWAB z$9BONo5}*(%kx zOXi-yM_cmB3>inPpI~)duvZykJ@^^aWzQ=eQ&STUa}2uT@lV&WoRzkUoE`rR0)`=l zFT%f|LA9fCw>`enm$p7W^E@U7RNBtsh{_-7vVz3DtB*y#*~(L9+x9*wn8VjWw|Q~q zKFsj1Yl>;}%MG3=PY`$g$_mnyhuV&~O~u~)968$0b2!Jkd;2MtAP#ZDYw9hmK_+M$ zb3pxyYC&|CuAbtiG8HZjj?MZJBFbt`ryf+c1dXFuC z0*ZQhBzNBd*}s6K_G}(|Z_9NDV162#y%WSNe|FTDDhx)K!c(mMJh@h87@8(^YdK$&d*^WQe8Z53 z(|@MRJ$Lk-&ii74MPIs80WsOFZ(NX23oR-?As+*aq6b?~62@fSVmM-_*cb1RzZ)`5$agEiL`-E9s7{GM2?(KNPgK1(+c*|-FKoy}X(D_b#etO|YR z(BGZ)0Ntfv-7R4GHoXp?l5g#*={S1{u-QzxCGng*oWr~@X-5f~RA14b8~B+pLKvr4 zfgL|7I>jlak9>D4=(i(cqYf7#318!OSR=^`xxvI!bBlS??`xxWeg?+|>MxaIdH1U~#1tHu zB{QMR?EGRmQ_l4p6YXJ{o(hh-7Tdm>TAX380TZZZyVkqHNzjUn*_|cb?T? zt;d2s-?B#Mc>T-gvBmQZx(y_cfkXZO~{N zT6rP7SD6g~n9QJ)8F*8uHxTLCAZ{l1Y&?6v)BOJZ)=R-pY=Y=&1}jE7fQ>USS}xP#exo57uND0i*rEk@$;nLvRB@u~s^dwRf?G?_enN@$t* zbL%JO=rV(3Ju8#GqUpeE3l_Wu1lN9Y{D4uaUe`g>zlj$1ER$6S6@{m1!~V|bYkhZA z%CvrDRTkHuajMU8;&RZ&itnC~iYLW4DVkP<$}>#&(`UO>!n)Po;Mt(SY8Yb`AS9lt znbX^i?Oe9r_o=?})IHKHoQGKXsps_SE{hwrg?6dMI|^+$CeC&z@*LuF+P`7LfZ*yr+KN8B4{Nzv<`A(wyR@!|gw{zB6Ha ziwPAYh)oJ(nlqSknu(8g9N&1hu0$vFK$W#mp%>X~AU1ay+EKWcFdif{% z#4!4aoVVJ;ULmkQf!ke2}3hqxLK>eq|-d7Ly7-J9zMpT`?dxo6HdfJA|t)?qPEVBDv z{y_b?4^|YA4%WW0VZd8C(ZgQzRI5(I^)=Ub`Y#MHc@nv0w-DaJAqsbEHDWG8Ia6ju zo-iyr*sq((gEwCC&^TYBWt4_@|81?=B-?#P6NMff(*^re zYqvDuO`K@`mjm_Jd;mW_tP`3$cS?R$jR1ZN09$YO%_iBqh5ftzSpMQQtxKFU=FYmP zeY^jph+g<4>YO;U^O>-NFLn~-RqlHvnZl2yd2A{Yc1G@Ga$d+Q&(f^tnPf+Z7serIU};17+2DU_f4Z z@GaPFut27d?!YiD+QP@)T=77cR9~MK@bd~pY%X(h%L={{OIb8IQmf-!xmZkm8A0Ga zQSWONI17_ru5wpHg3jI@i9D+_Y|pCqVuHJNdHUauTD=R$JcD2K_liQisqG$(sm=k9;L* z!L?*4B~ql7uioSX$zWJ?;q-SWXRFhz2Jt4%fOHA=Bwf|RzhwqdXGr78y$J)LR7&3T zE1WWz*>GPWKZ0%|@%6=fyx)5rzUpI;bCj>3RKzNG_1w$fIFCZ&UR0(7S?g}`&Pg$M zf`SLsz8wK82Vyj7;RyKmY{a8G{2BHG%w!^T|Njr!h9TO2LaP^_f22Q1=l$QiU84ao zHe_#{S6;qrC6w~7{y(hs-?-j?lbOfgH^E=XcSgnwW*eEz{_Z<_iT7q6h&WAVr806i~>Gqn6rM z>3}bMG&oq%DIriqR35=rtEdos5L6z)YC*Xq0U-$_+Il@RaU zXYX%+``hR28`(B*uJ6G9&iz>|)PS%!)9N`7=LcmcxH}k69HPyT-%S zH7+jBCC<%76cg_H-n41cTqnKn`u_V9p~XaTLUe3s{KRPSTeK6apP4Jg%VQ$e#72ms zxyWzmGSRwN?=fRgpx!?W&ZsrLfuhAsRxm%;_|P@3@3~BJwY4ZVBJ3f&$5x>`^fD?d zI+z!v#$!gz%FtL*%mR^Uwa*8LJFZ_;X!y$cD??W#c)31l@ervOa_Qk86R{HJiZb$f z&&&0xYmB{@D@yl~^l5IXtB_ou{xFiYP(Jr<9Ce{jCN z<3Rf2TD%}_N?y>bgWq|{`RKd}n>P4e8Z-D+(fn^4)+|pv$DcR&i+RHNhv$71F*McT zl`phYBlb;wO`b7)*10XF6UXhY9`@UR*6-#(Zp`vyU(__*te6xYtV&N0(zjMtev{tZ zapmGin===teMXjsS0>CYxUy<2izOKOPai0}!B9+6q$s3CF8W{xUwz?A0ADO5&BsiB z{SFt|KehNd-S#eiDq!y&+mW9N_!wH-i~q|oNm=mEzkx}B?Ehe%q$tK8f=QY#*6rH9 zNHHaG(9WBqzP!!TMEktSVuh$i$4A^b25LK}&1*4W?ul*5pZYjL1OZ@X9?3W7Y|T6} z1SXx0Wn-|!A;fZGGlYn9a1Jz5^8)~v#mXhmm>um{QiGG459N}L<&qyD+sy_ixD@AP zW0XV6w#3(JW>TEV}MD=O0O>k5H>p#&|O zD2mGf0Cz7+>l7`NuzGobt;(o@vb9YiOpHN8QJ9Uva|i7R?7nnq;L_iq+ZqPv*oGu! zN@GuJ9fm;yrEFga63m?1qy|5&fd32<%$yP$llh}Udrp>~fb>M>R55I@BsGYhCj8m1 zC=ziFh4@hoytpfrJlr}FsV|C(aV4PZ^8^`G29(+!Bk8APa#PemJqkF zE{IzwPaE)I&r`OxGk*vPErm6sGKaQJ&6FODW$;gAl_4b_j!oH4yE@ zP~Cl4?kp>Ccc~Nm+0kjIb`U0N7}zrQEN5!Ju|}t}LeXi!baZOyhlWha5lq{Ld2rdo zGz7hAJQt<6^cxXTe0xZjmADL85cC&H+~Lt2siIIh{$~+U#&#^{Ub22IA|ea6 z5j12XLc`~dh$$1>3o0Cgvo*ybi$c*z>n=5L&X|>Wy1~eagk;lcEnf^2^2xB=e58Z` z@Rw{1ssK)NRV+2O6c<8qFl%efHE;uy!mq(Xi1P*H2}LMi z3EqWN2U?eW{J$lSFxDJg-=&RH!=6P9!y|S~gmjg)gPKGMxq6r9cNIhW` zS})-obO}Ao_`;=>@fAwU&=|5$J;?~!s4LN2&XiMXEl>zk9M}tVEg#kkIkbKp%Ig2QJ2aCILCM1E=aN*iuz>;q#T_I7aVM=E4$m_#OWLnXQnFUnu?~(X>$@NP zBJ@Zw>@bmErSuW7SR2=6535wh-R`WZ+5dLqwTvw}Ks8~4F#hh0$Qn^l-z=;>D~St( z-1yEjCCgd*z5qXa*bJ7H2Tk54KiX&=Vd}z?%dcc z`N8oeYUKe17&|B5A-++RHh8WQ%;gN{vf%05@jZF%wn1Z_yk#M~Cn(i@MB_mpcbLj5 zR#QAtC`k=tZ*h|){Mjz`7bNL zGWOW=bjQhX@`Vw^xn#cVwn28c2D9vOb0TLLy~-?-%gOyHSeJ9a>P}5OF5$n}k-pvUa*pvLw)KvG~>QjNWS3LY1f*OkFwPZ5qC@+3^Bt=HZbf`alKY#{pn zdY}NEIgo1sd)^TPxVzO{uvU$|Z-jkK0p1x##LexgQ$zx1^bNPOG*u2RmZkIM!zFVz zz|IsP3I?qrlmjGS2w_(azCvGTnf~flqogV@Q%mH{76uLU(>UB zQZ?*ys3BO&TV{Pj_qEa-hkH7mOMe_Bnu3%CXCgu90XNKf$N)PUc3Ei-&~@tT zI^49Lm^+=TrI=h4h=W@jW{GjWd{_kVuSzAL6Pi@HKYYnnNbtcYdIRww+jY$(30=#p8*if(mzbvau z00#}4Qf+gH&ce_&8y3Z@CZV>b%&Zr7xuPSSqOmoaP@arwPrMx^jQBQQi>YvBUdpBn zI``MZ3I3HLqp)@vk^E|~)zw$0$VI_RPsL9u(kqulmS`tnb%4U)hm{)h@bG*jw@Y*#MX;Th1wu3TrO}Srn_+YWYesEgkO1 zv?P8uWB)is;#&=xBBLf+y5e4?%y>_8$1KwkAJ8UcW|0CIz89{LydfJKr^RF=JFPi}MAv|ecbuZ!YcTSxsD$(Pr#W*oytl?@+2 zXBFb32Kf_G3~EgOS7C`8w!tx}DcCT%+#qa76VSbnHo;4(oJ7)}mm?b5V65ir`7Z}s zR2)m15b#E}z_2@rf34wo!M^CnVoi# ze+S(IK({C6u=Sm{1>F~?)8t&fZpOOPcby;I3jO;7^xmLKM(<%i-nyj9mgw9F1Lq4|DZUHZ4)V9&6fQM(ZxbG{h+}(koiTu`SQw6#6q2Yg z-d+1+MRp$zYT2neIR2cKij2!R;C~ooQ3<;^8)_Gch&ZyEtiQwmF0Mb_)6)4lVEBF< zklXS7hvtu30uJR`3OzcqUNOdYsfrKSGkIQAk|4=&#ggxdU4^Y(;)$8}fQ>lTgQdJ{ zzie8+1$3@E;|a`kzuFh9Se}%RHTmBg)h$eH;gttjL_)pO^10?!bNev6{mLMaQpY<< z7M^ZXrg>tw;vU@9H=khbff?@nu)Yw4G% zGxobPTUR2p_ed7Lvx?dkrN^>Cv$Axuwk;Wj{5Z@#$sK@f4{7SHg%2bpcS{(~s;L(mz@9r$cK@m~ef&vf%1@ z@8&@LLO2lQso|bJD6}+_L1*D^}>oqg~$NipL>QlP3 zM#ATSy@ycMkKs5-0X8nFAtMhO_=$DlWR+@EaZ}`YduRD4A2@!at3NYRHmlENea9IF zN*s>mi?zy*Vv+F+&4-o`Wj}P3mLGM*&M(z|;?d82>hQkkY?e-hJ47mWOLCPL*MO04 z3lE(n2RM=IIo;Z?I=sKJ_h=iJHbQ2<}WW0b@I6Qf-{T=Qn#@N0yG5xH&ofEy^mZMPzd22nR`t!Q)VkNgf*VOxE z$XhOunG3ZN#`Ks$Hp~}`OX5vmHP={GYUJ+-g0%PS$*Qi5+-40M47zJ24vK1#? zb$s^%r?+>#lw$mpZaMa1aO%wlPm3~cno_(S%U&-R;6eK(@`CjswAW2)HfZ>ptItaZ|XqQ z&sHVVL>WCe|E4iPb2~gS5ITs6xfg(kmt&3$YcI=zTuqj37t|+9ojCr(G^ul#p{>k) zM94pI>~5VZ$!*Qurq<@RIXgP3sx-2kL$1Q~da%rnNIh?)&+c~*&e~CYPDhPYjb+Xu zKg5w^XB3(_9{Waa4E(-J-Kq_u6t_k?a8kEHqai-N-4#`SRerO!h}!cS%SMC<)tGix zOzVP^_t!HN&HIPL-ZpcgWitHM&yFRC7!k4zSI+-<_uQ}|tX)n{Ib;X>Xx>i_d*KkH zCzogKQFpP1408_2!ofU|iBq2R8hW6G zuqJs9Tyw{u%-uWczPLkM!MfKfflt+NK9Vk8E!C>AsJwNDRoe2~cL+UvqNP|5J8t)( z0$iMa!jhudJ+fqFn+um&@Oj6qXJd_3-l`S^I1#0fnt!z3?D*hAHr*u(*wR@`4O z#avrtg%s`Fh{?$FtBFM^$@@hW!8ZfF4;=n0<8In&X}-Rp=cd0TqT_ne46$j^r}FzE z26vX^!PzScuQfFfl1HEZ{zL?G88mcc76zHGizWiykBf4m83Z${So-+dZ~YGhm*RO7 zB1gdIdqnFi?qw+lPRFW5?}CQ3Me3G^muvll&4iN+*5#_mmIu;loULMwb4lu9U*dFM z-Sr**(0Ei~u=$3<6>C-G6z4_LNCx||6YtjS)<;hf)YJTPKXW+w%hhCTUAInIse9>r zl2YU6nRb$u-FJlWN*{{%sm_gi_UP5{=?5}5^D2vPzM=oPfNw~azZQ#P zl5z8RtSSiTIpEohC15i-Q1Bk{3&ElsD0uGAOxvbk29VUDmmA0w;^v`W#0`};O3DVE z&+-ca*`YcN%z*#VXWK9Qa-OEME#fykF%|7o=1Y+eF;Rtv0W4~kKRDx9YBHOWhC%^I z$Jec0cC7o37}Xt}cu)NH5R}NT+=2Nap*`^%O)vz?+{PV<2~qX%TzdJOGeKj5_QjqR&a3*K@= P-1+_A+?hGkL;m(J7kc&K diff --git a/flutter_local_notifications/example/ios/Runner/Info.plist b/flutter_local_notifications/example/ios/Runner/Info.plist index 449dca7e6..ffb007d95 100644 --- a/flutter_local_notifications/example/ios/Runner/Info.plist +++ b/flutter_local_notifications/example/ios/Runner/Info.plist @@ -3,7 +3,7 @@ CFBundleDevelopmentRegion - en + $(DEVELOPMENT_LANGUAGE) CFBundleExecutable $(EXECUTABLE_NAME) CFBundleIdentifier @@ -15,11 +15,11 @@ CFBundlePackageType APPL CFBundleShortVersionString - 1.0 + $(FLUTTER_BUILD_NAME) CFBundleSignature ???? CFBundleVersion - 1 + $(FLUTTER_BUILD_NUMBER) LSRequiresIPhoneOS UILaunchStoryboardName diff --git a/flutter_local_notifications/example/ios/Runner/Runner-Bridging-Header.h b/flutter_local_notifications/example/ios/Runner/Runner-Bridging-Header.h new file mode 100644 index 000000000..308a2a560 --- /dev/null +++ b/flutter_local_notifications/example/ios/Runner/Runner-Bridging-Header.h @@ -0,0 +1 @@ +#import "GeneratedPluginRegistrant.h" diff --git a/flutter_local_notifications/example/ios/Runner/main.m b/flutter_local_notifications/example/ios/Runner/main.m deleted file mode 100644 index 0ccc45001..000000000 --- a/flutter_local_notifications/example/ios/Runner/main.m +++ /dev/null @@ -1,9 +0,0 @@ -#import -#import -#import "AppDelegate.h" - -int main(int argc, char * argv[]) { - @autoreleasepool { - return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); - } -} diff --git a/flutter_local_notifications/example/lib/main.dart b/flutter_local_notifications/example/lib/main.dart index f95378f3a..3a7d79c5b 100644 --- a/flutter_local_notifications/example/lib/main.dart +++ b/flutter_local_notifications/example/lib/main.dart @@ -42,6 +42,8 @@ class ReceivedNotification { final String payload; } +String selectedNotificationPayload; + /// IMPORTANT: running the following code on its own won't work as there is /// setup required for each platform head project. /// @@ -55,6 +57,11 @@ Future main() async { final NotificationAppLaunchDetails notificationAppLaunchDetails = await flutterLocalNotificationsPlugin.getNotificationAppLaunchDetails(); + String initialRoute = HomePage.routeName; + if (notificationAppLaunchDetails?.didNotificationLaunchApp ?? false) { + selectedNotificationPayload = notificationAppLaunchDetails.payload; + initialRoute = SecondPage.routeName; + } const AndroidInitializationSettings initializationSettingsAndroid = AndroidInitializationSettings('app_icon'); @@ -85,13 +92,16 @@ Future main() async { if (payload != null) { debugPrint('notification payload: $payload'); } + selectedNotificationPayload = payload; selectNotificationSubject.add(payload); }); runApp( MaterialApp( - home: HomePage( - notificationAppLaunchDetails, - ), + initialRoute: initialRoute, + routes: { + HomePage.routeName: (_) => HomePage(notificationAppLaunchDetails), + SecondPage.routeName: (_) => SecondPage(selectedNotificationPayload) + }, ), ); } @@ -128,7 +138,10 @@ class HomePage extends StatefulWidget { Key key, }) : super(key: key); + static const String routeName = '/'; + final NotificationAppLaunchDetails notificationAppLaunchDetails; + bool get didNotificationLaunchApp => notificationAppLaunchDetails?.didNotificationLaunchApp ?? false; @@ -185,7 +198,7 @@ class _HomePageState extends State { context, MaterialPageRoute( builder: (BuildContext context) => - SecondScreen(receivedNotification.payload), + SecondPage(receivedNotification.payload), ), ); }, @@ -199,11 +212,7 @@ class _HomePageState extends State { void _configureSelectNotificationSubject() { selectNotificationSubject.stream.listen((String payload) async { - await Navigator.push( - context, - MaterialPageRoute( - builder: (BuildContext context) => SecondScreen(payload)), - ); + await Navigator.pushNamed(context, '/secondPage'); }); } @@ -1642,19 +1651,21 @@ class _HomePageState extends State { } } -class SecondScreen extends StatefulWidget { - const SecondScreen( +class SecondPage extends StatefulWidget { + const SecondPage( this.payload, { Key key, }) : super(key: key); + static const String routeName = '/secondPage'; + final String payload; @override - State createState() => SecondScreenState(); + State createState() => SecondPageState(); } -class SecondScreenState extends State { +class SecondPageState extends State { String _payload; @override void initState() { diff --git a/flutter_local_notifications/example/pubspec.yaml b/flutter_local_notifications/example/pubspec.yaml index cdaf1df7c..8a4c8849f 100644 --- a/flutter_local_notifications/example/pubspec.yaml +++ b/flutter_local_notifications/example/pubspec.yaml @@ -5,21 +5,21 @@ publish_to: none dependencies: flutter: sdk: flutter + flutter_local_notifications: + path: ../ cupertino_icons: ^0.1.3 device_info: ^0.4.2+4 http: ^0.12.0+4 path_provider: ^1.6.7 - shared_preferences: ^0.5.7 rxdart: ^0.24.0 - flutter_local_notifications: - path: ../ + shared_preferences: ^0.5.7 dev_dependencies: flutter_driver: sdk: flutter flutter_test: sdk: flutter - e2e: ^0.4.3+1 + integration_test: ^1.0.1 flutter: uses-material-design: true diff --git a/flutter_local_notifications/example/test_driver/flutter_local_notifications_e2e_test.dart b/flutter_local_notifications/example/test_driver/flutter_local_notifications_e2e_test.dart deleted file mode 100644 index 983c3863d..000000000 --- a/flutter_local_notifications/example/test_driver/flutter_local_notifications_e2e_test.dart +++ /dev/null @@ -1,5 +0,0 @@ -import 'dart:async'; - -import 'package:e2e/e2e_driver.dart' as e2e; - -Future main() async => e2e.main(); diff --git a/flutter_local_notifications/example/test_driver/integration_test.dart b/flutter_local_notifications/example/test_driver/integration_test.dart new file mode 100644 index 000000000..b38629cca --- /dev/null +++ b/flutter_local_notifications/example/test_driver/integration_test.dart @@ -0,0 +1,3 @@ +import 'package:integration_test/integration_test_driver.dart'; + +Future main() => integrationDriver(); diff --git a/flutter_local_notifications/ios/Classes/FlutterLocalNotificationsPlugin.m b/flutter_local_notifications/ios/Classes/FlutterLocalNotificationsPlugin.m index d4ac01af5..de3bb004d 100644 --- a/flutter_local_notifications/ios/Classes/FlutterLocalNotificationsPlugin.m +++ b/flutter_local_notifications/ios/Classes/FlutterLocalNotificationsPlugin.m @@ -228,7 +228,7 @@ - (void)initialize:(NSDictionary * _Nonnull)arguments result:(FlutterResult _Non if([self containsKey:REQUEST_BADGE_PERMISSION forDictionary:arguments]) { requestedBadgePermission = [arguments[REQUEST_BADGE_PERMISSION] boolValue]; } - [self requestPermissionsImpl:requestedSoundPermission alertPermission:requestedAlertPermission badgePermission:requestedBadgePermission checkLaunchNotification:true result:result]; + [self requestPermissionsImpl:requestedSoundPermission alertPermission:requestedAlertPermission badgePermission:requestedBadgePermission result:result]; _initialized = true; } @@ -246,13 +246,13 @@ - (void)requestPermissions:(NSDictionary * _Nonnull)arguments result:(FlutterRes if([self containsKey:BADGE_PERMISSION forDictionary:arguments]) { badgePermission = [arguments[BADGE_PERMISSION] boolValue]; } - [self requestPermissionsImpl:soundPermission alertPermission:alertPermission badgePermission:badgePermission checkLaunchNotification:false result:result]; + [self requestPermissionsImpl:soundPermission alertPermission:alertPermission badgePermission:badgePermission result:result]; } - (void)requestPermissionsImpl:(bool)soundPermission alertPermission:(bool)alertPermission badgePermission:(bool)badgePermission - checkLaunchNotification:(bool)checkLaunchNotification result:(FlutterResult _Nonnull)result{ + result:(FlutterResult _Nonnull)result{ if(@available(iOS 10.0, *)) { UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter]; @@ -267,9 +267,6 @@ - (void)requestPermissionsImpl:(bool)soundPermission authorizationOptions += UNAuthorizationOptionBadge; } [center requestAuthorizationWithOptions:(authorizationOptions) completionHandler:^(BOOL granted, NSError * _Nullable error) { - if(checkLaunchNotification && self->_launchPayload != nil) { - [self handleSelectNotification:self->_launchPayload]; - } result(@(granted)); }]; } else { @@ -285,10 +282,6 @@ - (void)requestPermissionsImpl:(bool)soundPermission } UIUserNotificationSettings *settings = [UIUserNotificationSettings settingsForTypes:notificationTypes categories:nil]; [[UIApplication sharedApplication] registerUserNotificationSettings:settings]; - if(checkLaunchNotification && _launchNotification != nil && [self isAFlutterLocalNotification:_launchNotification.userInfo]) { - NSString *payload = _launchNotification.userInfo[PAYLOAD]; - [self handleSelectNotification:payload]; - } result(@YES); } } @@ -602,16 +595,16 @@ - (UNCalendarNotificationTrigger *) buildUserNotificationCalendarTrigger:(id) ar NSCalendar *calendar = [NSCalendar currentCalendar]; NSTimeZone *timezone = [NSTimeZone timeZoneWithName:timeZoneName]; NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init]; - + // Needed for some countries, when phone DateTime format is 12H NSLocale *posix = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US_POSIX"]; - + [dateFormatter setDateFormat:@"yyyy-MM-dd'T'HH:mm:ss"]; [dateFormatter setTimeZone:timezone]; [dateFormatter setLocale:posix]; - + NSDate *date = [dateFormatter dateFromString:scheduledDateTime]; - + calendar.timeZone = timezone; if(matchDateComponents != nil) { if([matchDateComponents integerValue] == Time) { diff --git a/flutter_local_notifications/lib/src/flutter_local_notifications_plugin.dart b/flutter_local_notifications/lib/src/flutter_local_notifications_plugin.dart index 1d371a5f6..537e0ab1d 100644 --- a/flutter_local_notifications/lib/src/flutter_local_notifications_plugin.dart +++ b/flutter_local_notifications/lib/src/flutter_local_notifications_plugin.dart @@ -95,9 +95,6 @@ class FlutterLocalNotificationsPlugin { /// Initializes the plugin. /// /// Call this method on application before using the plugin further. - /// This should only be done once. When a notification created by this plugin - /// was used to launch the app, calling `initialize` is what will trigger - /// to the `onSelectNotification` callback to be fire. /// /// Will return a [bool] value to indicate if initialization succeeded. /// On iOS this is dependent on if permissions have been granted to show diff --git a/flutter_local_notifications/macos/Classes/FlutterLocalNotificationsPlugin.swift b/flutter_local_notifications/macos/Classes/FlutterLocalNotificationsPlugin.swift index 14b4a39bf..053e68984 100644 --- a/flutter_local_notifications/macos/Classes/FlutterLocalNotificationsPlugin.swift +++ b/flutter_local_notifications/macos/Classes/FlutterLocalNotificationsPlugin.swift @@ -156,9 +156,6 @@ public class FlutterLocalNotificationsPlugin: NSObject, FlutterPlugin, UNUserNot let requestedSoundPermission = arguments[MethodCallArguments.requestSoundPermission] as! Bool let requestedBadgePermission = arguments[MethodCallArguments.requestBadgePermission] as! Bool requestPermissionsImpl(soundPermission: requestedSoundPermission, alertPermission: requestedAlertPermission, badgePermission: requestedBadgePermission, result: result) - if(launchingAppFromNotification) { - handleSelectNotification(payload: launchPayload) - } initialized = true } else { diff --git a/flutter_local_notifications/pubspec.yaml b/flutter_local_notifications/pubspec.yaml index 4ff0e57a6..5c635c985 100644 --- a/flutter_local_notifications/pubspec.yaml +++ b/flutter_local_notifications/pubspec.yaml @@ -1,22 +1,24 @@ name: flutter_local_notifications -description: A cross platform plugin for displaying and scheduling local notifications for Flutter applications with the ability to customise for each platform. -version: 3.0.3 +description: A cross platform plugin for displaying and scheduling local + notifications for Flutter applications with the ability to customise for each + platform. +version: 4.0.0 homepage: https://github.com/MaikuB/flutter_local_notifications/tree/master/flutter_local_notifications dependencies: flutter: sdk: flutter - platform: ">=2.0.0 <4.0.0" flutter_local_notifications_platform_interface: ^2.0.0+1 - timezone: ^0.5.6 + platform: ">=2.0.0 <4.0.0" + timezone: ^0.6.1 dev_dependencies: flutter_driver: sdk: flutter flutter_test: sdk: flutter - mockito: ^4.1.1 - plugin_platform_interface: ^1.0.1 + mockito: ^4.1.3 + plugin_platform_interface: ^1.0.3 flutter: plugin: @@ -29,7 +31,6 @@ flutter: macos: pluginClass: FlutterLocalNotificationsPlugin - environment: sdk: ">=2.6.0 <3.0.0" flutter: ">=1.12.13+hotfix.5" From e824fde49d24a8a3fedb4925a82b18f8ee89f1ca Mon Sep 17 00:00:00 2001 From: Shapovalova Vera <47853911+VAShapovalova@users.noreply.github.com> Date: Wed, 10 Feb 2021 17:57:00 +0700 Subject: [PATCH 06/63] Extend notification channels management (#997) * add method getNotificationChannels * add example for method getNotificationChannels * update getNotification func * update example * tweak api docs for getNotificationChannels and update logic for extracting info * remove gradle wrapper props * rearrange get notification channels button in example app and bump plugin to 4.0.1 Co-authored-by: Michael Bui <25263378+MaikuB@users.noreply.github.com> --- flutter_local_notifications/CHANGELOG.md | 4 + .../FlutterLocalNotificationsPlugin.java | 70 +++++++++++++++-- .../example/lib/main.dart | 77 ++++++++++++++++++- .../platform_flutter_local_notifications.dart | 44 +++++++++++ flutter_local_notifications/pubspec.yaml | 2 +- 5 files changed, 188 insertions(+), 9 deletions(-) diff --git a/flutter_local_notifications/CHANGELOG.md b/flutter_local_notifications/CHANGELOG.md index 09eda086d..5d3af2213 100644 --- a/flutter_local_notifications/CHANGELOG.md +++ b/flutter_local_notifications/CHANGELOG.md @@ -1,3 +1,7 @@ +# [4.0.1] + +* Added the `getNotificationChannels` method to the `AndroidFlutterLocalNotificationsPlugin` class. Thanks to the PR from [Shapovalova Vera](https://github.com/VAShapovalova) + # [4.0.0] * **Breaking change** calling `initialize` will no longer trigger the `onSelectNotification` if a notification was tapped on prior to calling `initialize`. This was done as the `getNotificationAppLaunchDetails` method already provided a way to handle when an application was launched by a notification. Furthermore, calling `initialize` multiple times (e.g. on different pages) would have previously caused the `onSelectNotification` callback multiples times as well. This potentially results in the same notification being processed again diff --git a/flutter_local_notifications/android/src/main/java/com/dexterous/flutterlocalnotifications/FlutterLocalNotificationsPlugin.java b/flutter_local_notifications/android/src/main/java/com/dexterous/flutterlocalnotifications/FlutterLocalNotificationsPlugin.java index 6a699e984..cdf98dd7a 100644 --- a/flutter_local_notifications/android/src/main/java/com/dexterous/flutterlocalnotifications/FlutterLocalNotificationsPlugin.java +++ b/flutter_local_notifications/android/src/main/java/com/dexterous/flutterlocalnotifications/FlutterLocalNotificationsPlugin.java @@ -61,6 +61,7 @@ import java.time.ZonedDateTime; import java.time.format.DateTimeFormatter; import java.util.ArrayList; +import java.util.Arrays; import java.util.Calendar; import java.util.HashMap; import java.util.Iterator; @@ -95,6 +96,7 @@ public class FlutterLocalNotificationsPlugin implements MethodCallHandler, Plugi private static final String CREATE_NOTIFICATION_CHANNEL_METHOD = "createNotificationChannel"; private static final String DELETE_NOTIFICATION_CHANNEL_METHOD = "deleteNotificationChannel"; private static final String GET_ACTIVE_NOTIFICATIONS_METHOD = "getActiveNotifications"; + private static final String GET_NOTIFICATION_CHANNELS_METHOD = "getNotificationChannels"; private static final String PENDING_NOTIFICATION_REQUESTS_METHOD = "pendingNotificationRequests"; private static final String SHOW_METHOD = "show"; private static final String CANCEL_METHOD = "cancel"; @@ -114,6 +116,7 @@ public class FlutterLocalNotificationsPlugin implements MethodCallHandler, Plugi private static final String INVALID_LED_DETAILS_ERROR_CODE = "INVALID_LED_DETAILS"; private static final String GET_ACTIVE_NOTIFICATIONS_ERROR_CODE = "GET_ACTIVE_NOTIFICATIONS_ERROR_CODE"; private static final String GET_ACTIVE_NOTIFICATIONS_ERROR_MESSAGE = "Android version must be 6.0 or newer to use getActiveNotifications"; + private static final String GET_NOTIFICATION_CHANNELS_ERROR_CODE = "GET_NOTIFICATION_CHANNELS_ERROR_CODE"; private static final String INVALID_LED_DETAILS_ERROR_MESSAGE = "Must specify both ledOnMs and ledOffMs to configure the blink cycle on older versions of Android before Oreo"; private static final String NOTIFICATION_LAUNCHED_APP = "notificationLaunchedApp"; private static final String INVALID_DRAWABLE_RESOURCE_ERROR_MESSAGE = "The resource %s could not be found. Please make sure it has been added as a drawable resource to your Android head project."; @@ -279,14 +282,14 @@ private static void saveScheduledNotifications(Context context, ArrayList arguments) { NotificationDetails notificationDetails = NotificationDetails.from(arguments); @@ -1248,4 +1253,55 @@ private void getActiveNotifications(Result result) { result.error(GET_ACTIVE_NOTIFICATIONS_ERROR_CODE, e.getMessage(), e.getStackTrace()); } } + + private void getNotificationChannels(Result result) { + try { + NotificationManagerCompat notificationManagerCompat = getNotificationManager(applicationContext); + List channels = notificationManagerCompat.getNotificationChannels(); + List> channelsPayload = new ArrayList<>(); + for (NotificationChannel channel : channels) { + HashMap channelPayload = getMappedNotificationChannel(channel); + channelsPayload.add(channelPayload); + } + result.success(channelsPayload); + } catch (Throwable e) { + result.error(GET_NOTIFICATION_CHANNELS_ERROR_CODE, e.getMessage(), e.getStackTrace()); + } + } + + private HashMap getMappedNotificationChannel(NotificationChannel channel) { + HashMap channelPayload = new HashMap<>(); + if (VERSION.SDK_INT >= VERSION_CODES.O) { + channelPayload.put("id", channel.getId()); + channelPayload.put("name", channel.getName()); + channelPayload.put("description", channel.getDescription()); + channelPayload.put("groupId", channel.getGroup()); + channelPayload.put("showBadge", channel.canShowBadge()); + channelPayload.put("importance", channel.getImportance()); + Uri soundUri = channel.getSound(); + if (soundUri == null) { + channelPayload.put("sound", null); + channelPayload.put("playSound", false); + } else { + channelPayload.put("playSound", true); + List soundSources = Arrays.asList(SoundSource.values()); + if (soundUri.getScheme().equals("android.resource")) { + String[] splitUri = soundUri.toString().split("/"); + String resourceName = applicationContext.getResources().getResourceEntryName(Integer.parseInt(splitUri[splitUri.length - 1])); + if (resourceName != null) { + channelPayload.put("soundSource", soundSources.indexOf(SoundSource.RawResource)); + channelPayload.put("sound", resourceName); + } + } else { + channelPayload.put("soundSource", soundSources.indexOf(SoundSource.Uri)); + channelPayload.put("sound", soundUri.toString()); + } + } + channelPayload.put("enableVibration", channel.shouldVibrate()); + channelPayload.put("vibrationPattern", channel.getVibrationPattern()); + channelPayload.put("enableLights", channel.shouldShowLights()); + channelPayload.put("ledColor", channel.getLightColor()); + } + return channelPayload; + } } diff --git a/flutter_local_notifications/example/lib/main.dart b/flutter_local_notifications/example/lib/main.dart index 3a7d79c5b..9547be5c3 100644 --- a/flutter_local_notifications/example/lib/main.dart +++ b/flutter_local_notifications/example/lib/main.dart @@ -557,6 +557,12 @@ class _HomePageState extends State { await _deleteNotificationChannel(); }, ), + PaddedRaisedButton( + buttonText: 'Get notification channels', + onPressed: () async { + await _getNotificationChannels(); + }, + ), PaddedRaisedButton( buttonText: 'Get active notifications', onPressed: () async { @@ -570,7 +576,7 @@ class _HomePageState extends State { style: TextStyle(fontWeight: FontWeight.bold), ), PaddedRaisedButton( - buttonText: 'Show notification with subtitle', + buttonText: 'x notification with subtitle', onPressed: () async { await _showNotificationWithSubtitle(); }, @@ -1649,6 +1655,75 @@ class _HomePageState extends State { ); } } + + Future _getNotificationChannels() async { + final Widget notificationChannelsDialogContent = + await _getNotificationChannelsDialogContent(); + await showDialog( + context: context, + builder: (BuildContext context) => AlertDialog( + content: notificationChannelsDialogContent, + actions: [ + FlatButton( + onPressed: () { + Navigator.of(context).pop(); + }, + child: const Text('OK'), + ), + ], + ), + ); + } + + Future _getNotificationChannelsDialogContent() async { + try { + final List channels = + await flutterLocalNotificationsPlugin + .resolvePlatformSpecificImplementation< + AndroidFlutterLocalNotificationsPlugin>() + ?.getNotificationChannels(); + + return Container( + width: double.maxFinite, + child: ListView( + children: [ + const Text( + 'Notifications Channels', + style: TextStyle(fontWeight: FontWeight.bold), + ), + const Divider(color: Colors.black), + if (channels?.isEmpty) const Text('No notification channels'), + if (channels.isNotEmpty) + for (AndroidNotificationChannel channel in channels) + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text('id: ${channel.id}\n' + 'name: ${channel.name}\n' + 'description: ${channel.description}\n' + 'groupId: ${channel.groupId}\n' + 'importance: ${channel.importance.value}\n' + 'playSound: ${channel.playSound}\n' + 'sound: ${channel.sound?.sound}\n' + 'enableVibration: ${channel.enableVibration}\n' + 'vibrationPattern: ${channel.vibrationPattern}\n' + 'showBadge: ${channel.showBadge}\n' + 'enableLights: ${channel.enableLights}\n' + 'ledColor: ${channel.ledColor}\n'), + const Divider(color: Colors.black), + ], + ), + ], + ), + ); + } on PlatformException catch (error) { + return Text( + 'Error calling "getNotificationChannels"\n' + 'code: ${error.code}\n' + 'message: ${error.message}', + ); + } + } } class SecondPage extends StatefulWidget { diff --git a/flutter_local_notifications/lib/src/platform_flutter_local_notifications.dart b/flutter_local_notifications/lib/src/platform_flutter_local_notifications.dart index 58d21093c..6eb397624 100644 --- a/flutter_local_notifications/lib/src/platform_flutter_local_notifications.dart +++ b/flutter_local_notifications/lib/src/platform_flutter_local_notifications.dart @@ -2,16 +2,19 @@ import 'dart:async'; import 'package:flutter/foundation.dart'; import 'package:flutter/services.dart'; +import 'package:flutter/widgets.dart'; import 'package:flutter_local_notifications_platform_interface/flutter_local_notifications_platform_interface.dart'; import 'package:timezone/timezone.dart'; import 'helpers.dart'; import 'platform_specifics/android/active_notification.dart'; +import 'platform_specifics/android/enums.dart'; import 'platform_specifics/android/initialization_settings.dart'; import 'platform_specifics/android/method_channel_mappers.dart'; import 'platform_specifics/android/notification_channel.dart'; import 'platform_specifics/android/notification_channel_group.dart'; import 'platform_specifics/android/notification_details.dart'; +import 'platform_specifics/android/notification_sound.dart'; import 'platform_specifics/ios/enums.dart'; import 'platform_specifics/ios/initialization_settings.dart'; import 'platform_specifics/ios/method_channel_mappers.dart'; @@ -311,6 +314,47 @@ class AndroidFlutterLocalNotificationsPlugin ?.toList(); } + /// Returns the list of all notification channels. + /// + /// This method will return an empty list on Android versions older than 6.0. + Future> getNotificationChannels() async { + final List> notificationChannels = + await _channel.invokeListMethod('getNotificationChannels'); + + return notificationChannels + // ignore: always_specify_types + ?.map((a) => AndroidNotificationChannel( + a['id'], + a['name'], + a['description'], + groupId: a['groupId'], + showBadge: a['showBadge'], + importance: Importance(a['importance']), + playSound: a['playSound'], + sound: _getNotificationChannelSound(a), + enableLights: a['enableLights'], + enableVibration: a['enableVibration'], + vibrationPattern: a['vibrationPattern'], + ledColor: Color(a['ledColor']), + )) + ?.toList(); + } + + AndroidNotificationSound _getNotificationChannelSound( + Map channelMap) { + final int soundSourceIndex = channelMap['soundSource']; + AndroidNotificationSound sound; + if (soundSourceIndex != null) { + if (soundSourceIndex == + AndroidNotificationSoundSource.rawResource.index) { + sound = RawResourceAndroidNotificationSound(channelMap['sound']); + } else if (soundSourceIndex == AndroidNotificationSoundSource.uri.index) { + sound = UriAndroidNotificationSound(channelMap['sound']); + } + } + return sound; + } + Future _handleMethod(MethodCall call) { switch (call.method) { case 'selectNotification': diff --git a/flutter_local_notifications/pubspec.yaml b/flutter_local_notifications/pubspec.yaml index 5c635c985..87fcdcdb9 100644 --- a/flutter_local_notifications/pubspec.yaml +++ b/flutter_local_notifications/pubspec.yaml @@ -2,7 +2,7 @@ name: flutter_local_notifications description: A cross platform plugin for displaying and scheduling local notifications for Flutter applications with the ability to customise for each platform. -version: 4.0.0 +version: 4.0.1 homepage: https://github.com/MaikuB/flutter_local_notifications/tree/master/flutter_local_notifications dependencies: From e4d42abe88d925e7c43850a073f49c10b41a6f25 Mon Sep 17 00:00:00 2001 From: Michael Bui <25263378+MaikuB@users.noreply.github.com> Date: Wed, 10 Feb 2021 22:26:41 +1100 Subject: [PATCH 07/63] fix api docs for getNotificationChannels and amend 4.0.1 changelog entry (#1034) --- flutter_local_notifications/CHANGELOG.md | 2 +- .../lib/src/platform_flutter_local_notifications.dart | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/flutter_local_notifications/CHANGELOG.md b/flutter_local_notifications/CHANGELOG.md index 5d3af2213..536da4b67 100644 --- a/flutter_local_notifications/CHANGELOG.md +++ b/flutter_local_notifications/CHANGELOG.md @@ -1,6 +1,6 @@ # [4.0.1] -* Added the `getNotificationChannels` method to the `AndroidFlutterLocalNotificationsPlugin` class. Thanks to the PR from [Shapovalova Vera](https://github.com/VAShapovalova) +* [Android] added the `getNotificationChannels` method to the `AndroidFlutterLocalNotificationsPlugin` class. This can be used to a get list of all the notification channels on devices with Android 8.0 or newer. Thanks to the PR from [Shapovalova Vera](https://github.com/VAShapovalova) # [4.0.0] diff --git a/flutter_local_notifications/lib/src/platform_flutter_local_notifications.dart b/flutter_local_notifications/lib/src/platform_flutter_local_notifications.dart index 6eb397624..20b576eff 100644 --- a/flutter_local_notifications/lib/src/platform_flutter_local_notifications.dart +++ b/flutter_local_notifications/lib/src/platform_flutter_local_notifications.dart @@ -316,7 +316,8 @@ class AndroidFlutterLocalNotificationsPlugin /// Returns the list of all notification channels. /// - /// This method will return an empty list on Android versions older than 6.0. + /// This method is only applicable on Android 8.0 or newer. On older versions, + /// it will return an empty list. Future> getNotificationChannels() async { final List> notificationChannels = await _channel.invokeListMethod('getNotificationChannels'); From c21506bcae63f84813aa01e09330006cd4d138d2 Mon Sep 17 00:00:00 2001 From: Michael Bui <25263378+MaikuB@users.noreply.github.com> Date: Mon, 22 Feb 2021 19:55:01 +1100 Subject: [PATCH 08/63] [flutter_local_notifications] fix note in readme around onSelectNotification changes in 4.0 (#1048) * fix note in readme around onSelectNotification changes in 4.0 * switch to dev channel to build macos example --- .cirrus.yml | 2 +- flutter_local_notifications/CHANGELOG.md | 4 ++++ flutter_local_notifications/README.md | 2 +- flutter_local_notifications/pubspec.yaml | 2 +- 4 files changed, 7 insertions(+), 3 deletions(-) diff --git a/.cirrus.yml b/.cirrus.yml index 3ab328339..a4d9dfcee 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -25,7 +25,7 @@ task: pub_cache: folder: ~/.pub-cache upgrade_script: - - flutter channel master + - flutter channel dev - flutter upgrade setup_script: - flutter config --enable-macos-desktop diff --git a/flutter_local_notifications/CHANGELOG.md b/flutter_local_notifications/CHANGELOG.md index 536da4b67..9daa936e3 100644 --- a/flutter_local_notifications/CHANGELOG.md +++ b/flutter_local_notifications/CHANGELOG.md @@ -1,3 +1,7 @@ +# [4.0.1+1] + +* Fixed typo in readme around the note relating to version 4.0 of the plugin where `onSelectNotification` will not be triggered when an app is launched by tapping on a notification. + # [4.0.1] * [Android] added the `getNotificationChannels` method to the `AndroidFlutterLocalNotificationsPlugin` class. This can be used to a get list of all the notification channels on devices with Android 8.0 or newer. Thanks to the PR from [Shapovalova Vera](https://github.com/VAShapovalova) diff --git a/flutter_local_notifications/README.md b/flutter_local_notifications/README.md index d400aff40..bc0866ca0 100644 --- a/flutter_local_notifications/README.md +++ b/flutter_local_notifications/README.md @@ -302,7 +302,7 @@ The `IOSInitializationSettings` and `MacOSInitializationSettings` provides defau On iOS and macOS, initialisation may show a prompt to requires users to give the application permission to display notifications (note: permissions don't need to be requested on Android). Depending on when this happens, this may not be the ideal user experience for your application. If so, please refer to the next section on how to work around this. -*Note*: from version 4.0 of the plugin, calling `initialize` will now trigger the `onSelectNotification` callback when the application was started by tapping on a notification to trigger. Use the `getNotificationAppLaunchDetails` method that is available in the plugin if you need to handle a notification triggering the launch for an app e.g. change the home route of the app for deep-linking. +*Note*: from version 4.0 of the plugin, calling `initialize` will not trigger the `onSelectNotification` callback when the application was started by tapping on a notification to trigger. Use the `getNotificationAppLaunchDetails` method that is available in the plugin if you need to handle a notification triggering the launch for an app e.g. change the home route of the app for deep-linking. ### [iOS (all supported versions) and macOS 10.14+] Requesting notification permissions diff --git a/flutter_local_notifications/pubspec.yaml b/flutter_local_notifications/pubspec.yaml index 87fcdcdb9..a389059cb 100644 --- a/flutter_local_notifications/pubspec.yaml +++ b/flutter_local_notifications/pubspec.yaml @@ -2,7 +2,7 @@ name: flutter_local_notifications description: A cross platform plugin for displaying and scheduling local notifications for Flutter applications with the ability to customise for each platform. -version: 4.0.1 +version: 4.0.1+1 homepage: https://github.com/MaikuB/flutter_local_notifications/tree/master/flutter_local_notifications dependencies: From 2e434b5ed1a2db35121c3554d01f7ae0d7840cc8 Mon Sep 17 00:00:00 2001 From: Andrey Parvatkin Date: Sun, 7 Mar 2021 03:58:25 +0400 Subject: [PATCH 09/63] Don't request empty permissions (#1063) * Don't request empty permissions This fix prevents asking for permissions on the plugin initialization * add missing semicolon Co-authored-by: Andrey Parvatkin --- .../ios/Classes/FlutterLocalNotificationsPlugin.m | 4 ++++ .../macos/Classes/FlutterLocalNotificationsPlugin.swift | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/flutter_local_notifications/ios/Classes/FlutterLocalNotificationsPlugin.m b/flutter_local_notifications/ios/Classes/FlutterLocalNotificationsPlugin.m index de3bb004d..12fed21d9 100644 --- a/flutter_local_notifications/ios/Classes/FlutterLocalNotificationsPlugin.m +++ b/flutter_local_notifications/ios/Classes/FlutterLocalNotificationsPlugin.m @@ -253,6 +253,10 @@ - (void)requestPermissionsImpl:(bool)soundPermission alertPermission:(bool)alertPermission badgePermission:(bool)badgePermission result:(FlutterResult _Nonnull)result{ + if(!soundPermission && !alertPermission && !badgePermission) { + result(@NO); + return; + } if(@available(iOS 10.0, *)) { UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter]; diff --git a/flutter_local_notifications/macos/Classes/FlutterLocalNotificationsPlugin.swift b/flutter_local_notifications/macos/Classes/FlutterLocalNotificationsPlugin.swift index 053e68984..84dd2f47b 100644 --- a/flutter_local_notifications/macos/Classes/FlutterLocalNotificationsPlugin.swift +++ b/flutter_local_notifications/macos/Classes/FlutterLocalNotificationsPlugin.swift @@ -441,6 +441,10 @@ public class FlutterLocalNotificationsPlugin: NSObject, FlutterPlugin, UNUserNot @available(OSX 10.14, *) func requestPermissionsImpl(soundPermission: Bool, alertPermission: Bool, badgePermission: Bool, result: @escaping FlutterResult) { + if(!soundPermission && !alertPermission && !badgePermission) { + result(false) + return + } var options: UNAuthorizationOptions = [] if(soundPermission) { options.insert(.sound) From ac730a0298edd8335cde07a09a396c46cc8c9292 Mon Sep 17 00:00:00 2001 From: Michael Bui <25263378+MaikuB@users.noreply.github.com> Date: Sun, 7 Mar 2021 14:26:32 +1100 Subject: [PATCH 10/63] bump plugin to 4.0.1+2 and include details (#1075) --- flutter_local_notifications/CHANGELOG.md | 4 ++++ flutter_local_notifications/pubspec.yaml | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/flutter_local_notifications/CHANGELOG.md b/flutter_local_notifications/CHANGELOG.md index 9daa936e3..056354d12 100644 --- a/flutter_local_notifications/CHANGELOG.md +++ b/flutter_local_notifications/CHANGELOG.md @@ -1,3 +1,7 @@ +# [4.0.1+2] + +* [iOS/macOS] fixed issue where not requesting any permissions (i.e. all the boolean flags were set to false) would still cause a permissions prompt to appear. Thanks to the PR from [Andrey Parvatkin](https://github.com/badver) + # [4.0.1+1] * Fixed typo in readme around the note relating to version 4.0 of the plugin where `onSelectNotification` will not be triggered when an app is launched by tapping on a notification. diff --git a/flutter_local_notifications/pubspec.yaml b/flutter_local_notifications/pubspec.yaml index a389059cb..199a584a2 100644 --- a/flutter_local_notifications/pubspec.yaml +++ b/flutter_local_notifications/pubspec.yaml @@ -2,7 +2,7 @@ name: flutter_local_notifications description: A cross platform plugin for displaying and scheduling local notifications for Flutter applications with the ability to customise for each platform. -version: 4.0.1+1 +version: 4.0.1+2 homepage: https://github.com/MaikuB/flutter_local_notifications/tree/master/flutter_local_notifications dependencies: From 99407d9a78af7bbebc9be8b1cf315a6fc5852fa1 Mon Sep 17 00:00:00 2001 From: Michael Bui <25263378+MaikuB@users.noreply.github.com> Date: Sat, 20 Mar 2021 19:47:29 +1100 Subject: [PATCH 11/63] Migrate to null-safety (#1029) * migrate platform interface to null safety * bump dependencies used by platform interface * update cirrus script to use beta channel * change version number used for initial null safety release for platform interface * updated constraints for plugin_platform_interface dependency * remove obsolete todo comment in platform interface tests * fix PendingNotificationRequest class so that title, body and payload are nullable * migrate plugin to null safety and address deprecation warnings for buttons * update entitlements for macos example app to allow for network calls * fix cirrus script to build ios example app on beta channel * fix version mentioned in changelog entry * update version reqs of dependencies used by plugin_platform_interface * update platform interface to go back to null safety prelease of mockito * update cirrus script to use stable channel * change macos image used in cirrus script * restore steps to switch to stable channel when building on macos * bump plugin's dependency on platform interface * update platform interface stable nnbd release * bump plugin for nnbd stable release --- .cirrus.yml | 9 +- analysis_options.yaml | 4 +- flutter_local_notifications/CHANGELOG.md | 4 + .../example/analysis_options.yaml | 4 +- .../flutter_local_notifications_test.dart | 2 + .../contents.xcworkspacedata | 2 +- .../example/lib/main.dart | 218 +++++++------- .../macos/Runner/DebugProfile.entitlements | 2 + .../example/pubspec.yaml | 19 +- .../example/test_driver/integration_test.dart | 1 + .../flutter_local_notifications_plugin.dart | 112 ++++--- .../lib/src/helpers.dart | 2 +- .../lib/src/initialization_settings.dart | 6 +- .../lib/src/notification_details.dart | 6 +- .../platform_flutter_local_notifications.dart | 262 ++++++++--------- .../android/active_notification.dart | 6 +- .../src/platform_specifics/android/enums.dart | 2 +- .../platform_specifics/android/message.dart | 12 +- .../android/method_channel_mappers.dart | 88 +++--- .../android/notification_channel.dart | 8 +- .../android/notification_channel_group.dart | 2 +- .../android/notification_details.dart | 44 +-- .../android/notification_sound.dart | 4 +- .../platform_specifics/android/person.dart | 12 +- .../styles/big_picture_style_information.dart | 6 +- .../styles/big_text_style_information.dart | 4 +- .../styles/inbox_style_information.dart | 4 +- .../styles/messaging_style_information.dart | 9 +- .../ios/initialization_settings.dart | 9 +- .../ios/method_channel_mappers.dart | 4 +- .../ios/notification_attachment.dart | 4 +- .../ios/notification_details.dart | 16 +- .../macos/initialization_settings.dart | 7 +- .../macos/method_channel_mappers.dart | 4 +- .../macos/notification_attachment.dart | 4 +- .../macos/notification_details.dart | 16 +- .../lib/src/typedefs.dart | 4 +- flutter_local_notifications/pubspec.yaml | 14 +- .../flutter_local_notifications_test.dart | 6 - ...form_flutter_local_notifications_test.dart | 273 +++++++++--------- .../CHANGELOG.md | 4 + ...ocal_notifications_platform_interface.dart | 10 +- .../src/notification_app_launch_details.dart | 2 +- .../lib/src/types.dart | 6 +- .../pubspec.yaml | 12 +- ...notifications_platform_interface_test.dart | 2 +- 46 files changed, 623 insertions(+), 628 deletions(-) diff --git a/.cirrus.yml b/.cirrus.yml index a4d9dfcee..1dc4a3ed8 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -11,9 +11,12 @@ task: task: name: Build iOS example app osx_instance: - image: catalina-flutter + image: big-sur-xcode-12.4 pub_cache: folder: ~/.pub-cache + upgrade_script: + - flutter channel stable + - flutter upgrade build_script: - cd flutter_local_notifications/example - flutter build ios --no-codesign --debug @@ -21,11 +24,11 @@ task: task: name: Build macOS example app osx_instance: - image: catalina-flutter + image: big-sur-xcode-12.4 pub_cache: folder: ~/.pub-cache upgrade_script: - - flutter channel dev + - flutter channel stable - flutter upgrade setup_script: - flutter config --enable-macos-desktop diff --git a/analysis_options.yaml b/analysis_options.yaml index 8cfe1ab41..4ee9df615 100644 --- a/analysis_options.yaml +++ b/analysis_options.yaml @@ -7,7 +7,7 @@ linter: - always_specify_types - annotate_overrides - avoid_annotating_with_dynamic - - avoid_as + # - avoid_as - avoid_bool_literals_in_conditional_expressions - avoid_catches_without_on_clauses - avoid_catching_errors @@ -128,7 +128,7 @@ linter: - slash_for_doc_comments - sort_child_properties_last - sort_constructors_first - - sort_pub_dependencies + # - sort_pub_dependencies - sort_unnamed_constructors_first - test_types_in_equals - throw_in_finally diff --git a/flutter_local_notifications/CHANGELOG.md b/flutter_local_notifications/CHANGELOG.md index 056354d12..14cdfd4cf 100644 --- a/flutter_local_notifications/CHANGELOG.md +++ b/flutter_local_notifications/CHANGELOG.md @@ -1,3 +1,7 @@ +# [5.0.0] + +* **Breaking change** migrated to null safety. Some arguments that were formerly null (e.g. some boolean values) are now non-nullable with a default value that should retain the old behaviour + # [4.0.1+2] * [iOS/macOS] fixed issue where not requesting any permissions (i.e. all the boolean flags were set to false) would still cause a permissions prompt to appear. Thanks to the PR from [Andrey Parvatkin](https://github.com/badver) diff --git a/flutter_local_notifications/example/analysis_options.yaml b/flutter_local_notifications/example/analysis_options.yaml index 8df560e25..300cf1c0c 100644 --- a/flutter_local_notifications/example/analysis_options.yaml +++ b/flutter_local_notifications/example/analysis_options.yaml @@ -7,7 +7,7 @@ linter: - always_specify_types - annotate_overrides - avoid_annotating_with_dynamic - - avoid_as + # - avoid_as - avoid_bool_literals_in_conditional_expressions - avoid_catches_without_on_clauses - avoid_catching_errors @@ -128,7 +128,7 @@ linter: - slash_for_doc_comments - sort_child_properties_last - sort_constructors_first - - sort_pub_dependencies + # - sort_pub_dependencies - sort_unnamed_constructors_first - test_types_in_equals - throw_in_finally diff --git a/flutter_local_notifications/example/integration_test/flutter_local_notifications_test.dart b/flutter_local_notifications/example/integration_test/flutter_local_notifications_test.dart index 5ea363afb..a21e81aa0 100644 --- a/flutter_local_notifications/example/integration_test/flutter_local_notifications_test.dart +++ b/flutter_local_notifications/example/integration_test/flutter_local_notifications_test.dart @@ -1,3 +1,5 @@ +// @dart = 2.9 + import 'dart:io'; import 'package:flutter_local_notifications/flutter_local_notifications.dart'; diff --git a/flutter_local_notifications/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/flutter_local_notifications/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata index 1d526a16e..919434a62 100644 --- a/flutter_local_notifications/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata +++ b/flutter_local_notifications/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -2,6 +2,6 @@ + location = "self:"> diff --git a/flutter_local_notifications/example/lib/main.dart b/flutter_local_notifications/example/lib/main.dart index 9547be5c3..ae55ea630 100644 --- a/flutter_local_notifications/example/lib/main.dart +++ b/flutter_local_notifications/example/lib/main.dart @@ -22,27 +22,27 @@ final FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin = final BehaviorSubject didReceiveLocalNotificationSubject = BehaviorSubject(); -final BehaviorSubject selectNotificationSubject = - BehaviorSubject(); +final BehaviorSubject selectNotificationSubject = + BehaviorSubject(); const MethodChannel platform = MethodChannel('dexterx.dev/flutter_local_notifications_example'); class ReceivedNotification { ReceivedNotification({ - @required this.id, - @required this.title, - @required this.body, - @required this.payload, + required this.id, + required this.title, + required this.body, + required this.payload, }); final int id; - final String title; - final String body; - final String payload; + final String? title; + final String? body; + final String? payload; } -String selectedNotificationPayload; +String? selectedNotificationPayload; /// IMPORTANT: running the following code on its own won't work as there is /// setup required for each platform head project. @@ -55,11 +55,11 @@ Future main() async { await _configureLocalTimeZone(); - final NotificationAppLaunchDetails notificationAppLaunchDetails = + final NotificationAppLaunchDetails? notificationAppLaunchDetails = await flutterLocalNotificationsPlugin.getNotificationAppLaunchDetails(); String initialRoute = HomePage.routeName; if (notificationAppLaunchDetails?.didNotificationLaunchApp ?? false) { - selectedNotificationPayload = notificationAppLaunchDetails.payload; + selectedNotificationPayload = notificationAppLaunchDetails!.payload; initialRoute = SecondPage.routeName; } @@ -74,7 +74,7 @@ Future main() async { requestBadgePermission: false, requestSoundPermission: false, onDidReceiveLocalNotification: - (int id, String title, String body, String payload) async { + (int id, String? title, String? body, String? payload) async { didReceiveLocalNotificationSubject.add(ReceivedNotification( id: id, title: title, body: body, payload: payload)); }); @@ -88,7 +88,7 @@ Future main() async { iOS: initializationSettingsIOS, macOS: initializationSettingsMacOS); await flutterLocalNotificationsPlugin.initialize(initializationSettings, - onSelectNotification: (String payload) async { + onSelectNotification: (String? payload) async { if (payload != null) { debugPrint('notification payload: $payload'); } @@ -108,15 +108,16 @@ Future main() async { Future _configureLocalTimeZone() async { tz.initializeTimeZones(); - final String timeZoneName = await platform.invokeMethod('getTimeZoneName'); - tz.setLocalLocation(tz.getLocation(timeZoneName)); + final String? timeZoneName = + await platform.invokeMethod('getTimeZoneName'); + tz.setLocalLocation(tz.getLocation(timeZoneName!)); } -class PaddedRaisedButton extends StatelessWidget { - const PaddedRaisedButton({ - @required this.buttonText, - @required this.onPressed, - Key key, +class PaddedElevatedButton extends StatelessWidget { + const PaddedElevatedButton({ + required this.buttonText, + required this.onPressed, + Key? key, }) : super(key: key); final String buttonText; @@ -125,7 +126,7 @@ class PaddedRaisedButton extends StatelessWidget { @override Widget build(BuildContext context) => Padding( padding: const EdgeInsets.fromLTRB(0, 0, 0, 8), - child: RaisedButton( + child: ElevatedButton( onPressed: onPressed, child: Text(buttonText), ), @@ -135,12 +136,12 @@ class PaddedRaisedButton extends StatelessWidget { class HomePage extends StatefulWidget { const HomePage( this.notificationAppLaunchDetails, { - Key key, + Key? key, }) : super(key: key); static const String routeName = '/'; - final NotificationAppLaunchDetails notificationAppLaunchDetails; + final NotificationAppLaunchDetails? notificationAppLaunchDetails; bool get didNotificationLaunchApp => notificationAppLaunchDetails?.didNotificationLaunchApp ?? false; @@ -184,10 +185,10 @@ class _HomePageState extends State { context: context, builder: (BuildContext context) => CupertinoAlertDialog( title: receivedNotification.title != null - ? Text(receivedNotification.title) + ? Text(receivedNotification.title!) : null, content: receivedNotification.body != null - ? Text(receivedNotification.body) + ? Text(receivedNotification.body!) : null, actions: [ CupertinoDialogAction( @@ -211,7 +212,7 @@ class _HomePageState extends State { } void _configureSelectNotificationSubject() { - selectNotificationSubject.stream.listen((String payload) async { + selectNotificationSubject.stream.listen((String? payload) async { await Navigator.pushNamed(context, '/secondPage'); }); } @@ -268,20 +269,20 @@ class _HomePageState extends State { style: TextStyle(fontWeight: FontWeight.bold), ), TextSpan( - text: - widget.notificationAppLaunchDetails.payload, + text: widget + .notificationAppLaunchDetails!.payload, ) ], ), ), ), - PaddedRaisedButton( + PaddedElevatedButton( buttonText: 'Show plain notification with payload', onPressed: () async { await _showNotification(); }, ), - PaddedRaisedButton( + PaddedElevatedButton( buttonText: 'Show plain notification that has no title with ' 'payload', @@ -289,7 +290,7 @@ class _HomePageState extends State { await _showNotificationWithNoTitle(); }, ), - PaddedRaisedButton( + PaddedElevatedButton( buttonText: 'Show plain notification that has no body with ' 'payload', @@ -297,13 +298,13 @@ class _HomePageState extends State { await _showNotificationWithNoBody(); }, ), - PaddedRaisedButton( + PaddedElevatedButton( buttonText: 'Show notification with custom sound', onPressed: () async { await _showNotificationCustomSound(); }, ), - PaddedRaisedButton( + PaddedElevatedButton( buttonText: 'Schedule notification to appear in 5 seconds based ' 'on local time zone', @@ -311,13 +312,13 @@ class _HomePageState extends State { await _zonedScheduleNotification(); }, ), - PaddedRaisedButton( + PaddedElevatedButton( buttonText: 'Repeat notification every minute', onPressed: () async { await _repeatNotification(); }, ), - PaddedRaisedButton( + PaddedElevatedButton( buttonText: 'Schedule daily 10:00:00 am notification in your ' 'local time zone', @@ -325,7 +326,7 @@ class _HomePageState extends State { await _scheduleDailyTenAMNotification(); }, ), - PaddedRaisedButton( + PaddedElevatedButton( buttonText: 'Schedule daily 10:00:00 am notification in your ' "local time zone using last year's date", @@ -333,7 +334,7 @@ class _HomePageState extends State { await _scheduleDailyTenAMLastYearNotification(); }, ), - PaddedRaisedButton( + PaddedElevatedButton( buttonText: 'Schedule weekly 10:00:00 am notification in your ' 'local time zone', @@ -341,7 +342,7 @@ class _HomePageState extends State { await _scheduleWeeklyTenAMNotification(); }, ), - PaddedRaisedButton( + PaddedElevatedButton( buttonText: 'Schedule weekly Monday 10:00:00 am notification in ' 'your local time zone', @@ -349,25 +350,25 @@ class _HomePageState extends State { await _scheduleWeeklyMondayTenAMNotification(); }, ), - PaddedRaisedButton( + PaddedElevatedButton( buttonText: 'Show notification with no sound', onPressed: () async { await _showNotificationWithNoSound(); }, ), - PaddedRaisedButton( + PaddedElevatedButton( buttonText: 'Check pending notifications', onPressed: () async { await _checkPendingNotificationRequests(); }, ), - PaddedRaisedButton( + PaddedElevatedButton( buttonText: 'Cancel notification', onPressed: () async { await _cancelNotification(); }, ), - PaddedRaisedButton( + PaddedElevatedButton( buttonText: 'Cancel all notifications', onPressed: () async { await _cancelAllNotifications(); @@ -378,7 +379,7 @@ class _HomePageState extends State { 'Android-specific examples', style: TextStyle(fontWeight: FontWeight.bold), ), - PaddedRaisedButton( + PaddedElevatedButton( buttonText: 'Show plain notification with payload and update ' 'channel description', @@ -386,7 +387,7 @@ class _HomePageState extends State { await _showNotificationUpdateChannelDescription(); }, ), - PaddedRaisedButton( + PaddedElevatedButton( buttonText: 'Show plain notification as public on every ' 'lockscreen', @@ -394,7 +395,7 @@ class _HomePageState extends State { await _showPublicNotification(); }, ), - PaddedRaisedButton( + PaddedElevatedButton( buttonText: 'Show notification with custom vibration pattern, ' 'red LED and red icon', @@ -402,32 +403,32 @@ class _HomePageState extends State { await _showNotificationCustomVibrationIconLed(); }, ), - PaddedRaisedButton( + PaddedElevatedButton( buttonText: 'Show notification using Android Uri sound', onPressed: () async { await _showSoundUriNotification(); }, ), - PaddedRaisedButton( + PaddedElevatedButton( buttonText: 'Show notification that times out after 3 seconds', onPressed: () async { await _showTimeoutNotification(); }, ), - PaddedRaisedButton( + PaddedElevatedButton( buttonText: 'Show insistent notification', onPressed: () async { await _showInsistentNotification(); }, ), - PaddedRaisedButton( + PaddedElevatedButton( buttonText: 'Show big picture notification', onPressed: () async { await _showBigPictureNotification(); }, ), - PaddedRaisedButton( + PaddedElevatedButton( buttonText: 'Show big picture notification, hide large icon ' 'on expand', @@ -435,135 +436,135 @@ class _HomePageState extends State { await _showBigPictureNotificationHiddenLargeIcon(); }, ), - PaddedRaisedButton( + PaddedElevatedButton( buttonText: 'Show media notification', onPressed: () async { await _showNotificationMediaStyle(); }, ), - PaddedRaisedButton( + PaddedElevatedButton( buttonText: 'Show big text notification', onPressed: () async { await _showBigTextNotification(); }, ), - PaddedRaisedButton( + PaddedElevatedButton( buttonText: 'Show inbox notification', onPressed: () async { await _showInboxNotification(); }, ), - PaddedRaisedButton( + PaddedElevatedButton( buttonText: 'Show messaging notification', onPressed: () async { await _showMessagingNotification(); }, ), - PaddedRaisedButton( + PaddedElevatedButton( buttonText: 'Show grouped notifications', onPressed: () async { await _showGroupedNotifications(); }, ), - PaddedRaisedButton( + PaddedElevatedButton( buttonText: 'Show notification with tag', onPressed: () async { await _showNotificationWithTag(); }, ), - PaddedRaisedButton( + PaddedElevatedButton( buttonText: 'Cancel notification with tag', onPressed: () async { await _cancelNotificationWithTag(); }, ), - PaddedRaisedButton( + PaddedElevatedButton( buttonText: 'Show ongoing notification', onPressed: () async { await _showOngoingNotification(); }, ), - PaddedRaisedButton( + PaddedElevatedButton( buttonText: 'Show notification with no badge, alert only once', onPressed: () async { await _showNotificationWithNoBadge(); }, ), - PaddedRaisedButton( + PaddedElevatedButton( buttonText: 'Show progress notification - updates every second', onPressed: () async { await _showProgressNotification(); }, ), - PaddedRaisedButton( + PaddedElevatedButton( buttonText: 'Show indeterminate progress notification', onPressed: () async { await _showIndeterminateProgressNotification(); }, ), - PaddedRaisedButton( + PaddedElevatedButton( buttonText: 'Show notification without timestamp', onPressed: () async { await _showNotificationWithoutTimestamp(); }, ), - PaddedRaisedButton( + PaddedElevatedButton( buttonText: 'Show notification with custom timestamp', onPressed: () async { await _showNotificationWithCustomTimestamp(); }, ), - PaddedRaisedButton( + PaddedElevatedButton( buttonText: 'Show notification with custom sub-text', onPressed: () async { await _showNotificationWithCustomSubText(); }, ), - PaddedRaisedButton( + PaddedElevatedButton( buttonText: 'Show notification with chronometer', onPressed: () async { await _showNotificationWithChronometer(); }, ), - PaddedRaisedButton( + PaddedElevatedButton( buttonText: 'Show full-screen notification', onPressed: () async { await _showFullScreenNotification(); }, ), - PaddedRaisedButton( + PaddedElevatedButton( buttonText: 'Create grouped notification channels', onPressed: () async { await _createNotificationChannelGroup(); }, ), - PaddedRaisedButton( + PaddedElevatedButton( buttonText: 'Delete notification channel group', onPressed: () async { await _deleteNotificationChannelGroup(); }, ), - PaddedRaisedButton( + PaddedElevatedButton( buttonText: 'Create notification channel', onPressed: () async { await _createNotificationChannel(); }, ), - PaddedRaisedButton( + PaddedElevatedButton( buttonText: 'Delete notification channel', onPressed: () async { await _deleteNotificationChannel(); }, ), - PaddedRaisedButton( + PaddedElevatedButton( buttonText: 'Get notification channels', onPressed: () async { await _getNotificationChannels(); }, ), - PaddedRaisedButton( + PaddedElevatedButton( buttonText: 'Get active notifications', onPressed: () async { await _getActiveNotifications(); @@ -575,25 +576,25 @@ class _HomePageState extends State { 'iOS and macOS-specific examples', style: TextStyle(fontWeight: FontWeight.bold), ), - PaddedRaisedButton( - buttonText: 'x notification with subtitle', + PaddedElevatedButton( + buttonText: 'Show notification with subtitle', onPressed: () async { await _showNotificationWithSubtitle(); }, ), - PaddedRaisedButton( + PaddedElevatedButton( buttonText: 'Show notification with icon badge', onPressed: () async { await _showNotificationWithIconBadge(); }, ), - PaddedRaisedButton( + PaddedElevatedButton( buttonText: 'Show notification with attachment', onPressed: () async { await _showNotificationWithAttachment(); }, ), - PaddedRaisedButton( + PaddedElevatedButton( buttonText: 'Show notifications with thread identifier', onPressed: () async { await _showNotificationsWithThreadIdentifier(); @@ -631,13 +632,13 @@ class _HomePageState extends State { 'to see the full-screen intent in 5 seconds, press OK and TURN ' 'OFF your screen'), actions: [ - FlatButton( + TextButton( onPressed: () { Navigator.pop(context); }, child: const Text('Cancel'), ), - FlatButton( + TextButton( onPressed: () async { await flutterLocalNotificationsPlugin.zonedSchedule( 0, @@ -790,9 +791,9 @@ class _HomePageState extends State { /// this calls a method over a platform channel implemented within the /// example app to return the Uri for the default alarm sound and uses /// as the notification sound - final String alarmUri = await platform.invokeMethod('getAlarmUri'); + final String? alarmUri = await platform.invokeMethod('getAlarmUri'); final UriAndroidNotificationSound uriSound = - UriAndroidNotificationSound(alarmUri); + UriAndroidNotificationSound(alarmUri!); final AndroidNotificationDetails androidPlatformChannelSpecifics = AndroidNotificationDetails( 'uri channel id', 'uri channel name', 'uri channel description', @@ -836,7 +837,7 @@ class _HomePageState extends State { Future _downloadAndSaveFile(String url, String fileName) async { final Directory directory = await getApplicationDocumentsDirectory(); final String filePath = '${directory.path}/$fileName'; - final http.Response response = await http.get(url); + final http.Response response = await http.get(Uri.parse(url)); final File file = File(filePath); await file.writeAsBytes(response.bodyBytes); return filePath; @@ -947,7 +948,7 @@ class _HomePageState extends State { // use a platform channel to resolve an Android drawable resource to a URI. // This is NOT part of the notifications plugin. Calls made over this /// channel is handled by the app - final String imageUri = + final String? imageUri = await platform.invokeMethod('drawableToUri', 'food'); /// First two person objects will use icons that part of the Android app's @@ -1085,7 +1086,7 @@ class _HomePageState extends State { Text('${pendingNotificationRequests.length} pending notification ' 'requests'), actions: [ - FlatButton( + TextButton( onPressed: () { Navigator.of(context).pop(); }, @@ -1472,13 +1473,13 @@ class _HomePageState extends State { description: 'your channel group description'); await flutterLocalNotificationsPlugin .resolvePlatformSpecificImplementation< - AndroidFlutterLocalNotificationsPlugin>() + AndroidFlutterLocalNotificationsPlugin>()! .createNotificationChannelGroup(androidNotificationChannelGroup); // create channels associated with the group await flutterLocalNotificationsPlugin .resolvePlatformSpecificImplementation< - AndroidFlutterLocalNotificationsPlugin>() + AndroidFlutterLocalNotificationsPlugin>()! .createNotificationChannel(const AndroidNotificationChannel( 'grouped channel id 1', 'grouped channel name 1', @@ -1487,7 +1488,7 @@ class _HomePageState extends State { await flutterLocalNotificationsPlugin .resolvePlatformSpecificImplementation< - AndroidFlutterLocalNotificationsPlugin>() + AndroidFlutterLocalNotificationsPlugin>()! .createNotificationChannel(const AndroidNotificationChannel( 'grouped channel id 2', 'grouped channel name 2', @@ -1500,7 +1501,7 @@ class _HomePageState extends State { content: Text('Channel group with name ' '${androidNotificationChannelGroup.name} created'), actions: [ - FlatButton( + TextButton( onPressed: () { Navigator.of(context).pop(); }, @@ -1522,7 +1523,7 @@ class _HomePageState extends State { builder: (BuildContext context) => AlertDialog( content: const Text('Channel group with id $channelGroupId deleted'), actions: [ - FlatButton( + TextButton( onPressed: () { Navigator.of(context).pop(); }, @@ -1552,7 +1553,7 @@ class _HomePageState extends State { Text('Channel with name ${androidNotificationChannel.name} ' 'created'), actions: [ - FlatButton( + TextButton( onPressed: () { Navigator.of(context).pop(); }, @@ -1574,7 +1575,7 @@ class _HomePageState extends State { builder: (BuildContext context) => AlertDialog( content: const Text('Channel with id $channelId deleted'), actions: [ - FlatButton( + TextButton( onPressed: () { Navigator.of(context).pop(); }, @@ -1593,7 +1594,7 @@ class _HomePageState extends State { builder: (BuildContext context) => AlertDialog( content: activeNotificationsDialogContent, actions: [ - FlatButton( + TextButton( onPressed: () { Navigator.of(context).pop(); }, @@ -1614,11 +1615,11 @@ class _HomePageState extends State { } try { - final List activeNotifications = + final List? activeNotifications = await flutterLocalNotificationsPlugin .resolvePlatformSpecificImplementation< - AndroidFlutterLocalNotificationsPlugin>() - ?.getActiveNotifications(); + AndroidFlutterLocalNotificationsPlugin>()! + .getActiveNotifications(); return Column( crossAxisAlignment: CrossAxisAlignment.start, @@ -1629,7 +1630,7 @@ class _HomePageState extends State { style: TextStyle(fontWeight: FontWeight.bold), ), const Divider(color: Colors.black), - if (activeNotifications.isEmpty) + if (activeNotifications!.isEmpty) const Text('No active notifications'), if (activeNotifications.isNotEmpty) for (ActiveNotification activeNotification in activeNotifications) @@ -1664,7 +1665,7 @@ class _HomePageState extends State { builder: (BuildContext context) => AlertDialog( content: notificationChannelsDialogContent, actions: [ - FlatButton( + TextButton( onPressed: () { Navigator.of(context).pop(); }, @@ -1677,11 +1678,11 @@ class _HomePageState extends State { Future _getNotificationChannelsDialogContent() async { try { - final List channels = + final List? channels = await flutterLocalNotificationsPlugin .resolvePlatformSpecificImplementation< - AndroidFlutterLocalNotificationsPlugin>() - ?.getNotificationChannels(); + AndroidFlutterLocalNotificationsPlugin>()! + .getNotificationChannels(); return Container( width: double.maxFinite, @@ -1692,9 +1693,10 @@ class _HomePageState extends State { style: TextStyle(fontWeight: FontWeight.bold), ), const Divider(color: Colors.black), - if (channels?.isEmpty) const Text('No notification channels'), - if (channels.isNotEmpty) - for (AndroidNotificationChannel channel in channels) + if (channels?.isEmpty ?? true) + const Text('No notification channels') + else + for (AndroidNotificationChannel channel in channels!) Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ @@ -1729,19 +1731,19 @@ class _HomePageState extends State { class SecondPage extends StatefulWidget { const SecondPage( this.payload, { - Key key, + Key? key, }) : super(key: key); static const String routeName = '/secondPage'; - final String payload; + final String? payload; @override State createState() => SecondPageState(); } class SecondPageState extends State { - String _payload; + String? _payload; @override void initState() { super.initState(); @@ -1754,7 +1756,7 @@ class SecondPageState extends State { title: Text('Second Screen with payload: ${_payload ?? ''}'), ), body: Center( - child: RaisedButton( + child: ElevatedButton( onPressed: () { Navigator.pop(context); }, diff --git a/flutter_local_notifications/example/macos/Runner/DebugProfile.entitlements b/flutter_local_notifications/example/macos/Runner/DebugProfile.entitlements index dddb8a30c..08c3ab17c 100644 --- a/flutter_local_notifications/example/macos/Runner/DebugProfile.entitlements +++ b/flutter_local_notifications/example/macos/Runner/DebugProfile.entitlements @@ -8,5 +8,7 @@ com.apple.security.network.server + com.apple.security.network.client + diff --git a/flutter_local_notifications/example/pubspec.yaml b/flutter_local_notifications/example/pubspec.yaml index 8a4c8849f..4dc0dbffb 100644 --- a/flutter_local_notifications/example/pubspec.yaml +++ b/flutter_local_notifications/example/pubspec.yaml @@ -7,19 +7,20 @@ dependencies: sdk: flutter flutter_local_notifications: path: ../ - cupertino_icons: ^0.1.3 - device_info: ^0.4.2+4 - http: ^0.12.0+4 - path_provider: ^1.6.7 - rxdart: ^0.24.0 - shared_preferences: ^0.5.7 + cupertino_icons: ^1.0.2 + device_info: ^2.0.0 + http: ^0.13.0 + path_provider: ^2.0.0 + rxdart: ^0.26.0-nullsafety.1 + shared_preferences: ^2.0.1 dev_dependencies: flutter_driver: sdk: flutter flutter_test: sdk: flutter - integration_test: ^1.0.1 + integration_test: + sdk: flutter flutter: uses-material-design: true @@ -27,5 +28,5 @@ flutter: - icons/ environment: - sdk: ">=2.6.0 <3.0.0" - flutter: ">=1.12.13+hotfix.5" + sdk: ">=2.12.0-0 <3.0.0" + flutter: ">=1.26.0-0" # allows for using integration_test from SDK diff --git a/flutter_local_notifications/example/test_driver/integration_test.dart b/flutter_local_notifications/example/test_driver/integration_test.dart index b38629cca..ccc4438b1 100644 --- a/flutter_local_notifications/example/test_driver/integration_test.dart +++ b/flutter_local_notifications/example/test_driver/integration_test.dart @@ -1,3 +1,4 @@ +// @dart = 2.9 import 'package:integration_test/integration_test_driver.dart'; Future main() => integrationDriver(); diff --git a/flutter_local_notifications/lib/src/flutter_local_notifications_plugin.dart b/flutter_local_notifications/lib/src/flutter_local_notifications_plugin.dart index 537e0ab1d..7e424dc16 100644 --- a/flutter_local_notifications/lib/src/flutter_local_notifications_plugin.dart +++ b/flutter_local_notifications/lib/src/flutter_local_notifications_plugin.dart @@ -64,7 +64,7 @@ class FlutterLocalNotificationsPlugin { /// type for a result to be returned. For example, when the specified type /// argument is of type [AndroidFlutterLocalNotificationsPlugin], this will /// only return a result of that type when running on Android. - T resolvePlatformSpecificImplementation< + T? resolvePlatformSpecificImplementation< T extends FlutterLocalNotificationsPlatform>() { if (T == FlutterLocalNotificationsPlatform) { throw ArgumentError.value( @@ -76,17 +76,17 @@ class FlutterLocalNotificationsPlugin { T == AndroidFlutterLocalNotificationsPlugin && FlutterLocalNotificationsPlatform.instance is AndroidFlutterLocalNotificationsPlugin) { - return FlutterLocalNotificationsPlatform.instance; + return FlutterLocalNotificationsPlatform.instance as T?; } else if (_platform.isIOS && T == IOSFlutterLocalNotificationsPlugin && FlutterLocalNotificationsPlatform.instance is IOSFlutterLocalNotificationsPlugin) { - return FlutterLocalNotificationsPlatform.instance; + return FlutterLocalNotificationsPlatform.instance as T?; } else if (_platform.isMacOS && T == MacOSFlutterLocalNotificationsPlugin && FlutterLocalNotificationsPlatform.instance is MacOSFlutterLocalNotificationsPlugin) { - return FlutterLocalNotificationsPlatform.instance; + return FlutterLocalNotificationsPlatform.instance as T?; } return null; @@ -110,24 +110,24 @@ class FlutterLocalNotificationsPlugin { /// [IOSInitializationSettings.requestSoundPermission] values to false. /// [IOSFlutterLocalNotificationsPlugin.requestPermissions] can then be called /// to request permissions when needed. - Future initialize( + Future initialize( InitializationSettings initializationSettings, { - SelectNotificationCallback onSelectNotification, + SelectNotificationCallback? onSelectNotification, }) async { if (_platform.isAndroid) { return resolvePlatformSpecificImplementation< AndroidFlutterLocalNotificationsPlugin>() - ?.initialize(initializationSettings?.android, + ?.initialize(initializationSettings.android!, onSelectNotification: onSelectNotification); } else if (_platform.isIOS) { return await resolvePlatformSpecificImplementation< IOSFlutterLocalNotificationsPlugin>() - ?.initialize(initializationSettings?.iOS, + ?.initialize(initializationSettings.iOS!, onSelectNotification: onSelectNotification); } else if (_platform.isMacOS) { return await resolvePlatformSpecificImplementation< MacOSFlutterLocalNotificationsPlugin>() - ?.initialize(initializationSettings?.macOS, + ?.initialize(initializationSettings.macOS!, onSelectNotification: onSelectNotification); } return true; @@ -145,7 +145,8 @@ class FlutterLocalNotificationsPlugin { /// Note that this will return null for applications running on macOS /// versions older than 10.14. This is because there's currently no mechanism /// for plugins to receive information on lifecycle events. - Future getNotificationAppLaunchDetails() async { + Future + getNotificationAppLaunchDetails() async { if (_platform.isAndroid) { return await resolvePlatformSpecificImplementation< AndroidFlutterLocalNotificationsPlugin>() @@ -160,7 +161,7 @@ class FlutterLocalNotificationsPlugin { ?.getNotificationAppLaunchDetails(); } else { return await FlutterLocalNotificationsPlatform.instance - ?.getNotificationAppLaunchDetails() ?? + .getNotificationAppLaunchDetails() ?? const NotificationAppLaunchDetails(false, null); } } @@ -169,10 +170,10 @@ class FlutterLocalNotificationsPlugin { /// the app when a notification is tapped. Future show( int id, - String title, - String body, - NotificationDetails notificationDetails, { - String payload, + String? title, + String? body, + NotificationDetails? notificationDetails, { + String? payload, }) async { if (_platform.isAndroid) { await resolvePlatformSpecificImplementation< @@ -192,7 +193,7 @@ class FlutterLocalNotificationsPlugin { notificationDetails: notificationDetails?.macOS, payload: payload); } else { - await FlutterLocalNotificationsPlatform.instance?.show(id, title, body); + await FlutterLocalNotificationsPlatform.instance.show(id, title, body); } } @@ -204,13 +205,13 @@ class FlutterLocalNotificationsPlugin { /// The `tag` parameter specifies the Android tag. If it is provided, /// then the notification that matches both the id and the tag will /// be canceled. `tag` has no effect on other platforms. - Future cancel(int id, {String tag}) async { + Future cancel(int id, {String? tag}) async { if (_platform.isAndroid) { await resolvePlatformSpecificImplementation< AndroidFlutterLocalNotificationsPlugin>() ?.cancel(id, tag: tag); } else { - await FlutterLocalNotificationsPlatform.instance?.cancel(id); + await FlutterLocalNotificationsPlatform.instance.cancel(id); } } @@ -219,7 +220,7 @@ class FlutterLocalNotificationsPlugin { /// This applies to notifications that have been scheduled and those that /// have already been presented. Future cancelAll() async { - await FlutterLocalNotificationsPlatform.instance?.cancelAll(); + await FlutterLocalNotificationsPlatform.instance.cancelAll(); } /// Schedules a notification to be shown at the specified date and time. @@ -231,23 +232,22 @@ class FlutterLocalNotificationsPlugin { 'instead.') Future schedule( int id, - String title, - String body, + String? title, + String? body, DateTime scheduledDate, NotificationDetails notificationDetails, { - String payload, + String? payload, bool androidAllowWhileIdle = false, }) async { if (_platform.isAndroid) { await resolvePlatformSpecificImplementation< - AndroidFlutterLocalNotificationsPlugin>() - .schedule( - id, title, body, scheduledDate, notificationDetails?.android, + AndroidFlutterLocalNotificationsPlugin>()! + .schedule(id, title, body, scheduledDate, notificationDetails.android, payload: payload, androidAllowWhileIdle: androidAllowWhileIdle); } else if (_platform.isIOS) { await resolvePlatformSpecificImplementation< IOSFlutterLocalNotificationsPlugin>() - ?.schedule(id, title, body, scheduledDate, notificationDetails?.iOS, + ?.schedule(id, title, body, scheduledDate, notificationDetails.iOS, payload: payload); } else if (_platform.isMacOS) { throw UnimplementedError(); @@ -287,22 +287,21 @@ class FlutterLocalNotificationsPlugin { /// appear is 2020-10-20 10:00. Future zonedSchedule( int id, - String title, - String body, + String? title, + String? body, TZDateTime scheduledDate, NotificationDetails notificationDetails, { - @required - UILocalNotificationDateInterpretation - uiLocalNotificationDateInterpretation, - @required bool androidAllowWhileIdle, - String payload, - DateTimeComponents matchDateTimeComponents, + required UILocalNotificationDateInterpretation + uiLocalNotificationDateInterpretation, + required bool androidAllowWhileIdle, + String? payload, + DateTimeComponents? matchDateTimeComponents, }) async { if (_platform.isAndroid) { await resolvePlatformSpecificImplementation< - AndroidFlutterLocalNotificationsPlugin>() + AndroidFlutterLocalNotificationsPlugin>()! .zonedSchedule( - id, title, body, scheduledDate, notificationDetails?.android, + id, title, body, scheduledDate, notificationDetails.android, payload: payload, androidAllowWhileIdle: androidAllowWhileIdle, matchDateTimeComponents: matchDateTimeComponents); @@ -310,7 +309,7 @@ class FlutterLocalNotificationsPlugin { await resolvePlatformSpecificImplementation< IOSFlutterLocalNotificationsPlugin>() ?.zonedSchedule( - id, title, body, scheduledDate, notificationDetails?.iOS, + id, title, body, scheduledDate, notificationDetails.iOS, uiLocalNotificationDateInterpretation: uiLocalNotificationDateInterpretation, payload: payload, @@ -319,7 +318,7 @@ class FlutterLocalNotificationsPlugin { await resolvePlatformSpecificImplementation< MacOSFlutterLocalNotificationsPlugin>() ?.zonedSchedule( - id, title, body, scheduledDate, notificationDetails?.macOS, + id, title, body, scheduledDate, notificationDetails.macOS, payload: payload, matchDateTimeComponents: matchDateTimeComponents); } @@ -341,34 +340,33 @@ class FlutterLocalNotificationsPlugin { /// repeat. Future periodicallyShow( int id, - String title, - String body, + String? title, + String? body, RepeatInterval repeatInterval, NotificationDetails notificationDetails, { - String payload, + String? payload, bool androidAllowWhileIdle = false, }) async { if (_platform.isAndroid) { await resolvePlatformSpecificImplementation< AndroidFlutterLocalNotificationsPlugin>() ?.periodicallyShow(id, title, body, repeatInterval, - notificationDetails: notificationDetails?.android, + notificationDetails: notificationDetails.android, payload: payload, androidAllowWhileIdle: androidAllowWhileIdle); } else if (_platform.isIOS) { await resolvePlatformSpecificImplementation< IOSFlutterLocalNotificationsPlugin>() ?.periodicallyShow(id, title, body, repeatInterval, - notificationDetails: notificationDetails?.iOS, payload: payload); + notificationDetails: notificationDetails.iOS, payload: payload); } else if (_platform.isMacOS) { await resolvePlatformSpecificImplementation< MacOSFlutterLocalNotificationsPlugin>() ?.periodicallyShow(id, title, body, repeatInterval, - notificationDetails: notificationDetails?.macOS, - payload: payload); + notificationDetails: notificationDetails.macOS, payload: payload); } else { await FlutterLocalNotificationsPlatform.instance - ?.periodicallyShow(id, title, body, repeatInterval); + .periodicallyShow(id, title, body, repeatInterval); } } @@ -380,23 +378,23 @@ class FlutterLocalNotificationsPlugin { 'matchDateTimeComponents parameter.') Future showDailyAtTime( int id, - String title, - String body, + String? title, + String? body, Time notificationTime, NotificationDetails notificationDetails, { - String payload, + String? payload, }) async { if (_platform.isAndroid) { await resolvePlatformSpecificImplementation< AndroidFlutterLocalNotificationsPlugin>() ?.showDailyAtTime( - id, title, body, notificationTime, notificationDetails?.android, + id, title, body, notificationTime, notificationDetails.android, payload: payload); } else if (_platform.isIOS) { await resolvePlatformSpecificImplementation< IOSFlutterLocalNotificationsPlugin>() ?.showDailyAtTime( - id, title, body, notificationTime, notificationDetails?.iOS, + id, title, body, notificationTime, notificationDetails.iOS, payload: payload); } else if (_platform.isMacOS) { throw UnimplementedError(); @@ -411,24 +409,24 @@ class FlutterLocalNotificationsPlugin { 'the matchDateTimeComponents parameter.') Future showWeeklyAtDayAndTime( int id, - String title, - String body, + String? title, + String? body, Day day, Time notificationTime, NotificationDetails notificationDetails, { - String payload, + String? payload, }) async { if (_platform.isAndroid) { await resolvePlatformSpecificImplementation< AndroidFlutterLocalNotificationsPlugin>() ?.showWeeklyAtDayAndTime(id, title, body, day, notificationTime, - notificationDetails?.android, + notificationDetails.android, payload: payload); } else if (_platform.isIOS) { await resolvePlatformSpecificImplementation< IOSFlutterLocalNotificationsPlugin>() ?.showWeeklyAtDayAndTime( - id, title, body, day, notificationTime, notificationDetails?.iOS, + id, title, body, day, notificationTime, notificationDetails.iOS, payload: payload); } else if (_platform.isMacOS) { throw UnimplementedError(); @@ -437,5 +435,5 @@ class FlutterLocalNotificationsPlugin { /// Returns a list of notifications pending to be delivered/shown. Future> pendingNotificationRequests() => - FlutterLocalNotificationsPlatform.instance?.pendingNotificationRequests(); + FlutterLocalNotificationsPlatform.instance.pendingNotificationRequests(); } diff --git a/flutter_local_notifications/lib/src/helpers.dart b/flutter_local_notifications/lib/src/helpers.dart index aec75be0e..4d180b3c1 100644 --- a/flutter_local_notifications/lib/src/helpers.dart +++ b/flutter_local_notifications/lib/src/helpers.dart @@ -16,7 +16,7 @@ void validateId(int id) { /// time where `matchDateTimeComponents` is null. void validateDateIsInTheFuture( TZDateTime scheduledDate, - DateTimeComponents matchDateTimeComponents, + DateTimeComponents? matchDateTimeComponents, ) { if (matchDateTimeComponents != null) { return; diff --git a/flutter_local_notifications/lib/src/initialization_settings.dart b/flutter_local_notifications/lib/src/initialization_settings.dart index 0dd4e6e6e..e4937a759 100644 --- a/flutter_local_notifications/lib/src/initialization_settings.dart +++ b/flutter_local_notifications/lib/src/initialization_settings.dart @@ -12,11 +12,11 @@ class InitializationSettings { }); /// Settings for Android. - final AndroidInitializationSettings android; + final AndroidInitializationSettings? android; /// Settings for iOS. - final IOSInitializationSettings iOS; + final IOSInitializationSettings? iOS; /// Settings for iOS. - final MacOSInitializationSettings macOS; + final MacOSInitializationSettings? macOS; } diff --git a/flutter_local_notifications/lib/src/notification_details.dart b/flutter_local_notifications/lib/src/notification_details.dart index 1f2933e3f..be7cf51b5 100644 --- a/flutter_local_notifications/lib/src/notification_details.dart +++ b/flutter_local_notifications/lib/src/notification_details.dart @@ -12,11 +12,11 @@ class NotificationDetails { }); /// Notification details for Android. - final AndroidNotificationDetails android; + final AndroidNotificationDetails? android; /// Notification details for iOS. - final IOSNotificationDetails iOS; + final IOSNotificationDetails? iOS; /// Notification details for macOS. - final MacOSNotificationDetails macOS; + final MacOSNotificationDetails? macOS; } diff --git a/flutter_local_notifications/lib/src/platform_flutter_local_notifications.dart b/flutter_local_notifications/lib/src/platform_flutter_local_notifications.dart index 20b576eff..a15bc13a5 100644 --- a/flutter_local_notifications/lib/src/platform_flutter_local_notifications.dart +++ b/flutter_local_notifications/lib/src/platform_flutter_local_notifications.dart @@ -1,6 +1,5 @@ import 'dart:async'; -import 'package:flutter/foundation.dart'; import 'package:flutter/services.dart'; import 'package:flutter/widgets.dart'; import 'package:flutter_local_notifications_platform_interface/flutter_local_notifications_platform_interface.dart'; @@ -43,8 +42,9 @@ class MethodChannelFlutterLocalNotificationsPlugin Future cancelAll() => _channel.invokeMethod('cancelAll'); @override - Future getNotificationAppLaunchDetails() async { - final Map result = + Future + getNotificationAppLaunchDetails() async { + final Map? result = await _channel.invokeMethod('getNotificationAppLaunchDetails'); return result != null ? NotificationAppLaunchDetails(result['notificationLaunchedApp'], @@ -54,20 +54,21 @@ class MethodChannelFlutterLocalNotificationsPlugin @override Future> pendingNotificationRequests() async { - final List> pendingNotifications = + final List>? pendingNotifications = await _channel.invokeListMethod('pendingNotificationRequests'); return pendingNotifications - // ignore: always_specify_types - .map((p) => PendingNotificationRequest( - p['id'], p['title'], p['body'], p['payload'])) - .toList(); + // ignore: always_specify_types + ?.map((p) => PendingNotificationRequest( + p['id'], p['title'], p['body'], p['payload'])) + .toList() ?? + []; } } /// Android implementation of the local notifications plugin. class AndroidFlutterLocalNotificationsPlugin extends MethodChannelFlutterLocalNotificationsPlugin { - SelectNotificationCallback _onSelectNotification; + SelectNotificationCallback? _onSelectNotification; /// Initializes the plugin. Call this method on application before using the /// plugin further. @@ -75,9 +76,9 @@ class AndroidFlutterLocalNotificationsPlugin /// This should only be done once. When a notification created by this plugin /// was used to launch the app, calling `initialize` is what will trigger to /// the `onSelectNotification` callback to be fire. - Future initialize( + Future initialize( AndroidInitializationSettings initializationSettings, { - SelectNotificationCallback onSelectNotification, + SelectNotificationCallback? onSelectNotification, }) async { _onSelectNotification = onSelectNotification; _channel.setMethodCallHandler(_handleMethod); @@ -94,18 +95,18 @@ class AndroidFlutterLocalNotificationsPlugin 'Deprecated due to problems with time zones. Use zonedSchedule instead.') Future schedule( int id, - String title, - String body, + String? title, + String? body, DateTime scheduledDate, - AndroidNotificationDetails notificationDetails, { - String payload, + AndroidNotificationDetails? notificationDetails, { + String? payload, bool androidAllowWhileIdle = false, }) async { validateId(id); - final Map serializedPlatformSpecifics = + final Map serializedPlatformSpecifics = notificationDetails?.toMap() ?? {}; serializedPlatformSpecifics['allowWhileIdle'] = androidAllowWhileIdle; - await _channel.invokeMethod('schedule', { + await _channel.invokeMethod('schedule', { 'id': id, 'title': title, 'body': body, @@ -119,23 +120,23 @@ class AndroidFlutterLocalNotificationsPlugin /// relative to a specific time zone. Future zonedSchedule( int id, - String title, - String body, + String? title, + String? body, TZDateTime scheduledDate, - AndroidNotificationDetails notificationDetails, { - @required bool androidAllowWhileIdle, - String payload, - DateTimeComponents matchDateTimeComponents, + AndroidNotificationDetails? notificationDetails, { + required bool androidAllowWhileIdle, + String? payload, + DateTimeComponents? matchDateTimeComponents, }) async { validateId(id); validateDateIsInTheFuture(scheduledDate, matchDateTimeComponents); ArgumentError.checkNotNull(androidAllowWhileIdle, 'androidAllowWhileIdle'); - final Map serializedPlatformSpecifics = + final Map serializedPlatformSpecifics = notificationDetails?.toMap() ?? {}; serializedPlatformSpecifics['allowWhileIdle'] = androidAllowWhileIdle; await _channel.invokeMethod( 'zonedSchedule', - { + { 'id': id, 'title': title, 'body': body, @@ -155,14 +156,14 @@ class AndroidFlutterLocalNotificationsPlugin 'Deprecated due to problems with time zones. Use zonedSchedule instead.') Future showDailyAtTime( int id, - String title, - String body, + String? title, + String? body, Time notificationTime, - AndroidNotificationDetails notificationDetails, { - String payload, + AndroidNotificationDetails? notificationDetails, { + String? payload, }) async { validateId(id); - await _channel.invokeMethod('showDailyAtTime', { + await _channel.invokeMethod('showDailyAtTime', { 'id': id, 'title': title, 'body': body, @@ -179,16 +180,16 @@ class AndroidFlutterLocalNotificationsPlugin 'Deprecated due to problems with time zones. Use zonedSchedule instead.') Future showWeeklyAtDayAndTime( int id, - String title, - String body, + String? title, + String? body, Day day, Time notificationTime, - AndroidNotificationDetails notificationDetails, { - String payload, + AndroidNotificationDetails? notificationDetails, { + String? payload, }) async { validateId(id); - await _channel.invokeMethod('showWeeklyAtDayAndTime', { + await _channel.invokeMethod('showWeeklyAtDayAndTime', { 'id': id, 'title': title, 'body': body, @@ -204,15 +205,15 @@ class AndroidFlutterLocalNotificationsPlugin @override Future show( int id, - String title, - String body, { - AndroidNotificationDetails notificationDetails, - String payload, + String? title, + String? body, { + AndroidNotificationDetails? notificationDetails, + String? payload, }) { validateId(id); return _channel.invokeMethod( 'show', - { + { 'id': id, 'title': title, 'body': body, @@ -225,18 +226,18 @@ class AndroidFlutterLocalNotificationsPlugin @override Future periodicallyShow( int id, - String title, - String body, + String? title, + String? body, RepeatInterval repeatInterval, { - AndroidNotificationDetails notificationDetails, - String payload, + AndroidNotificationDetails? notificationDetails, + String? payload, bool androidAllowWhileIdle = false, }) async { validateId(id); - final Map serializedPlatformSpecifics = + final Map serializedPlatformSpecifics = notificationDetails?.toMap() ?? {}; serializedPlatformSpecifics['allowWhileIdle'] = androidAllowWhileIdle; - await _channel.invokeMethod('periodicallyShow', { + await _channel.invokeMethod('periodicallyShow', { 'id': id, 'title': title, 'body': body, @@ -256,10 +257,10 @@ class AndroidFlutterLocalNotificationsPlugin /// then the notification that matches both the id and the tag will /// be canceled. `tag` has no effect on other platforms. @override - Future cancel(int id, {String tag}) async { + Future cancel(int id, {String? tag}) async { validateId(id); - return _channel.invokeMethod('cancel', { + return _channel.invokeMethod('cancel', { 'id': id, 'tag': tag, }); @@ -300,8 +301,8 @@ class AndroidFlutterLocalNotificationsPlugin /// This method is only applicable to Android 6.0 or newer and will throw an /// [PlatformException] when called on a device with an incompatible Android /// version. - Future> getActiveNotifications() async { - final List> activeNotifications = + Future?> getActiveNotifications() async { + final List>? activeNotifications = await _channel.invokeListMethod('getActiveNotifications'); return activeNotifications // ignore: always_specify_types @@ -311,15 +312,15 @@ class AndroidFlutterLocalNotificationsPlugin a['title'], a['body'], )) - ?.toList(); + .toList(); } /// Returns the list of all notification channels. /// /// This method is only applicable on Android 8.0 or newer. On older versions, /// it will return an empty list. - Future> getNotificationChannels() async { - final List> notificationChannels = + Future?> getNotificationChannels() async { + final List>? notificationChannels = await _channel.invokeListMethod('getNotificationChannels'); return notificationChannels @@ -338,13 +339,13 @@ class AndroidFlutterLocalNotificationsPlugin vibrationPattern: a['vibrationPattern'], ledColor: Color(a['ledColor']), )) - ?.toList(); + .toList(); } - AndroidNotificationSound _getNotificationChannelSound( - Map channelMap) { - final int soundSourceIndex = channelMap['soundSource']; - AndroidNotificationSound sound; + AndroidNotificationSound? _getNotificationChannelSound( + Map channelMap) { + final int? soundSourceIndex = channelMap['soundSource']; + AndroidNotificationSound? sound; if (soundSourceIndex != null) { if (soundSourceIndex == AndroidNotificationSoundSource.rawResource.index) { @@ -359,7 +360,7 @@ class AndroidFlutterLocalNotificationsPlugin Future _handleMethod(MethodCall call) { switch (call.method) { case 'selectNotification': - return _onSelectNotification(call.arguments); + return _onSelectNotification!(call.arguments); default: return Future.error('Method not defined'); } @@ -369,9 +370,9 @@ class AndroidFlutterLocalNotificationsPlugin /// iOS implementation of the local notifications plugin. class IOSFlutterLocalNotificationsPlugin extends MethodChannelFlutterLocalNotificationsPlugin { - SelectNotificationCallback _onSelectNotification; + SelectNotificationCallback? _onSelectNotification; - DidReceiveLocalNotificationCallback _onDidReceiveLocalNotification; + DidReceiveLocalNotificationCallback? _onDidReceiveLocalNotification; /// Initializes the plugin. /// @@ -389,9 +390,9 @@ class IOSFlutterLocalNotificationsPlugin /// [IOSInitializationSettings.requestSoundPermission] values to false. /// [requestPermissions] can then be called to request permissions when /// needed. - Future initialize( + Future initialize( IOSInitializationSettings initializationSettings, { - SelectNotificationCallback onSelectNotification, + SelectNotificationCallback? onSelectNotification, }) async { _onSelectNotification = onSelectNotification; _onDidReceiveLocalNotification = @@ -403,12 +404,12 @@ class IOSFlutterLocalNotificationsPlugin /// Requests the specified permission(s) from user and returns current /// permission status. - Future requestPermissions({ - bool sound, - bool alert, - bool badge, + Future requestPermissions({ + bool sound = false, + bool alert = false, + bool badge = false, }) => - _channel.invokeMethod('requestPermissions', { + _channel.invokeMethod('requestPermissions', { 'sound': sound, 'alert': alert, 'badge': badge, @@ -420,14 +421,14 @@ class IOSFlutterLocalNotificationsPlugin 'Deprecated due to problems with time zones. Use zonedSchedule instead.') Future schedule( int id, - String title, - String body, + String? title, + String? body, DateTime scheduledDate, - IOSNotificationDetails notificationDetails, { - String payload, + IOSNotificationDetails? notificationDetails, { + String? payload, }) async { validateId(id); - await _channel.invokeMethod('schedule', { + await _channel.invokeMethod('schedule', { 'id': id, 'title': title, 'body': body, @@ -452,25 +453,24 @@ class IOSFlutterLocalNotificationsPlugin /// [UILocalNotificationDateInterpretation.wallClockTime]. Future zonedSchedule( int id, - String title, - String body, + String? title, + String? body, TZDateTime scheduledDate, - IOSNotificationDetails notificationDetails, { - @required - UILocalNotificationDateInterpretation - uiLocalNotificationDateInterpretation, - String payload, - DateTimeComponents matchDateTimeComponents, + IOSNotificationDetails? notificationDetails, { + required UILocalNotificationDateInterpretation + uiLocalNotificationDateInterpretation, + String? payload, + DateTimeComponents? matchDateTimeComponents, }) async { validateId(id); validateDateIsInTheFuture(scheduledDate, matchDateTimeComponents); ArgumentError.checkNotNull(uiLocalNotificationDateInterpretation, 'uiLocalNotificationDateInterpretation'); - final Map serializedPlatformSpecifics = + final Map serializedPlatformSpecifics = notificationDetails?.toMap() ?? {}; await _channel.invokeMethod( 'zonedSchedule', - { + { 'id': id, 'title': title, 'body': body, @@ -492,14 +492,14 @@ class IOSFlutterLocalNotificationsPlugin 'Deprecated due to problems with time zones. Use zonedSchedule instead.') Future showDailyAtTime( int id, - String title, - String body, + String? title, + String? body, Time notificationTime, - IOSNotificationDetails notificationDetails, { - String payload, + IOSNotificationDetails? notificationDetails, { + String? payload, }) async { validateId(id); - await _channel.invokeMethod('showDailyAtTime', { + await _channel.invokeMethod('showDailyAtTime', { 'id': id, 'title': title, 'body': body, @@ -516,15 +516,15 @@ class IOSFlutterLocalNotificationsPlugin 'Deprecated due to problems with time zones. Use zonedSchedule instead.') Future showWeeklyAtDayAndTime( int id, - String title, - String body, + String? title, + String? body, Day day, Time notificationTime, - IOSNotificationDetails notificationDetails, { - String payload, + IOSNotificationDetails? notificationDetails, { + String? payload, }) async { validateId(id); - await _channel.invokeMethod('showWeeklyAtDayAndTime', { + await _channel.invokeMethod('showWeeklyAtDayAndTime', { 'id': id, 'title': title, 'body': body, @@ -540,15 +540,15 @@ class IOSFlutterLocalNotificationsPlugin @override Future show( int id, - String title, - String body, { - IOSNotificationDetails notificationDetails, - String payload, + String? title, + String? body, { + IOSNotificationDetails? notificationDetails, + String? payload, }) { validateId(id); return _channel.invokeMethod( 'show', - { + { 'id': id, 'title': title, 'body': body, @@ -561,14 +561,14 @@ class IOSFlutterLocalNotificationsPlugin @override Future periodicallyShow( int id, - String title, - String body, + String? title, + String? body, RepeatInterval repeatInterval, { - IOSNotificationDetails notificationDetails, - String payload, + IOSNotificationDetails? notificationDetails, + String? payload, }) async { validateId(id); - await _channel.invokeMethod('periodicallyShow', { + await _channel.invokeMethod('periodicallyShow', { 'id': id, 'title': title, 'body': body, @@ -582,10 +582,10 @@ class IOSFlutterLocalNotificationsPlugin Future _handleMethod(MethodCall call) { switch (call.method) { case 'selectNotification': - return _onSelectNotification(call.arguments); + return _onSelectNotification!(call.arguments); case 'didReceiveLocalNotification': - return _onDidReceiveLocalNotification( + return _onDidReceiveLocalNotification!( call.arguments['id'], call.arguments['title'], call.arguments['body'], @@ -599,7 +599,7 @@ class IOSFlutterLocalNotificationsPlugin /// macOS implementation of the local notifications plugin. class MacOSFlutterLocalNotificationsPlugin extends MethodChannelFlutterLocalNotificationsPlugin { - SelectNotificationCallback _onSelectNotification; + SelectNotificationCallback? _onSelectNotification; /// Initializes the plugin. /// @@ -617,9 +617,9 @@ class MacOSFlutterLocalNotificationsPlugin /// [MacOSInitializationSettings.requestSoundPermission] values to false. /// [requestPermissions] can then be called to request permissions when /// needed. - Future initialize( + Future initialize( MacOSInitializationSettings initializationSettings, { - SelectNotificationCallback onSelectNotification, + SelectNotificationCallback? onSelectNotification, }) async { _onSelectNotification = onSelectNotification; _channel.setMethodCallHandler(_handleMethod); @@ -629,12 +629,12 @@ class MacOSFlutterLocalNotificationsPlugin /// Requests the specified permission(s) from user and returns current /// permission status. - Future requestPermissions({ - bool sound, - bool alert, - bool badge, + Future requestPermissions({ + bool? sound, + bool? alert, + bool? badge, }) => - _channel.invokeMethod('requestPermissions', { + _channel.invokeMethod('requestPermissions', { 'sound': sound, 'alert': alert, 'badge': badge, @@ -644,20 +644,20 @@ class MacOSFlutterLocalNotificationsPlugin /// relative to a specific time zone. Future zonedSchedule( int id, - String title, - String body, + String? title, + String? body, TZDateTime scheduledDate, - MacOSNotificationDetails notificationDetails, { - String payload, - DateTimeComponents matchDateTimeComponents, + MacOSNotificationDetails? notificationDetails, { + String? payload, + DateTimeComponents? matchDateTimeComponents, }) async { validateId(id); validateDateIsInTheFuture(scheduledDate, matchDateTimeComponents); - final Map serializedPlatformSpecifics = + final Map serializedPlatformSpecifics = notificationDetails?.toMap() ?? {}; await _channel.invokeMethod( 'zonedSchedule', - { + { 'id': id, 'title': title, 'body': body, @@ -675,15 +675,15 @@ class MacOSFlutterLocalNotificationsPlugin @override Future show( int id, - String title, - String body, { - MacOSNotificationDetails notificationDetails, - String payload, + String? title, + String? body, { + MacOSNotificationDetails? notificationDetails, + String? payload, }) { validateId(id); return _channel.invokeMethod( 'show', - { + { 'id': id, 'title': title, 'body': body, @@ -696,14 +696,14 @@ class MacOSFlutterLocalNotificationsPlugin @override Future periodicallyShow( int id, - String title, - String body, + String? title, + String? body, RepeatInterval repeatInterval, { - MacOSNotificationDetails notificationDetails, - String payload, + MacOSNotificationDetails? notificationDetails, + String? payload, }) async { validateId(id); - await _channel.invokeMethod('periodicallyShow', { + await _channel.invokeMethod('periodicallyShow', { 'id': id, 'title': title, 'body': body, @@ -717,7 +717,7 @@ class MacOSFlutterLocalNotificationsPlugin Future _handleMethod(MethodCall call) { switch (call.method) { case 'selectNotification': - return _onSelectNotification(call.arguments); + return _onSelectNotification!(call.arguments); default: return Future.error('Method not defined'); } diff --git a/flutter_local_notifications/lib/src/platform_specifics/android/active_notification.dart b/flutter_local_notifications/lib/src/platform_specifics/android/active_notification.dart index 235eee0e5..bd2b2f869 100644 --- a/flutter_local_notifications/lib/src/platform_specifics/android/active_notification.dart +++ b/flutter_local_notifications/lib/src/platform_specifics/android/active_notification.dart @@ -15,11 +15,11 @@ class ActiveNotification { /// The notification channel's id. /// /// Returned only on Android 8.0 or newer. - final String channelId; + final String? channelId; /// The notification's title. - final String title; + final String? title; /// The notification's content. - final String body; + final String? body; } diff --git a/flutter_local_notifications/lib/src/platform_specifics/android/enums.dart b/flutter_local_notifications/lib/src/platform_specifics/android/enums.dart index 6939f85f1..d84f7559d 100644 --- a/flutter_local_notifications/lib/src/platform_specifics/android/enums.dart +++ b/flutter_local_notifications/lib/src/platform_specifics/android/enums.dart @@ -95,7 +95,7 @@ class Importance { [unspecified, none, min, low, defaultImportance, high, max]; /// The integer representation. - final int value; + final int? value; } /// Priority for notifications on Android 7.1 and lower. diff --git a/flutter_local_notifications/lib/src/platform_specifics/android/message.dart b/flutter_local_notifications/lib/src/platform_specifics/android/message.dart index cf5827124..4d42e2c49 100644 --- a/flutter_local_notifications/lib/src/platform_specifics/android/message.dart +++ b/flutter_local_notifications/lib/src/platform_specifics/android/message.dart @@ -9,11 +9,7 @@ class Message { this.person, { this.dataMimeType, this.dataUri, - }) : assert( - timestamp != null, - 'timestamp must be provided', - ), - assert( + }) : assert( (dataMimeType == null && dataUri == null) || (dataMimeType != null && dataUri != null), 'Must provide both dataMimeType and dataUri together or not at all.', @@ -33,13 +29,13 @@ class Message { /// When this is set to `null` the `Person` given to /// [MessagingStyleInformation.person] i.e. this would indicate that the /// message was sent from the user. - final Person person; + final Person? person; /// MIME type for this message context when the [dataUri] is provided. - final String dataMimeType; + final String? dataMimeType; /// Uri containing the content. /// /// The original text will be used if the content or MIME type isn't supported - final String dataUri; + final String? dataUri; } diff --git a/flutter_local_notifications/lib/src/platform_specifics/android/method_channel_mappers.dart b/flutter_local_notifications/lib/src/platform_specifics/android/method_channel_mappers.dart index 2815cd375..857df1bbc 100644 --- a/flutter_local_notifications/lib/src/platform_specifics/android/method_channel_mappers.dart +++ b/flutter_local_notifications/lib/src/platform_specifics/android/method_channel_mappers.dart @@ -21,7 +21,7 @@ extension AndroidInitializationSettingsMapper on AndroidInitializationSettings { } extension MessageMapper on Message { - Map toMap() => { + Map toMap() => { 'text': text, 'timestamp': timestamp.millisecondsSinceEpoch, 'person': person?.toMap(), @@ -32,7 +32,7 @@ extension MessageMapper on Message { extension AndroidNotificationChannelGroupMapper on AndroidNotificationChannelGroup { - Map toMap() => { + Map toMap() => { 'id': id, 'name': name, 'description': description, @@ -40,7 +40,7 @@ extension AndroidNotificationChannelGroupMapper } extension AndroidNotificationChannelMapper on AndroidNotificationChannel { - Map toMap() => { + Map toMap() => { 'id': id, 'name': name, 'description': description, @@ -56,12 +56,12 @@ extension AndroidNotificationChannelMapper on AndroidNotificationChannel { 'ledColorGreen': ledColor?.green, 'ledColorBlue': ledColor?.blue, 'channelAction': - AndroidNotificationChannelAction.createIfNotExists?.index, + AndroidNotificationChannelAction.createIfNotExists.index, }..addAll(_convertNotificationSoundToMap(sound)); } Map _convertNotificationSoundToMap( - AndroidNotificationSound sound) { + AndroidNotificationSound? sound) { if (sound is RawResourceAndroidNotificationSound) { return { 'sound': sound.sound, @@ -78,7 +78,7 @@ Map _convertNotificationSoundToMap( } extension PersonMapper on Person { - Map toMap() => { + Map toMap() => { 'bot': bot, 'important': important, 'key': key, @@ -89,22 +89,22 @@ extension PersonMapper on Person { Map _convertIconToMap() { if (icon is DrawableResourceAndroidIcon) { return { - 'icon': icon.icon, + 'icon': icon!.icon, 'iconSource': AndroidIconSource.drawableResource.index, }; } else if (icon is BitmapFilePathAndroidIcon) { return { - 'icon': icon.icon, + 'icon': icon!.icon, 'iconSource': AndroidIconSource.bitmapFilePath.index, }; } else if (icon is ContentUriAndroidIcon) { return { - 'icon': icon.icon, + 'icon': icon!.icon, 'iconSource': AndroidIconSource.contentUri.index, }; } else if (icon is FlutterBitmapAssetAndroidIcon) { return { - 'icon': icon.icon, + 'icon': icon!.icon, 'iconSource': AndroidIconSource.flutterBitmapAsset.index, }; } else { @@ -114,21 +114,21 @@ extension PersonMapper on Person { } extension DefaultStyleInformationMapper on DefaultStyleInformation { - Map toMap() => _convertDefaultStyleInformationToMap(this); + Map toMap() => _convertDefaultStyleInformationToMap(this); } -Map _convertDefaultStyleInformationToMap( +Map _convertDefaultStyleInformationToMap( DefaultStyleInformation styleInformation) => - { + { 'htmlFormatContent': styleInformation.htmlFormatContent, 'htmlFormatTitle': styleInformation.htmlFormatTitle }; extension BigPictureStyleInformationMapper on BigPictureStyleInformation { - Map toMap() => _convertDefaultStyleInformationToMap(this) + Map toMap() => _convertDefaultStyleInformationToMap(this) ..addAll(_convertBigPictureToMap()) ..addAll(_convertLargeIconToMap()) - ..addAll({ + ..addAll({ 'contentTitle': contentTitle, 'summaryText': summaryText, 'htmlFormatContentTitle': htmlFormatContentTitle, @@ -155,12 +155,12 @@ extension BigPictureStyleInformationMapper on BigPictureStyleInformation { Map _convertLargeIconToMap() { if (largeIcon is DrawableResourceAndroidBitmap) { return { - 'largeIcon': largeIcon.bitmap, + 'largeIcon': largeIcon!.bitmap, 'largeIconBitmapSource': AndroidBitmapSource.drawable.index, }; } else if (largeIcon is FilePathAndroidBitmap) { return { - 'largeIcon': largeIcon.bitmap, + 'largeIcon': largeIcon!.bitmap, 'largeIconBitmapSource': AndroidBitmapSource.filePath.index, }; } else { @@ -170,8 +170,8 @@ extension BigPictureStyleInformationMapper on BigPictureStyleInformation { } extension BigTexStyleInformationMapper on BigTextStyleInformation { - Map toMap() => _convertDefaultStyleInformationToMap(this) - ..addAll({ + Map toMap() => _convertDefaultStyleInformationToMap(this) + ..addAll({ 'bigText': bigText, 'htmlFormatBigText': htmlFormatBigText, 'contentTitle': contentTitle, @@ -182,37 +182,37 @@ extension BigTexStyleInformationMapper on BigTextStyleInformation { } extension InboxStyleInformationMapper on InboxStyleInformation { - Map toMap() => _convertDefaultStyleInformationToMap(this) - ..addAll({ + Map toMap() => _convertDefaultStyleInformationToMap(this) + ..addAll({ 'contentTitle': contentTitle, 'htmlFormatContentTitle': htmlFormatContentTitle, 'summaryText': summaryText, 'htmlFormatSummaryText': htmlFormatSummaryText, - 'lines': lines ?? [], + 'lines': lines, 'htmlFormatLines': htmlFormatLines }); } extension MessagingStyleInformationMapper on MessagingStyleInformation { - Map toMap() => _convertDefaultStyleInformationToMap(this) - ..addAll({ - 'person': person?.toMap(), + Map toMap() => _convertDefaultStyleInformationToMap(this) + ..addAll({ + 'person': person.toMap(), 'conversationTitle': conversationTitle, 'groupConversation': groupConversation, 'messages': messages - ?.map((m) => m?.toMap()) // ignore: always_specify_types - ?.toList() + ?.map((m) => m.toMap()) // ignore: always_specify_types + .toList() }); } extension AndroidNotificationDetailsMapper on AndroidNotificationDetails { - Map toMap() => { + Map toMap() => { 'icon': icon, 'channelId': channelId, 'channelName': channelName, 'channelDescription': channelDescription, 'channelShowBadge': channelShowBadge, - 'channelAction': channelAction?.index, + 'channelAction': channelAction.index, 'importance': importance.value, 'priority': priority.value, 'playSound': playSound, @@ -256,42 +256,42 @@ extension AndroidNotificationDetailsMapper on AndroidNotificationDetails { ..addAll(_convertNotificationSoundToMap(sound)) ..addAll(_convertLargeIconToMap()); - Map _convertStyleInformationToMap() { + Map _convertStyleInformationToMap() { if (styleInformation is BigPictureStyleInformation) { - return { + return { 'style': AndroidNotificationStyle.bigPicture.index, 'styleInformation': - (styleInformation as BigPictureStyleInformation)?.toMap(), + (styleInformation as BigPictureStyleInformation?)?.toMap(), }; } else if (styleInformation is BigTextStyleInformation) { - return { + return { 'style': AndroidNotificationStyle.bigText.index, 'styleInformation': - (styleInformation as BigTextStyleInformation)?.toMap(), + (styleInformation as BigTextStyleInformation?)?.toMap(), }; } else if (styleInformation is InboxStyleInformation) { - return { + return { 'style': AndroidNotificationStyle.inbox.index, 'styleInformation': - (styleInformation as InboxStyleInformation)?.toMap(), + (styleInformation as InboxStyleInformation?)?.toMap(), }; } else if (styleInformation is MessagingStyleInformation) { - return { + return { 'style': AndroidNotificationStyle.messaging.index, 'styleInformation': - (styleInformation as MessagingStyleInformation)?.toMap(), + (styleInformation as MessagingStyleInformation?)?.toMap(), }; } else if (styleInformation is MediaStyleInformation) { - return { + return { 'style': AndroidNotificationStyle.media.index, 'styleInformation': - (styleInformation as MediaStyleInformation)?.toMap(), + (styleInformation as MediaStyleInformation?)?.toMap(), }; } else if (styleInformation is DefaultStyleInformation) { - return { + return { 'style': AndroidNotificationStyle.defaultStyle.index, 'styleInformation': - (styleInformation as DefaultStyleInformation)?.toMap(), + (styleInformation as DefaultStyleInformation?)?.toMap(), }; } else { return { @@ -304,12 +304,12 @@ extension AndroidNotificationDetailsMapper on AndroidNotificationDetails { Map _convertLargeIconToMap() { if (largeIcon is DrawableResourceAndroidBitmap) { return { - 'largeIcon': largeIcon.bitmap, + 'largeIcon': largeIcon!.bitmap, 'largeIconBitmapSource': AndroidBitmapSource.drawable.index, }; } else if (largeIcon is FilePathAndroidBitmap) { return { - 'largeIcon': largeIcon.bitmap, + 'largeIcon': largeIcon!.bitmap, 'largeIconBitmapSource': AndroidBitmapSource.filePath.index, }; } else { diff --git a/flutter_local_notifications/lib/src/platform_specifics/android/notification_channel.dart b/flutter_local_notifications/lib/src/platform_specifics/android/notification_channel.dart index 95c95b26a..5c365df0a 100644 --- a/flutter_local_notifications/lib/src/platform_specifics/android/notification_channel.dart +++ b/flutter_local_notifications/lib/src/platform_specifics/android/notification_channel.dart @@ -32,7 +32,7 @@ class AndroidNotificationChannel { final String description; /// The id of the group that the channel belongs to. - final String groupId; + final String? groupId; /// The importance of the notification. final Importance importance; @@ -49,7 +49,7 @@ class AndroidNotificationChannel { /// If [playSound] is set to true but this is not specified then the default /// sound is played. Tied to the specified channel and cannot be changed /// after the channel has been created for the first time. - final AndroidNotificationSound sound; + final AndroidNotificationSound? sound; /// Indicates if vibration should be enabled when the notification is /// displayed. @@ -69,13 +69,13 @@ class AndroidNotificationChannel { /// Requires setting [enableVibration] to true for it to work. /// Tied to the specified channel and cannot be changed after the channel has /// been created for the first time. - final Int64List vibrationPattern; + final Int64List? vibrationPattern; /// Specifies the light color of the notification. /// /// Tied to the specified channel and cannot be changed after the channel has /// been created for the first time. - final Color ledColor; + final Color? ledColor; /// Whether notifications posted to this channel can appear as application /// icon badges in a Launcher diff --git a/flutter_local_notifications/lib/src/platform_specifics/android/notification_channel_group.dart b/flutter_local_notifications/lib/src/platform_specifics/android/notification_channel_group.dart index da9e7171b..7a0559e71 100644 --- a/flutter_local_notifications/lib/src/platform_specifics/android/notification_channel_group.dart +++ b/flutter_local_notifications/lib/src/platform_specifics/android/notification_channel_group.dart @@ -16,5 +16,5 @@ class AndroidNotificationChannelGroup { /// The description of this group. /// /// Only applicable to Android 9.0 or newer. - final String description; + final String? description; } diff --git a/flutter_local_notifications/lib/src/platform_specifics/android/notification_details.dart b/flutter_local_notifications/lib/src/platform_specifics/android/notification_details.dart index a3544fba2..c33db0242 100644 --- a/flutter_local_notifications/lib/src/platform_specifics/android/notification_details.dart +++ b/flutter_local_notifications/lib/src/platform_specifics/android/notification_details.dart @@ -22,13 +22,13 @@ class AndroidNotificationDetails { this.enableVibration = true, this.vibrationPattern, this.groupKey, - this.setAsGroupSummary, + this.setAsGroupSummary = false, this.groupAlertBehavior = GroupAlertBehavior.all, this.autoCancel = true, - this.ongoing, + this.ongoing = false, this.color, this.largeIcon, - this.onlyAlertOnce, + this.onlyAlertOnce = false, this.showWhen = true, this.when, this.usesChronometer = false, @@ -57,7 +57,7 @@ class AndroidNotificationDetails { /// /// When this is set to `null`, the default icon given to /// [AndroidInitializationSettings.defaultIcon] will be used. - final String icon; + final String? icon; /// The channel's id. /// @@ -98,7 +98,7 @@ class AndroidNotificationDetails { /// /// For Android 8.0 or newer, this is tied to the specified channel and cannot /// be changed after the channel has been created for the first time. - final AndroidNotificationSound sound; + final AndroidNotificationSound? sound; /// Indicates if vibration should be enabled when the notification is /// displayed. @@ -118,16 +118,16 @@ class AndroidNotificationDetails { /// Requires setting [enableVibration] to true for it to work. /// For Android 8.0 or newer, this is tied to the specified channel and cannot /// be changed after the channel has been created for the first time. - final Int64List vibrationPattern; + final Int64List? vibrationPattern; /// Specifies the information of the rich notification style to apply to the /// notification. - final StyleInformation styleInformation; + final StyleInformation? styleInformation; /// Specifies the group that this notification belongs to. /// /// For Android 7.0 or newer. - final String groupKey; + final String? groupKey; /// Specifies if this notification will function as the summary for grouped /// notifications. @@ -147,10 +147,10 @@ class AndroidNotificationDetails { final bool ongoing; /// Specifies the color. - final Color color; + final Color? color; /// Specifics the large icon to use. - final AndroidBitmap largeIcon; + final AndroidBitmap? largeIcon; /// Specifies if you would only like the sound, vibrate and ticker to be /// played if the notification is not already showing. @@ -171,7 +171,7 @@ class AndroidNotificationDetails { /// "Unix epoch" 1970-01-01T00:00:00Z (UTC). If it's not specified but a /// timestamp should be shown (i.e. [showWhen] is set to `true`), /// then Android will default to showing when the notification occurred. - final int when; + final int? when; /// Show [when] as a stopwatch. /// @@ -196,20 +196,20 @@ class AndroidNotificationDetails { /// /// For Android 8.0 or newer, this is tied to the specified channel and cannot /// be changed after the channel has been created for the first time. - final Color ledColor; + final Color? ledColor; /// Specifies how long the light colour will remain on. /// /// This property is only applicable to Android versions older than 8.0. - final int ledOnMs; + final int? ledOnMs; /// Specifies how long the light colour will remain off. /// /// This property is only applicable to Android versions older than 8.0. - final int ledOffMs; + final int? ledOffMs; /// Specifies the "ticker" text which is sent to accessibility services. - final String ticker; + final String? ticker; /// The action to take for managing notification channels. /// @@ -218,16 +218,16 @@ class AndroidNotificationDetails { final AndroidNotificationChannelAction channelAction; /// Defines the notification visibility on the lockscreen. - final NotificationVisibility visibility; + final NotificationVisibility? visibility; /// The duration in milliseconds after which the notification will be /// cancelled if it hasn't already. - final int timeoutAfter; + final int? timeoutAfter; /// The notification category. /// /// Refer to Android notification API documentation at https://developer.android.com/reference/androidx/core/app/NotificationCompat.html#constants_2 for the available categories - final String category; + final String? category; /// Specifies whether the notification should launch a full-screen intent as /// soon as it triggers. @@ -245,14 +245,14 @@ class AndroidNotificationDetails { /// /// From Android 11, this affects if a messaging-style notification appears /// in the conversation space. - final String shortcutId; + final String? shortcutId; /// Specifies the additional flags. /// /// These flags will get added to the native Android notification's flags field: https://developer.android.com/reference/android/app/Notification#flags /// For a list of a values, refer to the documented constants prefixed with "FLAG_" (without the quotes) at https://developer.android.com/reference/android/app/Notification.html#constants_1. /// For example, use a value of 4 to allow the audio to repeat as documented at https://developer.android.com/reference/android/app/Notification.html#FLAG_INSISTEN - final Int32List additionalFlags; + final Int32List? additionalFlags; /// Provides some additional information that is displayed in the /// notification. @@ -268,11 +268,11 @@ class AndroidNotificationDetails { /// of text in the platform notification template. You should not be using /// setProgress(int, int, boolean) at the same time on those versions; they /// occupy the same place. - final String subText; + final String? subText; /// The notification tag. /// /// Showing notification with the same (tag, id) pair as a currently visible /// notification will replace the old notification with the new one. - final String tag; + final String? tag; } diff --git a/flutter_local_notifications/lib/src/platform_specifics/android/notification_sound.dart b/flutter_local_notifications/lib/src/platform_specifics/android/notification_sound.dart index 0d629ee5d..a43af0f89 100644 --- a/flutter_local_notifications/lib/src/platform_specifics/android/notification_sound.dart +++ b/flutter_local_notifications/lib/src/platform_specifics/android/notification_sound.dart @@ -12,11 +12,11 @@ class RawResourceAndroidNotificationSound implements AndroidNotificationSound { /// Constructs an instance of [RawResourceAndroidNotificationSound]. const RawResourceAndroidNotificationSound(this._sound); - final String _sound; + final String? _sound; /// The name of the raw resource for the notification sound. @override - String get sound => _sound; + String get sound => _sound!; } /// Represents a URI on the Android device that should be used for the diff --git a/flutter_local_notifications/lib/src/platform_specifics/android/person.dart b/flutter_local_notifications/lib/src/platform_specifics/android/person.dart index 2e289defa..88f9c90f8 100644 --- a/flutter_local_notifications/lib/src/platform_specifics/android/person.dart +++ b/flutter_local_notifications/lib/src/platform_specifics/android/person.dart @@ -4,9 +4,9 @@ import 'icon.dart'; class Person { /// Constructs an instance of [Person]. const Person({ - this.bot, + this.bot = false, this.icon, - this.important, + this.important = false, this.key, this.name, this.uri, @@ -16,17 +16,17 @@ class Person { final bool bot; /// Icon for this person. - final AndroidIcon icon; + final AndroidIcon? icon; /// Whether or not this is an important person. final bool important; /// Unique identifier for this person. - final String key; + final String? key; /// Name of this person. - final String name; + final String? name; /// Uri for this person. - final String uri; + final String? uri; } diff --git a/flutter_local_notifications/lib/src/platform_specifics/android/styles/big_picture_style_information.dart b/flutter_local_notifications/lib/src/platform_specifics/android/styles/big_picture_style_information.dart index e49cec4a1..7a3fe1e05 100644 --- a/flutter_local_notifications/lib/src/platform_specifics/android/styles/big_picture_style_information.dart +++ b/flutter_local_notifications/lib/src/platform_specifics/android/styles/big_picture_style_information.dart @@ -18,11 +18,11 @@ class BigPictureStyleInformation extends DefaultStyleInformation { }) : super(htmlFormatContent, htmlFormatTitle); /// Overrides ContentTitle in the big form of the template. - final String contentTitle; + final String? contentTitle; /// Set the first line of text after the detail section in the big form of /// the template. - final String summaryText; + final String? summaryText; /// Specifies if the overridden ContentTitle should have formatting applied /// through HTML markup. @@ -34,7 +34,7 @@ class BigPictureStyleInformation extends DefaultStyleInformation { /// The bitmap that will override the large icon when the big notification is /// shown. - final AndroidBitmap largeIcon; + final AndroidBitmap? largeIcon; /// The bitmap to be used as the payload for the BigPicture notification. final AndroidBitmap bigPicture; diff --git a/flutter_local_notifications/lib/src/platform_specifics/android/styles/big_text_style_information.dart b/flutter_local_notifications/lib/src/platform_specifics/android/styles/big_text_style_information.dart index 8cd161083..ccd2be6f8 100644 --- a/flutter_local_notifications/lib/src/platform_specifics/android/styles/big_text_style_information.dart +++ b/flutter_local_notifications/lib/src/platform_specifics/android/styles/big_text_style_information.dart @@ -20,11 +20,11 @@ class BigTextStyleInformation extends DefaultStyleInformation { final String bigText; /// Overrides ContentTitle in the big form of the template. - final String contentTitle; + final String? contentTitle; /// Set the first line of text after the detail section in the big form of /// the template. - final String summaryText; + final String? summaryText; /// Specifies if formatting should be applied to the longer text through /// HTML markup. diff --git a/flutter_local_notifications/lib/src/platform_specifics/android/styles/inbox_style_information.dart b/flutter_local_notifications/lib/src/platform_specifics/android/styles/inbox_style_information.dart index 1d146b304..a20e4d358 100644 --- a/flutter_local_notifications/lib/src/platform_specifics/android/styles/inbox_style_information.dart +++ b/flutter_local_notifications/lib/src/platform_specifics/android/styles/inbox_style_information.dart @@ -16,11 +16,11 @@ class InboxStyleInformation extends DefaultStyleInformation { }) : super(htmlFormatContent, htmlFormatTitle); /// Overrides ContentTitle in the big form of the template. - final String contentTitle; + final String? contentTitle; /// Set the first line of text after the detail section in the big form of /// the template. - final String summaryText; + final String? summaryText; /// The lines that form part of the digest section for inbox-style /// notifications. diff --git a/flutter_local_notifications/lib/src/platform_specifics/android/styles/messaging_style_information.dart b/flutter_local_notifications/lib/src/platform_specifics/android/styles/messaging_style_information.dart index b0bf81af5..1e4bdaf6a 100644 --- a/flutter_local_notifications/lib/src/platform_specifics/android/styles/messaging_style_information.dart +++ b/flutter_local_notifications/lib/src/platform_specifics/android/styles/messaging_style_information.dart @@ -13,18 +13,17 @@ class MessagingStyleInformation extends DefaultStyleInformation { this.messages, bool htmlFormatContent = false, bool htmlFormatTitle = false, - }) : assert(person?.name != null, 'Must provide the details of the person'), - super(htmlFormatContent, htmlFormatTitle); + }) : super(htmlFormatContent, htmlFormatTitle); /// The person displayed for any messages that are sent by the user. final Person person; /// The title to be displayed on this conversation. - final String conversationTitle; + final String? conversationTitle; /// Whether this conversation notification represents a group. - final bool groupConversation; + final bool? groupConversation; /// Messages to be displayed by this notification - final List messages; + final List? messages; } diff --git a/flutter_local_notifications/lib/src/platform_specifics/ios/initialization_settings.dart b/flutter_local_notifications/lib/src/platform_specifics/ios/initialization_settings.dart index bb7eac84c..ab22ce4be 100644 --- a/flutter_local_notifications/lib/src/platform_specifics/ios/initialization_settings.dart +++ b/flutter_local_notifications/lib/src/platform_specifics/ios/initialization_settings.dart @@ -11,12 +11,7 @@ class IOSInitializationSettings { this.defaultPresentSound = true, this.defaultPresentBadge = true, this.onDidReceiveLocalNotification, - }) : assert(requestAlertPermission != null), - assert(requestSoundPermission != null), - assert(requestBadgePermission != null), - assert(defaultPresentAlert != null), - assert(defaultPresentBadge != null), - assert(defaultPresentSound != null); + }); /// Request permission to display an alert. /// @@ -62,5 +57,5 @@ class IOSInitializationSettings { /// in the foreground. /// /// This property is only applicable to iOS versions older than 10. - final DidReceiveLocalNotificationCallback onDidReceiveLocalNotification; + final DidReceiveLocalNotificationCallback? onDidReceiveLocalNotification; } diff --git a/flutter_local_notifications/lib/src/platform_specifics/ios/method_channel_mappers.dart b/flutter_local_notifications/lib/src/platform_specifics/ios/method_channel_mappers.dart index 3dd60e33c..3dad655e1 100644 --- a/flutter_local_notifications/lib/src/platform_specifics/ios/method_channel_mappers.dart +++ b/flutter_local_notifications/lib/src/platform_specifics/ios/method_channel_mappers.dart @@ -22,7 +22,7 @@ extension IOSNotificationAttachmentMapper on IOSNotificationAttachment { } extension IOSNotificationDetailsMapper on IOSNotificationDetails { - Map toMap() => { + Map toMap() => { 'presentAlert': presentAlert, 'presentSound': presentSound, 'presentBadge': presentBadge, @@ -32,6 +32,6 @@ extension IOSNotificationDetailsMapper on IOSNotificationDetails { 'threadIdentifier': threadIdentifier, 'attachments': attachments ?.map((a) => a.toMap()) // ignore: always_specify_types - ?.toList() + .toList() }; } diff --git a/flutter_local_notifications/lib/src/platform_specifics/ios/notification_attachment.dart b/flutter_local_notifications/lib/src/platform_specifics/ios/notification_attachment.dart index c4a153f62..617146f15 100644 --- a/flutter_local_notifications/lib/src/platform_specifics/ios/notification_attachment.dart +++ b/flutter_local_notifications/lib/src/platform_specifics/ios/notification_attachment.dart @@ -4,7 +4,7 @@ class IOSNotificationAttachment { const IOSNotificationAttachment( this.filePath, { this.identifier, - }) : assert(filePath != null); + }); /// The local file path to the attachment. /// @@ -15,5 +15,5 @@ class IOSNotificationAttachment { /// The unique identifier for the attachment. /// /// When left empty, the iOS APIs will generate a unique identifier - final String identifier; + final String? identifier; } diff --git a/flutter_local_notifications/lib/src/platform_specifics/ios/notification_details.dart b/flutter_local_notifications/lib/src/platform_specifics/ios/notification_details.dart index 5d5d2dfe8..a31f8dc35 100644 --- a/flutter_local_notifications/lib/src/platform_specifics/ios/notification_details.dart +++ b/flutter_local_notifications/lib/src/platform_specifics/ios/notification_details.dart @@ -21,7 +21,7 @@ class IOSNotificationDetails { /// to [IOSInitializationSettings.defaultPresentAlert]. /// /// This property is only applicable to iOS 10 or newer. - final bool presentAlert; + final bool? presentAlert; /// Play a sound when the notification is triggered while app is in /// the foreground. @@ -30,7 +30,7 @@ class IOSNotificationDetails { /// [IOSInitializationSettings.defaultPresentSound]. /// /// This property is only applicable to iOS 10 or newer. - final bool presentSound; + final bool? presentSound; /// Apply the badge value when the notification is triggered while app is in /// the foreground. @@ -39,14 +39,14 @@ class IOSNotificationDetails { /// [IOSInitializationSettings.defaultPresentBadge]. /// /// This property is only applicable to iOS 10 or newer. - final bool presentBadge; + final bool? presentBadge; /// Specifies the name of the file to play for the notification. /// /// Requires setting [presentSound] to true. If [presentSound] is set to true /// but [sound] isn't specified then it will use the default notification /// sound. - final String sound; + final String? sound; /// Specify the number to display as the app icon's badge when the /// notification arrives. @@ -54,21 +54,21 @@ class IOSNotificationDetails { /// Specify the number `0` to remove the current badge, if present. Greater /// than `0` to display a badge with that number. /// Specify `null` to leave the current badge unchanged. - final int badgeNumber; + final int? badgeNumber; /// Specifies the list of attachments included with the notification. /// /// This property is only applicable to iOS 10 or newer. - final List attachments; + final List? attachments; /// Specifies the secondary description. /// /// This property is only applicable to iOS 10 or newer. - final String subtitle; + final String? subtitle; /// Specifies the thread identifier that can be used to group /// notifications together. /// /// This property is only applicable to iOS 10 or newer. - final String threadIdentifier; + final String? threadIdentifier; } diff --git a/flutter_local_notifications/lib/src/platform_specifics/macos/initialization_settings.dart b/flutter_local_notifications/lib/src/platform_specifics/macos/initialization_settings.dart index 0019e0f20..64770a821 100644 --- a/flutter_local_notifications/lib/src/platform_specifics/macos/initialization_settings.dart +++ b/flutter_local_notifications/lib/src/platform_specifics/macos/initialization_settings.dart @@ -8,12 +8,7 @@ class MacOSInitializationSettings { this.defaultPresentAlert = true, this.defaultPresentSound = true, this.defaultPresentBadge = true, - }) : assert(requestAlertPermission != null), - assert(requestSoundPermission != null), - assert(requestBadgePermission != null), - assert(defaultPresentAlert != null), - assert(defaultPresentBadge != null), - assert(defaultPresentSound != null); + }); /// Request permission to display an alert. /// diff --git a/flutter_local_notifications/lib/src/platform_specifics/macos/method_channel_mappers.dart b/flutter_local_notifications/lib/src/platform_specifics/macos/method_channel_mappers.dart index 6fddd3e18..f942c0032 100644 --- a/flutter_local_notifications/lib/src/platform_specifics/macos/method_channel_mappers.dart +++ b/flutter_local_notifications/lib/src/platform_specifics/macos/method_channel_mappers.dart @@ -22,7 +22,7 @@ extension MacOSNotificationAttachmentMapper on MacOSNotificationAttachment { } extension MacOSNotificationDetailsMapper on MacOSNotificationDetails { - Map toMap() => { + Map toMap() => { 'presentAlert': presentAlert, 'presentSound': presentSound, 'presentBadge': presentBadge, @@ -32,6 +32,6 @@ extension MacOSNotificationDetailsMapper on MacOSNotificationDetails { 'threadIdentifier': threadIdentifier, 'attachments': attachments ?.map((a) => a.toMap()) // ignore: always_specify_types - ?.toList() + .toList() }; } diff --git a/flutter_local_notifications/lib/src/platform_specifics/macos/notification_attachment.dart b/flutter_local_notifications/lib/src/platform_specifics/macos/notification_attachment.dart index 8786ce927..09db0315a 100644 --- a/flutter_local_notifications/lib/src/platform_specifics/macos/notification_attachment.dart +++ b/flutter_local_notifications/lib/src/platform_specifics/macos/notification_attachment.dart @@ -4,7 +4,7 @@ class MacOSNotificationAttachment { const MacOSNotificationAttachment( this.filePath, { this.identifier, - }) : assert(filePath != null); + }); /// The local file path to the attachment. /// @@ -15,5 +15,5 @@ class MacOSNotificationAttachment { /// The unique identifier for the attachment. /// /// When left empty, the macOS APIs will generate a unique identifier - final String identifier; + final String? identifier; } diff --git a/flutter_local_notifications/lib/src/platform_specifics/macos/notification_details.dart b/flutter_local_notifications/lib/src/platform_specifics/macos/notification_details.dart index 754eab280..6d3e54a00 100644 --- a/flutter_local_notifications/lib/src/platform_specifics/macos/notification_details.dart +++ b/flutter_local_notifications/lib/src/platform_specifics/macos/notification_details.dart @@ -21,7 +21,7 @@ class MacOSNotificationDetails { /// to [MacOSInitializationSettings.defaultPresentAlert]. /// /// This property is only applicable to macOS 10.14 or newer. - final bool presentAlert; + final bool? presentAlert; /// Play a sound when the notification is triggered while app is in /// the foreground. @@ -30,7 +30,7 @@ class MacOSNotificationDetails { /// [MacOSInitializationSettings.defaultPresentSound]. /// /// This property is only applicable to macOS 10.14 or newer. - final bool presentSound; + final bool? presentSound; /// Apply the badge value when the notification is triggered while app is in /// the foreground. @@ -39,14 +39,14 @@ class MacOSNotificationDetails { /// [MacOSInitializationSettings.defaultPresentBadge]. /// /// This property is only applicable to macOS 10.14 or newer. - final bool presentBadge; + final bool? presentBadge; /// Specifies the name of the file to play for the notification. /// /// Requires setting [presentSound] to true. If [presentSound] is set to true /// but [sound] isn't specified then it will use the default notification /// sound. - final String sound; + final String? sound; /// Specify the number to display as the app icon's badge when the /// notification arrives. @@ -54,19 +54,19 @@ class MacOSNotificationDetails { /// Specify the number `0` to remove the current badge, if present. Greater /// than `0` to display a badge with that number. /// Specify `null` to leave the current badge unchanged. - final int badgeNumber; + final int? badgeNumber; /// Specifies the list of attachments included with the notification. /// /// This property is only applicable to macOS 10.14 or newer. - final List attachments; + final List? attachments; /// Specifies the secondary description. - final String subtitle; + final String? subtitle; /// Specifies the thread identifier that can be used to group /// notifications together. /// /// This property is only applicable to macOS 10.14 or newer. - final String threadIdentifier; + final String? threadIdentifier; } diff --git a/flutter_local_notifications/lib/src/typedefs.dart b/flutter_local_notifications/lib/src/typedefs.dart index b5926c250..bb89484ae 100644 --- a/flutter_local_notifications/lib/src/typedefs.dart +++ b/flutter_local_notifications/lib/src/typedefs.dart @@ -2,11 +2,11 @@ import 'dart:async'; /// Signature of callback passed to [initialize] that is triggered when user /// taps on a notification. -typedef SelectNotificationCallback = Future Function(String payload); +typedef SelectNotificationCallback = Future Function(String? payload); /// Signature of the callback that is triggered when a notification is shown /// whilst the app is in the foreground. /// /// This property is only applicable to iOS versions older than 10. typedef DidReceiveLocalNotificationCallback = Future Function( - int id, String title, String body, String payload); + int id, String? title, String? body, String? payload); diff --git a/flutter_local_notifications/pubspec.yaml b/flutter_local_notifications/pubspec.yaml index 199a584a2..92a95185f 100644 --- a/flutter_local_notifications/pubspec.yaml +++ b/flutter_local_notifications/pubspec.yaml @@ -2,23 +2,23 @@ name: flutter_local_notifications description: A cross platform plugin for displaying and scheduling local notifications for Flutter applications with the ability to customise for each platform. -version: 4.0.1+2 +version: 5.0.0 homepage: https://github.com/MaikuB/flutter_local_notifications/tree/master/flutter_local_notifications dependencies: flutter: sdk: flutter - flutter_local_notifications_platform_interface: ^2.0.0+1 - platform: ">=2.0.0 <4.0.0" - timezone: ^0.6.1 + flutter_local_notifications_platform_interface: ^3.0.0 + platform: ^3.0.0 + timezone: ^0.7.0-nullsafety.0 dev_dependencies: flutter_driver: sdk: flutter flutter_test: sdk: flutter - mockito: ^4.1.3 - plugin_platform_interface: ^1.0.3 + mockito: ^5.0.0-nullsafety.7 + plugin_platform_interface: ^2.0.0 flutter: plugin: @@ -32,5 +32,5 @@ flutter: pluginClass: FlutterLocalNotificationsPlugin environment: - sdk: ">=2.6.0 <3.0.0" + sdk: '>=2.12.0-0 <3.0.0' flutter: ">=1.12.13+hotfix.5" diff --git a/flutter_local_notifications/test/flutter_local_notifications_test.dart b/flutter_local_notifications/test/flutter_local_notifications_test.dart index ce5a1d1bf..38fbab4b7 100644 --- a/flutter_local_notifications/test/flutter_local_notifications_test.dart +++ b/flutter_local_notifications/test/flutter_local_notifications_test.dart @@ -11,12 +11,6 @@ void main() { MockFlutterLocalNotificationsPlugin(); FlutterLocalNotificationsPlatform.instance = mock; - test( - 'Throws assertion error when creating an IOSNotificationAttachment with ' - 'no file path', () { - expect(() => IOSNotificationAttachment(null), throwsAssertionError); - }); - test('Creates IOSNotificationAttachment when file path is specified', () { expect( const IOSNotificationAttachment(''), isA()); diff --git a/flutter_local_notifications/test/platform_flutter_local_notifications_test.dart b/flutter_local_notifications/test/platform_flutter_local_notifications_test.dart index cb2375b74..3d17d8250 100644 --- a/flutter_local_notifications/test/platform_flutter_local_notifications_test.dart +++ b/flutter_local_notifications/test/platform_flutter_local_notifications_test.dart @@ -12,7 +12,7 @@ import 'package:timezone/timezone.dart' as tz; void main() { // TODO(maikub): add tests for `periodicallyShow` after https://github.com/dart-lang/sdk/issues/28985 is resolved TestWidgetsFlutterBinding.ensureInitialized(); - FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin; + late FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin; group('Android', () { const MethodChannel channel = @@ -26,15 +26,12 @@ void main() { channel.setMockMethodCallHandler((methodCall) async { log.add(methodCall); if (methodCall.method == 'pendingNotificationRequests') { - return Future>>.value( - >[]); + return >[]; } else if (methodCall.method == 'getNotificationAppLaunchDetails') { - return Future>.value({}); + return null; } else if (methodCall.method == 'getActiveNotifications') { - return Future>>.value( - >[]); + return >[]; } - return Future.value(); }); }); @@ -65,7 +62,7 @@ void main() { 1, 'notification title', 'notification body', null); expect( log.last, - isMethodCall('show', arguments: { + isMethodCall('show', arguments: { 'id': 1, 'title': 'notification title', 'body': 'notification body', @@ -96,7 +93,7 @@ void main() { 'title': 'notification title', 'body': 'notification body', 'payload': '', - 'platformSpecifics': { + 'platformSpecifics': { 'icon': null, 'channelId': 'channelId', 'channelName': 'channelName', @@ -110,15 +107,15 @@ void main() { 'enableVibration': true, 'vibrationPattern': null, 'groupKey': null, - 'setAsGroupSummary': null, + 'setAsGroupSummary': false, 'groupAlertBehavior': GroupAlertBehavior.all.index, 'autoCancel': true, - 'ongoing': null, + 'ongoing': false, 'colorAlpha': null, 'colorRed': null, 'colorGreen': null, 'colorBlue': null, - 'onlyAlertOnce': null, + 'onlyAlertOnce': false, 'showWhen': true, 'when': null, 'usesChronometer': false, @@ -175,7 +172,7 @@ void main() { 'title': 'notification title', 'body': 'notification body', 'payload': '', - 'platformSpecifics': { + 'platformSpecifics': { 'icon': null, 'channelId': 'channelId', 'channelName': 'channelName', @@ -189,15 +186,15 @@ void main() { 'enableVibration': true, 'vibrationPattern': null, 'groupKey': null, - 'setAsGroupSummary': null, + 'setAsGroupSummary': false, 'groupAlertBehavior': GroupAlertBehavior.all.index, 'autoCancel': true, - 'ongoing': null, + 'ongoing': false, 'colorAlpha': null, 'colorRed': null, 'colorGreen': null, 'colorBlue': null, - 'onlyAlertOnce': null, + 'onlyAlertOnce': false, 'showWhen': true, 'when': null, 'usesChronometer': false, @@ -256,7 +253,7 @@ void main() { 'title': 'notification title', 'body': 'notification body', 'payload': '', - 'platformSpecifics': { + 'platformSpecifics': { 'icon': null, 'channelId': 'channelId', 'channelName': 'channelName', @@ -270,15 +267,15 @@ void main() { 'enableVibration': true, 'vibrationPattern': null, 'groupKey': null, - 'setAsGroupSummary': null, + 'setAsGroupSummary': false, 'groupAlertBehavior': GroupAlertBehavior.all.index, 'autoCancel': true, - 'ongoing': null, + 'ongoing': false, 'colorAlpha': null, 'colorRed': null, 'colorGreen': null, 'colorBlue': null, - 'onlyAlertOnce': null, + 'onlyAlertOnce': false, 'showWhen': true, 'when': timestamp, 'usesChronometer': false, @@ -336,7 +333,7 @@ void main() { 'title': 'notification title', 'body': 'notification body', 'payload': '', - 'platformSpecifics': { + 'platformSpecifics': { 'icon': null, 'channelId': 'channelId', 'channelName': 'channelName', @@ -350,15 +347,15 @@ void main() { 'enableVibration': true, 'vibrationPattern': null, 'groupKey': null, - 'setAsGroupSummary': null, + 'setAsGroupSummary': false, 'groupAlertBehavior': GroupAlertBehavior.all.index, 'autoCancel': true, - 'ongoing': null, + 'ongoing': false, 'colorAlpha': null, 'colorRed': null, 'colorGreen': null, 'colorBlue': null, - 'onlyAlertOnce': null, + 'onlyAlertOnce': false, 'showWhen': true, 'when': timestamp, 'usesChronometer': true, @@ -419,7 +416,7 @@ void main() { 'title': 'notification title', 'body': 'notification body', 'payload': '', - 'platformSpecifics': { + 'platformSpecifics': { 'icon': null, 'channelId': 'channelId', 'channelName': 'channelName', @@ -435,15 +432,15 @@ void main() { 'enableVibration': true, 'vibrationPattern': null, 'groupKey': null, - 'setAsGroupSummary': null, + 'setAsGroupSummary': false, 'groupAlertBehavior': GroupAlertBehavior.all.index, 'autoCancel': true, - 'ongoing': null, + 'ongoing': false, 'colorAlpha': null, 'colorRed': null, 'colorGreen': null, 'colorBlue': null, - 'onlyAlertOnce': null, + 'onlyAlertOnce': false, 'showWhen': true, 'when': null, 'usesChronometer': false, @@ -503,7 +500,7 @@ void main() { 'title': 'notification title', 'body': 'notification body', 'payload': '', - 'platformSpecifics': { + 'platformSpecifics': { 'icon': null, 'channelId': 'channelId', 'channelName': 'channelName', @@ -519,15 +516,15 @@ void main() { 'enableVibration': true, 'vibrationPattern': null, 'groupKey': null, - 'setAsGroupSummary': null, + 'setAsGroupSummary': false, 'groupAlertBehavior': GroupAlertBehavior.all.index, 'autoCancel': true, - 'ongoing': null, + 'ongoing': false, 'colorAlpha': null, 'colorRed': null, 'colorGreen': null, 'colorBlue': null, - 'onlyAlertOnce': null, + 'onlyAlertOnce': false, 'showWhen': true, 'when': null, 'usesChronometer': false, @@ -588,7 +585,7 @@ void main() { 'title': 'notification title', 'body': 'notification body', 'payload': '', - 'platformSpecifics': { + 'platformSpecifics': { 'icon': null, 'channelId': 'channelId', 'channelName': 'channelName', @@ -602,15 +599,15 @@ void main() { 'enableVibration': true, 'vibrationPattern': null, 'groupKey': null, - 'setAsGroupSummary': null, + 'setAsGroupSummary': false, 'groupAlertBehavior': GroupAlertBehavior.all.index, 'autoCancel': true, - 'ongoing': null, + 'ongoing': false, 'colorAlpha': null, 'colorRed': null, 'colorGreen': null, 'colorBlue': null, - 'onlyAlertOnce': null, + 'onlyAlertOnce': false, 'showWhen': true, 'when': null, 'usesChronometer': false, @@ -673,7 +670,7 @@ void main() { 'title': 'notification title', 'body': 'notification body', 'payload': '', - 'platformSpecifics': { + 'platformSpecifics': { 'icon': null, 'channelId': 'channelId', 'channelName': 'channelName', @@ -687,15 +684,15 @@ void main() { 'enableVibration': true, 'vibrationPattern': null, 'groupKey': null, - 'setAsGroupSummary': null, + 'setAsGroupSummary': false, 'groupAlertBehavior': GroupAlertBehavior.all.index, 'autoCancel': true, - 'ongoing': null, + 'ongoing': false, 'colorAlpha': null, 'colorRed': null, 'colorGreen': null, 'colorBlue': null, - 'onlyAlertOnce': null, + 'onlyAlertOnce': false, 'showWhen': true, 'when': null, 'usesChronometer': false, @@ -719,7 +716,7 @@ void main() { 'shortcutId': null, 'subText': null, 'style': AndroidNotificationStyle.bigPicture.index, - 'styleInformation': { + 'styleInformation': { 'htmlFormatContent': false, 'htmlFormatTitle': false, 'bigPicture': 'bigPictureDrawable', @@ -773,7 +770,7 @@ void main() { 'title': 'notification title', 'body': 'notification body', 'payload': '', - 'platformSpecifics': { + 'platformSpecifics': { 'icon': null, 'channelId': 'channelId', 'channelName': 'channelName', @@ -787,15 +784,15 @@ void main() { 'enableVibration': true, 'vibrationPattern': null, 'groupKey': null, - 'setAsGroupSummary': null, + 'setAsGroupSummary': false, 'groupAlertBehavior': GroupAlertBehavior.all.index, 'autoCancel': true, - 'ongoing': null, + 'ongoing': false, 'colorAlpha': null, 'colorRed': null, 'colorGreen': null, 'colorBlue': null, - 'onlyAlertOnce': null, + 'onlyAlertOnce': false, 'showWhen': true, 'when': null, 'usesChronometer': false, @@ -867,7 +864,7 @@ void main() { 'title': 'notification title', 'body': 'notification body', 'payload': '', - 'platformSpecifics': { + 'platformSpecifics': { 'icon': null, 'channelId': 'channelId', 'channelName': 'channelName', @@ -881,15 +878,15 @@ void main() { 'enableVibration': true, 'vibrationPattern': null, 'groupKey': null, - 'setAsGroupSummary': null, + 'setAsGroupSummary': false, 'groupAlertBehavior': GroupAlertBehavior.all.index, 'autoCancel': true, - 'ongoing': null, + 'ongoing': false, 'colorAlpha': null, 'colorRed': null, 'colorGreen': null, 'colorBlue': null, - 'onlyAlertOnce': null, + 'onlyAlertOnce': false, 'showWhen': true, 'when': null, 'usesChronometer': false, @@ -913,7 +910,7 @@ void main() { 'shortcutId': null, 'subText': null, 'style': AndroidNotificationStyle.bigPicture.index, - 'styleInformation': { + 'styleInformation': { 'htmlFormatContent': false, 'htmlFormatTitle': false, 'bigPicture': 'bigPictureFilePath', @@ -967,7 +964,7 @@ void main() { 'title': 'notification title', 'body': 'notification body', 'payload': '', - 'platformSpecifics': { + 'platformSpecifics': { 'icon': null, 'channelId': 'channelId', 'channelName': 'channelName', @@ -981,15 +978,15 @@ void main() { 'enableVibration': true, 'vibrationPattern': null, 'groupKey': null, - 'setAsGroupSummary': null, + 'setAsGroupSummary': false, 'groupAlertBehavior': GroupAlertBehavior.all.index, 'autoCancel': true, - 'ongoing': null, + 'ongoing': false, 'colorAlpha': null, 'colorRed': null, 'colorGreen': null, 'colorBlue': null, - 'onlyAlertOnce': null, + 'onlyAlertOnce': false, 'showWhen': true, 'when': null, 'usesChronometer': false, @@ -1059,7 +1056,7 @@ void main() { 'title': 'notification title', 'body': 'notification body', 'payload': '', - 'platformSpecifics': { + 'platformSpecifics': { 'icon': null, 'channelId': 'channelId', 'channelName': 'channelName', @@ -1073,15 +1070,15 @@ void main() { 'enableVibration': true, 'vibrationPattern': null, 'groupKey': null, - 'setAsGroupSummary': null, + 'setAsGroupSummary': false, 'groupAlertBehavior': GroupAlertBehavior.all.index, 'autoCancel': true, - 'ongoing': null, + 'ongoing': false, 'colorAlpha': null, 'colorRed': null, 'colorGreen': null, 'colorBlue': null, - 'onlyAlertOnce': null, + 'onlyAlertOnce': false, 'showWhen': true, 'when': null, 'usesChronometer': false, @@ -1105,7 +1102,7 @@ void main() { 'shortcutId': null, 'subText': null, 'style': AndroidNotificationStyle.inbox.index, - 'styleInformation': { + 'styleInformation': { 'htmlFormatContent': false, 'htmlFormatTitle': false, 'lines': ['line1'], @@ -1155,7 +1152,7 @@ void main() { 'title': 'notification title', 'body': 'notification body', 'payload': '', - 'platformSpecifics': { + 'platformSpecifics': { 'icon': null, 'channelId': 'channelId', 'channelName': 'channelName', @@ -1169,15 +1166,15 @@ void main() { 'enableVibration': true, 'vibrationPattern': null, 'groupKey': null, - 'setAsGroupSummary': null, + 'setAsGroupSummary': false, 'groupAlertBehavior': GroupAlertBehavior.all.index, 'autoCancel': true, - 'ongoing': null, + 'ongoing': false, 'colorAlpha': null, 'colorRed': null, 'colorGreen': null, 'colorBlue': null, - 'onlyAlertOnce': null, + 'onlyAlertOnce': false, 'showWhen': true, 'when': null, 'usesChronometer': false, @@ -1242,7 +1239,7 @@ void main() { 'title': 'notification title', 'body': 'notification body', 'payload': '', - 'platformSpecifics': { + 'platformSpecifics': { 'icon': null, 'channelId': 'channelId', 'channelName': 'channelName', @@ -1256,15 +1253,15 @@ void main() { 'enableVibration': true, 'vibrationPattern': null, 'groupKey': null, - 'setAsGroupSummary': null, + 'setAsGroupSummary': false, 'groupAlertBehavior': GroupAlertBehavior.all.index, 'autoCancel': true, - 'ongoing': null, + 'ongoing': false, 'colorAlpha': null, 'colorRed': null, 'colorGreen': null, 'colorBlue': null, - 'onlyAlertOnce': null, + 'onlyAlertOnce': false, 'showWhen': true, 'when': null, 'usesChronometer': false, @@ -1326,7 +1323,7 @@ void main() { 'title': 'notification title', 'body': 'notification body', 'payload': '', - 'platformSpecifics': { + 'platformSpecifics': { 'icon': null, 'channelId': 'channelId', 'channelName': 'channelName', @@ -1340,15 +1337,15 @@ void main() { 'enableVibration': true, 'vibrationPattern': null, 'groupKey': null, - 'setAsGroupSummary': null, + 'setAsGroupSummary': false, 'groupAlertBehavior': GroupAlertBehavior.all.index, 'autoCancel': true, - 'ongoing': null, + 'ongoing': false, 'colorAlpha': null, 'colorRed': null, 'colorGreen': null, 'colorBlue': null, - 'onlyAlertOnce': null, + 'onlyAlertOnce': false, 'showWhen': true, 'when': null, 'usesChronometer': false, @@ -1417,7 +1414,7 @@ void main() { 'title': 'notification title', 'body': 'notification body', 'payload': '', - 'platformSpecifics': { + 'platformSpecifics': { 'icon': null, 'channelId': 'channelId', 'channelName': 'channelName', @@ -1431,15 +1428,15 @@ void main() { 'enableVibration': true, 'vibrationPattern': null, 'groupKey': null, - 'setAsGroupSummary': null, + 'setAsGroupSummary': false, 'groupAlertBehavior': GroupAlertBehavior.all.index, 'autoCancel': true, - 'ongoing': null, + 'ongoing': false, 'colorAlpha': null, 'colorRed': null, 'colorGreen': null, 'colorBlue': null, - 'onlyAlertOnce': null, + 'onlyAlertOnce': false, 'showWhen': true, 'when': null, 'usesChronometer': false, @@ -1463,20 +1460,20 @@ void main() { 'shortcutId': null, 'subText': null, 'style': AndroidNotificationStyle.messaging.index, - 'styleInformation': { + 'styleInformation': { 'htmlFormatContent': false, 'htmlFormatTitle': false, - 'person': { - 'bot': null, - 'important': null, + 'person': { + 'bot': false, + 'important': false, 'key': null, 'name': 'name', 'uri': null, }, 'conversationTitle': null, 'groupConversation': null, - 'messages': >[ - { + 'messages': >[ + { 'text': 'message 1', 'timestamp': messageDateTime.millisecondsSinceEpoch, 'person': null, @@ -1537,7 +1534,7 @@ void main() { 'title': 'notification title', 'body': 'notification body', 'payload': '', - 'platformSpecifics': { + 'platformSpecifics': { 'icon': null, 'channelId': 'channelId', 'channelName': 'channelName', @@ -1551,15 +1548,15 @@ void main() { 'enableVibration': true, 'vibrationPattern': null, 'groupKey': null, - 'setAsGroupSummary': null, + 'setAsGroupSummary': false, 'groupAlertBehavior': GroupAlertBehavior.all.index, 'autoCancel': true, - 'ongoing': null, + 'ongoing': false, 'colorAlpha': null, 'colorRed': null, 'colorGreen': null, 'colorBlue': null, - 'onlyAlertOnce': null, + 'onlyAlertOnce': false, 'showWhen': true, 'when': null, 'usesChronometer': false, @@ -1597,8 +1594,8 @@ void main() { }, 'conversationTitle': 'conversationTitle', 'groupConversation': true, - 'messages': >[ - { + 'messages': >[ + { 'text': 'message 1', 'timestamp': messageDateTime.millisecondsSinceEpoch, 'person': null, @@ -1645,7 +1642,7 @@ void main() { 'payload': '', 'timeZoneName': 'Australia/Sydney', 'scheduledDateTime': _convertDateToISO8601String(scheduledDate), - 'platformSpecifics': { + 'platformSpecifics': { 'allowWhileIdle': true, 'icon': null, 'channelId': 'channelId', @@ -1660,15 +1657,15 @@ void main() { 'enableVibration': true, 'vibrationPattern': null, 'groupKey': null, - 'setAsGroupSummary': null, + 'setAsGroupSummary': false, 'groupAlertBehavior': GroupAlertBehavior.all.index, 'autoCancel': true, - 'ongoing': null, + 'ongoing': false, 'colorAlpha': null, 'colorRed': null, 'colorGreen': null, 'colorBlue': null, - 'onlyAlertOnce': null, + 'onlyAlertOnce': false, 'showWhen': true, 'when': null, 'usesChronometer': false, @@ -1736,7 +1733,7 @@ void main() { 'timeZoneName': 'Australia/Sydney', 'scheduledDateTime': _convertDateToISO8601String(scheduledDate), 'matchDateTimeComponents': DateTimeComponents.time.index, - 'platformSpecifics': { + 'platformSpecifics': { 'allowWhileIdle': true, 'icon': null, 'channelId': 'channelId', @@ -1751,15 +1748,15 @@ void main() { 'enableVibration': true, 'vibrationPattern': null, 'groupKey': null, - 'setAsGroupSummary': null, + 'setAsGroupSummary': false, 'groupAlertBehavior': GroupAlertBehavior.all.index, 'autoCancel': true, - 'ongoing': null, + 'ongoing': false, 'colorAlpha': null, 'colorRed': null, 'colorGreen': null, 'colorBlue': null, - 'onlyAlertOnce': null, + 'onlyAlertOnce': false, 'showWhen': true, 'when': null, 'usesChronometer': false, @@ -1828,7 +1825,7 @@ void main() { 'scheduledDateTime': _convertDateToISO8601String(scheduledDate), 'matchDateTimeComponents': DateTimeComponents.dayOfWeekAndTime.index, - 'platformSpecifics': { + 'platformSpecifics': { 'allowWhileIdle': true, 'icon': null, 'channelId': 'channelId', @@ -1843,15 +1840,15 @@ void main() { 'enableVibration': true, 'vibrationPattern': null, 'groupKey': null, - 'setAsGroupSummary': null, + 'setAsGroupSummary': false, 'groupAlertBehavior': GroupAlertBehavior.all.index, 'autoCancel': true, - 'ongoing': null, + 'ongoing': false, 'colorAlpha': null, 'colorRed': null, 'colorGreen': null, 'colorBlue': null, - 'onlyAlertOnce': null, + 'onlyAlertOnce': false, 'showWhen': true, 'when': null, 'usesChronometer': false, @@ -1889,12 +1886,12 @@ void main() { test('without description', () async { await flutterLocalNotificationsPlugin .resolvePlatformSpecificImplementation< - AndroidFlutterLocalNotificationsPlugin>() + AndroidFlutterLocalNotificationsPlugin>()! .createNotificationChannelGroup( const AndroidNotificationChannelGroup('groupId', 'groupName')); expect(log, [ isMethodCall('createNotificationChannelGroup', - arguments: { + arguments: { 'id': 'groupId', 'name': 'groupName', 'description': null, @@ -1904,7 +1901,7 @@ void main() { test('with description', () async { await flutterLocalNotificationsPlugin .resolvePlatformSpecificImplementation< - AndroidFlutterLocalNotificationsPlugin>() + AndroidFlutterLocalNotificationsPlugin>()! .createNotificationChannelGroup( const AndroidNotificationChannelGroup('groupId', 'groupName', description: 'groupDescription')); @@ -1922,11 +1919,11 @@ void main() { test('createNotificationChannel with default settings', () async { await flutterLocalNotificationsPlugin .resolvePlatformSpecificImplementation< - AndroidFlutterLocalNotificationsPlugin>() + AndroidFlutterLocalNotificationsPlugin>()! .createNotificationChannel(const AndroidNotificationChannel( 'channelId', 'channelName', 'channelDescription')); expect(log, [ - isMethodCall('createNotificationChannel', arguments: { + isMethodCall('createNotificationChannel', arguments: { 'id': 'channelId', 'name': 'channelName', 'description': 'channelDescription', @@ -1942,7 +1939,7 @@ void main() { 'ledColorGreen': null, 'ledColorBlue': null, 'channelAction': - AndroidNotificationChannelAction.createIfNotExists?.index, + AndroidNotificationChannelAction.createIfNotExists.index, }) ]); }); @@ -1950,7 +1947,7 @@ void main() { test('createNotificationChannel with non-default settings', () async { await flutterLocalNotificationsPlugin .resolvePlatformSpecificImplementation< - AndroidFlutterLocalNotificationsPlugin>() + AndroidFlutterLocalNotificationsPlugin>()! .createNotificationChannel(const AndroidNotificationChannel( 'channelId', 'channelName', @@ -1964,7 +1961,7 @@ void main() { ledColor: Color.fromARGB(255, 255, 0, 0), )); expect(log, [ - isMethodCall('createNotificationChannel', arguments: { + isMethodCall('createNotificationChannel', arguments: { 'id': 'channelId', 'name': 'channelName', 'description': 'channelDescription', @@ -1980,7 +1977,7 @@ void main() { 'ledColorGreen': 0, 'ledColorBlue': 0, 'channelAction': - AndroidNotificationChannelAction.createIfNotExists?.index, + AndroidNotificationChannelAction.createIfNotExists.index, }) ]); }); @@ -1988,7 +1985,7 @@ void main() { test('deleteNotificationChannel', () async { await flutterLocalNotificationsPlugin .resolvePlatformSpecificImplementation< - AndroidFlutterLocalNotificationsPlugin>() + AndroidFlutterLocalNotificationsPlugin>()! .deleteNotificationChannel('channelId'); expect(log, [ isMethodCall('deleteNotificationChannel', arguments: 'channelId') @@ -1998,7 +1995,7 @@ void main() { test('getActiveNotifications', () async { await flutterLocalNotificationsPlugin .resolvePlatformSpecificImplementation< - AndroidFlutterLocalNotificationsPlugin>() + AndroidFlutterLocalNotificationsPlugin>()! .getActiveNotifications(); expect(log, [isMethodCall('getActiveNotifications', arguments: null)]); @@ -2007,7 +2004,7 @@ void main() { test('cancel', () async { await flutterLocalNotificationsPlugin.cancel(1); expect(log, [ - isMethodCall('cancel', arguments: { + isMethodCall('cancel', arguments: { 'id': 1, 'tag': null, }) @@ -2040,7 +2037,7 @@ void main() { debugDefaultTargetPlatformOverride = TargetPlatform.android; await flutterLocalNotificationsPlugin .resolvePlatformSpecificImplementation< - AndroidFlutterLocalNotificationsPlugin>() + AndroidFlutterLocalNotificationsPlugin>()! .getActiveNotifications(); expect(log, [isMethodCall('getActiveNotifications', arguments: null)]); @@ -2063,15 +2060,13 @@ void main() { flutterLocalNotificationsPlugin = FlutterLocalNotificationsPlugin.private( FakePlatform(operatingSystem: 'ios')); // ignore: always_specify_types - channel.setMockMethodCallHandler((methodCall) { + channel.setMockMethodCallHandler((methodCall) async { log.add(methodCall); if (methodCall.method == 'pendingNotificationRequests') { - return Future>>.value( - >[]); + return ?>[]; } else if (methodCall.method == 'getNotificationAppLaunchDetails') { - return Future>.value({}); + return null; } - return Future.value(); }); }); @@ -2131,7 +2126,7 @@ void main() { 1, 'notification title', 'notification body', null); expect( log.last, - isMethodCall('show', arguments: { + isMethodCall('show', arguments: { 'id': 1, 'title': 'notification title', 'body': 'notification body', @@ -2169,7 +2164,7 @@ void main() { 'title': 'notification title', 'body': 'notification body', 'payload': '', - 'platformSpecifics': { + 'platformSpecifics': { 'presentAlert': true, 'presentBadge': true, 'presentSound': true, @@ -2232,7 +2227,7 @@ void main() { UILocalNotificationDateInterpretation.absoluteTime.index, 'scheduledDateTime': _convertDateToISO8601String(scheduledDate), 'timeZoneName': 'Australia/Sydney', - 'platformSpecifics': { + 'platformSpecifics': { 'presentAlert': true, 'presentBadge': true, 'presentSound': true, @@ -2296,7 +2291,7 @@ void main() { 'scheduledDateTime': _convertDateToISO8601String(scheduledDate), 'timeZoneName': 'Australia/Sydney', 'matchDateTimeComponents': DateTimeComponents.time.index, - 'platformSpecifics': { + 'platformSpecifics': { 'presentAlert': true, 'presentBadge': true, 'presentSound': true, @@ -2361,7 +2356,7 @@ void main() { 'timeZoneName': 'Australia/Sydney', 'matchDateTimeComponents': DateTimeComponents.dayOfWeekAndTime.index, - 'platformSpecifics': { + 'platformSpecifics': { 'presentAlert': true, 'presentBadge': true, 'presentSound': true, @@ -2382,20 +2377,20 @@ void main() { test('requestPermissions with default settings', () async { await flutterLocalNotificationsPlugin .resolvePlatformSpecificImplementation< - IOSFlutterLocalNotificationsPlugin>() + IOSFlutterLocalNotificationsPlugin>()! .requestPermissions(); expect(log, [ - isMethodCall('requestPermissions', arguments: { - 'sound': null, - 'badge': null, - 'alert': null, + isMethodCall('requestPermissions', arguments: { + 'sound': false, + 'badge': false, + 'alert': false, }) ]); }); test('requestPermissions with all settings requested', () async { await flutterLocalNotificationsPlugin .resolvePlatformSpecificImplementation< - IOSFlutterLocalNotificationsPlugin>() + IOSFlutterLocalNotificationsPlugin>()! .requestPermissions(sound: true, badge: true, alert: true); expect(log, [ isMethodCall('requestPermissions', arguments: { @@ -2439,15 +2434,13 @@ void main() { flutterLocalNotificationsPlugin = FlutterLocalNotificationsPlugin.private( FakePlatform(operatingSystem: 'macos')); // ignore: always_specify_types - channel.setMockMethodCallHandler((methodCall) { + channel.setMockMethodCallHandler((methodCall) async { log.add(methodCall); if (methodCall.method == 'pendingNotificationRequests') { - return Future>>.value( - >[]); + return ?>[]; } else if (methodCall.method == 'getNotificationAppLaunchDetails') { - return Future>.value({}); + return null; } - return Future.value(); }); }); @@ -2507,7 +2500,7 @@ void main() { 1, 'notification title', 'notification body', null); expect( log.last, - isMethodCall('show', arguments: { + isMethodCall('show', arguments: { 'id': 1, 'title': 'notification title', 'body': 'notification body', @@ -2607,7 +2600,7 @@ void main() { 'payload': '', 'scheduledDateTime': _convertDateToISO8601String(scheduledDate), 'timeZoneName': 'Australia/Sydney', - 'platformSpecifics': { + 'platformSpecifics': { 'subtitle': null, 'presentAlert': true, 'presentBadge': true, @@ -2669,7 +2662,7 @@ void main() { 'scheduledDateTime': _convertDateToISO8601String(scheduledDate), 'timeZoneName': 'Australia/Sydney', 'matchDateTimeComponents': DateTimeComponents.time.index, - 'platformSpecifics': { + 'platformSpecifics': { 'subtitle': null, 'presentAlert': true, 'presentBadge': true, @@ -2732,7 +2725,7 @@ void main() { 'timeZoneName': 'Australia/Sydney', 'matchDateTimeComponents': DateTimeComponents.dayOfWeekAndTime.index, - 'platformSpecifics': { + 'platformSpecifics': { 'subtitle': null, 'presentAlert': true, 'presentBadge': true, @@ -2754,10 +2747,10 @@ void main() { test('requestPermissions with default settings', () async { await flutterLocalNotificationsPlugin .resolvePlatformSpecificImplementation< - MacOSFlutterLocalNotificationsPlugin>() + MacOSFlutterLocalNotificationsPlugin>()! .requestPermissions(); expect(log, [ - isMethodCall('requestPermissions', arguments: { + isMethodCall('requestPermissions', arguments: { 'sound': null, 'badge': null, 'alert': null, @@ -2767,7 +2760,7 @@ void main() { test('requestPermissions with all settings requested', () async { await flutterLocalNotificationsPlugin .resolvePlatformSpecificImplementation< - MacOSFlutterLocalNotificationsPlugin>() + MacOSFlutterLocalNotificationsPlugin>()! .requestPermissions(sound: true, badge: true, alert: true); expect(log, [ isMethodCall('requestPermissions', arguments: { diff --git a/flutter_local_notifications_platform_interface/CHANGELOG.md b/flutter_local_notifications_platform_interface/CHANGELOG.md index 0ec565b6c..3881d2a14 100644 --- a/flutter_local_notifications_platform_interface/CHANGELOG.md +++ b/flutter_local_notifications_platform_interface/CHANGELOG.md @@ -1,3 +1,7 @@ +## [3.0.0] + +* Migrated to null safety + ## [2.0.0+1] * Added more API docs diff --git a/flutter_local_notifications_platform_interface/lib/flutter_local_notifications_platform_interface.dart b/flutter_local_notifications_platform_interface/lib/flutter_local_notifications_platform_interface.dart index 4e76483ef..e15b1cbd9 100644 --- a/flutter_local_notifications_platform_interface/lib/flutter_local_notifications_platform_interface.dart +++ b/flutter_local_notifications_platform_interface/lib/flutter_local_notifications_platform_interface.dart @@ -12,7 +12,7 @@ abstract class FlutterLocalNotificationsPlatform extends PlatformInterface { /// Constructs an instance of [FlutterLocalNotificationsPlatform]. FlutterLocalNotificationsPlatform() : super(token: _token); - static FlutterLocalNotificationsPlatform _instance; + static late FlutterLocalNotificationsPlatform _instance; static final Object _token = Object(); @@ -28,14 +28,16 @@ abstract class FlutterLocalNotificationsPlatform extends PlatformInterface { } /// Returns info on if a notification had been used to launch the application. - Future getNotificationAppLaunchDetails() async { + Future + getNotificationAppLaunchDetails() async { throw UnimplementedError( 'getNotificationAppLaunchDetails() has not been implemented'); } /// Show a notification with an optional payload that will be passed back to /// the app when a notification is tapped on. - Future show(int id, String title, String body, {String payload}) async { + Future show(int id, String? title, String? body, + {String? payload}) async { throw UnimplementedError('show() has not been implemented'); } @@ -44,7 +46,7 @@ abstract class FlutterLocalNotificationsPlatform extends PlatformInterface { /// notification will be an hour after the method has been called and then /// every hour after that. Future periodicallyShow( - int id, String title, String body, RepeatInterval repeatInterval) { + int id, String? title, String? body, RepeatInterval repeatInterval) { throw UnimplementedError('periodicallyShow() has not been implemented'); } diff --git a/flutter_local_notifications_platform_interface/lib/src/notification_app_launch_details.dart b/flutter_local_notifications_platform_interface/lib/src/notification_app_launch_details.dart index bcdf482f6..0079e2fa1 100644 --- a/flutter_local_notifications_platform_interface/lib/src/notification_app_launch_details.dart +++ b/flutter_local_notifications_platform_interface/lib/src/notification_app_launch_details.dart @@ -8,5 +8,5 @@ class NotificationAppLaunchDetails { final bool didNotificationLaunchApp; /// The payload of the notification that launched the app - final String payload; + final String? payload; } diff --git a/flutter_local_notifications_platform_interface/lib/src/types.dart b/flutter_local_notifications_platform_interface/lib/src/types.dart index 73cf10219..a4018dc7f 100644 --- a/flutter_local_notifications_platform_interface/lib/src/types.dart +++ b/flutter_local_notifications_platform_interface/lib/src/types.dart @@ -23,11 +23,11 @@ class PendingNotificationRequest { final int id; /// The notification's title. - final String title; + final String? title; /// The notification's body. - final String body; + final String? body; /// The notification's payload. - final String payload; + final String? payload; } diff --git a/flutter_local_notifications_platform_interface/pubspec.yaml b/flutter_local_notifications_platform_interface/pubspec.yaml index dccad6f06..b251ddc5f 100644 --- a/flutter_local_notifications_platform_interface/pubspec.yaml +++ b/flutter_local_notifications_platform_interface/pubspec.yaml @@ -1,18 +1,22 @@ name: flutter_local_notifications_platform_interface description: A common platform interface for the flutter_local_notifications plugin. -version: 2.0.0+1 +version: 3.0.0 homepage: https://github.com/MaikuB/flutter_local_notifications/tree/master/flutter_local_notifications_platform_interface environment: - sdk: ">=2.1.0 <3.0.0" + sdk: '>=2.12.0-0 <3.0.0' flutter: ">=1.10.0" dependencies: flutter: sdk: flutter - plugin_platform_interface: ^1.0.2 + plugin_platform_interface: ^2.0.0 dev_dependencies: flutter_test: sdk: flutter - mockito: ^4.1.1 \ No newline at end of file + # TODO(mbui): Update to stable 5.0.0 release when dependency conflict + # with flutter_driver has been resolved: + # Because mockito >=5.0.0 depends on analyzer ^1.0.0 which depends on crypto ^3.0.0 + # every version of flutter_driver from sdk depends on crypto 2.1.5. + mockito: ^5.0.0-nullsafety.7 diff --git a/flutter_local_notifications_platform_interface/test/flutter_local_notifications_platform_interface_test.dart b/flutter_local_notifications_platform_interface/test/flutter_local_notifications_platform_interface_test.dart index 80a55ea46..8dd31ce63 100644 --- a/flutter_local_notifications_platform_interface/test/flutter_local_notifications_platform_interface_test.dart +++ b/flutter_local_notifications_platform_interface/test/flutter_local_notifications_platform_interface_test.dart @@ -21,7 +21,7 @@ void main() { expect(() { FlutterLocalNotificationsPlatform.instance = ImplementsFlutterLocalNotificationsPlatform(); - }, throwsA(isInstanceOf())); + }, throwsAssertionError); }); test('Can be mocked with `implements`', () { From c8816e3fe4e5ae7798c5e733d54f8b1d96a3e12b Mon Sep 17 00:00:00 2001 From: Michael Bui <25263378+MaikuB@users.noreply.github.com> Date: Mon, 5 Apr 2021 19:59:24 +1000 Subject: [PATCH 12/63] [flutter_local_notifications] update readme around initialisation and testing (#1111) * update import used for UILocalNotificationDateInterpretation * updates to readme around initialisation and testing --- flutter_local_notifications/CHANGELOG.md | 5 +++++ flutter_local_notifications/README.md | 5 ++++- .../lib/src/flutter_local_notifications_plugin.dart | 2 +- flutter_local_notifications/pubspec.yaml | 2 +- 4 files changed, 11 insertions(+), 3 deletions(-) diff --git a/flutter_local_notifications/CHANGELOG.md b/flutter_local_notifications/CHANGELOG.md index 14cdfd4cf..7a14cd2db 100644 --- a/flutter_local_notifications/CHANGELOG.md +++ b/flutter_local_notifications/CHANGELOG.md @@ -1,3 +1,8 @@ +# [5.0.0+1] + +* Add link to explanation of the `onDidReceiveLocalNotification` callback to the initialisation section of the readme +* Updated testing section to clarify behaviour on platforms that aren't supported + # [5.0.0] * **Breaking change** migrated to null safety. Some arguments that were formerly null (e.g. some boolean values) are now non-nullable with a default value that should retain the old behaviour diff --git a/flutter_local_notifications/README.md b/flutter_local_notifications/README.md index bc0866ca0..8393f88d2 100644 --- a/flutter_local_notifications/README.md +++ b/flutter_local_notifications/README.md @@ -302,6 +302,9 @@ The `IOSInitializationSettings` and `MacOSInitializationSettings` provides defau On iOS and macOS, initialisation may show a prompt to requires users to give the application permission to display notifications (note: permissions don't need to be requested on Android). Depending on when this happens, this may not be the ideal user experience for your application. If so, please refer to the next section on how to work around this. +For an explanation of the `onDidReceiveLocalNotification` callback associated with the `IOSInitializationSettings` class, please read [this](https://github.com/MaikuB/flutter_local_notifications/tree/master/flutter_local_notifications#handling-notifications-whilst-the-app-is-in-the-foreground). + + *Note*: from version 4.0 of the plugin, calling `initialize` will not trigger the `onSelectNotification` callback when the application was started by tapping on a notification to trigger. Use the `getNotificationAppLaunchDetails` method that is available in the plugin if you need to handle a notification triggering the launch for an app e.g. change the home route of the app for deep-linking. ### [iOS (all supported versions) and macOS 10.14+] Requesting notification permissions @@ -584,6 +587,6 @@ Check the source code for a sample test suite that has been kindly implemented ( If you decide to use the plugin class directly as part of your tests, the methods will be mostly no-op and methods that return data will return default values. -Part of this is because the plugin detects if you're running on a supported plugin to determine which platform implementation of the plugin should be used. If it's neither Android or iOS, then it defaults to the aforementioned behaviour to reduce friction when writing tests. If this not desired then consider using mocks. +Part of this is because the plugin detects if you're running on a supported plugin to determine which platform implementation of the plugin should be used. If the platform isn't supported, it will default to the aforementioned behaviour to reduce friction when writing tests. If this not desired then consider using mocks. If a platform-specific implementation of the plugin is required for your tests, a [named constructor](https://pub.dev/documentation/flutter_local_notifications/latest/flutter_local_notifications/FlutterLocalNotificationsPlugin/FlutterLocalNotificationsPlugin.private.html) is available that allows you to specify the platform required e.g. a [`FakePlatform`](https://api.flutter.dev/flutter/package-platform_platform/FakePlatform-class.html). diff --git a/flutter_local_notifications/lib/src/flutter_local_notifications_plugin.dart b/flutter_local_notifications/lib/src/flutter_local_notifications_plugin.dart index 7e424dc16..86ea743a7 100644 --- a/flutter_local_notifications/lib/src/flutter_local_notifications_plugin.dart +++ b/flutter_local_notifications/lib/src/flutter_local_notifications_plugin.dart @@ -6,7 +6,7 @@ import 'package:flutter_local_notifications_platform_interface/flutter_local_not import 'package:platform/platform.dart'; import 'package:timezone/timezone.dart'; -import '../flutter_local_notifications.dart'; +import 'platform_specifics/ios/enums.dart'; import 'initialization_settings.dart'; import 'notification_details.dart'; import 'platform_flutter_local_notifications.dart'; diff --git a/flutter_local_notifications/pubspec.yaml b/flutter_local_notifications/pubspec.yaml index 92a95185f..471f3d930 100644 --- a/flutter_local_notifications/pubspec.yaml +++ b/flutter_local_notifications/pubspec.yaml @@ -2,7 +2,7 @@ name: flutter_local_notifications description: A cross platform plugin for displaying and scheduling local notifications for Flutter applications with the ability to customise for each platform. -version: 5.0.0 +version: 5.0.0+1 homepage: https://github.com/MaikuB/flutter_local_notifications/tree/master/flutter_local_notifications dependencies: From c0087bce1f8fedd04de2c8a3cd1d259315242155 Mon Sep 17 00:00:00 2001 From: Michael Bui <25263378+MaikuB@users.noreply.github.com> Date: Mon, 5 Apr 2021 20:13:12 +1000 Subject: [PATCH 13/63] updated timezone dependency (#1112) --- flutter_local_notifications/CHANGELOG.md | 1 + flutter_local_notifications/pubspec.yaml | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/flutter_local_notifications/CHANGELOG.md b/flutter_local_notifications/CHANGELOG.md index 7a14cd2db..010b90af6 100644 --- a/flutter_local_notifications/CHANGELOG.md +++ b/flutter_local_notifications/CHANGELOG.md @@ -2,6 +2,7 @@ * Add link to explanation of the `onDidReceiveLocalNotification` callback to the initialisation section of the readme * Updated testing section to clarify behaviour on platforms that aren't supported +* Updated `timezone` dependency # [5.0.0] diff --git a/flutter_local_notifications/pubspec.yaml b/flutter_local_notifications/pubspec.yaml index 471f3d930..5b13d7af7 100644 --- a/flutter_local_notifications/pubspec.yaml +++ b/flutter_local_notifications/pubspec.yaml @@ -10,7 +10,7 @@ dependencies: sdk: flutter flutter_local_notifications_platform_interface: ^3.0.0 platform: ^3.0.0 - timezone: ^0.7.0-nullsafety.0 + timezone: ^0.7.0 dev_dependencies: flutter_driver: From 7a7ef599a5e027cf8f29144e640072c018aee544 Mon Sep 17 00:00:00 2001 From: Siddhartha joshi Date: Tue, 27 Apr 2021 19:26:47 +0545 Subject: [PATCH 14/63] fix typo in readme (#1138) --- flutter_local_notifications/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flutter_local_notifications/README.md b/flutter_local_notifications/README.md index 8393f88d2..ea90eaef0 100644 --- a/flutter_local_notifications/README.md +++ b/flutter_local_notifications/README.md @@ -152,7 +152,7 @@ When specifying the large icon bitmap or big picture bitmap (associated with the #### Full-screen intent notifications -If your application needs the ability to schedule full-screen intent notifications, add the following attributes to the activity you're opening. For a Flutter application that is typically only ony activity extends from `FlutterActivity`. These attributes ensure the screen turns on and shows when the device is locked. +If your application needs the ability to schedule full-screen intent notifications, add the following attributes to the activity you're opening. For a Flutter application that is typically only one activity extends from `FlutterActivity`. These attributes ensure the screen turns on and shows when the device is locked. ```xml Date: Wed, 28 Apr 2021 00:04:08 +1000 Subject: [PATCH 15/63] [flutter_local_notifications] update example app to use flutter_native_timezone and improve readme (#1143) * update example app to use flutter_native_timezone and update readme * fix grammar issue in full-screen intent section of readme * add changelog entry on rewording in readme * bump plugin version --- flutter_local_notifications/CHANGELOG.md | 6 ++++++ flutter_local_notifications/README.md | 7 +++++-- .../MainActivity.kt | 3 --- .../example/ios/Runner/AppDelegate.swift | 10 ---------- flutter_local_notifications/example/lib/main.dart | 4 ++-- .../macos/Flutter/GeneratedPluginRegistrant.swift | 2 ++ .../example/macos/Runner/MainFlutterWindow.swift | 10 +--------- flutter_local_notifications/example/pubspec.yaml | 1 + flutter_local_notifications/pubspec.yaml | 2 +- 9 files changed, 18 insertions(+), 27 deletions(-) diff --git a/flutter_local_notifications/CHANGELOG.md b/flutter_local_notifications/CHANGELOG.md index 010b90af6..bd08eeb95 100644 --- a/flutter_local_notifications/CHANGELOG.md +++ b/flutter_local_notifications/CHANGELOG.md @@ -1,3 +1,9 @@ +# [5.0.0+2] + +* Updated example app to use the [flutter_native_timezone](https://pub.dev/packages/flutter_native_timezone) plugin to get the timezone +* Updated readme to mention effect of using same notification id +* Fixed wording and typo in full-screen intent notifications section of the readme. Thanks to PR from [Siddhartha Joshi](https://github.com/cimplesid) + # [5.0.0+1] * Add link to explanation of the `onDidReceiveLocalNotification` callback to the initialisation section of the readme diff --git a/flutter_local_notifications/README.md b/flutter_local_notifications/README.md index ea90eaef0..08ff645fb 100644 --- a/flutter_local_notifications/README.md +++ b/flutter_local_notifications/README.md @@ -152,7 +152,8 @@ When specifying the large icon bitmap or big picture bitmap (associated with the #### Full-screen intent notifications -If your application needs the ability to schedule full-screen intent notifications, add the following attributes to the activity you're opening. For a Flutter application that is typically only one activity extends from `FlutterActivity`. These attributes ensure the screen turns on and shows when the device is locked. +If your application needs the ability to schedule full-screen intent notifications, add the following attributes to the activity you're opening. For a Flutter application, there is typically only one activity extends from `FlutterActivity`. These attributes ensure the screen turns on and shows when the device is locked. + ```xml Void in - if ("getTimeZoneName" == call.method) { - result(TimeZone.current.identifier) - } - }) GeneratedPluginRegistrant.register(with: self) return super.application(application, didFinishLaunchingWithOptions: launchOptions) diff --git a/flutter_local_notifications/example/lib/main.dart b/flutter_local_notifications/example/lib/main.dart index ae55ea630..c4a7fc293 100644 --- a/flutter_local_notifications/example/lib/main.dart +++ b/flutter_local_notifications/example/lib/main.dart @@ -8,6 +8,7 @@ import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_local_notifications/flutter_local_notifications.dart'; +import 'package:flutter_native_timezone/flutter_native_timezone.dart'; import 'package:http/http.dart' as http; import 'package:path_provider/path_provider.dart'; import 'package:rxdart/subjects.dart'; @@ -108,8 +109,7 @@ Future main() async { Future _configureLocalTimeZone() async { tz.initializeTimeZones(); - final String? timeZoneName = - await platform.invokeMethod('getTimeZoneName'); + final String? timeZoneName = await FlutterNativeTimezone.getLocalTimezone(); tz.setLocalLocation(tz.getLocation(timeZoneName!)); } diff --git a/flutter_local_notifications/example/macos/Flutter/GeneratedPluginRegistrant.swift b/flutter_local_notifications/example/macos/Flutter/GeneratedPluginRegistrant.swift index 7e7d18d10..3b8574d1d 100644 --- a/flutter_local_notifications/example/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/flutter_local_notifications/example/macos/Flutter/GeneratedPluginRegistrant.swift @@ -6,11 +6,13 @@ import FlutterMacOS import Foundation import flutter_local_notifications +import flutter_native_timezone import path_provider_macos import shared_preferences_macos func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { FlutterLocalNotificationsPlugin.register(with: registry.registrar(forPlugin: "FlutterLocalNotificationsPlugin")) + FlutterNativeTimezonePlugin.register(with: registry.registrar(forPlugin: "FlutterNativeTimezonePlugin")) PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin")) } diff --git a/flutter_local_notifications/example/macos/Runner/MainFlutterWindow.swift b/flutter_local_notifications/example/macos/Runner/MainFlutterWindow.swift index b5c45b2b3..8da9d3141 100644 --- a/flutter_local_notifications/example/macos/Runner/MainFlutterWindow.swift +++ b/flutter_local_notifications/example/macos/Runner/MainFlutterWindow.swift @@ -8,16 +8,8 @@ class MainFlutterWindow: NSWindow { self.contentViewController = flutterViewController self.setFrame(windowFrame, display: true) - let channel = FlutterMethodChannel(name: "dexterx.dev/flutter_local_notifications_example", - binaryMessenger: flutterViewController.engine.binaryMessenger) - channel.setMethodCallHandler({ - (call: FlutterMethodCall, result: @escaping FlutterResult) -> Void in - if ("getTimeZoneName" == call.method) { - result(TimeZone.current.identifier) - } - }) RegisterGeneratedPlugins(registry: flutterViewController) super.awakeFromNib() } -} \ No newline at end of file +} diff --git a/flutter_local_notifications/example/pubspec.yaml b/flutter_local_notifications/example/pubspec.yaml index 4dc0dbffb..0d8a59ab4 100644 --- a/flutter_local_notifications/example/pubspec.yaml +++ b/flutter_local_notifications/example/pubspec.yaml @@ -9,6 +9,7 @@ dependencies: path: ../ cupertino_icons: ^1.0.2 device_info: ^2.0.0 + flutter_native_timezone: ^1.0.10 http: ^0.13.0 path_provider: ^2.0.0 rxdart: ^0.26.0-nullsafety.1 diff --git a/flutter_local_notifications/pubspec.yaml b/flutter_local_notifications/pubspec.yaml index 5b13d7af7..545c7cbf8 100644 --- a/flutter_local_notifications/pubspec.yaml +++ b/flutter_local_notifications/pubspec.yaml @@ -2,7 +2,7 @@ name: flutter_local_notifications description: A cross platform plugin for displaying and scheduling local notifications for Flutter applications with the ability to customise for each platform. -version: 5.0.0+1 +version: 5.0.0+2 homepage: https://github.com/MaikuB/flutter_local_notifications/tree/master/flutter_local_notifications dependencies: From a12ef074c820d3a02fa2d0e375272411ac5edda7 Mon Sep 17 00:00:00 2001 From: Michael Bui <25263378+MaikuB@users.noreply.github.com> Date: Wed, 28 Apr 2021 08:42:35 +1000 Subject: [PATCH 16/63] update readme on getting the local timezone and add link to example app (#1144) --- flutter_local_notifications/CHANGELOG.md | 5 +++++ flutter_local_notifications/README.md | 7 +++++-- flutter_local_notifications/pubspec.yaml | 2 +- 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/flutter_local_notifications/CHANGELOG.md b/flutter_local_notifications/CHANGELOG.md index bd08eeb95..38872e4c0 100644 --- a/flutter_local_notifications/CHANGELOG.md +++ b/flutter_local_notifications/CHANGELOG.md @@ -1,3 +1,8 @@ +# [5.0.0+3] + +* Updated readme on how to get the local timezone +* Added link to location of example app to the readme + # [5.0.0+2] * Updated example app to use the [flutter_native_timezone](https://pub.dev/packages/flutter_native_timezone) plugin to get the timezone diff --git a/flutter_local_notifications/README.md b/flutter_local_notifications/README.md index 08ff645fb..068ea7a11 100644 --- a/flutter_local_notifications/README.md +++ b/flutter_local_notifications/README.md @@ -250,13 +250,16 @@ Future onDidReceiveLocalNotification( ## ❓ Usage + Before going on to copy-paste the code snippets in this section, double-check you have configured your application correctly. If you encounter any issues please refer to the API docs and the sample code in the `example` directory before opening a request on Github. ### Example app -The `example` directory has a sample application that demonstrates the features of this plugin. + +The [`example`](https://github.com/MaikuB/flutter_local_notifications/tree/master/flutter_local_notifications/example) directory has a sample application that demonstrates the features of this plugin. ### API reference + Checkout the lovely [API documentation](https://pub.dev/documentation/flutter_local_notifications/latest/flutter_local_notifications/flutter_local_notifications-library.html) generated by pub. @@ -413,7 +416,7 @@ Once the time zone database has been initialised, developers may optionally want tz.setLocalLocation(tz.getLocation(timeZoneName)); ``` -The `timezone` package doesn't provide a way to obtain the current time zone on the device so developers will need to use platform channels (which is what the example app does) or use other packages that may be able to provide the information (e.g. [`flutter_native_timezone`](https://pub.dev/packages/flutter_native_timezone)). +The `timezone` package doesn't provide a way to obtain the current time zone on the device so developers will need to use [platform channels](https://flutter.dev/docs/development/platform-integration/platform-channels) or use other packages that may be able to provide the information. The example app uses the [`flutter_native_timezone`](https://pub.dev/packages/flutter_native_timezone) plugin. Assuming the local location has been set, the `zonedScheduled` method can then be called in a manner similar to the following code diff --git a/flutter_local_notifications/pubspec.yaml b/flutter_local_notifications/pubspec.yaml index 545c7cbf8..cb480881e 100644 --- a/flutter_local_notifications/pubspec.yaml +++ b/flutter_local_notifications/pubspec.yaml @@ -2,7 +2,7 @@ name: flutter_local_notifications description: A cross platform plugin for displaying and scheduling local notifications for Flutter applications with the ability to customise for each platform. -version: 5.0.0+2 +version: 5.0.0+3 homepage: https://github.com/MaikuB/flutter_local_notifications/tree/master/flutter_local_notifications dependencies: From 8068a27760833beecd72b34fd9c4835a2d2be1d6 Mon Sep 17 00:00:00 2001 From: Michael Bui <25263378+MaikuB@users.noreply.github.com> Date: Tue, 4 May 2021 18:41:24 +1000 Subject: [PATCH 17/63] re-add attributes to example app for full-screen intent notifications on Android (#1154) --- flutter_local_notifications/CHANGELOG.md | 4 ++++ .../example/android/app/src/main/AndroidManifest.xml | 4 +++- flutter_local_notifications/pubspec.yaml | 2 +- 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/flutter_local_notifications/CHANGELOG.md b/flutter_local_notifications/CHANGELOG.md index 38872e4c0..7e2a13a43 100644 --- a/flutter_local_notifications/CHANGELOG.md +++ b/flutter_local_notifications/CHANGELOG.md @@ -1,3 +1,7 @@ +# [5.0.0+4] + +* Fixed example app to re-add attributes to the Android app's `AndroidManifest.xml` to allow full-screen intent notifications to work + # [5.0.0+3] * Updated readme on how to get the local timezone diff --git a/flutter_local_notifications/example/android/app/src/main/AndroidManifest.xml b/flutter_local_notifications/example/android/app/src/main/AndroidManifest.xml index bf3ee1689..70b6be1b8 100644 --- a/flutter_local_notifications/example/android/app/src/main/AndroidManifest.xml +++ b/flutter_local_notifications/example/android/app/src/main/AndroidManifest.xml @@ -9,7 +9,9 @@ android:theme="@style/LaunchTheme" android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode" android:hardwareAccelerated="true" - android:windowSoftInputMode="adjustResize"> + android:windowSoftInputMode="adjustResize" + android:showWhenLocked="true" + android:turnScreenOn="true"> + + diff --git a/flutter_local_notifications/example/lib/main.dart b/flutter_local_notifications/example/lib/main.dart index 38eac4711..42c8b67fb 100644 --- a/flutter_local_notifications/example/lib/main.dart +++ b/flutter_local_notifications/example/lib/main.dart @@ -579,6 +579,18 @@ class _HomePageState extends State { await _getActiveNotifications(); }, ), + PaddedElevatedButton( + buttonText: 'Start foreground service', + onPressed: () async { + await _startForegroundService(); + }, + ), + PaddedElevatedButton( + buttonText: 'Stop foreground service', + onPressed: () async { + await _stopForegroundService(); + }, + ), ], if (!kIsWeb && (Platform.isIOS || Platform.isMacOS)) ...[ @@ -1605,6 +1617,28 @@ class _HomePageState extends State { ); } + Future _startForegroundService() async { + const AndroidNotificationDetails androidPlatformChannelSpecifics = + AndroidNotificationDetails( + 'your channel id', 'your channel name', 'your channel description', + importance: Importance.max, + priority: Priority.high, + ticker: 'ticker'); + await flutterLocalNotificationsPlugin + .resolvePlatformSpecificImplementation< + AndroidFlutterLocalNotificationsPlugin>() + ?.startForegroundService(1, 'plain title', 'plain body', + notificationDetails: androidPlatformChannelSpecifics, + payload: 'item x'); + } + + Future _stopForegroundService() async { + await flutterLocalNotificationsPlugin + .resolvePlatformSpecificImplementation< + AndroidFlutterLocalNotificationsPlugin>() + ?.stopForegroundService(); + } + Future _createNotificationChannel() async { const AndroidNotificationChannel androidNotificationChannel = AndroidNotificationChannel( diff --git a/flutter_local_notifications/lib/src/platform_flutter_local_notifications.dart b/flutter_local_notifications/lib/src/platform_flutter_local_notifications.dart index a15bc13a5..db16ddea7 100644 --- a/flutter_local_notifications/lib/src/platform_flutter_local_notifications.dart +++ b/flutter_local_notifications/lib/src/platform_flutter_local_notifications.dart @@ -202,6 +202,95 @@ class AndroidFlutterLocalNotificationsPlugin }); } + /// Starts an Android foreground service with the given notification. + /// + /// The `id` must not be 0, since Android itself does not allow starting + /// a foreground service with a notification id of 0. + /// + /// Since not all users of this plugin need such a service, it was not + /// added to this plugins Android manifest. Thie means you have to add + /// it if you want to use the foreground service functionality. Add the + /// foreground service permission to your apps `AndroidManifest.xml` like + /// described in the [official Android documentation](https://developer.android.com/guide/components/foreground-services#request-foreground-service-permissions): + /// ```xml + /// + /// ``` + /// Furthermore, add the `service` itself to your `AndroidManifest.xml` + /// (inside the `` tag): + /// ```xml + /// + /// + /// ``` + /// While the `android:name` must exactly match this value, you can configure + /// the other parameters as you like, although it is recommended to copy the + /// value for `android:exported`. Suitable values for + /// `foregroundServiceType` can be found [here](https://developer.android.com/reference/android/app/Service#startForeground(int,%20android.app.Notification,%20int)). + /// + /// The notification of the foreground service can be updated by + /// simply calling this method multiple times. + /// + /// Information on selecting an appropriate `startType` for your app's usecase + /// should be taken from the official Android documentation, check [`Service.onStartCommand`](https://developer.android.com/reference/android/app/Service#onStartCommand(android.content.Intent,%20int,%20int)). + /// The there mentioned constants can be found in [AndroidServiceStartType]. + /// + /// The notification for the foreground service will not be dismissable + /// and automatically removed when using [stopForegroundService]. + /// + /// `foregroundServiceType` is a set of foreground service types to apply to + /// the service start. It might be `null` or omitted, but it must never + /// be empty! + /// If `foregroundServiceType` is set, [`Service.startForeground(int id, Notification notification, int foregroundServiceType)`](https://developer.android.com/reference/android/app/Service#startForeground(int,%20android.app.Notification,%20int)) + /// will be invoked , else [`Service.startForeground(int id, Notification notification)`](https://developer.android.com/reference/android/app/Service#startForeground(int,%20android.app.Notification)) is used. + /// On devices older than [`Build.VERSION_CODES.Q`](https://developer.android.com/reference/android/os/Build.VERSION_CODES#Q), `foregroundServiceType` will be ignored. + /// Note that `foregroundServiceType` (the parameter in this method) + /// must be a subset of the `android:foregroundServiceType` + /// defined in your `AndroidManifest.xml` (the one from the section above)! + Future startForegroundService(int id, String? title, String? body, + {AndroidNotificationDetails? notificationDetails, + String? payload, + AndroidServiceStartType startType = AndroidServiceStartType.startSticky, + Set? foregroundServiceTypes}) { + validateId(id); + if (id == 0) { + throw ArgumentError.value(id, 'id', + 'The id of a notification used for an Android foreground service must not be 0!'); // ignore: lines_longer_than_80_chars + } + if (foregroundServiceTypes?.isEmpty ?? false) { + throw ArgumentError.value(foregroundServiceTypes, 'foregroundServiceType', + 'foregroundServiceType may be null but it must never be empty!'); + } + return _channel.invokeMethod('startForegroundService', { + 'notificationData': { + 'id': id, + 'title': title, + 'body': body, + 'payload': payload ?? '', + 'platformSpecifics': notificationDetails?.toMap(), + }, + 'startType': startType.value, + 'foregroundServiceTypes': foregroundServiceTypes + ?.map((AndroidServiceForegroundType type) => type.value) + .toList() + }); + } + + /// Stops a foreground service. + /// + /// If the foreground service was not started, this function + /// does nothing. + /// + /// It is sufficient to call this method once to stop the + /// foreground service, even if [startForegroundService] was called + /// multiple times. + Future stopForegroundService() => + _channel.invokeMethod('stopForegroundService'); + @override Future show( int id, diff --git a/flutter_local_notifications/lib/src/platform_specifics/android/enums.dart b/flutter_local_notifications/lib/src/platform_specifics/android/enums.dart index 558f1d3be..264a8064a 100644 --- a/flutter_local_notifications/lib/src/platform_specifics/android/enums.dart +++ b/flutter_local_notifications/lib/src/platform_specifics/android/enums.dart @@ -1,3 +1,5 @@ +import 'package:flutter/cupertino.dart'; + /// Specifies the source for a bitmap used by Android notifications. enum AndroidBitmapSource { /// A drawable. @@ -65,9 +67,99 @@ enum AndroidNotificationChannelAction { update } +/// The available foreground types for an Android service. +@immutable +class AndroidServiceForegroundType { + /// Constructs an instance of [AndroidServiceForegroundType]. + const AndroidServiceForegroundType(this.value); + + /// Corresponds to [`ServiceInfo.FOREGROUND_SERVICE_TYPE_MANIFEST`](https://developer.android.com/reference/android/content/pm/ServiceInfo#FOREGROUND_SERVICE_TYPE_MANIFEST). + static const AndroidServiceForegroundType foregroundServiceTypeManifest = + AndroidServiceForegroundType(-1); + + /// Corresponds to [`ServiceInfo.FOREGROUND_SERVICE_TYPE_NONE`](https://developer.android.com/reference/android/content/pm/ServiceInfo#FOREGROUND_SERVICE_TYPE_NONE). + static const AndroidServiceForegroundType foregroundServiceTypeNone = + AndroidServiceForegroundType(0); + + /// Corresponds to [`ServiceInfo.FOREGROUND_SERVICE_TYPE_DATA_SYNC`](https://developer.android.com/reference/android/content/pm/ServiceInfo#FOREGROUND_SERVICE_TYPE_DATA_SYNC). + static const AndroidServiceForegroundType foregroundServiceTypeDataSync = + AndroidServiceForegroundType(1); + + /// Corresponds to [`ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK`](https://developer.android.com/reference/android/content/pm/ServiceInfo#FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK). + static const AndroidServiceForegroundType foregroundServiceTypeMediaPlayback = + AndroidServiceForegroundType(2); + + /// Corresponds to [`ServiceInfo.FOREGROUND_SERVICE_TYPE_PHONE_CALL`](https://developer.android.com/reference/android/content/pm/ServiceInfo#FOREGROUND_SERVICE_TYPE_PHONE_CALL). + static const AndroidServiceForegroundType foregroundServiceTypePhoneCall = + AndroidServiceForegroundType(4); + + /// Corresponds to [`ServiceInfo.FOREGROUND_SERVICE_TYPE_LOCATION`](https://developer.android.com/reference/android/content/pm/ServiceInfo#FOREGROUND_SERVICE_TYPE_LOCATION). + static const AndroidServiceForegroundType foregroundServiceTypeLocation = + AndroidServiceForegroundType(8); + + /// Corresponds to [`ServiceInfo.FOREGROUND_SERVICE_TYPE_CONNECTED_DEVICE`](https://developer.android.com/reference/android/content/pm/ServiceInfo#FOREGROUND_SERVICE_TYPE_CONNECTED_DEVICE). + static const AndroidServiceForegroundType + foregroundServiceTypeConnectedDevice = AndroidServiceForegroundType(16); + + /// Corresponds to [`ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PROJECTION`](https://developer.android.com/reference/android/content/pm/ServiceInfo#FOREGROUND_SERVICE_TYPE_MEDIA_PROJECTION). + static const AndroidServiceForegroundType + foregroundServiceTypeMediaProjection = AndroidServiceForegroundType(32); + + /// Corresponds to [`ServiceInfo.FOREGROUND_SERVICE_TYPE_CAMERA`](https://developer.android.com/reference/android/content/pm/ServiceInfo#FOREGROUND_SERVICE_TYPE_CAMERA). + static const AndroidServiceForegroundType foregroundServiceTypeCamera = + AndroidServiceForegroundType(64); + + /// Corresponds to [`ServiceInfo.FOREGROUND_SERVICE_TYPE_MICROPHONE`](https://developer.android.com/reference/android/content/pm/ServiceInfo#FOREGROUND_SERVICE_TYPE_MICROPHONE). + static const AndroidServiceForegroundType foregroundServiceTypeMicrophone = + AndroidServiceForegroundType(128); + + /// The integer representation. + final int value; + + @override + int get hashCode => value; + + @override + bool operator ==(Object other) => + other is AndroidServiceForegroundType && other.value == value; +} + +/// The available start types for an Android service. +@immutable +class AndroidServiceStartType { + /// Constructs an instance of [AndroidServiceStartType]. + const AndroidServiceStartType(this.value); + + /// Corresponds to [`Service.START_STICKY_COMPATIBILITY`](https://developer.android.com/reference/android/app/Service#START_STICKY_COMPATIBILITY). + static const AndroidServiceStartType startStickyCompatibility = + AndroidServiceStartType(0); + + /// Corresponds to [`Service.START_STICKY`](https://developer.android.com/reference/android/app/Service#START_STICKY). + static const AndroidServiceStartType startSticky = AndroidServiceStartType(1); + + /// Corresponds to [`Service.START_NOT_STICKY`](https://developer.android.com/reference/android/app/Service#START_NOT_STICKY). + static const AndroidServiceStartType startNotSticky = + AndroidServiceStartType(2); + + /// Corresponds to [`Service.START_REDELIVER_INTENT`](https://developer.android.com/reference/android/app/Service#START_REDELIVER_INTENT). + static const AndroidServiceStartType startRedeliverIntent = + AndroidServiceStartType(3); + + /// The integer representation. + final int value; + + @override + int get hashCode => value; + + @override + bool operator ==(Object other) => + other is AndroidServiceStartType && other.value == value; +} + /// The available importance levels for Android notifications. /// /// Required for Android 8.0 or newer. +@immutable class Importance { /// Constructs an instance of [Importance]. const Importance(this.value); @@ -98,10 +190,21 @@ class Importance { [unspecified, none, min, low, defaultImportance, high, max]; /// The integer representation. + // TODO(maikub): This should not be nullable, + // but changing is a breaking change. final int? value; + + @override + // TODO(maikub): value should not be nullable in the first place. + // once that's fixed, remove the ?? here! + int get hashCode => value ?? 0; + + @override + bool operator ==(Object other) => other is Importance && other.value == value; } /// Priority for notifications on Android 7.1 and lower. +@immutable class Priority { /// Constructs an instance of [Priority]. const Priority(this.value); @@ -127,6 +230,11 @@ class Priority { /// The integer representation. final int value; + + @override + int get hashCode => value; + @override + bool operator ==(Object other) => other is Priority && other.value == value; } /// The available alert behaviours for grouped notifications. diff --git a/flutter_local_notifications/pubspec.yaml b/flutter_local_notifications/pubspec.yaml index 6b568a95f..7115ac774 100644 --- a/flutter_local_notifications/pubspec.yaml +++ b/flutter_local_notifications/pubspec.yaml @@ -2,7 +2,7 @@ name: flutter_local_notifications description: A cross platform plugin for displaying and scheduling local notifications for Flutter applications with the ability to customise for each platform. -version: 8.0.0 +version: 8.1.0 homepage: https://github.com/MaikuB/flutter_local_notifications/tree/master/flutter_local_notifications dependencies: diff --git a/flutter_local_notifications/test/platform_flutter_local_notifications_test.dart b/flutter_local_notifications/test/platform_flutter_local_notifications_test.dart index 3d17d8250..6e2e0fc11 100644 --- a/flutter_local_notifications/test/platform_flutter_local_notifications_test.dart +++ b/flutter_local_notifications/test/platform_flutter_local_notifications_test.dart @@ -2049,6 +2049,44 @@ void main() { isMethodCall('getNotificationAppLaunchDetails', arguments: null) ]); }); + + test('startForegroundService', () async { + const AndroidInitializationSettings androidInitializationSettings = + AndroidInitializationSettings('app_icon'); + const InitializationSettings initializationSettings = + InitializationSettings(android: androidInitializationSettings); + await flutterLocalNotificationsPlugin.initialize(initializationSettings); + await flutterLocalNotificationsPlugin + .resolvePlatformSpecificImplementation< + AndroidFlutterLocalNotificationsPlugin>()! + .startForegroundService(1, 'notification title', 'notification body'); + expect( + log.last, + isMethodCall('startForegroundService', arguments: { + 'notificationData': { + 'id': 1, + 'title': 'notification title', + 'body': 'notification body', + 'payload': '', + 'platformSpecifics': null, + }, + 'startType': AndroidServiceStartType.startSticky.value, + 'foregroundServiceTypes': null + })); + }); + + test('stopForegroundService', () async { + await flutterLocalNotificationsPlugin + .resolvePlatformSpecificImplementation< + AndroidFlutterLocalNotificationsPlugin>()! + .stopForegroundService(); + expect( + log.last, + isMethodCall( + 'stopForegroundService', + arguments: null, + )); + }); }); group('iOS', () { From c1ec04e266f4ff595151a70ac668af97b7368587 Mon Sep 17 00:00:00 2001 From: Yaroslav Pronin Date: Fri, 6 Aug 2021 13:20:30 +0300 Subject: [PATCH 32/63] [Linux] Using TMPDIR env as a primary temporary path (#1262) TMPDIR is a standard variable on UNIX/Linux systems, and is often used in containers such as Flatpak to redirect to a temporary folder inside a sandbox. This allows not to make hard bindings to the /tmp directory --- .../lib/src/platform_info.dart | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/flutter_local_notifications_linux/lib/src/platform_info.dart b/flutter_local_notifications_linux/lib/src/platform_info.dart index 9815d660e..1939cd86d 100644 --- a/flutter_local_notifications_linux/lib/src/platform_info.dart +++ b/flutter_local_notifications_linux/lib/src/platform_info.dart @@ -27,8 +27,15 @@ class LinuxPlatformInfo { final int pid = _posix.getpid(); final int userId = _posix.getuid(); final int sessionId = _posix.getsid(pid); + final Map env = Platform.environment; + final String? tmpdir = env['TMPDIR']; runtimeDir = Directory( - path.join('/tmp', processName, '$userId', '$sessionId'), + path.join( + tmpdir == null || tmpdir.isEmpty ? '/tmp' : tmpdir, + processName, + '$userId', + '$sessionId', + ), ); } else { runtimeDir = Directory(path.join(xdg.runtimeDir!.path, processName)); From 003256827e45e5b78b456cb5ad37c1cf2b6cf3ba Mon Sep 17 00:00:00 2001 From: Yaroslav Pronin Date: Fri, 6 Aug 2021 15:54:39 +0300 Subject: [PATCH 33/63] Update documentation (#1266) --- flutter_local_notifications_linux/lib/src/platform_info.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flutter_local_notifications_linux/lib/src/platform_info.dart b/flutter_local_notifications_linux/lib/src/platform_info.dart index 1939cd86d..08d6ecfb8 100644 --- a/flutter_local_notifications_linux/lib/src/platform_info.dart +++ b/flutter_local_notifications_linux/lib/src/platform_info.dart @@ -75,6 +75,6 @@ class LinuxPlatformInfoData { /// other file objects should be placed /// (Corresponds to `$XDG_RUNTIME_DIR` environment variable). /// Please see XDG Base Directory Specification https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html - /// If `$XDG_RUNTIME_DIR` is not set, the following directory structure is used: `/tmp/APP_NAME/USER_ID/SESSION_ID` + /// If `$XDG_RUNTIME_DIR` is not set, the following directory structure is used: `/[$TMPDIR|tmp]/APP_NAME/USER_ID/SESSION_ID` final String? runtimePath; } From 0a942861d0ebfe319e93d2eadeb51917e81042bf Mon Sep 17 00:00:00 2001 From: Michael Bui <25263378+MaikuB@users.noreply.github.com> Date: Fri, 6 Aug 2021 23:28:26 +1000 Subject: [PATCH 34/63] [flutter_local_notifications_linux] bump linux plugin for release (#1267) * bump linux plugin for release * add info on fixing multiple initialisation error --- flutter_local_notifications_linux/CHANGELOG.md | 6 ++++++ flutter_local_notifications_linux/pubspec.yaml | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/flutter_local_notifications_linux/CHANGELOG.md b/flutter_local_notifications_linux/CHANGELOG.md index 66f370f7f..1c7db0673 100644 --- a/flutter_local_notifications_linux/CHANGELOG.md +++ b/flutter_local_notifications_linux/CHANGELOG.md @@ -1,3 +1,9 @@ +## [0.2.0] + +* Fixed issue when an app using the plugin is built on the web by using conditional imports +* Changed the logic where notification IDs are saved so that `$XDG_RUNTIME_DIR` environment variable is not set but `$TMPDIR` is set, then they are saved to a file within the `/$TMPDIR/APP_NAME/USER_ID/SESSION_ID` directory. If `$TMPDIR` is not set then, it would save to `/tmp/APP_NAME/USER_ID/SESSION_ID` +* Fixed an issue where errors would occur if the plugin was initialised multiple times + ## [0.1.0+1] * Point to types within platform interface diff --git a/flutter_local_notifications_linux/pubspec.yaml b/flutter_local_notifications_linux/pubspec.yaml index f718778b5..603ecd32c 100644 --- a/flutter_local_notifications_linux/pubspec.yaml +++ b/flutter_local_notifications_linux/pubspec.yaml @@ -1,6 +1,6 @@ name: flutter_local_notifications_linux description: Linux implementation of the flutter_local_notifications plugin -version: 0.1.0+1 +version: 0.2.0 homepage: https://github.com/MaikuB/flutter_local_notifications/tree/master/flutter_local_notifications dependencies: From b7fa709a61207e9cd86b8d5be911c3b49b655776 Mon Sep 17 00:00:00 2001 From: Michael Bui <25263378+MaikuB@users.noreply.github.com> Date: Sat, 7 Aug 2021 12:42:05 +1000 Subject: [PATCH 35/63] [flutter_local_notifications] address unchecked operation and allow createNotificationChannel to be called even when channel exists already (#1272) * address unchecked operation warning * fix issue with trying to update a notification * update changelog to add link to issue on unchecked operation --- flutter_local_notifications/CHANGELOG.md | 5 ++ .../FlutterLocalNotificationsPlugin.java | 64 +++++++++++-------- flutter_local_notifications/pubspec.yaml | 2 +- 3 files changed, 43 insertions(+), 28 deletions(-) diff --git a/flutter_local_notifications/CHANGELOG.md b/flutter_local_notifications/CHANGELOG.md index 5ed32fa52..93b719c79 100644 --- a/flutter_local_notifications/CHANGELOG.md +++ b/flutter_local_notifications/CHANGELOG.md @@ -1,3 +1,8 @@ +# [8.1.1] + +* [Android] fixed issue [1263](https://github.com/MaikuB/flutter_local_notifications/issues/1263) around an unchecked/unsafe operation warning +* [Android] fixed issue [1246](https://github.com/MaikuB/flutter_local_notifications/issues/1246) where calling `createNotificationChannel()` wasn't update a notification channel's name/description + # [8.1.0] * [Android] added the `startForegroundService()` and `stopForegroundService()` methods to the `AndroidFlutterLocalNotificationsPlugin` class. This can be used to start and stop a foreground service that shows a foreground service respectively. Refer to the API docs for more details on how to use this. The example app has been updated to demonstrate their usage. Thanks to the PR from [EPNW](https://github.com/EPNW) diff --git a/flutter_local_notifications/android/src/main/java/com/dexterous/flutterlocalnotifications/FlutterLocalNotificationsPlugin.java b/flutter_local_notifications/android/src/main/java/com/dexterous/flutterlocalnotifications/FlutterLocalNotificationsPlugin.java index 6a20eb2fa..13862a5a1 100644 --- a/flutter_local_notifications/android/src/main/java/com/dexterous/flutterlocalnotifications/FlutterLocalNotificationsPlugin.java +++ b/flutter_local_notifications/android/src/main/java/com/dexterous/flutterlocalnotifications/FlutterLocalNotificationsPlugin.java @@ -17,7 +17,6 @@ import android.media.AudioAttributes; import android.media.RingtoneManager; import android.net.Uri; -import android.os.Build; import android.os.Build.VERSION; import android.os.Build.VERSION_CODES; import android.service.notification.StatusBarNotification; @@ -167,7 +166,10 @@ private static void initAndroidThreeTen(Context context) { } protected static Notification createNotification(Context context, NotificationDetails notificationDetails) { - setupNotificationChannel(context, NotificationChannelDetails.fromNotificationDetails(notificationDetails)); + NotificationChannelDetails notificationChannelDetails = NotificationChannelDetails.fromNotificationDetails(notificationDetails); + if(canCreateNotificationChannel(context, notificationChannelDetails)) { + setupNotificationChannel(context, notificationChannelDetails); + } Intent intent = getLaunchIntent(context); intent.setAction(SELECT_NOTIFICATION); intent.putExtra(PAYLOAD, notificationDetails.payload); @@ -231,6 +233,17 @@ protected static Notification createNotification(Context context, NotificationDe return notification; } + private static Boolean canCreateNotificationChannel(Context context, NotificationChannelDetails notificationChannelDetails) { + if (VERSION.SDK_INT >= VERSION_CODES.O) { + NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE); + NotificationChannel notificationChannel = notificationManager.getNotificationChannel(notificationChannelDetails.id); + // only create/update the channel when needed/specified. Allow this happen to when channelAction may be null to support cases where notifications had been + // created on older versions of the plugin where channel management options weren't available back then + return ((notificationChannel == null && (notificationChannelDetails.channelAction == null || notificationChannelDetails.channelAction == NotificationChannelAction.CreateIfNotExists)) || (notificationChannel != null && notificationChannelDetails.channelAction == NotificationChannelAction.Update)); + } + return false; + } + private static void setSmallIcon(Context context, NotificationDetails notificationDetails, NotificationCompat.Builder builder) { if (!StringUtils.isNullOrEmpty(notificationDetails.icon)) { builder.setSmallIcon(getDrawableResourceId(context, notificationDetails.icon)); @@ -459,6 +472,7 @@ private static int getDrawableResourceId(Context context, String name) { return context.getResources().getIdentifier(name, DRAWABLE, context.getPackageName()); } + @SuppressWarnings("unchecked") private static Bitmap getBitmapFromSource(Context context, Object data, BitmapSource bitmapSource) { Bitmap bitmap = null; if (bitmapSource == BitmapSource.DrawableResource) { @@ -748,32 +762,28 @@ private static void setBigTextStyle(NotificationDetails notificationDetails, Not private static void setupNotificationChannel(Context context, NotificationChannelDetails notificationChannelDetails) { if (VERSION.SDK_INT >= VERSION_CODES.O) { NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE); - NotificationChannel notificationChannel = notificationManager.getNotificationChannel(notificationChannelDetails.id); - // only create/update the channel when needed/specified. Allow this happen to when channelAction may be null to support cases where notifications had been - // created on older versions of the plugin where channel management options weren't available back then - if ((notificationChannel == null && (notificationChannelDetails.channelAction == null || notificationChannelDetails.channelAction == NotificationChannelAction.CreateIfNotExists)) || (notificationChannel != null && notificationChannelDetails.channelAction == NotificationChannelAction.Update)) { - notificationChannel = new NotificationChannel(notificationChannelDetails.id, notificationChannelDetails.name, notificationChannelDetails.importance); - notificationChannel.setDescription(notificationChannelDetails.description); - notificationChannel.setGroup(notificationChannelDetails.groupId); - if (notificationChannelDetails.playSound) { - AudioAttributes audioAttributes = new AudioAttributes.Builder().setUsage(AudioAttributes.USAGE_NOTIFICATION).build(); - Uri uri = retrieveSoundResourceUri(context, notificationChannelDetails.sound, notificationChannelDetails.soundSource); - notificationChannel.setSound(uri, audioAttributes); - } else { - notificationChannel.setSound(null, null); - } - notificationChannel.enableVibration(BooleanUtils.getValue(notificationChannelDetails.enableVibration)); - if (notificationChannelDetails.vibrationPattern != null && notificationChannelDetails.vibrationPattern.length > 0) { - notificationChannel.setVibrationPattern(notificationChannelDetails.vibrationPattern); - } - boolean enableLights = BooleanUtils.getValue(notificationChannelDetails.enableLights); - notificationChannel.enableLights(enableLights); - if (enableLights && notificationChannelDetails.ledColor != null) { - notificationChannel.setLightColor(notificationChannelDetails.ledColor); - } - notificationChannel.setShowBadge(BooleanUtils.getValue(notificationChannelDetails.showBadge)); - notificationManager.createNotificationChannel(notificationChannel); + NotificationChannel notificationChannel = new NotificationChannel(notificationChannelDetails.id, notificationChannelDetails.name, notificationChannelDetails.importance); + notificationChannel.setDescription(notificationChannelDetails.description); + notificationChannel.setGroup(notificationChannelDetails.groupId); + if (notificationChannelDetails.playSound) { + AudioAttributes audioAttributes = new AudioAttributes.Builder().setUsage(AudioAttributes.USAGE_NOTIFICATION).build(); + Uri uri = retrieveSoundResourceUri(context, notificationChannelDetails.sound, notificationChannelDetails.soundSource); + notificationChannel.setSound(uri, audioAttributes); + } else { + notificationChannel.setSound(null, null); + } + notificationChannel.enableVibration(BooleanUtils.getValue(notificationChannelDetails.enableVibration)); + if (notificationChannelDetails.vibrationPattern != null && notificationChannelDetails.vibrationPattern.length > 0) { + notificationChannel.setVibrationPattern(notificationChannelDetails.vibrationPattern); } + boolean enableLights = BooleanUtils.getValue(notificationChannelDetails.enableLights); + notificationChannel.enableLights(enableLights); + if (enableLights && notificationChannelDetails.ledColor != null) { + notificationChannel.setLightColor(notificationChannelDetails.ledColor); + } + notificationChannel.setShowBadge(BooleanUtils.getValue(notificationChannelDetails.showBadge)); + notificationManager.createNotificationChannel(notificationChannel); + } } diff --git a/flutter_local_notifications/pubspec.yaml b/flutter_local_notifications/pubspec.yaml index 7115ac774..3bd45e0de 100644 --- a/flutter_local_notifications/pubspec.yaml +++ b/flutter_local_notifications/pubspec.yaml @@ -2,7 +2,7 @@ name: flutter_local_notifications description: A cross platform plugin for displaying and scheduling local notifications for Flutter applications with the ability to customise for each platform. -version: 8.1.0 +version: 8.1.1 homepage: https://github.com/MaikuB/flutter_local_notifications/tree/master/flutter_local_notifications dependencies: From b12e9724e4ee83c11de9247ac8b54f0fde3c8fba Mon Sep 17 00:00:00 2001 From: Yaroslav Pronin Date: Wed, 11 Aug 2021 11:08:00 +0300 Subject: [PATCH 36/63] [flutter_local_notifications_linux] Replace 'developer.gnome.org' links to 'specifications.freedesktop.org' (#1274) * Replace 'developer.gnome.org' links to 'specifications.freedesktop.org' GNOME refresh the developer portal and removed some of the old links. * Mention about the default notification sound * Update LinuxRawIconData documentation * Update LinuxRawIconData documentation --- .../lib/src/flutter_local_notifications.dart | 2 +- .../lib/src/flutter_local_notifications_platform_linux.dart | 2 +- .../lib/src/model/capabilities.dart | 2 +- flutter_local_notifications_linux/lib/src/model/hint.dart | 2 +- flutter_local_notifications_linux/lib/src/model/icon.dart | 3 ++- .../lib/src/model/initialization_settings.dart | 1 + .../lib/src/model/notification_details.dart | 1 + .../lib/src/notification_info.dart | 2 +- 8 files changed, 9 insertions(+), 6 deletions(-) diff --git a/flutter_local_notifications_linux/lib/src/flutter_local_notifications.dart b/flutter_local_notifications_linux/lib/src/flutter_local_notifications.dart index 8e74951c4..c49cefe3c 100644 --- a/flutter_local_notifications_linux/lib/src/flutter_local_notifications.dart +++ b/flutter_local_notifications_linux/lib/src/flutter_local_notifications.dart @@ -81,7 +81,7 @@ class LinuxFlutterLocalNotificationsPlugin /// Note: the system ID is unique only within the current user session, /// so it's undesirable to save it to persistable storage without any /// invalidation/update. For more information, please see - /// Desktop Notifications Specification https://developer.gnome.org/notification-spec/#basic-design + /// Desktop Notifications Specification https://specifications.freedesktop.org/notification-spec/latest/ar01s02.html @override Future> getSystemIdMap() => _manager.getSystemIdMap(); } diff --git a/flutter_local_notifications_linux/lib/src/flutter_local_notifications_platform_linux.dart b/flutter_local_notifications_linux/lib/src/flutter_local_notifications_platform_linux.dart index 4248fe640..a72ae4e42 100644 --- a/flutter_local_notifications_linux/lib/src/flutter_local_notifications_platform_linux.dart +++ b/flutter_local_notifications_linux/lib/src/flutter_local_notifications_platform_linux.dart @@ -43,6 +43,6 @@ abstract class FlutterLocalNotificationsPlatformLinux /// Note: the system ID is unique only within the current user session, /// so it's undesirable to save it to persistable storage without any /// invalidation/update. For more information, please see - /// Desktop Notifications Specification https://developer.gnome.org/notification-spec/#basic-design + /// Desktop Notifications Specification https://specifications.freedesktop.org/notification-spec/latest/ar01s02.html Future> getSystemIdMap(); } diff --git a/flutter_local_notifications_linux/lib/src/model/capabilities.dart b/flutter_local_notifications_linux/lib/src/model/capabilities.dart index 361a47ad7..01c5fcf53 100644 --- a/flutter_local_notifications_linux/lib/src/model/capabilities.dart +++ b/flutter_local_notifications_linux/lib/src/model/capabilities.dart @@ -41,7 +41,7 @@ class LinuxServerCapabilities { /// Supports markup in the body text. The markup is XML-based, and consists /// of a small subset of HTML along with a few additional tags. - /// For more information, see Desktop Notifications Specification https://developer.gnome.org/notification-spec/#markup + /// For more information, see Desktop Notifications Specification https://specifications.freedesktop.org/notification-spec/latest/ar01s04.html /// If marked up text is sent to a server /// that does not give this cap, the markup will show through as regular text /// so must be stripped clientside. diff --git a/flutter_local_notifications_linux/lib/src/model/hint.dart b/flutter_local_notifications_linux/lib/src/model/hint.dart index beae48817..60c27d441 100644 --- a/flutter_local_notifications_linux/lib/src/model/hint.dart +++ b/flutter_local_notifications_linux/lib/src/model/hint.dart @@ -5,7 +5,7 @@ import 'enums.dart'; /// Represents a custom Linux notification hint. /// Hints are a way to provide extra data to a notification server that /// the server may be able to make use of. -/// For more information, please see Desktop Notifications Specification https://developer.gnome.org/notification-spec/#hints +/// For more information, please see Desktop Notifications Specification https://specifications.freedesktop.org/notification-spec/latest/ar01s08.html @optionalTypeArgs class LinuxNotificationCustomHint { /// Constructs an instance of [LinuxNotificationCustomHint]. diff --git a/flutter_local_notifications_linux/lib/src/model/icon.dart b/flutter_local_notifications_linux/lib/src/model/icon.dart index f4628a48b..e393b3702 100644 --- a/flutter_local_notifications_linux/lib/src/model/icon.dart +++ b/flutter_local_notifications_linux/lib/src/model/icon.dart @@ -70,7 +70,7 @@ class LinuxRawIconData { this.hasAlpha = false, }) : rowStride = rowStride ?? ((width * channels * bitsPerSample) / 8).ceil(); - /// Raw data for the image in bytes. + /// Raw data (decoded from the image format) for the image in bytes. final Uint8List data; /// Width of the image in pixels @@ -86,6 +86,7 @@ class LinuxRawIconData { final int bitsPerSample; /// The number of channels in the image (e.g. 3 for RGB, 4 for RGBA). + /// If [hasAlpha] is `true`, must be 4. final int channels; /// Determines if the image has an alpha channel diff --git a/flutter_local_notifications_linux/lib/src/model/initialization_settings.dart b/flutter_local_notifications_linux/lib/src/model/initialization_settings.dart index 3d6fa433e..cd10aefc4 100644 --- a/flutter_local_notifications_linux/lib/src/model/initialization_settings.dart +++ b/flutter_local_notifications_linux/lib/src/model/initialization_settings.dart @@ -21,6 +21,7 @@ class LinuxInitializationSettings { final LinuxNotificationIcon? defaultIcon; /// Specifies the default sound for notifications. + /// Typical value is `ThemeLinuxSound('message')` final LinuxNotificationSound? defaultSound; /// Causes the server to suppress playing any sounds, if it has that ability. diff --git a/flutter_local_notifications_linux/lib/src/model/notification_details.dart b/flutter_local_notifications_linux/lib/src/model/notification_details.dart index 3b2aa9caf..6057b6a42 100644 --- a/flutter_local_notifications_linux/lib/src/model/notification_details.dart +++ b/flutter_local_notifications_linux/lib/src/model/notification_details.dart @@ -29,6 +29,7 @@ class LinuxNotificationDetails { final LinuxNotificationIcon? icon; /// Specifies the notification sound. + /// Typical value is `ThemeLinuxSound('message')` final LinuxNotificationSound? sound; /// Specifies the category for notification. diff --git a/flutter_local_notifications_linux/lib/src/notification_info.dart b/flutter_local_notifications_linux/lib/src/notification_info.dart index 01a28f853..b3e5c54d9 100644 --- a/flutter_local_notifications_linux/lib/src/notification_info.dart +++ b/flutter_local_notifications_linux/lib/src/notification_info.dart @@ -22,7 +22,7 @@ class LinuxNotificationInfo { final int id; /// Notification id, which is returned by the system, - /// see Desktop Notifications Specification https://developer.gnome.org/notification-spec/ + /// see Desktop Notifications Specification https://specifications.freedesktop.org/notification-spec/latest/ final int systemId; /// Notification payload, that will be passed back to the app From 4e210ff479c5c184921528baf834fce5814b38d5 Mon Sep 17 00:00:00 2001 From: Michael Bui <25263378+MaikuB@users.noreply.github.com> Date: Wed, 11 Aug 2021 19:50:05 +1000 Subject: [PATCH 37/63] [flutter_local_notifications] updated api docs for initialize method (#1280) * updated api docs for initialize method * fix api docs for initalize method of linux plugin --- flutter_local_notifications/CHANGELOG.md | 4 ++++ .../flutter_local_notifications_plugin.dart | 3 +++ .../platform_flutter_local_notifications.dart | 18 +++++++++++------- flutter_local_notifications/pubspec.yaml | 2 +- ...ter_local_notifications_platform_linux.dart | 3 --- 5 files changed, 19 insertions(+), 11 deletions(-) diff --git a/flutter_local_notifications/CHANGELOG.md b/flutter_local_notifications/CHANGELOG.md index 93b719c79..def42e728 100644 --- a/flutter_local_notifications/CHANGELOG.md +++ b/flutter_local_notifications/CHANGELOG.md @@ -1,3 +1,7 @@ +# [8.1.1+1] + +* Updated API docs for the `initialize()` to mention that the `getNotificationAppLaunchDetails()` method should be used to handle when a notification launches an application + # [8.1.1] * [Android] fixed issue [1263](https://github.com/MaikuB/flutter_local_notifications/issues/1263) around an unchecked/unsafe operation warning diff --git a/flutter_local_notifications/lib/src/flutter_local_notifications_plugin.dart b/flutter_local_notifications/lib/src/flutter_local_notifications_plugin.dart index 02af68291..fceb29d0c 100644 --- a/flutter_local_notifications/lib/src/flutter_local_notifications_plugin.dart +++ b/flutter_local_notifications/lib/src/flutter_local_notifications_plugin.dart @@ -114,6 +114,9 @@ class FlutterLocalNotificationsPlugin { /// [IOSInitializationSettings.requestSoundPermission] values to false. /// [IOSFlutterLocalNotificationsPlugin.requestPermissions] can then be called /// to request permissions when needed. + /// + /// To handle when a notification launched an application, use + /// [getNotificationAppLaunchDetails]. Future initialize( InitializationSettings initializationSettings, { SelectNotificationCallback? onSelectNotification, diff --git a/flutter_local_notifications/lib/src/platform_flutter_local_notifications.dart b/flutter_local_notifications/lib/src/platform_flutter_local_notifications.dart index db16ddea7..ba39a9aca 100644 --- a/flutter_local_notifications/lib/src/platform_flutter_local_notifications.dart +++ b/flutter_local_notifications/lib/src/platform_flutter_local_notifications.dart @@ -70,12 +70,13 @@ class AndroidFlutterLocalNotificationsPlugin extends MethodChannelFlutterLocalNotificationsPlugin { SelectNotificationCallback? _onSelectNotification; - /// Initializes the plugin. Call this method on application before using the + /// Initializes the plugin. + /// + /// Call this method on application before using the /// plugin further. /// - /// This should only be done once. When a notification created by this plugin - /// was used to launch the app, calling `initialize` is what will trigger to - /// the `onSelectNotification` callback to be fire. + /// To handle when a notification launched an application, use + /// [getNotificationAppLaunchDetails]. Future initialize( AndroidInitializationSettings initializationSettings, { SelectNotificationCallback? onSelectNotification, @@ -466,9 +467,6 @@ class IOSFlutterLocalNotificationsPlugin /// Initializes the plugin. /// /// Call this method on application before using the plugin further. - /// This should only be done once. When a notification created by this plugin - /// was used to launch the app, calling `initialize` is what will trigger to - /// the `onSelectNotification` callback to be fire. /// /// Initialisation may also request notification permissions where users will /// see a permissions prompt. This may be fine in cases where it's acceptable @@ -479,6 +477,9 @@ class IOSFlutterLocalNotificationsPlugin /// [IOSInitializationSettings.requestSoundPermission] values to false. /// [requestPermissions] can then be called to request permissions when /// needed. + /// + /// To handle when a notification launched an application, use + /// [getNotificationAppLaunchDetails]. Future initialize( IOSInitializationSettings initializationSettings, { SelectNotificationCallback? onSelectNotification, @@ -706,6 +707,9 @@ class MacOSFlutterLocalNotificationsPlugin /// [MacOSInitializationSettings.requestSoundPermission] values to false. /// [requestPermissions] can then be called to request permissions when /// needed. + /// + /// To handle when a notification launched an application, use + /// [getNotificationAppLaunchDetails]. Future initialize( MacOSInitializationSettings initializationSettings, { SelectNotificationCallback? onSelectNotification, diff --git a/flutter_local_notifications/pubspec.yaml b/flutter_local_notifications/pubspec.yaml index 3bd45e0de..329266887 100644 --- a/flutter_local_notifications/pubspec.yaml +++ b/flutter_local_notifications/pubspec.yaml @@ -2,7 +2,7 @@ name: flutter_local_notifications description: A cross platform plugin for displaying and scheduling local notifications for Flutter applications with the ability to customise for each platform. -version: 8.1.1 +version: 8.1.1+1 homepage: https://github.com/MaikuB/flutter_local_notifications/tree/master/flutter_local_notifications dependencies: diff --git a/flutter_local_notifications_linux/lib/src/flutter_local_notifications_platform_linux.dart b/flutter_local_notifications_linux/lib/src/flutter_local_notifications_platform_linux.dart index a72ae4e42..1a49c7c6e 100644 --- a/flutter_local_notifications_linux/lib/src/flutter_local_notifications_platform_linux.dart +++ b/flutter_local_notifications_linux/lib/src/flutter_local_notifications_platform_linux.dart @@ -13,9 +13,6 @@ abstract class FlutterLocalNotificationsPlatformLinux /// Initializes the plugin. /// /// Call this method on application before using the plugin further. - /// This should only be done once. When a notification created by this plugin - /// was used to launch the app, calling `initialize` is what will trigger to - /// the `onSelectNotification` callback to be fire. Future initialize( LinuxInitializationSettings initializationSettings, { SelectNotificationCallback? onSelectNotification, From 025244580d5cb7d54b897e5b05de549dece3b1f4 Mon Sep 17 00:00:00 2001 From: Michael Bui <25263378+MaikuB@users.noreply.github.com> Date: Wed, 11 Aug 2021 20:09:52 +1000 Subject: [PATCH 38/63] bump linux plugin (#1281) --- flutter_local_notifications_linux/CHANGELOG.md | 4 ++++ flutter_local_notifications_linux/pubspec.yaml | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/flutter_local_notifications_linux/CHANGELOG.md b/flutter_local_notifications_linux/CHANGELOG.md index 1c7db0673..deb349439 100644 --- a/flutter_local_notifications_linux/CHANGELOG.md +++ b/flutter_local_notifications_linux/CHANGELOG.md @@ -1,3 +1,7 @@ +## [0.2.0+1] + +* Fixed links to GNOME developer documentation referenced in API docs + ## [0.2.0] * Fixed issue when an app using the plugin is built on the web by using conditional imports diff --git a/flutter_local_notifications_linux/pubspec.yaml b/flutter_local_notifications_linux/pubspec.yaml index 603ecd32c..a9275adf6 100644 --- a/flutter_local_notifications_linux/pubspec.yaml +++ b/flutter_local_notifications_linux/pubspec.yaml @@ -1,6 +1,6 @@ name: flutter_local_notifications_linux description: Linux implementation of the flutter_local_notifications plugin -version: 0.2.0 +version: 0.2.0+1 homepage: https://github.com/MaikuB/flutter_local_notifications/tree/master/flutter_local_notifications dependencies: From ecda9504cd81c2fdedc60d3112189571e29a61d6 Mon Sep 17 00:00:00 2001 From: Xavier H Date: Mon, 30 Aug 2021 12:17:59 +0200 Subject: [PATCH 39/63] Increase timezone version dependency (#1297) --- flutter_local_notifications/pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flutter_local_notifications/pubspec.yaml b/flutter_local_notifications/pubspec.yaml index 329266887..59ed16912 100644 --- a/flutter_local_notifications/pubspec.yaml +++ b/flutter_local_notifications/pubspec.yaml @@ -10,7 +10,7 @@ dependencies: sdk: flutter flutter_local_notifications_platform_interface: ^4.0.1 platform: ^3.0.0 - timezone: ^0.7.0 + timezone: ^0.8.0 dev_dependencies: flutter_driver: From e9463e2553381d71e5c4bfdec1b2dec06870895e Mon Sep 17 00:00:00 2001 From: Michael Bui <25263378+MaikuB@users.noreply.github.com> Date: Mon, 30 Aug 2021 20:39:23 +1000 Subject: [PATCH 40/63] bump plugin to 8.1.1+2 (#1298) --- flutter_local_notifications/CHANGELOG.md | 4 ++++ flutter_local_notifications/pubspec.yaml | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/flutter_local_notifications/CHANGELOG.md b/flutter_local_notifications/CHANGELOG.md index def42e728..727d6afa2 100644 --- a/flutter_local_notifications/CHANGELOG.md +++ b/flutter_local_notifications/CHANGELOG.md @@ -1,3 +1,7 @@ +# [8.1.1+2] + +* Bumped `timezone` dependency. Thanks to the PR from [Xavier H.](https://github.com/xvrh) + # [8.1.1+1] * Updated API docs for the `initialize()` to mention that the `getNotificationAppLaunchDetails()` method should be used to handle when a notification launches an application diff --git a/flutter_local_notifications/pubspec.yaml b/flutter_local_notifications/pubspec.yaml index 59ed16912..da6d7ac1a 100644 --- a/flutter_local_notifications/pubspec.yaml +++ b/flutter_local_notifications/pubspec.yaml @@ -2,7 +2,7 @@ name: flutter_local_notifications description: A cross platform plugin for displaying and scheduling local notifications for Flutter applications with the ability to customise for each platform. -version: 8.1.1+1 +version: 8.1.1+2 homepage: https://github.com/MaikuB/flutter_local_notifications/tree/master/flutter_local_notifications dependencies: From 3b5f61d231cb70a6e57998cdedb52bd4569fb535 Mon Sep 17 00:00:00 2001 From: Denis Shakinov Date: Thu, 9 Sep 2021 13:24:35 +0300 Subject: [PATCH 41/63] Add new DateTimeComponents (DayOfMonthAndTime, DateAndTime) (#1131) --- .../FlutterLocalNotificationsPlugin.java | 20 +++++++ .../models/DateTimeComponents.java | 4 +- .../example/lib/main.dart | 52 +++++++++++++++++++ .../Classes/FlutterLocalNotificationsPlugin.m | 21 +++++++- .../lib/src/types.dart | 6 +++ 5 files changed, 101 insertions(+), 2 deletions(-) diff --git a/flutter_local_notifications/android/src/main/java/com/dexterous/flutterlocalnotifications/FlutterLocalNotificationsPlugin.java b/flutter_local_notifications/android/src/main/java/com/dexterous/flutterlocalnotifications/FlutterLocalNotificationsPlugin.java index 13862a5a1..68de3b9ee 100644 --- a/flutter_local_notifications/android/src/main/java/com/dexterous/flutterlocalnotifications/FlutterLocalNotificationsPlugin.java +++ b/flutter_local_notifications/android/src/main/java/com/dexterous/flutterlocalnotifications/FlutterLocalNotificationsPlugin.java @@ -885,6 +885,16 @@ static String getNextFireDateMatchingDateTimeComponents(NotificationDetails noti nextFireDate = nextFireDate.plusDays(1); } return DateTimeFormatter.ISO_LOCAL_DATE_TIME.format(nextFireDate); + } else if (notificationDetails.matchDateTimeComponents == DateTimeComponents.DayOfMonthAndTime) { + while (nextFireDate.getDayOfMonth() != scheduledDateTime.getDayOfMonth()) { + nextFireDate = nextFireDate.plusDays(1); + } + return DateTimeFormatter.ISO_LOCAL_DATE_TIME.format(nextFireDate); + } else if (notificationDetails.matchDateTimeComponents == DateTimeComponents.DateAndTime) { + while (nextFireDate.getMonthValue() != scheduledDateTime.getMonthValue() || nextFireDate.getDayOfMonth() != scheduledDateTime.getDayOfMonth()) { + nextFireDate = nextFireDate.plusDays(1); + } + return DateTimeFormatter.ISO_LOCAL_DATE_TIME.format(nextFireDate); } } else { org.threeten.bp.ZoneId zoneId = org.threeten.bp.ZoneId.of(notificationDetails.timeZoneName); @@ -902,6 +912,16 @@ static String getNextFireDateMatchingDateTimeComponents(NotificationDetails noti nextFireDate = nextFireDate.plusDays(1); } return org.threeten.bp.format.DateTimeFormatter.ISO_LOCAL_DATE_TIME.format(nextFireDate); + } else if (notificationDetails.matchDateTimeComponents == DateTimeComponents.DayOfMonthAndTime) { + while (nextFireDate.getDayOfMonth() != scheduledDateTime.getDayOfMonth()) { + nextFireDate = nextFireDate.plusDays(1); + } + return org.threeten.bp.format.DateTimeFormatter.ISO_LOCAL_DATE_TIME.format(nextFireDate); + } else if (notificationDetails.matchDateTimeComponents == DateTimeComponents.DateAndTime) { + while (nextFireDate.getMonthValue() != scheduledDateTime.getMonthValue() || nextFireDate.getDayOfMonth() != scheduledDateTime.getDayOfMonth()) { + nextFireDate = nextFireDate.plusDays(1); + } + return org.threeten.bp.format.DateTimeFormatter.ISO_LOCAL_DATE_TIME.format(nextFireDate); } } return null; diff --git a/flutter_local_notifications/android/src/main/java/com/dexterous/flutterlocalnotifications/models/DateTimeComponents.java b/flutter_local_notifications/android/src/main/java/com/dexterous/flutterlocalnotifications/models/DateTimeComponents.java index 9de0e2865..1f4722777 100644 --- a/flutter_local_notifications/android/src/main/java/com/dexterous/flutterlocalnotifications/models/DateTimeComponents.java +++ b/flutter_local_notifications/android/src/main/java/com/dexterous/flutterlocalnotifications/models/DateTimeComponents.java @@ -5,5 +5,7 @@ @Keep public enum DateTimeComponents { Time, - DayOfWeekAndTime + DayOfWeekAndTime, + DayOfMonthAndTime, + DateAndTime } diff --git a/flutter_local_notifications/example/lib/main.dart b/flutter_local_notifications/example/lib/main.dart index 42c8b67fb..7c33fed82 100644 --- a/flutter_local_notifications/example/lib/main.dart +++ b/flutter_local_notifications/example/lib/main.dart @@ -348,6 +348,22 @@ class _HomePageState extends State { }, ), ], + PaddedElevatedButton( + buttonText: + 'Schedule monthly Monday 10:00:00 am notification in ' + 'your local time zone', + onPressed: () async { + await _scheduleMonthlyMondayTenAMNotification(); + }, + ), + PaddedElevatedButton( + buttonText: + 'Schedule yearly Monday 10:00:00 am notification in ' + 'your local time zone', + onPressed: () async { + await _scheduleYearlyMondayTenAMNotification(); + }, + ), PaddedElevatedButton( buttonText: 'Show notification with no sound', onPressed: () async { @@ -1282,6 +1298,42 @@ class _HomePageState extends State { matchDateTimeComponents: DateTimeComponents.dayOfWeekAndTime); } + Future _scheduleMonthlyMondayTenAMNotification() async { + await flutterLocalNotificationsPlugin.zonedSchedule( + 0, + 'monthly scheduled notification title', + 'monthly scheduled notification body', + _nextInstanceOfMondayTenAM(), + const NotificationDetails( + android: AndroidNotificationDetails( + 'monthly notification channel id', + 'monthly notification channel name', + 'monthly notificationdescription'), + ), + androidAllowWhileIdle: true, + uiLocalNotificationDateInterpretation: + UILocalNotificationDateInterpretation.absoluteTime, + matchDateTimeComponents: DateTimeComponents.dayOfMonthAndTime); + } + + Future _scheduleYearlyMondayTenAMNotification() async { + await flutterLocalNotificationsPlugin.zonedSchedule( + 0, + 'yearly scheduled notification title', + 'yearly scheduled notification body', + _nextInstanceOfMondayTenAM(), + const NotificationDetails( + android: AndroidNotificationDetails( + 'yearly notification channel id', + 'yearly notification channel name', + 'yearly notificationdescription'), + ), + androidAllowWhileIdle: true, + uiLocalNotificationDateInterpretation: + UILocalNotificationDateInterpretation.absoluteTime, + matchDateTimeComponents: DateTimeComponents.dateAndTime); + } + tz.TZDateTime _nextInstanceOfTenAM() { final tz.TZDateTime now = tz.TZDateTime.now(tz.local); tz.TZDateTime scheduledDate = diff --git a/flutter_local_notifications/ios/Classes/FlutterLocalNotificationsPlugin.m b/flutter_local_notifications/ios/Classes/FlutterLocalNotificationsPlugin.m index 12fed21d9..d8f9f4105 100644 --- a/flutter_local_notifications/ios/Classes/FlutterLocalNotificationsPlugin.m +++ b/flutter_local_notifications/ios/Classes/FlutterLocalNotificationsPlugin.m @@ -82,7 +82,9 @@ typedef NS_ENUM(NSInteger, RepeatInterval) { typedef NS_ENUM(NSInteger, DateTimeComponents) { Time, - DayOfWeekAndTime + DayOfWeekAndTime, + DayOfMonthAndTime, + DateAndTime }; typedef NS_ENUM(NSInteger, UILocalNotificationDateInterpretation) { @@ -382,6 +384,10 @@ - (void)zonedSchedule:(NSDictionary * _Nonnull)arguments result:(FlutterResult _ notification.repeatInterval = NSCalendarUnitDay; } else if([matchDateComponents integerValue] == DayOfWeekAndTime) { notification.repeatInterval = NSCalendarUnitWeekOfYear; + } else if([matchDateComponents integerValue] == DayOfMonthAndTime) { + notification.repeatInterval = NSCalendarUnitMonth; + } else if([matchDateComponents integerValue] == DateAndTime) { + notification.repeatInterval = NSCalendarUnitYear; } } [[UIApplication sharedApplication] scheduleLocalNotification:notification]; @@ -624,6 +630,19 @@ - (UNCalendarNotificationTrigger *) buildUserNotificationCalendarTrigger:(id) ar NSCalendarUnitMinute| NSCalendarUnitSecond | NSCalendarUnitTimeZone) fromDate:date]; return [UNCalendarNotificationTrigger triggerWithDateMatchingComponents:dateComponents repeats:YES]; + } else if([matchDateComponents integerValue] == DayOfMonthAndTime) { + NSDateComponents *dateComponents = [calendar components:( NSCalendarUnitDay | + NSCalendarUnitHour | + NSCalendarUnitMinute| + NSCalendarUnitSecond | NSCalendarUnitTimeZone) fromDate:date]; + return [UNCalendarNotificationTrigger triggerWithDateMatchingComponents:dateComponents repeats:YES]; + } else if([matchDateComponents integerValue] == DateAndTime) { + NSDateComponents *dateComponents = [calendar components:( NSCalendarUnitMonth | + NSCalendarUnitDay | + NSCalendarUnitHour | + NSCalendarUnitMinute| + NSCalendarUnitSecond | NSCalendarUnitTimeZone) fromDate:date]; + return [UNCalendarNotificationTrigger triggerWithDateMatchingComponents:dateComponents repeats:YES]; } return nil; } diff --git a/flutter_local_notifications/lib/src/types.dart b/flutter_local_notifications/lib/src/types.dart index 121f4c192..e0748d417 100644 --- a/flutter_local_notifications/lib/src/types.dart +++ b/flutter_local_notifications/lib/src/types.dart @@ -66,4 +66,10 @@ enum DateTimeComponents { /// The day of the week and time. dayOfWeekAndTime, + + /// The day of the month and time. + dayOfMonthAndTime, + + /// The date and time. + dateAndTime, } From de74460193b69ef341a70cad54aed3a4a8069603 Mon Sep 17 00:00:00 2001 From: Michael Bui <25263378+MaikuB@users.noreply.github.com> Date: Thu, 9 Sep 2021 20:52:21 +1000 Subject: [PATCH 42/63] bump version to 8.2.0 and fix channel description in example (#1307) --- flutter_local_notifications/CHANGELOG.md | 4 ++++ flutter_local_notifications/example/lib/main.dart | 2 +- flutter_local_notifications/pubspec.yaml | 2 +- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/flutter_local_notifications/CHANGELOG.md b/flutter_local_notifications/CHANGELOG.md index 727d6afa2..ac019ad14 100644 --- a/flutter_local_notifications/CHANGELOG.md +++ b/flutter_local_notifications/CHANGELOG.md @@ -1,3 +1,7 @@ +# [8.2.0] + +* Added `dayOfMonthAndTime` and `dateAndTime` values to the `DateTimeComponents` enum. These allow for creating monthly and yearly notifications respectively. Thanks to the PR from [Denis Shakinov](https://github.com/DenisShakinov) + # [8.1.1+2] * Bumped `timezone` dependency. Thanks to the PR from [Xavier H.](https://github.com/xvrh) diff --git a/flutter_local_notifications/example/lib/main.dart b/flutter_local_notifications/example/lib/main.dart index 7c33fed82..7d1deeecb 100644 --- a/flutter_local_notifications/example/lib/main.dart +++ b/flutter_local_notifications/example/lib/main.dart @@ -1326,7 +1326,7 @@ class _HomePageState extends State { android: AndroidNotificationDetails( 'yearly notification channel id', 'yearly notification channel name', - 'yearly notificationdescription'), + 'yearly notification description'), ), androidAllowWhileIdle: true, uiLocalNotificationDateInterpretation: diff --git a/flutter_local_notifications/pubspec.yaml b/flutter_local_notifications/pubspec.yaml index da6d7ac1a..46040ceae 100644 --- a/flutter_local_notifications/pubspec.yaml +++ b/flutter_local_notifications/pubspec.yaml @@ -2,7 +2,7 @@ name: flutter_local_notifications description: A cross platform plugin for displaying and scheduling local notifications for Flutter applications with the ability to customise for each platform. -version: 8.1.1+2 +version: 8.2.0 homepage: https://github.com/MaikuB/flutter_local_notifications/tree/master/flutter_local_notifications dependencies: From 276c7bc8c2f1356afc25df7d7b215c17616fe40b Mon Sep 17 00:00:00 2001 From: Aneesh Rao Date: Thu, 9 Sep 2021 17:37:12 +0530 Subject: [PATCH 43/63] Fix some grammatical errors (#1308) --- flutter_local_notifications/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/flutter_local_notifications/README.md b/flutter_local_notifications/README.md index 625454070..5fb95d18e 100644 --- a/flutter_local_notifications/README.md +++ b/flutter_local_notifications/README.md @@ -59,7 +59,7 @@ A cross platform plugin for displaying local notifications. * Retrieve a list of pending notification requests that have been scheduled to be shown in the future * Cancelling/removing notification by id or all of them * Specify a custom notification sound -* Ability to handle when a user has tapped on a notification, when the app is the foreground, background or terminated +* Ability to handle when a user has tapped on a notification, when the app is in the foreground, background or is terminated * Determine if an app was launched due to tapping on a notification * [Android] Configuring the importance level * [Android] Configuring the priority @@ -67,7 +67,7 @@ A cross platform plugin for displaying local notifications. * [Android] Configure the default icon for all notifications * [Android] Configure the icon for each notification (overrides the default when specified) * [Android] Configure the large icon for each notification. The icon can be a drawable or a file on the device -* [Android] Formatting notification content via ([HTML markup](https://developer.android.com/guide/topics/resources/string-resource.html#StylingWithHTML)) +* [Android] Formatting notification content via [HTML markup](https://developer.android.com/guide/topics/resources/string-resource.html#StylingWithHTML) * [Android] Support for the following notification styles * Big picture * Big text From 9c0d23a20c558462d9c8ee6cb530b42b7699e014 Mon Sep 17 00:00:00 2001 From: Michael Bui <25263378+MaikuB@users.noreply.github.com> Date: Tue, 5 Oct 2021 21:27:22 +1100 Subject: [PATCH 44/63] [flutter_local_notifications] Android 12 support, Linux support and platform detection rework (#1268) * updates to support Android 12 * update changelog entry on android 12 support * Update build.gradle - swap "android-S" to 31 (#1232) Instead of specifying the compileSdkVersion by the preview name, it is best to use the actual API number, and android-S's version number is 31. By difference, some machines cannot fetch version "android-S", as it is a preview SDK build, whereas 31 is a non-preview build, and can be fetched by all machines. * bump version for release * fixed android example permission (#1239) * bump to 7.0.0-dev.3 * Revert "[flutter_local_notifications] remove support for Linux to fix issue where apps can't build on web (#1248)" This reverts commit e6ce8ddb0d1ab49164ae205c133adfe37bcb91be. * add details for 9.0.0-dev.1 prerelease and update Importance so that value is non-nullable * use defaultTargetPlatform to check the platform instead of the platform package * use clock package and add tests for periodically show * bump dependency on Linux plugin * updated example app to include guards to prevent errors if web is enabled and running * add changelog entry for Android 12 support * try to switch task to build Linux example to app to use stable image as desktop support is on stable * bump to 9.0.0-dev.2 * Add a real example of ByteDataLinuxIcon usage (#1278) * update SelectNotificationCallback to return void * update linux plugin's dependency on platform interface * bump main plugin dependency on platform interface * make notification channel optional and address linter issues on sorting directives * make channelDescription in AndroidNotificationDetails class optional * bump to 9.0.0-dev.4 * [README] Fix paragraph link (#1309) * [flutter_local_notifications]: allow byte arrays as source for icons (#1316) * allow bitmaps as source for icons * extend message example to show ByteArrayAndroidIcon usage * apply refactor suggestions and use https * rename function and fix java unsafe operations as suggested by @MaikuB * specify type annotation for icon property of Person class * bump version * fix typo in changelog entry * exclude generated_plugin_registrant from analyzer * update changelog and pubspec for 9.0.0 stable release * remove extra asterisks in 9.0.0 changelog entry * update readme since onSelectNotification typedef now maps to void function * update DidReceiveLocalNotificationCallback typedef * update platform interface for stable release * update linux plugin to point to latest stable platform interface * update cross-platform facing plugin to use latest stable dependencies * bump timezone dependency Co-authored-by: tim-mardesen <85238116+tim-mardesen@users.noreply.github.com> Co-authored-by: Willham12 <87827270+Willham12@users.noreply.github.com> Co-authored-by: Yaroslav Pronin Co-authored-by: Alexander Petermann --- .cirrus.yml | 24 + flutter_local_notifications/CHANGELOG.md | 16 +- flutter_local_notifications/README.md | 54 +- .../android/build.gradle | 2 +- .../android/src/main/AndroidManifest.xml | 5 +- .../FlutterLocalNotificationsPlugin.java | 74 +- .../models/IconSource.java | 3 +- .../models/NotificationDetails.java | 12 +- .../models/PersonDetails.java | 4 +- .../example/analysis_options.yaml | 5 +- .../android/app/src/main/AndroidManifest.xml | 3 +- .../example/icons/1.5x/app_icon_density.png | Bin 0 -> 544 bytes .../example/icons/2.0x/app_icon_density.png | Bin 0 -> 721 bytes .../example/icons/3.0x/app_icon_density.png | Bin 0 -> 1031 bytes .../example/icons/4.0x/app_icon_density.png | Bin 0 -> 1443 bytes .../example/icons/app_icon_density.png | Bin 0 -> 442 bytes .../example/lib/main.dart | 549 ++++++++-- .../example/pubspec.yaml | 1 + .../lib/flutter_local_notifications.dart | 1 + .../flutter_local_notifications_plugin.dart | 103 +- .../lib/src/helpers.dart | 3 +- .../lib/src/initialization_settings.dart | 6 + .../lib/src/notification_details.dart | 6 + .../platform_flutter_local_notifications.dart | 43 +- .../src/platform_specifics/android/enums.dart | 14 +- .../src/platform_specifics/android/icon.dart | 60 +- .../android/method_channel_mappers.dart | 27 +- .../android/notification_channel.dart | 6 +- .../android/notification_details.dart | 8 +- .../platform_specifics/android/person.dart | 2 +- .../lib/src/typedefs.dart | 4 +- flutter_local_notifications/pubspec.yaml | 9 +- ...oid_flutter_local_notifications_test.dart} | 947 +++--------------- .../flutter_local_notifications_test.dart | 2 +- .../ios_flutter_local_notifications_test.dart | 450 +++++++++ ...acos_flutter_local_notifications_test.dart | 445 ++++++++ .../test/utils/date_formatter.dart | 27 + .../CHANGELOG.md | 4 + .../lib/src/notifications_manager.dart | 2 +- .../pubspec.yaml | 4 +- .../CHANGELOG.md | 4 + .../lib/src/typedefs.dart | 2 +- .../pubspec.yaml | 2 +- 43 files changed, 1843 insertions(+), 1090 deletions(-) create mode 100644 flutter_local_notifications/example/icons/1.5x/app_icon_density.png create mode 100644 flutter_local_notifications/example/icons/2.0x/app_icon_density.png create mode 100644 flutter_local_notifications/example/icons/3.0x/app_icon_density.png create mode 100644 flutter_local_notifications/example/icons/4.0x/app_icon_density.png create mode 100644 flutter_local_notifications/example/icons/app_icon_density.png rename flutter_local_notifications/test/{platform_flutter_local_notifications_test.dart => android_flutter_local_notifications_test.dart} (71%) create mode 100644 flutter_local_notifications/test/ios_flutter_local_notifications_test.dart create mode 100644 flutter_local_notifications/test/macos_flutter_local_notifications_test.dart create mode 100644 flutter_local_notifications/test/utils/date_formatter.dart diff --git a/.cirrus.yml b/.cirrus.yml index 1dc4a3ed8..1e22aa7a6 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -36,6 +36,20 @@ task: - cd flutter_local_notifications/example - flutter build macos +task: + name: Build Linux example app + container: + image: cirrusci/flutter:stable + pub_cache: + folder: ~/.pub-cache + setup_script: + - apt update + - apt install cmake ninja-build clang pkg-config libgtk-3-dev -y + - flutter config --enable-linux-desktop + build_script: + - cd flutter_local_notifications/example + - flutter build linux + task: name: Run platform interface tests container: @@ -77,3 +91,13 @@ task: - cd flutter_local_notifications - cd example - flutter drive --driver=test_driver/integration_test.dart --target=integration_test/flutter_local_notifications_test.dart + +task: + name: Run Linux plugin tests + container: + image: cirrusci/flutter:stable + pub_cache: + folder: ~/.pub-cache + test_script: + - cd flutter_local_notifications_linux + - flutter test diff --git a/flutter_local_notifications/CHANGELOG.md b/flutter_local_notifications/CHANGELOG.md index ac019ad14..dd10131f1 100644 --- a/flutter_local_notifications/CHANGELOG.md +++ b/flutter_local_notifications/CHANGELOG.md @@ -1,3 +1,17 @@ +# [9.0.0] + +* **Breaking change** the `SelectNotificationCallback` and `DidReceiveLocalNotificationCallback` typedefs now map to functions that returns `void` instead of a `Future`. This change was done to better communicate the plugin doesn't actually await any asynchronous computation and is similar to how button pressed callbacks work for Flutter where they are typically use [`VoidCallback`](https://api.flutter.dev/flutter/dart-ui/VoidCallback.html) +* Updated example app to show how to display notification where a byte array is used to specify the icon on Linux +* **Breaking change** the `value` property of the `Importance` class is now non-nullable +* **Breaking change** the `FlutterLocalNotificationsPlugin.private()` constructor that was visible for testing purposes has been removed. The plugin now uses the [`defaultTargetPlatform`](https://api.flutter.dev/flutter/foundation/defaultTargetPlatform.html) property from the Flutter framework to determine the platform an application running on. This removes the need for depending on the `platform` package. To write tests that require a platform-specific implementation of the plugin, the [debugDefaultTargetPlatformOverride](https://api.flutter.dev/flutter/foundation/debugDefaultTargetPlatformOverride.html) property can be used to do so +* **Breaking change** fixed issue [1306](https://github.com/MaikuB/flutter_local_notifications/issues/1306) where an Android notification channel description should have been optional. This means the `description` property of the `AndroidNotificationChannel` class and the `channelDescription` property of the `AndroidNotificationDetails` class are now named parameters +* **Breaking change** the `AndroidIcon` class is now a generic class i.e. `AndroidIcon` and it's `icon` property has been renamed to `data`. With this change, the type of the `icon` property that belongs to the `Person` class has changed from `AndroidIcon?` to `AndroidIcon?` +* [Android] Added the `ByteArrayAndroidIcon` class that implements the `AndroidIcon` class. This allows using a byte array to use as the icon for a person in a message style notification. A `ByteArrayAndroidIcon.fromBase64String()` named constructor is also available that will enable this using a base-64 encoded string. Thanks to the PR from [Alexander Petermann](https://github.com/lexxxel) +* [Android] Android 12 support +* Restored Linux support +* Fixed grammatical errors in readme. Thanks to PR from [Aneesh Rao](https://github.com/sidrao2006) +* Plugin now uses the [`clock`](https://pub.dev/packages/clock) package for internal logic that relies on geting the current time, such as validating that the date for a scheduled notification is set in the future + # [8.2.0] * Added `dayOfMonthAndTime` and `dateAndTime` values to the `DateTimeComponents` enum. These allow for creating monthly and yearly notifications respectively. Thanks to the PR from [Denis Shakinov](https://github.com/DenisShakinov) @@ -24,7 +38,7 @@ * **Breaking change** the `AndroidBitmap` class is now a generic class i.e. `AndroidBitmap`. This has resulted in the following changes * the type of the `largeIcon` property that belongs to the `AndroidNotificationDetails` class has changed from `AndroidBitmap` to `AndroidBitmap` * the type of the `largeIcon` and `bigPicture` properties that belongs to the `BigPictureStyleInformation` class has changed from `AndroidBitmap` to `AndroidBitmap` -* [Android] Added the `ByteArrayAndroidBitmap` class that implements the `AndroidBitmaps` class. This allows using a byte array to use as the large icon for a notification or as big picture if the big picture style has been applied. A `ByteArrayAndroidBitmap.fromBase64String()` named constructor is also available that will enable this using a base-64 encoded string. Thanks to the PR from [Alexander Petermann](https://github.com/lexxxel) +* [Android] Added the `ByteArrayAndroidBitmap` class that implements the `AndroidBitmap` class. This allows using a byte array to use as the large icon for a notification or as big picture if the big picture style has been applied. A `ByteArrayAndroidBitmap.fromBase64String()` named constructor is also available that will enable this using a base-64 encoded string. Thanks to the PR from [Alexander Petermann](https://github.com/lexxxel) # [7.0.0] diff --git a/flutter_local_notifications/README.md b/flutter_local_notifications/README.md index 5fb95d18e..0fec2cf4d 100644 --- a/flutter_local_notifications/README.md +++ b/flutter_local_notifications/README.md @@ -16,6 +16,7 @@ A cross platform plugin for displaying local notifications. - [Updating application badge](#updating-application-badge) - [Custom notification sounds](#custom-notification-sounds) - [macOS differences](#macos-differences) + - [Linux limitations](#linux-limitations) - **[📷 Screenshots](#-screenshots)** - **[👏 Acknowledgements](#-acknowledgements)** - **[⚙️ Android Setup](#️-android-setup)** @@ -47,6 +48,7 @@ A cross platform plugin for displaying local notifications. * **Android 4.1+**. Uses the [NotificationCompat APIs](https://developer.android.com/reference/androidx/core/app/NotificationCompat) so it can be run older Android devices * **iOS 8.0+**. On iOS versions older than 10, the plugin will use the UILocalNotification APIs. The [UserNotification APIs](https://developer.apple.com/documentation/usernotifications) (aka the User Notifications Framework) is used on iOS 10 or newer. * **macOS 10.11+**. On macOS versions older than 10.14, the plugin will use the [NSUserNotification APIs](https://developer.apple.com/documentation/foundation/nsusernotification). The [UserNotification APIs](https://developer.apple.com/documentation/usernotifications) (aka the User Notifications Framework) is used on macOS 10.14 or newer. +* **Linux**. Uses the [Desktop Notifications Specification](https://developer.gnome.org/notification-spec/). ## ✨ Features @@ -84,6 +86,14 @@ A cross platform plugin for displaying local notifications. * [Android] Start a foreground service * [iOS (all supported versions) & macOS 10.14+] Request notification permissions and customise the permissions being requested around displaying notifications * [iOS 10 or newer and macOS 10.14 or newer] Display notifications with attachments +* [Linux] Ability to to use themed/Flutter Assets icons and sound +* [Linux] Ability to to set the category +* [Linux] Configuring the urgency +* [Linux] Configuring the timeout (depends on system implementation) +* [Linux] Ability to set custom notification location (depends on system implementation) +* [Linux] Ability to set custom hints +* [Linux] Ability to suppress sound +* [Linux] Resident and transient notifications ## ⚠ Caveats and limitations The cross-platform facing API exposed by the `FlutterLocalNotificationsPlugin` class doesn't expose platform-specific methods as its goal is to provide an abstraction for all platforms. As such, platform-specific configuration is passed in as data. There are platform-specific implementations of the plugin that can be obtained by calling the [`resolvePlatformSpecificImplementation`](https://pub.dev/documentation/flutter_local_notifications/latest/flutter_local_notifications/FlutterLocalNotificationsPlugin/resolvePlatformSpecificImplementation.html). An example of using this is provided in the section on requesting permissions on iOS. In spite of this, there may still be gaps that don't cover your use case and don't make sense to add as they don't fit with the plugin's architecture or goals. Developers can fork or maintain their own code for showing notifications in these situations. @@ -115,6 +125,14 @@ Due to limitations currently within the macOS Flutter engine, `getNotificationAp The `schedule`, `showDailyAtTime` and `showWeeklyAtDayAndTime` methods that were implemented before macOS support was added and have been marked as deprecated aren't implemented on macOS. +##### Linux limitations + +Capabilities depend on the system notification server implementation, therefore, not all features listed in `LinuxNotificationDetails` may be supported. One of the ways to check some capabilities is to call the `LinuxFlutterLocalNotificationsPlugin.getCapabilities()` method. + +Scheduled/pending notifications is currently not supported due to the lack of a scheduler API. + +To respond to notification after the application is terminated, your application should be registered as DBus activatable (see [DBusApplicationLaunching](https://wiki.gnome.org/HowDoI/DBusApplicationLaunching) for more information), and register action before activating the application. This is difficult to do in a plugin because plugins instantiate during application activation, so `getNotificationAppLaunchDetails` can't be implemented without changing the main user application. + ## 📷 Screenshots | Platform | Screenshot | @@ -122,7 +140,7 @@ The `schedule`, `showDailyAtTime` and `showWeeklyAtDayAndTime` methods that were | Android | | | iOS | | | macOS | | - +| Linux | | ## 👏 Acknowledgements @@ -220,7 +238,7 @@ flutterLocalNotificationsPlugin.initialize(initializationSettings, ... -Future onDidReceiveLocalNotification( +void onDidReceiveLocalNotification( int id, String title, String body, String payload) async { // display a dialog with the notification details, tap ok to go to another page showDialog( @@ -290,7 +308,7 @@ await flutterLocalNotificationsPlugin.initialize(initializationSettings, Initialisation can be done is in the `main` function of your application or can be done within the first page shown in your app. Developers can refer to the example app that has code for the initialising within the `main` function. The code above has been simplified for explaining the concepts. Here we have specified the default icon to use for notifications on Android (refer to the *Android setup* section) and designated the function (`selectNotification`) that should fire when a notification has been tapped on via the `onSelectNotification` callback. Specifying this callback is entirely optional but here it will trigger navigation to another page and display the payload associated with the notification. ```dart -Future selectNotification(String payload) async { +void selectNotification(String payload) async { if (payload != null) { debugPrint('notification payload: $payload'); } @@ -376,11 +394,11 @@ Here the call to `flutterLocalNotificationsPlugin.resolvePlatformSpecificImpleme ```dart const AndroidNotificationDetails androidPlatformChannelSpecifics = - AndroidNotificationDetails( - 'your channel id', 'your channel name', 'your channel description', + AndroidNotificationDetails('your channel id', 'your channel name', + channelDescription: 'your channel description', importance: Importance.max, priority: Priority.high, - showWhen: false); + ticker: 'ticker'); const NotificationDetails platformChannelSpecifics = NotificationDetails(android: androidPlatformChannelSpecifics); await flutterLocalNotificationsPlugin.show( @@ -428,8 +446,9 @@ await flutterLocalNotificationsPlugin.zonedSchedule( 'scheduled body', tz.TZDateTime.now(tz.local).add(const Duration(seconds: 5)), const NotificationDetails( - android: AndroidNotificationDetails('your channel id', - 'your channel name', 'your channel description')), + android: AndroidNotificationDetails( + 'your channel id', 'your channel name', + channelDescription: 'your channel description')), androidAllowWhileIdle: true, uiLocalNotificationDateInterpretation: UILocalNotificationDateInterpretation.absoluteTime); @@ -447,8 +466,9 @@ If you are trying to update your code so it doesn't use the deprecated methods f ```dart const AndroidNotificationDetails androidPlatformChannelSpecifics = - AndroidNotificationDetails('repeating channel id', - 'repeating channel name', 'repeating description'); + AndroidNotificationDetails( + 'repeating channel id', 'repeating channel name', + channelDescription: 'repeating description'); const NotificationDetails platformChannelSpecifics = NotificationDetails(android: androidPlatformChannelSpecifics); await flutterLocalNotificationsPlugin.periodicallyShow(0, 'repeating title', @@ -495,8 +515,8 @@ const String groupChannelName = 'grouped channel name'; const String groupChannelDescription = 'grouped channel description'; // example based on https://developer.android.com/training/notify-user/group.html const AndroidNotificationDetails firstNotificationAndroidSpecifics = - AndroidNotificationDetails( - groupChannelId, groupChannelName, groupChannelDescription, + AndroidNotificationDetails(groupChannelId, groupChannelName, + channelDescription: groupChannelDescription, importance: Importance.max, priority: Priority.high, groupKey: groupKey); @@ -505,8 +525,8 @@ const NotificationDetails firstNotificationPlatformSpecifics = await flutterLocalNotificationsPlugin.show(1, 'Alex Faarborg', 'You will not believe...', firstNotificationPlatformSpecifics); const AndroidNotificationDetails secondNotificationAndroidSpecifics = - AndroidNotificationDetails( - groupChannelId, groupChannelName, groupChannelDescription, + AndroidNotificationDetails(groupChannelId, groupChannelName, + channelDescription: groupChannelDescription, importance: Importance.max, priority: Priority.high, groupKey: groupKey); @@ -532,8 +552,8 @@ const InboxStyleInformation inboxStyleInformation = InboxStyleInformation( contentTitle: '2 messages', summaryText: 'janedoe@example.com'); const AndroidNotificationDetails androidPlatformChannelSpecifics = - AndroidNotificationDetails( - groupChannelId, groupChannelName, groupChannelDescription, + AndroidNotificationDetails(groupChannelId, groupChannelName, + channelDescription: groupChannelDescription, styleInformation: inboxStyleInformation, groupKey: groupKey, setAsGroupSummary: true); @@ -596,4 +616,4 @@ If you decide to use the plugin class directly as part of your tests, the method Part of this is because the plugin detects if you're running on a supported plugin to determine which platform implementation of the plugin should be used. If the platform isn't supported, it will default to the aforementioned behaviour to reduce friction when writing tests. If this not desired then consider using mocks. -If a platform-specific implementation of the plugin is required for your tests, a [named constructor](https://pub.dev/documentation/flutter_local_notifications/latest/flutter_local_notifications/FlutterLocalNotificationsPlugin/FlutterLocalNotificationsPlugin.private.html) is available that allows you to specify the platform required e.g. a [`FakePlatform`](https://api.flutter.dev/flutter/package-platform_platform/FakePlatform-class.html). +If a platform-specific implementation of the plugin is required for your tests, use the [debugDefaultTargetPlatformOverride](https://api.flutter.dev/flutter/foundation/debugDefaultTargetPlatformOverride.html) property provided by the Flutter framework. diff --git a/flutter_local_notifications/android/build.gradle b/flutter_local_notifications/android/build.gradle index 5208dad42..d49ac5c84 100644 --- a/flutter_local_notifications/android/build.gradle +++ b/flutter_local_notifications/android/build.gradle @@ -22,7 +22,7 @@ rootProject.allprojects { apply plugin: 'com.android.library' android { - compileSdkVersion 30 + compileSdkVersion 31 defaultConfig { minSdkVersion 16 diff --git a/flutter_local_notifications/android/src/main/AndroidManifest.xml b/flutter_local_notifications/android/src/main/AndroidManifest.xml index 5888fc2c3..37c7c21b0 100644 --- a/flutter_local_notifications/android/src/main/AndroidManifest.xml +++ b/flutter_local_notifications/android/src/main/AndroidManifest.xml @@ -3,9 +3,10 @@ + - - + + diff --git a/flutter_local_notifications/android/src/main/java/com/dexterous/flutterlocalnotifications/FlutterLocalNotificationsPlugin.java b/flutter_local_notifications/android/src/main/java/com/dexterous/flutterlocalnotifications/FlutterLocalNotificationsPlugin.java index 68de3b9ee..d4472d681 100644 --- a/flutter_local_notifications/android/src/main/java/com/dexterous/flutterlocalnotifications/FlutterLocalNotificationsPlugin.java +++ b/flutter_local_notifications/android/src/main/java/com/dexterous/flutterlocalnotifications/FlutterLocalNotificationsPlugin.java @@ -173,7 +173,11 @@ protected static Notification createNotification(Context context, NotificationDe Intent intent = getLaunchIntent(context); intent.setAction(SELECT_NOTIFICATION); intent.putExtra(PAYLOAD, notificationDetails.payload); - PendingIntent pendingIntent = PendingIntent.getActivity(context, notificationDetails.id, intent, PendingIntent.FLAG_UPDATE_CURRENT); + int flags = PendingIntent.FLAG_UPDATE_CURRENT; + if (VERSION.SDK_INT >= VERSION_CODES.M) { + flags |= PendingIntent.FLAG_IMMUTABLE; + } + PendingIntent pendingIntent = PendingIntent.getActivity(context, notificationDetails.id, intent, flags); DefaultStyleInformation defaultStyleInformation = (DefaultStyleInformation) notificationDetails.styleInformation; NotificationCompat.Builder builder = new NotificationCompat.Builder(context, notificationDetails.channelId) .setContentTitle(defaultStyleInformation.htmlFormatTitle ? fromHtml(notificationDetails.title) : notificationDetails.title) @@ -343,7 +347,7 @@ private static void scheduleNotification(Context context, final NotificationDeta String notificationDetailsJson = gson.toJson(notificationDetails); Intent notificationIntent = new Intent(context, ScheduledNotificationReceiver.class); notificationIntent.putExtra(NOTIFICATION_DETAILS, notificationDetailsJson); - PendingIntent pendingIntent = PendingIntent.getBroadcast(context, notificationDetails.id, notificationIntent, PendingIntent.FLAG_UPDATE_CURRENT); + PendingIntent pendingIntent = getBroadcastPendingIntent(context, notificationDetails.id, notificationIntent); AlarmManager alarmManager = getAlarmManager(context); if (BooleanUtils.getValue(notificationDetails.allowWhileIdle)) { @@ -362,7 +366,7 @@ private static void zonedScheduleNotification(Context context, final Notificatio String notificationDetailsJson = gson.toJson(notificationDetails); Intent notificationIntent = new Intent(context, ScheduledNotificationReceiver.class); notificationIntent.putExtra(NOTIFICATION_DETAILS, notificationDetailsJson); - PendingIntent pendingIntent = PendingIntent.getBroadcast(context, notificationDetails.id, notificationIntent, PendingIntent.FLAG_UPDATE_CURRENT); + PendingIntent pendingIntent = getBroadcastPendingIntent(context, notificationDetails.id, notificationIntent); AlarmManager alarmManager = getAlarmManager(context); long epochMilli = VERSION.SDK_INT >= VERSION_CODES.O ? ZonedDateTime.of(LocalDateTime.parse(notificationDetails.scheduledDateTime), ZoneId.of(notificationDetails.timeZoneName)).toInstant().toEpochMilli() : org.threeten.bp.ZonedDateTime.of(org.threeten.bp.LocalDateTime.parse(notificationDetails.scheduledDateTime), org.threeten.bp.ZoneId.of(notificationDetails.timeZoneName)).toInstant().toEpochMilli(); if (BooleanUtils.getValue(notificationDetails.allowWhileIdle)) { @@ -383,12 +387,28 @@ static void scheduleNextRepeatingNotification(Context context, NotificationDetai String notificationDetailsJson = gson.toJson(notificationDetails); Intent notificationIntent = new Intent(context, ScheduledNotificationReceiver.class); notificationIntent.putExtra(NOTIFICATION_DETAILS, notificationDetailsJson); - PendingIntent pendingIntent = PendingIntent.getBroadcast(context, notificationDetails.id, notificationIntent, PendingIntent.FLAG_UPDATE_CURRENT); + PendingIntent pendingIntent = getBroadcastPendingIntent(context, notificationDetails.id, notificationIntent); AlarmManager alarmManager = getAlarmManager(context); AlarmManagerCompat.setExactAndAllowWhileIdle(alarmManager, AlarmManager.RTC_WAKEUP, notificationTriggerTime, pendingIntent); saveScheduledNotification(context, notificationDetails); } + private static PendingIntent getActivityPendingIntent(Context context, int id, Intent intent) { + int flags = PendingIntent.FLAG_UPDATE_CURRENT; + if (VERSION.SDK_INT >= VERSION_CODES.M) { + flags |= PendingIntent.FLAG_IMMUTABLE; + } + return PendingIntent.getActivity(context, id, intent, flags); + } + + private static PendingIntent getBroadcastPendingIntent(Context context, int id, Intent intent) { + int flags = PendingIntent.FLAG_UPDATE_CURRENT; + if (VERSION.SDK_INT >= VERSION_CODES.M) { + flags |= PendingIntent.FLAG_IMMUTABLE; + } + return PendingIntent.getBroadcast(context, id, intent, flags); + } + private static void repeatNotification(Context context, NotificationDetails notificationDetails, Boolean updateScheduledNotificationsCache) { long repeatInterval = calculateRepeatIntervalMilliseconds(notificationDetails); @@ -412,7 +432,7 @@ private static void repeatNotification(Context context, NotificationDetails noti String notificationDetailsJson = gson.toJson(notificationDetails); Intent notificationIntent = new Intent(context, ScheduledNotificationReceiver.class); notificationIntent.putExtra(NOTIFICATION_DETAILS, notificationDetailsJson); - PendingIntent pendingIntent = PendingIntent.getBroadcast(context, notificationDetails.id, notificationIntent, PendingIntent.FLAG_UPDATE_CURRENT); + PendingIntent pendingIntent = getBroadcastPendingIntent(context, notificationDetails.id, notificationIntent); AlarmManager alarmManager = getAlarmManager(context); if (BooleanUtils.getValue(notificationDetails.allowWhileIdle)) { @@ -473,6 +493,21 @@ private static int getDrawableResourceId(Context context, String name) { } @SuppressWarnings("unchecked") + private static byte[] castObjectToByteArray(Object data) { + byte[] byteArray; + // if data is deserialized by gson, it is of the wrong type and we have to convert it + if (data instanceof ArrayList) { + List l = (ArrayList) data; + byteArray = new byte[l.size()]; + for (int i = 0; i < l.size(); i++) { + byteArray[i] = (byte) l.get(i).intValue(); + } + } else { + byteArray = (byte[]) data; + } + return byteArray; + } + private static Bitmap getBitmapFromSource(Context context, Object data, BitmapSource bitmapSource) { Bitmap bitmap = null; if (bitmapSource == BitmapSource.DrawableResource) { @@ -480,39 +515,29 @@ private static Bitmap getBitmapFromSource(Context context, Object data, BitmapSo } else if (bitmapSource == BitmapSource.FilePath) { bitmap = BitmapFactory.decodeFile((String) data); } else if (bitmapSource == BitmapSource.ByteArray) { - byte[] byteArray; - // if data is deserialized by gson, it is of the wrong type and we have to convert it - if (data instanceof ArrayList) { - List l = (ArrayList) data; - byteArray = new byte[l.size()]; - for (int i = 0; i < l.size(); i++) { - byteArray[i] = (byte) l.get(i).intValue(); - } - } else { - byteArray = (byte[]) data; - } + byte[] byteArray = castObjectToByteArray(data); bitmap = BitmapFactory.decodeByteArray(byteArray, 0, byteArray.length); } return bitmap; } - private static IconCompat getIconFromSource(Context context, String iconPath, IconSource iconSource) { + private static IconCompat getIconFromSource(Context context, Object data, IconSource iconSource) { IconCompat icon = null; switch (iconSource) { case DrawableResource: - icon = IconCompat.createWithResource(context, getDrawableResourceId(context, iconPath)); + icon = IconCompat.createWithResource(context, getDrawableResourceId(context, (String) data)); break; case BitmapFilePath: - icon = IconCompat.createWithBitmap(BitmapFactory.decodeFile(iconPath)); + icon = IconCompat.createWithBitmap(BitmapFactory.decodeFile((String) data)); break; case ContentUri: - icon = IconCompat.createWithContentUri(iconPath); + icon = IconCompat.createWithContentUri((String) data); break; case FlutterBitmapAsset: try { FlutterLoader flutterLoader = FlutterInjector.instance().flutterLoader(); - AssetFileDescriptor assetFileDescriptor = context.getAssets().openFd(flutterLoader.getLookupKeyForAsset(iconPath)); + AssetFileDescriptor assetFileDescriptor = context.getAssets().openFd(flutterLoader.getLookupKeyForAsset((String) data)); FileInputStream fileInputStream = assetFileDescriptor.createInputStream(); icon = IconCompat.createWithBitmap(BitmapFactory.decodeStream(fileInputStream)); fileInputStream.close(); @@ -521,6 +546,9 @@ private static IconCompat getIconFromSource(Context context, String iconPath, Ic throw new RuntimeException(e); } break; + case ByteArray: + byte[] byteArray = castObjectToByteArray(data); + icon = IconCompat.createWithData(byteArray, 0, byteArray.length); default: break; } @@ -1209,7 +1237,7 @@ private boolean hasInvalidIcon(Result result, String icon) { private void cancelNotification(Integer id, String tag) { Intent intent = new Intent(applicationContext, ScheduledNotificationReceiver.class); - PendingIntent pendingIntent = PendingIntent.getBroadcast(applicationContext, id, intent, PendingIntent.FLAG_UPDATE_CURRENT); + PendingIntent pendingIntent = getBroadcastPendingIntent(applicationContext, id, intent); AlarmManager alarmManager = getAlarmManager(applicationContext); alarmManager.cancel(pendingIntent); NotificationManagerCompat notificationManager = getNotificationManager(applicationContext); @@ -1233,7 +1261,7 @@ private void cancelAllNotifications(Result result) { Intent intent = new Intent(applicationContext, ScheduledNotificationReceiver.class); for (NotificationDetails scheduledNotification : scheduledNotifications) { - PendingIntent pendingIntent = PendingIntent.getBroadcast(applicationContext, scheduledNotification.id, intent, PendingIntent.FLAG_UPDATE_CURRENT); + PendingIntent pendingIntent = getBroadcastPendingIntent(applicationContext, scheduledNotification.id, intent); AlarmManager alarmManager = getAlarmManager(applicationContext); alarmManager.cancel(pendingIntent); } diff --git a/flutter_local_notifications/android/src/main/java/com/dexterous/flutterlocalnotifications/models/IconSource.java b/flutter_local_notifications/android/src/main/java/com/dexterous/flutterlocalnotifications/models/IconSource.java index f37613c51..7ea8db878 100644 --- a/flutter_local_notifications/android/src/main/java/com/dexterous/flutterlocalnotifications/models/IconSource.java +++ b/flutter_local_notifications/android/src/main/java/com/dexterous/flutterlocalnotifications/models/IconSource.java @@ -7,5 +7,6 @@ public enum IconSource { DrawableResource, BitmapFilePath, ContentUri, - FlutterBitmapAsset + FlutterBitmapAsset, + ByteArray } diff --git a/flutter_local_notifications/android/src/main/java/com/dexterous/flutterlocalnotifications/models/NotificationDetails.java b/flutter_local_notifications/android/src/main/java/com/dexterous/flutterlocalnotifications/models/NotificationDetails.java index d4bf92ca5..91a0a7288 100644 --- a/flutter_local_notifications/android/src/main/java/com/dexterous/flutterlocalnotifications/models/NotificationDetails.java +++ b/flutter_local_notifications/android/src/main/java/com/dexterous/flutterlocalnotifications/models/NotificationDetails.java @@ -191,10 +191,10 @@ public static NotificationDetails from(Map arguments) { notificationDetails.body = (String) arguments.get(BODY); notificationDetails.scheduledDateTime = (String) arguments.get(SCHEDULED_DATE_TIME); notificationDetails.timeZoneName = (String) arguments.get(TIME_ZONE_NAME); - if(arguments.containsKey(SCHEDULED_NOTIFICATION_REPEAT_FREQUENCY)) { + if (arguments.containsKey(SCHEDULED_NOTIFICATION_REPEAT_FREQUENCY)) { notificationDetails.scheduledNotificationRepeatFrequency = ScheduledNotificationRepeatFrequency.values()[(Integer) arguments.get(SCHEDULED_NOTIFICATION_REPEAT_FREQUENCY)]; } - if(arguments.containsKey(MATCH_DATE_TIME_COMPONENTS)) { + if (arguments.containsKey(MATCH_DATE_TIME_COMPONENTS)) { notificationDetails.matchDateTimeComponents = DateTimeComponents.values()[(Integer) arguments.get(MATCH_DATE_TIME_COMPONENTS)]; } if (arguments.containsKey(MILLISECONDS_SINCE_EPOCH)) { @@ -214,7 +214,7 @@ public static NotificationDetails from(Map arguments) { if (arguments.containsKey(DAY)) { notificationDetails.day = (Integer) arguments.get(DAY); } - + readPlatformSpecifics(arguments, notificationDetails); return notificationDetails; } @@ -299,8 +299,8 @@ private static void readGroupingInformation(NotificationDetails notificationDeta private static void readSoundInformation(NotificationDetails notificationDetails, Map platformChannelSpecifics) { notificationDetails.playSound = (Boolean) platformChannelSpecifics.get(PLAY_SOUND); notificationDetails.sound = (String) platformChannelSpecifics.get(SOUND); - Integer soundSourceIndex = (Integer)platformChannelSpecifics.get(SOUND_SOURCE); - if(soundSourceIndex != null) { + Integer soundSourceIndex = (Integer) platformChannelSpecifics.get(SOUND_SOURCE); + if (soundSourceIndex != null) { notificationDetails.soundSource = SoundSource.values()[soundSourceIndex]; } } @@ -372,7 +372,7 @@ private static PersonDetails readPersonDetails(Map person) { return null; } Boolean bot = (Boolean) person.get(BOT); - String icon = (String) person.get(ICON); + Object icon = person.get(ICON); Integer iconSourceIndex = (Integer) person.get(ICON_SOURCE); IconSource iconSource = iconSourceIndex == null ? null : IconSource.values()[iconSourceIndex]; Boolean important = (Boolean) person.get(IMPORTANT); diff --git a/flutter_local_notifications/android/src/main/java/com/dexterous/flutterlocalnotifications/models/PersonDetails.java b/flutter_local_notifications/android/src/main/java/com/dexterous/flutterlocalnotifications/models/PersonDetails.java index 8c080c2c5..8a9cdb89e 100644 --- a/flutter_local_notifications/android/src/main/java/com/dexterous/flutterlocalnotifications/models/PersonDetails.java +++ b/flutter_local_notifications/android/src/main/java/com/dexterous/flutterlocalnotifications/models/PersonDetails.java @@ -7,14 +7,14 @@ @Keep public class PersonDetails implements Serializable { public Boolean bot; - public String icon; + public Object icon; public IconSource iconBitmapSource; public Boolean important; public String key; public String name; public String uri; - public PersonDetails(Boolean bot, String icon, IconSource iconSource, Boolean important, String key, String name, String uri) { + public PersonDetails(Boolean bot, Object icon, IconSource iconSource, Boolean important, String key, String name, String uri) { this.bot = bot; this.icon = icon; this.iconBitmapSource = iconSource; diff --git a/flutter_local_notifications/example/analysis_options.yaml b/flutter_local_notifications/example/analysis_options.yaml index 300cf1c0c..14ccfc93f 100644 --- a/flutter_local_notifications/example/analysis_options.yaml +++ b/flutter_local_notifications/example/analysis_options.yaml @@ -1,3 +1,6 @@ +analyzer: + exclude: + - lib/generated_plugin_registrant.dart linter: rules: - always_declare_return_types @@ -161,4 +164,4 @@ linter: - use_string_buffers - use_to_and_as_if_applicable - valid_regexps - - void_checks \ No newline at end of file + - void_checks diff --git a/flutter_local_notifications/example/android/app/src/main/AndroidManifest.xml b/flutter_local_notifications/example/android/app/src/main/AndroidManifest.xml index b219c2694..0b2815d0d 100644 --- a/flutter_local_notifications/example/android/app/src/main/AndroidManifest.xml +++ b/flutter_local_notifications/example/android/app/src/main/AndroidManifest.xml @@ -11,7 +11,8 @@ android:hardwareAccelerated="true" android:windowSoftInputMode="adjustResize" android:showWhenLocked="true" - android:turnScreenOn="true"> + android:turnScreenOn="true" + android:exported="true">