Example #1
0
        /// <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);
        }
Example #2
0
        // 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));
                    }
                }
            }
        }
Example #3
0
        // 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)));
                    }
                }
            }
        }
Example #4
0
        /// <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();
        }
Example #5
0
        /// <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);
            }
        }
Example #6
0
        /// <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);
                }
            }
        }
Example #7
0
        /// <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);
                }
            }
        }
Example #8
0
        /// <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);
            }
        }
Example #9
0
        /// <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);
        }
Example #10
0
        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));
        }