/// <summary> /// Schedules a notification to be delivered. /// </summary> /// <param name="notification">The notification to deliver.</param> public PendingNotification ScheduleNotification(IGameNotification notification) { if (!Initialized) { throw new InvalidOperationException("Must call Initialize() first."); } if (notification == null || Platform == null) { return(null); } // If we queue, don't schedule immediately. // Also immediately schedule non-time based deliveries (for iOS) if ((mode & OperatingMode.Queue) != OperatingMode.Queue || notification.DeliveryTime == null) { Platform.ScheduleNotification(notification); } else if (!notification.Id.HasValue) { // Generate an ID for items that don't have one (just so they can be identified later) int id = Math.Abs(DateTime.Now.ToString("yyMMddHHmmssffffff").GetHashCode()); notification.Id = id; } // Register pending notification var result = new PendingNotification(notification); PendingNotifications.Add(result); return(result); }
// Clear foreground notifications and reschedule stuff from a file private void OnForegrounding() { PendingNotifications.Clear(); Platform.OnForeground(); // Deserialize saved items IList <IGameNotification> loaded = Serializer?.Deserialize(Platform); // Foregrounding if ((mode & OperatingMode.ClearOnForegrounding) == OperatingMode.ClearOnForegrounding) { // Clear on foregrounding Platform.CancelAllScheduledNotifications(); // Only reschedule in reschedule mode, and if we loaded any items if (loaded == null || (mode & OperatingMode.RescheduleAfterClearing) != OperatingMode.RescheduleAfterClearing) { return; } // Reschedule notifications from deserialization foreach (IGameNotification savedNotification in loaded) { if (savedNotification.DeliveryTime > DateTime.Now) { PendingNotification pendingNotification = ScheduleNotification(savedNotification); pendingNotification.Reschedule = true; } } } else { // Just create PendingNotification wrappers for all deserialized items. // We're not rescheduling them because they were not cleared if (loaded == null) { return; } foreach (IGameNotification savedNotification in loaded) { if (savedNotification.DeliveryTime > DateTime.Now) { PendingNotifications.Add(new PendingNotification(savedNotification)); } } } }
// Clear foreground notifications and reschedule stuff from a file private void OnForegrounding() { PendingNotifications.Clear(); _platform.OnForeground(); // Deserialize saved items var notifications = JsonUtility.FromJson <List <SerializableNotification> >(PlayerPrefs.GetString("notifications")); // Foregrounding if ((Mode & OperatingMode.ClearOnForegrounding) == OperatingMode.ClearOnForegrounding) { // Clear on foregrounding _platform.CancelAllScheduledNotifications(); // Only reschedule in reschedule mode, and if we loaded any items if (notifications == null || (Mode & OperatingMode.RescheduleAfterClearing) != OperatingMode.RescheduleAfterClearing) { return; } // Reschedule notifications from deserialization foreach (var savedNotification in notifications) { if (savedNotification.DeliveryTime > DateTime.Now) { var pendingNotification = ScheduleNotification(savedNotification.AsGameNotification(_platform)); pendingNotification.Reschedule = true; } } } else { // Just create PendingNotification wrappers for all deserialized items. // We're not rescheduling them because they were not cleared if (notifications == null) { return; } foreach (var savedNotification in notifications) { if (savedNotification.DeliveryTime > DateTime.Now) { PendingNotifications.Add(new PendingNotification(savedNotification.AsGameNotification(_platform))); } } } }
/// <summary> /// Cancels all scheduled notifications. /// </summary> /// <exception cref="InvalidOperationException"><see cref="Initialize"/> has not been called.</exception> public void CancelAllNotifications() { if (!Initialized) { throw new InvalidOperationException("Must call Initialize() first."); } if (Platform == null) { return; } Platform.CancelAllScheduledNotifications(); PendingNotifications.Clear(); }
/// <summary> /// Event fired by <see cref="_platform"/> when a notification is received. /// </summary> private void OnNotificationReceived(IGameNotification deliveredNotification) { // Ignore for background messages (this happens on Android sometimes) if (!_inForeground) { return; } // Find in pending list int deliveredIndex = PendingNotifications.FindIndex( scheduledNotification => scheduledNotification.Notification.Id == deliveredNotification.Id); if (deliveredIndex >= 0) { OnLocalNotificationDelivered?.Invoke(PendingNotifications[deliveredIndex]); PendingNotifications.RemoveAt(deliveredIndex); } }
/// <summary> /// Check pending list for expired notifications, when in queue mode. /// </summary> protected virtual void Update() { if ((mode & OperatingMode.Queue) != OperatingMode.Queue) { return; } // Check each pending notification for expiry, then remove it for (int i = PendingNotifications.Count - 1; i >= 0; --i) { PendingNotification queuedNotification = PendingNotifications[i]; DateTime? time = queuedNotification.Notification.DeliveryTime; if (time != null && time < DateTime.Now) { PendingNotifications.RemoveAt(i); LocalNotificationExpired?.Invoke(queuedNotification); } } }
/// <summary> /// Check pending list for expired notifications, when in queue mode. /// </summary> public override void OnUpdate(float deltaTime) { base.OnUpdate(deltaTime); if (PendingNotifications == null || !PendingNotifications.Any() || (mode & OperatingMode.Queue) != OperatingMode.Queue) { return; } // Check each pending notification for expiry, then remove it for (int i = PendingNotifications.Count - 1; i >= 0; --i) { PendingNotification queuedNotification = PendingNotifications[i]; DateTime? time = queuedNotification.Notification.DeliveryTime; if (time != null && time < DateTime.Now) { PendingNotifications.RemoveAt(i); LocalNotificationExpired?.Invoke(queuedNotification); } } }
/// <summary> /// Cancels a scheduled notification. /// </summary> /// <param name="notificationId">The ID of the notification to cancel.</param> /// <exception cref="InvalidOperationException"><see cref="Initialize"/> has not been called.</exception> public void CancelNotification(int notificationId) { if (!Initialized) { throw new InvalidOperationException("Must call Initialize() first."); } if (Platform == null) { return; } Platform.CancelNotification(notificationId); // Remove the cancelled notification from scheduled list int index = PendingNotifications.FindIndex(scheduledNotification => scheduledNotification.Notification.Id == notificationId); if (index >= 0) { PendingNotifications.RemoveAt(index); } }
/// <summary> /// Respond to application foreground/background events. /// </summary> protected void OnApplicationFocus(bool hasFocus) { if (Platform == null || !Initialized) { return; } inForeground = hasFocus; if (hasFocus) { OnForegrounding(); return; } Platform.OnBackground(); // Backgrounding // Queue future dated notifications if ((mode & OperatingMode.Queue) == OperatingMode.Queue) { // Filter out past events for (var i = PendingNotifications.Count - 1; i >= 0; i--) { PendingNotification pendingNotification = PendingNotifications[i]; // Ignore already scheduled ones if (pendingNotification.Notification.Scheduled) { continue; } // If a non-scheduled notification is in the past (or not within our threshold) // just remove it immediately if (pendingNotification.Notification.DeliveryTime != null && pendingNotification.Notification.DeliveryTime - DateTime.Now < MinimumNotificationTime) { PendingNotifications.RemoveAt(i); } } // Sort notifications by delivery time, if no notifications have a badge number set bool noBadgeNumbersSet = PendingNotifications.All(notification => notification.Notification.BadgeNumber == null); if (noBadgeNumbersSet && AutoBadging) { PendingNotifications.Sort((a, b) => { if (!a.Notification.DeliveryTime.HasValue) { return(1); } if (!b.Notification.DeliveryTime.HasValue) { return(-1); } return(a.Notification.DeliveryTime.Value.CompareTo(b.Notification.DeliveryTime.Value)); }); // Set badge numbers incrementally var badgeNum = 1; foreach (PendingNotification pendingNotification in PendingNotifications) { if (pendingNotification.Notification.DeliveryTime.HasValue && !pendingNotification.Notification.Scheduled) { pendingNotification.Notification.BadgeNumber = badgeNum++; } } } for (int i = PendingNotifications.Count - 1; i >= 0; i--) { PendingNotification pendingNotification = PendingNotifications[i]; // Ignore already scheduled ones if (pendingNotification.Notification.Scheduled) { continue; } // Schedule it now Platform.ScheduleNotification(pendingNotification.Notification); } // Clear badge numbers again (for saving) if (noBadgeNumbersSet && AutoBadging) { foreach (PendingNotification pendingNotification in PendingNotifications) { if (pendingNotification.Notification.DeliveryTime.HasValue) { pendingNotification.Notification.BadgeNumber = null; } } } } // Calculate notifications to save var notificationsToSave = new List <PendingNotification>(PendingNotifications.Count); foreach (PendingNotification pendingNotification in PendingNotifications) { // If we're in clear mode, add nothing unless we're in rescheduling mode // Otherwise add everything if ((mode & OperatingMode.ClearOnForegrounding) == OperatingMode.ClearOnForegrounding) { if ((mode & OperatingMode.RescheduleAfterClearing) != OperatingMode.RescheduleAfterClearing) { continue; } // In reschedule mode, add ones that have been scheduled, are marked for // rescheduling, and that have a time if (pendingNotification.Reschedule && pendingNotification.Notification.Scheduled && pendingNotification.Notification.DeliveryTime.HasValue) { notificationsToSave.Add(pendingNotification); } } else { // In non-clear mode, just add all scheduled notifications if (pendingNotification.Notification.Scheduled) { notificationsToSave.Add(pendingNotification); } } } // Save to disk Serializer.Serialize(notificationsToSave); }
public IEventStoreConnection Build() { if (m_configuration == null) { m_configuration = new EventStoreConnectionConfiguration(); m_configure(m_configuration); m_configuration.AssertConfigurationCompleted(); m_factory = new StorageFactory(); } var connectivityState = new EventStoreConnectionState(); var journalTable = new EventJournalTable(m_factory.CreateTable( m_configuration.StorageConnectionString, m_configuration.JournalTableName)); var deploymentTable = m_factory.CreateTable( m_configuration.StorageConnectionString, m_configuration.EventStoreDeploymentTableName); var sessionFactory = new EventStreamConsumingSessionFactory( m_factory.CreateBlobContainer( m_configuration.StorageConnectionString, m_configuration.StreamConsumerSessionsBlobContainerName)); var pipelineFactory = new EventMutationPipelineFactory( m_configuration.IncomingMessageMutators, m_configuration.OutgoingMessageMutators); var queues = Enumerable .Range(0, m_configuration.NotificationQueuePartitionCount) .Select(index => m_factory.CreateQueue( m_configuration.StorageConnectionString, m_configuration.NotificationQueueName + "-" + index.ToString("D3"))) .ToArray(); var eventJournal = new EventJournal(journalTable); var consumersRegistry = new EventStreamConsumers(deploymentTable); var consumersService = new ConsumersService(consumersRegistry, eventJournal); var notificationHub = new NotificationHub( new PollingJob("NotificationHubPollingJob", new PollingTimeout()), new NotificationsChannel(queues, new NotificationFormatter()), new ReceivedNotificationProcessor()); var pendingNotificationTable = m_factory.CreateTable(m_configuration.StorageConnectionString, m_configuration.PendingNotificationsTableName); var pendingNotifications = new PendingNotifications(pendingNotificationTable); var pendingNotificationsChaserTimeout = new PollingTimeout( TimeSpan.FromMinutes(Constants.Settings.PENDING_NOTIFICATIONS_CHASER_INITIAL_TIMEOUT_IN_MINUTES), Constants.Settings.PENDING_NOTIFICATIONS_CHASER_TIMEOUT_MULTIPLIER, Constants.Settings.PENDING_NOTIFICATIONS_CHASER_TIMEOUT_INCREASING_THRESHOLD, TimeSpan.FromMinutes(Constants.Settings.PENDING_NOTIFICATIONS_CHASER_MAX_TIMEOUT_IN_MINUTES)); var pendingNotificationsChaser = new PendingNotificationsChaser( pendingNotifications, notificationHub, new PollingJob("PendingNotificationsChaserPollingJob", pendingNotificationsChaserTimeout), m_factory.CreateBlobContainer( m_configuration.StorageConnectionString, m_configuration.PendingNotificationsChaserExclusiveAccessLockBlobContainerName).CreateBlockBlob( m_configuration.PendingNotificationsChaserExclusiveAccessLockBlobName)); connectivityState.ConnectionCreated += (sender, args) => { if (m_configuration.BackgroundProcessingEnabled) { foreach (var notificationListener in m_configuration.NotificationListeners) { notificationHub.Subscribe(notificationListener); } notificationHub.StartNotificationProcessing(args.Connection); pendingNotificationsChaser.Start(); } }; connectivityState.ConnectionClosing += (sender, args) => { if (m_configuration.BackgroundProcessingEnabled) { notificationHub.StopNotificationProcessing(); pendingNotificationsChaser.Stop(); } }; connectivityState.ConnectionClosed += (sender, args) => { if (m_configuration.BackgroundProcessingEnabled) { foreach (var notificationListener in m_configuration.NotificationListeners) { notificationHub.Unsubscribe(notificationListener); } } }; return(new EventStoreConnection( connectivityState, eventJournal, notificationHub, pendingNotifications, consumersRegistry, sessionFactory, pipelineFactory, consumersService)); }