/// <summary>
        /// Called just prior to a notification being presented while the app is in the foreground.
        /// </summary>
        /// <param name="center"></param>
        /// <param name="notification"></param>
        /// <param name="completionHandler"></param>
        public override async void WillPresentNotification(UNUserNotificationCenter center, UNNotification notification, Action <UNNotificationPresentationOptions> completionHandler)
        {
            string identifier = notification?.Request?.Identifier;

            SensusServiceHelper.Get().Logger.Log("Notification delivered:  " + (identifier ?? "[null identifier]"), LoggingLevel.Normal, GetType());

            // if the notification is for a callback, service the callback and do not show the notification. we use the local
            // notification loop to schedule callback events and don't want to display the messages when the app is foregrounded.
            NSDictionary         notificationInfo  = notification?.Request?.Content?.UserInfo;
            iOSCallbackScheduler callbackScheduler = SensusContext.Current.CallbackScheduler as iOSCallbackScheduler;

            if (callbackScheduler.IsCallback(notificationInfo))
            {
                await callbackScheduler.RaiseCallbackAsync(notificationInfo);

                completionHandler?.Invoke(UNNotificationPresentationOptions.None);
            }
            else if (identifier == Notifier.PENDING_SURVEY_TEXT_NOTIFICATION_ID)
            {
                completionHandler?.Invoke(UNNotificationPresentationOptions.Alert | UNNotificationPresentationOptions.Badge | UNNotificationPresentationOptions.Sound);
            }
            else if (identifier == Notifier.PENDING_SURVEY_BADGE_NOTIFICATION_ID)
            {
                completionHandler?.Invoke(UNNotificationPresentationOptions.Badge);
            }
            else
            {
                completionHandler?.Invoke(UNNotificationPresentationOptions.Alert | UNNotificationPresentationOptions.Badge | UNNotificationPresentationOptions.Sound);
            }
        }
        public async Task IssueNotificationAsync(UNNotificationRequest request)
        {
            // although we should never, we might be getting in null requests from somewhere. bail if we do.
            if (request == null)
            {
                SensusException.Report("Null notification request.");
                return;
            }

            // don't issue silent notifications from the background, as they will be displayed to the user upon delivery, and this will confuse the user (they're
            // not designed to be seen). this can happen in a race condition where sensus transitions to the background but has a small amount of time to execute,
            // and in that time a silent callback (e.g., for local data store) is scheduled. checking for background state below will help mitigate this.
            bool abort = false;

            SensusContext.Current.MainThreadSynchronizer.ExecuteThreadSafe(() =>
            {
                iOSCallbackScheduler callbackScheduler = SensusContext.Current.CallbackScheduler as iOSCallbackScheduler;
                ScheduledCallback callback             = callbackScheduler.TryGetCallback(request?.Content?.UserInfo);
                abort = (callback?.Silent ?? false) && UIApplication.SharedApplication.ApplicationState == UIApplicationState.Background;
            });

            if (abort)
            {
                SensusServiceHelper.Get().Logger.Log("Aborting notification:  Will not issue silent notification from background.", LoggingLevel.Normal, GetType());
                return;
            }

            await UNUserNotificationCenter.Current.AddNotificationRequestAsync(request);

            SensusServiceHelper.Get().Logger.Log("Notification " + request.Identifier + " requested for " + ((request.Trigger as UNCalendarNotificationTrigger)?.NextTriggerDate.ToString() ?? "[time not specified]") + ".", LoggingLevel.Normal, GetType());
        }
Beispiel #3
0
        /// <summary>
        /// Pre-10.0:  Handles notifications received when the app is in the foreground. See <see cref="UNUserNotificationDelegate.WillPresentNotification"/> for
        /// the corresponding handler for iOS 10.0 and above.
        /// </summary>
        /// <param name="application">Application.</param>
        /// <param name="notification">Notification.</param>
        public override void ReceivedLocalNotification(UIApplication application, UILocalNotification notification)
        {
            // UILocalNotifications were obsoleted in iOS 10.0, and we should not be receiving them via this app delegate
            // method. we won't have any idea how to service them on iOS 10.0 and above. report the problem and bail.
            if (UIDevice.CurrentDevice.CheckSystemVersion(10, 0))
            {
                SensusException.Report("Received UILocalNotification in iOS 10 or later.");
            }
            else
            {
                // we're in iOS < 10.0, which means we should have a notifier and scheduler to handle the notification.

                // cancel notification (removing it from the tray), since it has served its purpose
                (SensusContext.Current.Notifier as IUILocalNotificationNotifier)?.CancelNotification(notification);

                iOSCallbackScheduler callbackScheduler = SensusContext.Current.CallbackScheduler as iOSCallbackScheduler;

                if (callbackScheduler == null)
                {
                    SensusException.Report("We don't have an iOSCallbackScheduler.");
                }
                else if (notification.UserInfo == null)
                {
                    SensusException.Report("Null user info passed to ReceivedLocalNotification.");
                }
                else
                {
                    // run asynchronously to release the UI thread
                    System.Threading.Tasks.Task.Run(async() =>
                    {
                        // we've tried pulling some of the code below out of the UI thread, but we do not receive/process
                        // the callback notifications when doing so..
                        await SensusContext.Current.MainThreadSynchronizer.ExecuteThreadSafe(async() =>
                        {
                            // service the callback if we've got one (not all notification userinfo bundles are for callbacks)
                            if (callbackScheduler.IsCallback(notification.UserInfo))
                            {
                                await callbackScheduler.ServiceCallbackAsync(notification.UserInfo);
                            }

                            // check whether the user opened the notification to open sensus, indicated by an application state that is not active. we'll
                            // also get notifications when the app is active, since we use them for timed callback events.
                            if (application.ApplicationState != UIApplicationState.Active)
                            {
                                // if the user opened the notification, display the page associated with the notification, if any.
                                callbackScheduler.OpenDisplayPage(notification.UserInfo);

                                // provide some generic feedback if the user responded to a silent callback notification
                                if (callbackScheduler.TryGetCallback(notification.UserInfo)?.Silent ?? false)
                                {
                                    await SensusServiceHelper.Get().FlashNotificationAsync("Study Updated.");
                                }
                            }
                        });
                    });
                }
            }
        }
        /// <summary>
        /// Called just prior to a notification being presented while the app is in the foreground.
        /// </summary>
        /// <param name="center"></param>
        /// <param name="notification"></param>
        /// <param name="completionHandler"></param>
        public override void WillPresentNotification(UNUserNotificationCenter center, UNNotification notification, Action <UNNotificationPresentationOptions> completionHandler)
        {
            SensusServiceHelper.Get().Logger.Log("Notification delivered:  " + (notification?.Request?.Identifier ?? "[null identifier]"), LoggingLevel.Normal, GetType());

            // long story:  app is backgrounded, and multiple non-silent sensus notifications appear in the iOS tray. the user taps one of these, which
            // dismisses the tapped notification and brings up sensus. upon activation sensus then updates and reissues all notifications. these reissued
            // notifications will come directly to the app as long as it's in the foreground. the original notifications that were in the iOS notification
            // tray will still be there, despite the fact that the notifications have been sent to the app via the current method. short story:  we need to
            // cancel each notification as it comes in to remove it from the notification center.
            SensusContext.Current.Notifier.CancelNotification(notification?.Request?.Identifier);

            iOSCallbackScheduler callbackScheduler = SensusContext.Current.CallbackScheduler as iOSCallbackScheduler;

            if (callbackScheduler.IsCallback(notification?.Request?.Content?.UserInfo))
            {
                callbackScheduler.ServiceCallbackAsync(notification?.Request?.Content?.UserInfo);
            }
        }
        /// <summary>
        /// Called when the user taps a notification.
        /// </summary>
        /// <param name="center"></param>
        /// <param name="response"></param>
        /// <param name="completionHandler"></param>
        public override void DidReceiveNotificationResponse(UNUserNotificationCenter center, UNNotificationResponse response, Action completionHandler)
        {
            UNNotificationRequest request          = response?.Notification?.Request;
            NSDictionary          notificationInfo = request?.Content?.UserInfo;

            if (notificationInfo != null)
            {
                SensusServiceHelper.Get().Logger.Log("Notification received user response:  " + (request.Identifier ?? "[null identifier]"), LoggingLevel.Normal, GetType());

                // if the notification is associated with a particular UI page to display, show that page now.
                iOSCallbackScheduler callbackScheduler = SensusContext.Current.CallbackScheduler as iOSCallbackScheduler;
                callbackScheduler.OpenDisplayPage(notificationInfo);

                // provide some generic feedback if the user responded to a silent notification. this should only happen in race cases where
                // a silent notification is issued just before we enter background.
                if (callbackScheduler.TryGetCallback(notificationInfo)?.Silent ?? false)
                {
                    SensusServiceHelper.Get().FlashNotificationAsync("Study Updated.");
                }
            }

            completionHandler();
        }