using System; using System.Collections.Generic; using System.Globalization; using System.Threading.Tasks; using Foundation; using UIKit; using UserNotifications; namespace gehGassi.LocalNotifications.Platforms { public class NotificationService : UNUserNotificationCenterDelegate, INotificationService, IUNUserNotificationCenterDelegate { public static readonly NSString TITLE = new NSString("Title"); public static readonly NSString BODY = new NSString("Body"); public static readonly NSString NOTIFICATION_ID = new NSString("NoticationId"); public static readonly NSString NOTIFICATION_ID_FIREBASE = new NSString("notification_id"); public static readonly NSString PAYLOAD = new NSString("Payload"); public static readonly NSString PAYLOAD_FIREBASE = new NSString("data"); public static readonly NSString PRESENT_ALERT = new NSString("PresentAlert"); public static readonly NSString PLAY_SOUND = new NSString("PlaySound"); public static readonly NSString SOUND = new NSString("Sound"); public static readonly NSString SHOW_BADGE = new NSString("ShowBadge"); public static readonly NSString BADGE_NUMBER = new NSString("BadgeNumber"); public static readonly NSString NOTIFICATION_LAUNCHED_APP = new NSString("NotificationLaunchedApp"); public static readonly NSString NOTIFICATION_REQUEST = new NSString("NotificationRequest"); public static readonly NSString NOTIFICATION_ISLOCAL = new NSString("NotificationIsLocal"); public NotificationService() { } public static void Initialize(NSDictionary options) { UNUserNotificationCenter.Current.Delegate = new LocalNotificationDelegate(); } private static NotificationTappedEventHandler onNotificationTapped; public event NotificationTappedEventHandler OnNotificationTapped { add => onNotificationTapped += value; remove => onNotificationTapped -= value; } public static void OnPlatformNotificationTapped(string payload, int? notificationId, bool isLocal) { NotificationEventArgs e = new NotificationEventArgs() { NotificationId = notificationId, Payload = payload, IsLocal = isLocal }; onNotificationTapped?.Invoke(e); } private static NotificationReceivedEventHandler onNotificationReceived; public event NotificationReceivedEventHandler OnNotificationReceived { add => onNotificationReceived += value; remove => onNotificationReceived -= value; } public static void OnPlatformNotificationReceived(string payload, int? notificationId, bool isLocal) { NotificationEventArgs e = new NotificationEventArgs() { NotificationId = notificationId, Payload = payload, IsLocal = isLocal }; onNotificationReceived?.Invoke(e); } public void RegisterForPushNotifications() { // Register your app for remote notifications. if (UIDevice.CurrentDevice.CheckSystemVersion(10, 0)) { // For iOS 10 display notification (sent via APNS) UNUserNotificationCenter.Current.Delegate = new LocalNotificationDelegate(); var authOptions = UNAuthorizationOptions.Alert | UNAuthorizationOptions.Badge | UNAuthorizationOptions.Sound; UNUserNotificationCenter.Current.RequestAuthorization(authOptions, async (granted, error) => { Console.WriteLine(granted); await Task.Delay(500); }); } else { // iOS 9 or before var allNotificationTypes = UIUserNotificationType.Alert | UIUserNotificationType.Badge | UIUserNotificationType.Sound; var settings = UIUserNotificationSettings.GetSettingsForTypes(allNotificationTypes, null); UIApplication.SharedApplication.RegisterUserNotificationSettings(settings); } UIApplication.SharedApplication.RegisterForRemoteNotifications(); } public void UnRegisterForPushNotifications() { } public static Task RequestPermissions() => RequestPermissionsAsync(); public static async Task RequestPermissionsAsync() { try { if (UIDevice.CurrentDevice.CheckSystemVersion(10, 0) == false) return true; UNNotificationSettings settings = await UNUserNotificationCenter.Current.GetNotificationSettingsAsync().ConfigureAwait(false); if (settings.AlertSetting == UNNotificationSetting.Enabled) return true; var authOptions = UNAuthorizationOptions.Alert | UNAuthorizationOptions.Badge | UNAuthorizationOptions.Sound; var (alertsAllowed, error) = await UNUserNotificationCenter.Current.RequestAuthorizationAsync(authOptions).ConfigureAwait(false); return alertsAllowed; } catch (Exception ex) { Console.WriteLine("Error in RequestPermissionsAsync: " + ex.Message); return false; } } private UNMutableNotificationContent BuildStandardNotificationContent(NotificationRequest notificationRequest) { UNMutableNotificationContent content = new UNMutableNotificationContent(); content.Title = notificationRequest.Title; content.Body = notificationRequest.Description; IosOptions iOSOptions = notificationRequest.iOS; if (!string.IsNullOrEmpty(iOSOptions.Sound)) content.Sound = UNNotificationSound.GetSound(iOSOptions.Sound); if (notificationRequest.iOS.BadgeNumber.HasValue) content.Badge = new NSNumber(iOSOptions.BadgeNumber.Value); content.Sound ??= UNNotificationSound.Default; content.UserInfo = BuildUserDictionary(notificationRequest); return content; } private NSDictionary BuildUserDictionary(NotificationRequest notificationRequest) { string notificationRequestJson = notificationRequest.ToJson(); NSMutableDictionary userDictionary = new NSMutableDictionary { { NOTIFICATION_ID, new NSNumber(notificationRequest.NotificationId) }, { TITLE, new NSString(notificationRequest.Title) }, { PRESENT_ALERT, new NSNumber(notificationRequest.iOS.PresentAlert) }, { PLAY_SOUND, new NSNumber(notificationRequest.iOS.PlaySound) }, { SHOW_BADGE, new NSNumber(notificationRequest.iOS.ShowBadge) }, { PAYLOAD, new NSString(notificationRequest.Payload) }, { NOTIFICATION_REQUEST, new NSString(notificationRequestJson) }, { NOTIFICATION_ISLOCAL, new NSNumber(1) } }; return userDictionary; } public async Task ShowAsync(int notificationId, string title, string description, string payload, AndroidOptions androidOptions = null, IosOptions iOSOptions = null) { UNCalendarNotificationTrigger trigger = null; try { if (!await RequestPermissionsAsync().ConfigureAwait(false)) return; iOSOptions ??= new IosOptions(); NotificationRequest notificationRequest = GetNotificationRequest(notificationId, title, description, payload, iOSOptions); UNMutableNotificationContent content = BuildStandardNotificationContent(notificationRequest); NSDateComponents notifyTime = GetNsDateComponentsFromDateTime(notificationRequest.NotifyTime); trigger = UNCalendarNotificationTrigger.CreateTrigger(notifyTime, false); await AddNotificationRequestAsync(notificationId.ToString(), content, trigger).ConfigureAwait(false); } catch (Exception ex) { Console.WriteLine("Error in Show: " + ex.Message); } finally { trigger?.Dispose(); } } public async Task ShowHourlyAsync(int notificationId, string title, string description, Time time, string payload, AndroidOptions androidOptions = null, IosOptions iOSOptions = null) { await RepeatAsync(notificationId, title, description, Day.None, time, NotificationRepeat.Hourly, payload, iOSOptions).ConfigureAwait(false); } public async Task ShowDailyAsync(int notificationId, string title, string description, Time time, string payload, AndroidOptions androidOptions = null, IosOptions iOSOptions = null) { await RepeatAsync(notificationId, title, description, Day.None, time, NotificationRepeat.Daily, payload, iOSOptions).ConfigureAwait(false); } public async Task ShowWeeklyAsync(int notificationId, string title, string description, Day weekDay, Time time, string payload, AndroidOptions androidOptions = null, IosOptions iOSOptions = null) { await RepeatAsync(notificationId, title, description, weekDay, time, NotificationRepeat.Weekly, payload, iOSOptions).ConfigureAwait(false); } public async Task ScheduleAsync(int notificationId, string title, string description, DateTime dateTime, string payload, AndroidOptions androidOptions = null, IosOptions iOSOptions = null) { UNCalendarNotificationTrigger trigger = null; try { if (!await RequestPermissionsAsync().ConfigureAwait(false)) return; if (iOSOptions == null) iOSOptions = new IosOptions(); //To Epoch timestamp var notifyTimeSinceEpoch = dateTime.GetMilliSecondsSinceEpoch(); NotificationRequest notificationRequest = GetNotificationRequest(notificationId, title, description, payload, iOSOptions); notificationRequest.NotifyTime = dateTime; notificationRequest.NotifyTimeSinceEpoch = notifyTimeSinceEpoch; UNMutableNotificationContent content = BuildStandardNotificationContent(notificationRequest); NSDateComponents notifyTime = GetNsDateComponentsFromDateTime((DateTime?)dateTime); trigger = UNCalendarNotificationTrigger.CreateTrigger(notifyTime, false); await AddNotificationRequestAsync(notificationId.ToString(), content, trigger).ConfigureAwait(false); } catch (Exception ex) { Console.WriteLine("Error in Schedule: " + ex.Message); } finally { trigger?.Dispose(); } } private async Task RepeatAsync(int notificationId, string title, string description, Day weekDay, Time time, NotificationRepeat repeatInterval, string payload, IosOptions iOSOptions) { UNCalendarNotificationTrigger trigger = null; try { iOSOptions ??= new IosOptions(); NotificationRequest notificationRequest = new NotificationRequest() { NotificationId = notificationId, Title = title, Description = description, CalledAt = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(), RepeatInterval = repeatInterval, WeekDay = weekDay, Time = time, Repeats = true, Payload = payload, iOS = iOSOptions, Android = null }; UNMutableNotificationContent content = BuildStandardNotificationContent(notificationRequest); NSDateComponents notifyTime = GetNsDateComponents(notificationRequest.WeekDay, notificationRequest.Time); trigger = UNCalendarNotificationTrigger.CreateTrigger(notifyTime, notificationRequest.Repeats); //Console.WriteLine("NextTriggerDate : " + trigger.NextTriggerDate); await AddNotificationRequestAsync(notificationId.ToString(), content, trigger); } catch (Exception ex) { Console.WriteLine("Error in Repeat[iOS]: " + ex.Message); } finally { trigger?.Dispose(); } } public async Task AddNotificationRequestAsync(string notificationId, UNNotificationContent notificationContent, UNCalendarNotificationTrigger trigger) { var notificationRequest = UNNotificationRequest.FromIdentifier(notificationId, notificationContent, trigger); await UNUserNotificationCenter.Current.AddNotificationRequestAsync(notificationRequest).ConfigureAwait(false); } public async Task AddNotificationRequestAsync(string notificationId, UNNotificationContent notificationContent, UNTimeIntervalNotificationTrigger trigger) { var notificationRequest = UNNotificationRequest.FromIdentifier(notificationId, notificationContent, trigger); await UNUserNotificationCenter.Current.AddNotificationRequestAsync(notificationRequest).ConfigureAwait(false); } public async Task CancelAsync(int notificationId) { try { await Task.Delay(1); string[] itemList = new[] { notificationId.ToString(CultureInfo.CurrentCulture) }; UNUserNotificationCenter.Current.RemovePendingNotificationRequests(itemList); UNUserNotificationCenter.Current.RemoveDeliveredNotifications(itemList); } catch (Exception ex) { Console.WriteLine("Error in Cancel: " + ex.Message); } } public async Task CancelAllAsync() { try { await Task.Delay(1); UNUserNotificationCenter.Current.RemoveAllPendingNotificationRequests(); UNUserNotificationCenter.Current.RemoveAllDeliveredNotifications(); } catch (Exception ex) { Console.WriteLine("Error in CancelAll: " + ex.Message); } } public async Task> GetPendingNotificationRequestsAsync() { await Task.Delay(1); List scheduledNotifications = new List(); var notifications = await UNUserNotificationCenter.Current.GetPendingNotificationRequestsAsync(); //UILocalNotification[] notifications = UIApplication.SharedApplication.ScheduledLocalNotifications; foreach (var notification in notifications) { if (notification != null) { var dictionary = GetParameters(notification.Content.UserInfo); string notificationRequestJson = (string)dictionary[NOTIFICATION_REQUEST]; if (!string.IsNullOrEmpty(notificationRequestJson)) { NotificationRequest notificationRequest = notificationRequestJson.JsonToObject(); scheduledNotifications.Add(notificationRequest); } else { scheduledNotifications.Add(new NotificationRequest() { NotificationId = int.Parse((string)dictionary[NOTIFICATION_ID]), Title = (string)dictionary[TITLE], Description = notification.Content.Body, Payload = (string)dictionary[PAYLOAD], }); //scheduledNotifications.Add(new NotificationRequest() //{ // NotificationId = int.Parse((string)dictionary[NOTIFICATION_ID]), // Title = (string)dictionary[TITLE], // Description = notification.Content.AlertBody, // Payload = (string)dictionary[PAYLOAD], //}); } } } return scheduledNotifications; } #region Others private static IDictionary GetParameters(NSDictionary dictionary) { var parameters = new Dictionary(); foreach (var data in dictionary) parameters.Add($"{data.Key}", $"{data.Value}"); return parameters; } private NSDateComponents GetNsDateComponentsFromDateTime(DateTime? notifyTime) { var dateTime = notifyTime ?? DateTime.Now.AddSeconds(1); return new NSDateComponents() { Day = dateTime.Day, Hour = dateTime.Hour, Minute = dateTime.Minute, Second = dateTime.Second }; } private NSDateComponents GetNsDateComponents(Day weekDay, Time time) { var notifyTime = new NSDateComponents(); notifyTime.Hour = time.Hour; notifyTime.Minute = time.Minute; notifyTime.Second = time.Second; if (weekDay != Day.None) notifyTime.Weekday = (int)weekDay; return notifyTime; } private NotificationRequest GetNotificationRequest(int notificationId, string title, string description, string payload, IosOptions iOSOptions) { return new NotificationRequest() { NotificationId = notificationId, Title = title, Description = description, Payload = payload, iOS = iOSOptions, }; } #endregion } }