コード例 #1
0
ファイル: Program.cs プロジェクト: HaKDMoDz/Zazumo
		public override void DidEnterBackground (UIApplication application)
		{
			ourTask = application.BeginBackgroundTask(delegate
			{    //this is the action that will run when the task expires
				if (ourTask != 0) //this check is because we want to avoid ending the same task twice
				{
				    application.EndBackgroundTask(ourTask); //end the task
				    ourTask = 0; //reset the id
				}
			});

		    //we start an asynchronous operation
		    //so that we make sure that DidEnterBackground
		    //executes normally
		    new System.Action(delegate
		    {
		        MonoGameGame.EnterBackground();
		
		        //Since we are in an asynchronous method,
		        //we have to make sure that EndBackgroundTask
		        //will run on the application's main thread
		        //or we might have unexpected behavior.
		        application.BeginInvokeOnMainThread(delegate
		        {
			            if (ourTask != 0) //same as above
			            {
			                application.EndBackgroundTask(ourTask);
			                ourTask = 0;
			            }
			       });
			}).BeginInvoke(null, null);	
		}
コード例 #2
0
        /// <Docs>Reference to the UIApplication that invoked this delegate method.</Docs>
        /// <remarks>Application are allocated approximately 5 seconds to complete this method. Application developers should use this
        /// time to save user data and tasks, and remove sensitive information from the screen.</remarks>
        /// <altmember cref="M:MonoTouch.UIKit.UIApplicationDelegate.WillEnterForeground"></altmember>
        /// <summary>
        /// Dids the enter background.
        /// </summary>
        /// <param name="application">Application.</param>
        public override void DidEnterBackground(UIApplication application)
        {
            Console.WriteLine("DidEnterBackground called...");

            // Ask iOS for additional background time and prepare upload.
            taskId = application.BeginBackgroundTask(delegate {
                if (taskId != 0)
                {
                    application.EndBackgroundTask(taskId);
                    taskId = 0;
                }
            });

            new System.Action(async delegate {
                await PrepareUpload();

                application.BeginInvokeOnMainThread(delegate {
                    if (taskId != 0)
                    {
                        application.EndBackgroundTask(taskId);
                        taskId = 0;
                    }
                });
            }).BeginInvoke(null, null);
        }
コード例 #3
0
		/// <Docs>Reference to the UIApplication that invoked this delegate method.</Docs>
		/// <remarks>Application are allocated approximately 5 seconds to complete this method. Application developers should use this
		/// time to save user data and tasks, and remove sensitive information from the screen.</remarks>
		/// <altmember cref="M:MonoTouch.UIKit.UIApplicationDelegate.WillEnterForeground"></altmember>
		/// <summary>
		/// Dids the enter background.
		/// </summary>
		/// <param name="application">Application.</param>
		public override void DidEnterBackground (UIApplication application)
		{
			Console.WriteLine("DidEnterBackground called...");

			// Ask iOS for additional background time and prepare upload.
			taskId = application.BeginBackgroundTask (delegate {
				if (taskId != 0) {
					application.EndBackgroundTask(taskId);
					taskId = 0;
				}
			});

			new System.Action (async delegate {

				await PrepareUpload();

				application.BeginInvokeOnMainThread(delegate {
					if (taskId != 0) {
						application.EndBackgroundTask(taskId);
						taskId = 0;
					}
				});

			}).BeginInvoke (null, null);
		}
コード例 #4
0
        public override void DidEnterBackground(UIApplication application)
        {
            ourTask = application.BeginBackgroundTask(delegate
            {                                               //this is the action that will run when the task expires
                if (ourTask != 0)                           //this check is because we want to avoid ending the same task twice
                {
                    application.EndBackgroundTask(ourTask); //end the task
                    ourTask = 0;                            //reset the id
                }
            });

            //we start an asynchronous operation
            //so that we make sure that DidEnterBackground
            //executes normally
            new System.Action(delegate
            {
                MonoGameGame.EnterBackground();

                //Since we are in an asynchronous method,
                //we have to make sure that EndBackgroundTask
                //will run on the application's main thread
                //or we might have unexpected behavior.
                application.BeginInvokeOnMainThread(delegate
                {
                    if (ourTask != 0)                 //same as above
                    {
                        application.EndBackgroundTask(ourTask);
                        ourTask = 0;
                    }
                });
            }).BeginInvoke(null, null);
        }
コード例 #5
0
ファイル: AppDelegate.cs プロジェクト: xcodeuuuuu66699/gMusic
        public void SetUpApp(UIApplication app)
        {
            try
            {
                Strings.Culture = new System.Globalization.CultureInfo(NSLocale.CurrentLocale.LanguageCode);
            }
            catch (Exception ex)
            {
                LogManager.Shared.Log($"Error setting Culture {System.Threading.Thread.CurrentThread.CurrentCulture}");
            }
            SimpleAuth.OnePassword.Activate();
            ApiManager.Shared.Load();
            App.AlertFunction = (t, m) => { new UIAlertView(t, m, null, "Ok").Show(); };
            App.Invoker       = app.BeginInvokeOnMainThread;
            App.OnPlaying     = () =>
            {
                if (playingBackground != 0)
                {
                    return;
                }
                playingBackground = app.BeginBackgroundTask(() =>
                {
                    app.EndBackgroundTask(playingBackground);
                    playingBackground = 0;
                });
            };
            App.OnStopped = () =>
            {
                if (playingBackground == 0)
                {
                    return;
                }
                app.EndBackgroundTask(playingBackground);
                playingBackground = 0;
            };

            App.OnShowSpinner = (title) => { BTProgressHUD.ShowContinuousProgress(title, ProgressHUD.MaskType.Clear); };

            App.OnDismissSpinner  = BTProgressHUD.Dismiss;
            App.OnCheckForOffline = (message) =>
            {
                var tcs = new System.Threading.Tasks.TaskCompletionSource <bool>();
                new AlertView(Strings.DoYouWantToContinue, message)
                {
                    { Strings.Continue, () => tcs.TrySetResult(true) },
                    { Strings.Nevermind, () => tcs.TrySetResult(false), true },
                }.Show(window.RootViewController);
                return(tcs.Task);
            };
            NotificationManager.Shared.LanguageChanged += (s, e) =>
            {
                window.RootViewController = new RootViewController();
            };
#pragma warning disable 4014
            App.Start();
#pragma warning restore 4014
            AutolockPowerWatcher.Shared.CheckStatus();
        }
コード例 #6
0
ファイル: AppDelegate.cs プロジェクト: caverject10/sensus
        // This method should be used to release shared resources and it should store the application state.
        // If your application supports background exection this method is called instead of WillTerminate
        // when the user quits.
        public async override void DidEnterBackground(UIApplication uiApplication)
        {
            nint enterBackgroundTaskId = uiApplication.BeginBackgroundTask(() =>
            {
                // not much to do if we run out of time. just report it.
                string message = "Ran out of background time while entering background.";
                SensusServiceHelper.Get().Logger.Log(message, LoggingLevel.Normal, GetType());
                SensusException.Report(message);
            });

            iOSSensusServiceHelper serviceHelper = SensusServiceHelper.Get() as iOSSensusServiceHelper;

            // if the callback scheduler is timer-based and gps is not running then we need to request remote notifications
            if (SensusContext.Current.CallbackScheduler is iOSTimerCallbackScheduler scheduler)
            {
                bool gpsIsRunning = SensusServiceHelper.Get().GetRunningProtocols().SelectMany(x => x.Probes).OfType <ListeningLocationProbe>().Any(x => x.Enabled);

                await scheduler.RequestNotificationsAsync(gpsIsRunning);
            }

            // save app state
            await serviceHelper.SaveAsync();

            uiApplication.EndBackgroundTask(enterBackgroundTaskId);
        }
コード例 #7
0
        public override void DidEnterBackground(UIApplication application)
        {
            // Console.WriteLine ("AppDelegate.DidEnterBackground method fired.");
            nint   taskID = 0;
            Action backgroundTimerExpired = delegate {
                application.EndBackgroundTask(taskID);
                taskID = UIApplication.BackgroundTaskInvalid;
            };

            taskID = application.BeginBackgroundTask(backgroundTimerExpired);

            myLocationDelegate.DumpLocationsBufferToDatabase();

            application.EndBackgroundTask(taskID);
            taskID = UIApplication.BackgroundTaskInvalid;
        }
コード例 #8
0
ファイル: AppDelegate.cs プロジェクト: jasonhan803/gMusic
        public void SetUpApp(UIApplication app)
        {
            SimpleAuth.OnePassword.Activate();
            ApiManager.Shared.Load();
            App.AlertFunction = (t, m) => { new UIAlertView(t, m, null, "Ok").Show(); };
            App.Invoker       = app.BeginInvokeOnMainThread;
            App.OnPlaying     = () =>
            {
                if (playingBackground != 0)
                {
                    return;
                }
                playingBackground = app.BeginBackgroundTask(() =>
                {
                    app.EndBackgroundTask(playingBackground);
                    playingBackground = 0;
                });
            };
            App.OnStopped = () =>
            {
                if (playingBackground == 0)
                {
                    return;
                }
                app.EndBackgroundTask(playingBackground);
                playingBackground = 0;
            };

            App.OnShowSpinner = (title) => { BTProgressHUD.ShowContinuousProgress(title, ProgressHUD.MaskType.Clear); };

            App.OnDismissSpinner  = BTProgressHUD.Dismiss;
            App.OnCheckForOffline = (message) =>
            {
                var tcs = new System.Threading.Tasks.TaskCompletionSource <bool>();
                new AlertView(Strings.DoYouWantToContinue, message)
                {
                    { Strings.Continue, () => tcs.TrySetResult(true) },
                    { Strings.Nevermind, () => tcs.TrySetResult(false), true },
                }.Show(window.RootViewController);
                return(tcs.Task);
            };
#pragma warning disable 4014
            App.Start();
#pragma warning restore 4014
        }
コード例 #9
0
 public override void WillEnterForeground(UIApplication application)
 {
     // Called as part of the transiton from background to active state.
     // Here you can undo many of the changes made on entering the background.
     if (_isInBackground)
     {
         application.EndBackgroundTask(_backgroundTaskId);
         _isInBackground = false;
     }
 }
コード例 #10
0
ファイル: AppDelegate.cs プロジェクト: cyrsis/LearnXamarin
 public override void WillEnterForeground(UIApplication app)
 {
     Logger.Log("WillEnterForeground() - coming from background into foreground");
     // End task, no longer needed.
     if (this.taskId != -1)
     {
         app.EndBackgroundTask(this.taskId);
         this.taskId = -1;
     }
 }
コード例 #11
0
        public override void WillEnterForeground(UIApplication app)
        {
            Logger.Log("WillEnterForeground() - coming from background into foreground");

            // ---------------------------------------------------------------
            // Exercise-1
            if (this.taskId != -1)
            {
                app.EndBackgroundTask(this.taskId);
                this.taskId = -1;
            }
        }
コード例 #12
0
        public override Thread RunInBackground(VoidDelegate task)
        {
            ParameterizedThreadStart pts = delegate {
                int           ourTask     = 0;
                UIApplication application = UIApplication.SharedApplication;

                if (UIDevice.CurrentDevice.IsMultitaskingSupported)
                {
                    application.BeginBackgroundTask(delegate {
                        //expired
                        if (ourTask != 0)
                        {
                            application.EndBackgroundTask(ourTask);
                            ourTask = 0;
                        }
                    });
                }

                task();

                if (ourTask != 0)
                {
                    application.BeginInvokeOnMainThread(delegate
                    {
                        if (ourTask != 0) //same as above
                        {
                            application.EndBackgroundTask(ourTask);
                            ourTask = 0;
                        }
                    });
                }
            };

            Thread t = new Thread(pts);

            t.Priority     = ThreadPriority.Highest;
            t.IsBackground = true;
            t.Start();
            return(t);
        }
コード例 #13
0
        public override void DidEnterBackground(UIApplication application)
        {
            Trace.WriteLine(Trace.kKinskyTouch, "AppDelegate.DidEnterBackground");

            int task = 0;

            task = application.BeginBackgroundTask(delegate {
                if (task != 0)
                {
                    application.EndBackgroundTask(task);
                    task = 0;
                }
            });


            Trace.WriteLine(Trace.kKinskyTouch, "AppDelegate.DidEnterBackground 1");

            iScheduler.Schedule(() =>
            {
                Trace.WriteLine(Trace.kKinskyTouch, "AppDelegate.DidEnterBackground 2");
                if (iStackStarted)
                {
                    Helper.Helper.Stack.Stop();
                    iStackStarted = false;
                }
                Trace.WriteLine(Trace.kKinskyTouch, "AppDelegate.DidEnterBackground 3");

                application.BeginInvokeOnMainThread(delegate {
                    Trace.WriteLine(Trace.kKinskyTouch, "AppDelegate.DidEnterBackground 4");
                    if (task != 0)
                    {
                        application.EndBackgroundTask(task);
                        task = 0;
                    }
                });
            });

            Trace.WriteLine(Trace.kKinskyTouch, "AppDelegate.DidEnterBackground 5");
        }
コード例 #14
0
        public override void DidEnterBackground(UIApplication application)
        {
            bgTask = application.BeginBackgroundTask(() => {
                application.EndBackgroundTask(bgTask);
                bgTask = UIApplication.BackgroundTaskInvalid;
            });

            DispatchQueue.GetGlobalQueue(DispatchQueuePriority.Default).DispatchAsync(() => {
                Console.WriteLine("Starting background task {0}", bgTask);

                // track some events and set some people properties
                var mixpanel = Mixpanel.SharedInstance;

                mixpanel?.RegisterSuperProperties(NSDictionary.FromObjectAndKey(new NSString("Background Super Property"), new NSString("Hi!")));
                mixpanel?.Track("Background Event");
                mixpanel?.People.Set("Background Property", new NSDate());

                Console.WriteLine("Ending background task {0}", bgTask);
                application.EndBackgroundTask(bgTask);
                bgTask = UIApplication.BackgroundTaskInvalid;
            });

            Console.WriteLine("Dispatched background task {0}", bgTask);
        }
コード例 #15
0
ファイル: AppDelegate.cs プロジェクト: caverject10/sensus
        public override async void RegisteredForRemoteNotifications(UIApplication application, NSData deviceToken)
        {
            // hang on to the token. we register for remote notifications after initializing the helper,
            // so this should be fine.
            iOSSensusServiceHelper serviceHelper = SensusServiceHelper.Get() as iOSSensusServiceHelper;

            serviceHelper.PushNotificationTokenData = deviceToken;

            // update push notification registrations. this depends on internet connectivity to S3
            // so it might hang if connectivity is poor. ensure we don't violate execution limits.
            CancellationTokenSource cancellationTokenSource = new CancellationTokenSource();
            nint updatePushNotificationRegistrationsTaskId  = application.BeginBackgroundTask(cancellationTokenSource.Cancel);
            await serviceHelper.UpdatePushNotificationRegistrationsAsync(cancellationTokenSource.Token);

            application.EndBackgroundTask(updatePushNotificationRegistrationsTaskId);
        }
コード例 #16
0
ファイル: AppDelegate.cs プロジェクト: nc3tq/sensus-1
        // This method should be used to release shared resources and it should store the application state.
        // If your application supports background exection this method is called instead of WillTerminate
        // when the user quits.
        public override void DidEnterBackground(UIApplication uiApplication)
        {
            (SensusContext.Current.CallbackScheduler as iOSCallbackScheduler).CancelSilentNotifications();

            iOSSensusServiceHelper serviceHelper = SensusServiceHelper.Get() as iOSSensusServiceHelper;

            // save app state in background
            nint saveTaskId = uiApplication.BeginBackgroundTask(() =>
            {
                // not much we can do if we run out of time...
            });

            serviceHelper.SaveAsync().ContinueWith(finishedTask =>
            {
                uiApplication.EndBackgroundTask(saveTaskId);
            });
        }
コード例 #17
0
        // This method should be used to release shared resources and it should store the application state.
        // If your application supports background exection this method is called instead of WillTerminate
        // when the user quits.
        public override void DidEnterBackground(UIApplication uiApplication)
        {
            (SensusContext.Current.Notifier as IiOSNotifier).CancelSilentNotifications();

            iOSSensusServiceHelper serviceHelper = SensusServiceHelper.Get() as iOSSensusServiceHelper;

            serviceHelper.IssuePendingSurveysNotificationAsync(true, true);

            // save app state in background
            nint saveTaskId = uiApplication.BeginBackgroundTask(() =>
            {
            });

            serviceHelper.SaveAsync(() =>
            {
                uiApplication.EndBackgroundTask(saveTaskId);
            });
        }
コード例 #18
0
ファイル: AppDelegate.cs プロジェクト: w-bonelli/sensus
        // This method should be used to release shared resources and it should store the application state.
        // If your application supports background exection this method is called instead of WillTerminate
        // when the user quits.
        public override void DidEnterBackground(UIApplication uiApplication)
        {
            iOSSensusServiceHelper serviceHelper = SensusServiceHelper.Get() as iOSSensusServiceHelper;

            // app is no longer active, so reset the activation ID
            serviceHelper.ActivationId = null;

            serviceHelper.IssuePendingSurveysNotificationAsync(true, true);

            // save app state in background
            nint saveTaskId = uiApplication.BeginBackgroundTask(() =>
            {
            });

            serviceHelper.SaveAsync(() =>
            {
                uiApplication.EndBackgroundTask(saveTaskId);
            });
        }
コード例 #19
0
ファイル: AppDelegate.cs プロジェクト: haunthy/sensus
        // This method should be used to release shared resources and it should store the application state.
        // If your application supports background exection this method is called instead of WillTerminate
        // when the user quits.
        public override void DidEnterBackground(UIApplication application)
        {
            iOSSensusServiceHelper serviceHelper = SensusServiceHelper.Get() as iOSSensusServiceHelper;

            // app is no longer active, so reset the activation ID
            serviceHelper.ActivationId = null;

            // leave the user a notification if a prompt is currently running
            if (iOSSensusServiceHelper.PromptForInputsRunning)
                serviceHelper.IssueNotificationAsync("Please open to provide responses.", null);

            // save app state in background
            nint saveTaskId = application.BeginBackgroundTask(() =>
                {
                });

            serviceHelper.SaveAsync(() =>
                {
                    application.EndBackgroundTask(saveTaskId);
                });
        }
コード例 #20
0
        // This method should be used to release shared resources and it should store the application state.
        // If your application supports background exection this method is called instead of WillTerminate
        // when the user quits.
        public async override void DidEnterBackground(UIApplication uiApplication)
        {
            nint enterBackgroundTaskId = uiApplication.BeginBackgroundTask(() =>
            {
                // not much to do if we run out of time. just report it.
                string message = "Ran out of background time while entering background.";
                SensusServiceHelper.Get().Logger.Log(message, LoggingLevel.Normal, GetType());
                SensusException.Report(message);
            });

            iOSSensusServiceHelper serviceHelper = SensusServiceHelper.Get() as iOSSensusServiceHelper;

            // cancel all silent notifications, which should never be presented to the user. if these notifications
            // are not cancelled and the app enters the background, then they will appear in the notification
            // tray and confuse the user.
            (SensusContext.Current.CallbackScheduler as iOSCallbackScheduler).CancelSilentNotifications();

            // save app state
            await serviceHelper.SaveAsync();

            uiApplication.EndBackgroundTask(enterBackgroundTaskId);
        }
コード例 #21
0
ファイル: AppDelegate.cs プロジェクト: caverject10/sensus
        public override bool FinishedLaunching(UIApplication uiApplication, NSDictionary launchOptions)
        {
            DateTime finishLaunchStartTime = DateTime.Now;

            UIDevice.CurrentDevice.BatteryMonitoringEnabled = true;

            SensusContext.Current = new iOSSensusContext
            {
                Platform = Sensus.Context.Platform.iOS,
                MainThreadSynchronizer        = new MainConcurrent(),
                SymmetricEncryption           = new SymmetricEncryption(SensusServiceHelper.ENCRYPTION_KEY),
                PowerConnectionChangeListener = new iOSPowerConnectionChangeListener()
            };

            SensusContext.Current.CallbackScheduler   = new iOSTimerCallbackScheduler(); // new UNUserNotificationCallbackScheduler();
            SensusContext.Current.Notifier            = new UNUserNotificationNotifier();
            UNUserNotificationCenter.Current.Delegate = new UNUserNotificationDelegate();

            // we've seen cases where previously terminated runs of the app leave behind
            // local notifications. clear these out now. any callbacks these notifications
            // would have triggered are about to be rescheduled when the app is actived.
            (SensusContext.Current.Notifier as iOSNotifier).RemoveAllNotifications();

            // initialize stuff prior to app load
            Forms.Init();
            FormsMaps.Init();

            // initialize the syncfusion charting system
#pragma warning disable RECS0026 // Possible unassigned object created by 'new'
            new SfChartRenderer();
#pragma warning restore RECS0026 // Possible unassigned object created by 'new'

            ZXing.Net.Mobile.Forms.iOS.Platform.Init();

            // load the app, which starts crash reporting and analytics telemetry.
            LoadApplication(new App());

            // we have observed that, if the  app is in the background and a push notification arrives,
            // then ios may attempt to launch the app and call this method but only provide a few (~5)
            // seconds for this method to return. exceeding this time results in a fored termination by
            // ios. as we're about to deserialize a large JSON object below when initializing the service
            // helper, we need to be careful about taking up too much time. start a background task to
            // obtain as much background time as possible and report timeouts. needs to be after app load
            // in case we need to report a timeout exception.
            nint finishLaunchingTaskId = uiApplication.BeginBackgroundTask(() =>
            {
                string message = "Ran out of time while finishing app launch.";
                SensusException.Report(message);
                Console.Error.WriteLine(message);
            });

            // initialize service helper. must come after context initialization. desirable to come
            // after app loading, in case we crash. crash reporting is initialized when the app
            // object is created. nothing in the app creating and loading loop will depend on having
            // an initialized service helper, so we should be fine.
            SensusServiceHelper.Initialize(() => new iOSSensusServiceHelper());

            // register for push notifications. must come after service helper initialization as we use
            // the serivce helper below to submit the remote notification token to the backends. if the
            // user subsequently denies authorization to display notifications, then all remote notifications
            // will simply be delivered to the app silently, per the following:
            //
            // https://developer.apple.com/documentation/uikit/uiapplication/1623078-registerforremotenotifications
            //
            UIApplication.SharedApplication.RegisterForRemoteNotifications();

#if UI_TESTING
            Forms.ViewInitialized += (sender, e) =>
            {
                if (!string.IsNullOrWhiteSpace(e.View.StyleId))
                {
                    e.NativeView.AccessibilityIdentifier = e.View.StyleId;
                }
            };

            Calabash.Start();
#endif

            uiApplication.EndBackgroundTask(finishLaunchingTaskId);

            // must come after app load
            base.FinishedLaunching(uiApplication, launchOptions);

            // record how long we took to launch. ios is eager to kill apps that don't start fast enough, so log information
            // to help with debugging.
            DateTime finishLaunchEndTime = DateTime.Now;
            SensusServiceHelper.Get().Logger.Log("Took " + (finishLaunchEndTime - finishLaunchStartTime) + " to finish launching.", LoggingLevel.Normal, GetType());

            return(true);
        }
コード例 #22
0
ファイル: AppDelegate.cs プロジェクト: caverject10/sensus
        public override async void DidReceiveRemoteNotification(UIApplication application, NSDictionary userInfo, Action <UIBackgroundFetchResult> completionHandler)
        {
            UIBackgroundFetchResult remoteNotificationResult;

            // set up a cancellation token for processing within limits. the token will be cancelled
            // if we run out of time or an exception is thrown in this method.
            CancellationTokenSource cancellationTokenSource = null;
            nint?receiveRemoteNotificationTaskId            = null;

            try
            {
                cancellationTokenSource = new CancellationTokenSource();

                // we have limited time to process remote notifications:  https://developer.apple.com/documentation/usernotifications/setting_up_a_remote_notification_server/pushing_updates_to_your_app_silently
                // start background task to (1) obtain any possible background processing time and (2) get
                // notified when background time is about to expire. we used to hard code a fixed amount
                // of time (~30 seconds per above link), but this might change without us knowing about it.
                SensusServiceHelper.Get().Logger.Log("Starting background task for remote notification processing.", LoggingLevel.Normal, GetType());
                receiveRemoteNotificationTaskId = application.BeginBackgroundTask(() =>
                {
                    SensusServiceHelper.Get().Logger.Log("Cancelling token for remote notification processing due to iOS background processing limitations.", LoggingLevel.Normal, GetType());
                    cancellationTokenSource.Cancel();
                });

                NSDictionary aps   = userInfo[new NSString("aps")] as NSDictionary;
                NSDictionary alert = aps[new NSString("alert")] as NSDictionary;

                PushNotification pushNotification = new PushNotification
                {
                    Id         = (userInfo[new NSString("id")] as NSString).ToString(),
                    ProtocolId = (userInfo[new NSString("protocol")] as NSString).ToString(),
                    Update     = bool.Parse((userInfo[new NSString("update")] as NSString).ToString()),
                    Title      = (alert[new NSString("title")] as NSString).ToString(),
                    Body       = (alert[new NSString("body")] as NSString).ToString(),
                    Sound      = (aps[new NSString("sound")] as NSString).ToString()
                };

                // backend key might be blank
                string backendKeyString = (userInfo[new NSString("backend-key")] as NSString).ToString();
                if (!string.IsNullOrWhiteSpace(backendKeyString))
                {
                    pushNotification.BackendKey = new Guid(backendKeyString);
                }

                await SensusContext.Current.Notifier.ProcessReceivedPushNotificationAsync(pushNotification, cancellationTokenSource.Token);

                // even if the cancellation token was cancelled, we were still successful at downloading the updates.
                // any amount of update application we were able to do in addition to the download is bonus. updates
                // will continue to be applied on subsequent push notifications and health tests.
                remoteNotificationResult = UIBackgroundFetchResult.NewData;
            }
            catch (Exception ex)
            {
                SensusException.Report("Exception while processing remote notification:  " + ex.Message + ". Push notification dictionary content:  " + userInfo, ex);

                // we might have already cancelled the token, but we might have also just hit a bug. in
                // either case, cancel the token and set the result accordingly.
                try
                {
                    cancellationTokenSource.Cancel();
                }
                catch (Exception)
                { }

                remoteNotificationResult = UIBackgroundFetchResult.Failed;
            }
            finally
            {
                // we're done. ensure that the cancellation token cannot be cancelled any
                // longer (e.g., due to background time expiring above) by disposing it.
                if (cancellationTokenSource != null)
                {
                    try
                    {
                        cancellationTokenSource.Dispose();
                    }
                    catch (Exception)
                    { }
                }

                // end the background task
                if (receiveRemoteNotificationTaskId.HasValue)
                {
                    try
                    {
                        SensusServiceHelper.Get().Logger.Log("Ending background task for remote notification processing.", LoggingLevel.Normal, GetType());
                        application.EndBackgroundTask(receiveRemoteNotificationTaskId.Value);
                    }
                    catch (Exception)
                    { }
                }
            }

            // invoke the completion handler to let ios know that, and how, we have finished.
            completionHandler?.Invoke(remoteNotificationResult);
        }