From 31ef1ccd9810fcbc5b0237848fb1899d35e6ea85 Mon Sep 17 00:00:00 2001 From: David Britch Date: Thu, 3 Dec 2020 11:43:55 +0000 Subject: [PATCH] Add scheduled notifications example. (#679) --- .../AlarmHandler.cs | 19 ++++++ .../AndroidNotificationManager.cs | 59 +++++++++++++++---- .../LocalNotifications.Android.csproj | 4 +- .../MainActivity.cs | 4 +- .../Properties/AndroidManifest.xml | 8 +-- .../iOSNotificationManager.cs | 40 +++++++++---- .../LocalNotifications/App.xaml.cs | 2 +- .../INotificationManager.cs | 5 +- .../LocalNotifications/MainPage.xaml | 12 +++- .../LocalNotifications/MainPage.xaml.cs | 10 +++- 10 files changed, 124 insertions(+), 39 deletions(-) create mode 100644 LocalNotifications/LocalNotifications/LocalNotifications.Android/AlarmHandler.cs diff --git a/LocalNotifications/LocalNotifications/LocalNotifications.Android/AlarmHandler.cs b/LocalNotifications/LocalNotifications/LocalNotifications.Android/AlarmHandler.cs new file mode 100644 index 0000000000..9f5b4fd67c --- /dev/null +++ b/LocalNotifications/LocalNotifications/LocalNotifications.Android/AlarmHandler.cs @@ -0,0 +1,19 @@ +using Android.Content; + +namespace LocalNotifications.Droid +{ + [BroadcastReceiver(Enabled = true, Label = "Local Notifications Broadcast Receiver")] + public class AlarmHandler : BroadcastReceiver + { + public override void OnReceive(Context context, Intent intent) + { + if (intent?.Extras != null) + { + string title = intent.GetStringExtra(AndroidNotificationManager.TitleKey); + string message = intent.GetStringExtra(AndroidNotificationManager.MessageKey); + + AndroidNotificationManager.Instance.Show(title, message); + } + } + } +} diff --git a/LocalNotifications/LocalNotifications/LocalNotifications.Android/AndroidNotificationManager.cs b/LocalNotifications/LocalNotifications/LocalNotifications.Android/AndroidNotificationManager.cs index ef50c03bdb..43487edb3c 100644 --- a/LocalNotifications/LocalNotifications/LocalNotifications.Android/AndroidNotificationManager.cs +++ b/LocalNotifications/LocalNotifications/LocalNotifications.Android/AndroidNotificationManager.cs @@ -15,36 +15,33 @@ public class AndroidNotificationManager : INotificationManager const string channelId = "default"; const string channelName = "Default"; const string channelDescription = "The default channel for notifications."; - const int pendingIntentId = 0; public const string TitleKey = "title"; public const string MessageKey = "message"; bool channelInitialized = false; - int messageId = -1; + int messageId = 0; + int pendingIntentId = 0; + NotificationManager manager; public event EventHandler NotificationReceived; + public static AndroidNotificationManager Instance { get; private set; } + public void Initialize() { CreateNotificationChannel(); + Instance = this; } - public int ScheduleNotification(string title, string message) + public void Show(string title, string message) { - if (!channelInitialized) - { - CreateNotificationChannel(); - } - - messageId++; - Intent intent = new Intent(AndroidApp.Context, typeof(MainActivity)); intent.PutExtra(TitleKey, title); intent.PutExtra(MessageKey, message); - PendingIntent pendingIntent = PendingIntent.GetActivity(AndroidApp.Context, pendingIntentId, intent, PendingIntentFlags.OneShot); + PendingIntent pendingIntent = PendingIntent.GetActivity(AndroidApp.Context, pendingIntentId++, intent, PendingIntentFlags.UpdateCurrent); NotificationCompat.Builder builder = new NotificationCompat.Builder(AndroidApp.Context, channelId) .SetContentIntent(pendingIntent) @@ -55,9 +52,31 @@ public int ScheduleNotification(string title, string message) .SetDefaults((int)NotificationDefaults.Sound | (int)NotificationDefaults.Vibrate); Notification notification = builder.Build(); - manager.Notify(messageId, notification); + manager.Notify(messageId++, notification); + } - return messageId; + public void SendNotification(string title, string message, DateTime? notifyTime = null) + { + if (!channelInitialized) + { + CreateNotificationChannel(); + } + + if (notifyTime != null) + { + Intent intent = new Intent(AndroidApp.Context, typeof(AlarmHandler)); + intent.PutExtra(TitleKey, title); + intent.PutExtra(MessageKey, message); + + PendingIntent pendingIntent = PendingIntent.GetBroadcast(AndroidApp.Context, pendingIntentId++, intent, PendingIntentFlags.CancelCurrent); + long triggerTime = GetNotifyTime(notifyTime.Value); + AlarmManager alarmManager = GetAlarmManager(); + alarmManager.Set(AlarmType.RtcWakeup, triggerTime, pendingIntent); + } + else + { + Show(title, message); + } } public void ReceiveNotification(string title, string message) @@ -86,5 +105,19 @@ void CreateNotificationChannel() channelInitialized = true; } + + AlarmManager GetAlarmManager() + { + AlarmManager alarmManager = Android.App.Application.Context.GetSystemService(Context.AlarmService) as AlarmManager; + return alarmManager; + } + + long GetNotifyTime(DateTime notifyTime) + { + DateTime utcTime = TimeZoneInfo.ConvertTimeToUtc(notifyTime); + double epochDiff = (new DateTime(1970, 1, 1) - DateTime.MinValue).TotalSeconds; + long utcAlarmTime = utcTime.AddSeconds(-epochDiff).Ticks / 10000; + return utcAlarmTime; // milliseconds + } } } \ No newline at end of file diff --git a/LocalNotifications/LocalNotifications/LocalNotifications.Android/LocalNotifications.Android.csproj b/LocalNotifications/LocalNotifications/LocalNotifications.Android/LocalNotifications.Android.csproj index 001cb0d902..cfc72ae76f 100644 --- a/LocalNotifications/LocalNotifications/LocalNotifications.Android/LocalNotifications.Android.csproj +++ b/LocalNotifications/LocalNotifications/LocalNotifications.Android/LocalNotifications.Android.csproj @@ -15,7 +15,6 @@ Properties\AndroidManifest.xml Resources Assets - false v9.0 true true @@ -60,6 +59,7 @@ + @@ -87,7 +87,7 @@ - {48AB845E-F987-41F0-B81A-1C178087A374} + {A7BE66D7-177D-4188-87A1-E6948B3D1C7C} LocalNotifications diff --git a/LocalNotifications/LocalNotifications/LocalNotifications.Android/MainActivity.cs b/LocalNotifications/LocalNotifications/LocalNotifications.Android/MainActivity.cs index 60ef293656..d144da3611 100644 --- a/LocalNotifications/LocalNotifications/LocalNotifications.Android/MainActivity.cs +++ b/LocalNotifications/LocalNotifications/LocalNotifications.Android/MainActivity.cs @@ -36,8 +36,8 @@ void CreateNotificationFromIntent(Intent intent) { if (intent?.Extras != null) { - string title = intent.Extras.GetString(AndroidNotificationManager.TitleKey); - string message = intent.Extras.GetString(AndroidNotificationManager.MessageKey); + string title = intent.GetStringExtra(AndroidNotificationManager.TitleKey); + string message = intent.GetStringExtra(AndroidNotificationManager.MessageKey); DependencyService.Get().ReceiveNotification(title, message); } diff --git a/LocalNotifications/LocalNotifications/LocalNotifications.Android/Properties/AndroidManifest.xml b/LocalNotifications/LocalNotifications/LocalNotifications.Android/Properties/AndroidManifest.xml index a340dd821b..55494cc80a 100644 --- a/LocalNotifications/LocalNotifications/LocalNotifications.Android/Properties/AndroidManifest.xml +++ b/LocalNotifications/LocalNotifications/LocalNotifications.Android/Properties/AndroidManifest.xml @@ -1,6 +1,6 @@ - + - - - + + + diff --git a/LocalNotifications/LocalNotifications/LocalNotifications.iOS/iOSNotificationManager.cs b/LocalNotifications/LocalNotifications/LocalNotifications.iOS/iOSNotificationManager.cs index 67db24bc81..9d3b64ae74 100644 --- a/LocalNotifications/LocalNotifications/LocalNotifications.iOS/iOSNotificationManager.cs +++ b/LocalNotifications/LocalNotifications/LocalNotifications.iOS/iOSNotificationManager.cs @@ -1,4 +1,5 @@ using System; +using Foundation; using UserNotifications; using Xamarin.Forms; @@ -7,7 +8,7 @@ namespace LocalNotifications.iOS { public class iOSNotificationManager : INotificationManager { - int messageId = -1; + int messageId = 0; bool hasNotificationsPermission; @@ -22,12 +23,12 @@ public void Initialize() }); } - public int ScheduleNotification(string title, string message) + public void SendNotification(string title, string message, DateTime? notifyTime = null) { // EARLY OUT: app doesn't have permissions - if(!hasNotificationsPermission) + if (!hasNotificationsPermission) { - return -1; + return; } messageId++; @@ -38,11 +39,19 @@ public int ScheduleNotification(string title, string message) Subtitle = "", Body = message, Badge = 1 - }; + }; - // Local notifications can be time or location based - // Create a time-based trigger, interval is in seconds and must be greater than 0 - var trigger = UNTimeIntervalNotificationTrigger.CreateTrigger(0.25, false); + UNNotificationTrigger trigger; + if (notifyTime != null) + { + // Create a calendar-based trigger. + trigger = UNCalendarNotificationTrigger.CreateTrigger(GetNSDateComponents(notifyTime.Value), false); + } + else + { + // Create a time-based trigger, interval is in seconds and must be greater than 0. + trigger = UNTimeIntervalNotificationTrigger.CreateTrigger(0.25, false); + } var request = UNNotificationRequest.FromIdentifier(messageId.ToString(), content, trigger); UNUserNotificationCenter.Current.AddNotificationRequest(request, (err) => @@ -52,8 +61,6 @@ public int ScheduleNotification(string title, string message) throw new Exception($"Failed to schedule notification: {err}"); } }); - - return messageId; } public void ReceiveNotification(string title, string message) @@ -65,5 +72,18 @@ public void ReceiveNotification(string title, string message) }; NotificationReceived?.Invoke(null, args); } + + NSDateComponents GetNSDateComponents(DateTime dateTime) + { + return new NSDateComponents + { + Month = dateTime.Month, + Day = dateTime.Day, + Year = dateTime.Year, + Hour = dateTime.Hour, + Minute = dateTime.Minute, + Second = dateTime.Second + }; + } } } \ No newline at end of file diff --git a/LocalNotifications/LocalNotifications/LocalNotifications/App.xaml.cs b/LocalNotifications/LocalNotifications/LocalNotifications/App.xaml.cs index ca98935754..d538fd042f 100644 --- a/LocalNotifications/LocalNotifications/LocalNotifications/App.xaml.cs +++ b/LocalNotifications/LocalNotifications/LocalNotifications/App.xaml.cs @@ -8,7 +8,7 @@ public App() { InitializeComponent(); - // use the dependency service to get a platform-specific implementation and initialize it + // Use the dependency service to get a platform-specific implementation and initialize it. DependencyService.Get().Initialize(); MainPage = new MainPage(); diff --git a/LocalNotifications/LocalNotifications/LocalNotifications/INotificationManager.cs b/LocalNotifications/LocalNotifications/LocalNotifications/INotificationManager.cs index a81db0544c..536f850e7e 100644 --- a/LocalNotifications/LocalNotifications/LocalNotifications/INotificationManager.cs +++ b/LocalNotifications/LocalNotifications/LocalNotifications/INotificationManager.cs @@ -5,11 +5,8 @@ namespace LocalNotifications public interface INotificationManager { event EventHandler NotificationReceived; - void Initialize(); - - int ScheduleNotification(string title, string message); - + void SendNotification(string title, string message, DateTime? notifyTime = null); void ReceiveNotification(string title, string message); } } diff --git a/LocalNotifications/LocalNotifications/LocalNotifications/MainPage.xaml b/LocalNotifications/LocalNotifications/LocalNotifications/MainPage.xaml index ba9a212210..790770ca54 100644 --- a/LocalNotifications/LocalNotifications/LocalNotifications/MainPage.xaml +++ b/LocalNotifications/LocalNotifications/LocalNotifications/MainPage.xaml @@ -9,13 +9,21 @@ Padding="10"> -