예제 #1
0
        public async Task DeletePushNotificationRequestAsync(Guid backendKey, Protocol protocol, CancellationToken cancellationToken)
        {
            if (protocol == null)
            {
                SensusException.Report("Received null PNR protocol.");
                return;
            }

            try
            {
                await protocol.RemoteDataStore.DeletePushNotificationRequestAsync(backendKey, cancellationToken);

                RemovePushNotificationRequestToDelete(backendKey);
            }
            catch (Exception deleteException)
            {
                SensusServiceHelper.Get().Logger.Log("Exception while deleting push notification request:  " + deleteException.Message, LoggingLevel.Normal, GetType());

                // hang on to the push notification for deleting in the future, e.g., when internet is restored.
                AddPushNotificationRequestToDelete(backendKey, protocol.Id);
            }
            finally
            {
                // we just attempted to delete the push notification request, so it does not make sense for the
                // request to be pending sending. remove any pending push notification sendings.
                RemovePushNotificationRequestToSend(backendKey);
            }
        }
예제 #2
0
        public void IssueNotificationAsync(UNNotificationRequest request, Action <NSError> errorCallback = null)
        {
            // don't issue silent notifications from the background, as they will be delivered and will confuse the user (they're
            // not designed to be seen).
            bool abort = false;

            SensusContext.Current.MainThreadSynchronizer.ExecuteThreadSafe(() =>
            {
                abort = IsSilent(request?.Content?.UserInfo) && UIApplication.SharedApplication.ApplicationState != UIApplicationState.Active;
            });

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

            UNUserNotificationCenter.Current.AddNotificationRequest(request, error =>
            {
                if (error == null)
                {
                    SensusServiceHelper.Get().Logger.Log("Notification " + request.Identifier + " requested for " + ((request.Trigger as UNCalendarNotificationTrigger)?.NextTriggerDate.ToString() ?? "[time not specified]") + ".", LoggingLevel.Normal, GetType());
                }
                else
                {
                    SensusServiceHelper.Get().Logger.Log("Failed to add notification request:  " + error.Description, LoggingLevel.Normal, GetType());
                    SensusException.Report("Failed to add notification request:  " + error.Description);
                }

                errorCallback?.Invoke(error);
            });
        }
        public override async void OnReceive(global::Android.Content.Context context, Intent intent)
        {
            // this method is usually called on the UI thread and can crash the app if it throws an exception
            try
            {
                if (intent == null)
                {
                    throw new ArgumentNullException(nameof(intent));
                }

                SensusServiceHelper serviceHelper = SensusServiceHelper.Get();

                // service helper will be null for a time when the app is starting up
                if (serviceHelper != null)
                {
                    if (intent.Action == AndroidNotifier.FOREGROUND_SERVICE_NOTIFICATION_ACTION_PAUSE)
                    {
                        IEnumerable <Protocol> pausableProtocols = serviceHelper.RegisteredProtocols.Where(protocol => protocol.AllowPause);

                        await(Application.Current as App).DetailPage.Navigation.PushAsync(new SnoozeProtocolsPage(pausableProtocols));
                    }
                    else if (intent.Action == AndroidNotifier.FOREGROUND_SERVICE_NOTIFICATION_ACTION_RESUME)
                    {
                        foreach (Protocol protocol in serviceHelper.RegisteredProtocols)
                        {
                            await protocol.ResumeAsync();
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                SensusException.Report("Exception while handling notification action:  " + ex.Message, ex);
            }
        }
예제 #4
0
        private string CloseFile()
        {
            string path = _currentPath;

            lock (_fileLocker)
            {
                if (_currentFile != null)
                {
                    try
                    {
                        // close the JSON array and close the file
                        byte[] jsonCloseArrayBytes = Encoding.UTF8.GetBytes(Environment.NewLine + "]");
                        _currentFile.Write(jsonCloseArrayBytes, 0, jsonCloseArrayBytes.Length);
                        _currentFile.Flush();
                        _currentFile.Close();
                        _currentFile.Dispose();
                        _currentFile = null;
                        _currentPath = null;
                        _totalFilesClosed++;
                        _totalDataWrittenToCurrentFile = 0;
                    }
                    catch (Exception ex)
                    {
                        SensusException.Report("Exception while closing file:  " + ex.Message, ex);
                    }
                }
            }

            return(path);
        }
예제 #5
0
        public override void OnReceive(global::Android.Content.Context context, Intent intent)
        {
            // this method is usually called on the UI thread and can crash the app if it throws an exception
            try
            {
                if (intent == null)
                {
                    throw new ArgumentNullException(nameof(intent));
                }

                if (intent.Action == BluetoothDevice.ActionFound && DEVICE_FOUND != null)
                {
                    BluetoothDevice device = intent.GetParcelableExtra(BluetoothDevice.ExtraDevice) as BluetoothDevice;
                    DEVICE_FOUND(this, device);
                }
                else if (intent.Action == BluetoothAdapter.ActionStateChanged && STATE_CHANGED != null)
                {
                    int stateInt = intent.GetIntExtra(BluetoothAdapter.ExtraState, -1);

                    if (stateInt != -1)
                    {
                        STATE_CHANGED(this, (State)stateInt);
                    }
                }
            }
            catch (Exception ex)
            {
                SensusException.Report("Exception in BLE broadcast receiver:  " + ex.Message, ex);
            }
        }
예제 #6
0
        public virtual async Task <ScheduledCallbackState> ScheduleCallbackAsync(ScheduledCallback callback)
        {
            // the next execution time is computed from the time the current method is called, as the
            // caller may hang on to the ScheduledCallback for some time before calling the current method.
            // we set the time here, before adding it to the collection below, so that any callback
            // in the collection will certainly have a next execution time.
            callback.NextExecution = DateTime.Now + callback.Delay;

            if (callback.State != ScheduledCallbackState.Created)
            {
                SensusException.Report("Attemped to schedule callback " + callback.Id + ", which is in the " + callback.State + " state and not the " + ScheduledCallbackState.Created + " state.");
                callback.State = ScheduledCallbackState.Unknown;
            }
            else if (_idCallback.TryAdd(callback.Id, callback))
            {
                callback.InvocationId = Guid.NewGuid().ToString();

                BatchNextExecutionWithToleratedDelay(callback);

                // the state needs to be updated after batching is performed, so that other callbacks don't
                // attempt to batch with it, but before the invocations are requested, so that if the invocation
                // comes back immediately (e.g., being scheduled in the past) the callback is scheduled and
                // ready to run.
                callback.State = ScheduledCallbackState.Scheduled;

                await RequestLocalInvocationAsync(callback);
            }
            else
            {
                SensusServiceHelper.Get().Logger.Log("Attempted to schedule duplicate callback for " + callback.Id + ".", LoggingLevel.Normal, GetType());
            }

            return(callback.State);
        }
예제 #7
0
        public override void OnReceive(global::Android.Content.Context context, Intent intent)
        {
            try
            {
                if (intent == null)
                {
                    throw new ArgumentNullException(nameof(intent));
                }

                if (intent.Action == BluetoothDevice.ActionFound)
                {
                    AndroidBluetoothDevice device = new AndroidBluetoothDevice
                    {
                        Device    = intent.GetParcelableExtra(BluetoothDevice.ExtraDevice) as BluetoothDevice,
                        Rssi      = intent.GetShortExtra(BluetoothDevice.ExtraRssi, short.MinValue),
                        Timestamp = DateTimeOffset.UtcNow
                    };

                    lock (_devices)
                    {
                        _devices.Add(device);
                    }
                }
            }
            catch (Exception ex)
            {
                SensusException.Report("Exception in BLE broadcast receiver:  " + ex.Message, ex);
            }
        }
예제 #8
0
        protected PollingProbe()
        {
            _pollingSleepDurationMS = DefaultPollingSleepDurationMS;
            _pollingTimeoutMinutes  = 5;
            _isPolling = false;
            _pollTimes = new List <DateTime>();

#if __IOS__
            _significantChangePoll = false;
            _significantChangePollOverridesScheduledPolls = false;
            _locationManager = new CLLocationManager();
            _locationManager.LocationsUpdated += async(sender, e) =>
            {
                await Task.Run(async() =>
                {
                    try
                    {
                        CancellationTokenSource canceller = new CancellationTokenSource();

                        // if the callback specified a timeout, request cancellation at the specified time.
                        if (_pollCallback.CallbackTimeout.HasValue)
                        {
                            canceller.CancelAfter(_pollCallback.CallbackTimeout.Value);
                        }

                        await _pollCallback.Action(_pollCallback.Id, canceller.Token, () => { });
                    }
                    catch (Exception ex)
                    {
                        SensusException.Report("Failed significant change poll.", ex);
                    }
                });
            };
#endif
        }
예제 #9
0
        public void OpenDisplayPage(DisplayPage displayPage)
        {
            if (displayPage == DisplayPage.None)
            {
                return;
            }

            SensusContext.Current.MainThreadSynchronizer.ExecuteThreadSafe(async() =>
            {
                Page desiredTopPage = null;

                if (displayPage == DisplayPage.PendingSurveys)
                {
                    desiredTopPage = new PendingScriptsPage();
                }
                else
                {
                    SensusException.Report("Unrecognized display page:  " + displayPage);
                    return;
                }

                Page currentTopPage = Application.Current.MainPage.Navigation.NavigationStack.LastOrDefault();

                if (currentTopPage == null || desiredTopPage.GetType() != currentTopPage.GetType())
                {
                    await Application.Current.MainPage.Navigation.PushAsync(desiredTopPage);
                }
            });
        }
예제 #10
0
        public DataStore Copy()
        {
            JsonSerializerSettings settings = new JsonSerializerSettings
            {
                PreserveReferencesHandling = PreserveReferencesHandling.None,
                ReferenceLoopHandling      = ReferenceLoopHandling.Ignore,
                TypeNameHandling           = TypeNameHandling.All,
                ConstructorHandling        = ConstructorHandling.AllowNonPublicDefaultConstructor
            };

            try
            {
                SensusServiceHelper.Get().FlashNotificationsEnabled = false;
                return(JsonConvert.DeserializeObject <DataStore>(JsonConvert.SerializeObject(this, settings), settings));
            }
            catch (Exception ex)
            {
                string message = $"Failed to copy data store:  {ex.Message}";
                SensusServiceHelper.Get().Logger.Log(message, LoggingLevel.Normal, GetType());
                SensusException.Report(message, ex);
                return(null);
            }
            finally
            {
                SensusServiceHelper.Get().FlashNotificationsEnabled = true;
            }
        }
예제 #11
0
        public override Task KeepDeviceAwakeAsync()
        {
            if (_wakeLock != null)
            {
                lock (_wakeLock)
                {
                    bool firstAcquisition = !_wakeLock.IsHeld;

                    _wakeLock.Acquire();

                    Logger.Log("Wake lock acquired" + (firstAcquisition ? " for the first time" : "") + ".", LoggingLevel.Normal, GetType());

                    // if this is the first successful acquisition, then mark the time.
                    if (firstAcquisition && _wakeLock.IsHeld)
                    {
                        if (_wakeLockTimestamp != null)
                        {
                            SensusException.Report("Acquired wake lock for the first time but with an existing timestamp.");
                        }

                        _wakeLockTimestamp = DateTime.Now;
                    }
                }
            }

            return(Task.CompletedTask);
        }
예제 #12
0
        private void WriteToNewPath()
        {
            lock (_storageDirectoryLocker)
            {
                _path = null;
                int pathNumber = 0;
                while (pathNumber++ < int.MaxValue && _path == null)
                {
                    try
                    {
                        _path = Path.Combine(StorageDirectory, pathNumber.ToString());
                    }
                    catch (Exception ex)
                    {
                        throw SensusException.Report("Failed to get path to local file:  " + ex.Message, ex);
                    }

                    // create an empty file at the path if one does not exist
                    if (File.Exists(_path))
                    {
                        _path = null;
                    }
                    else
                    {
                        File.Create(_path).Dispose();
                    }
                }

                if (_path == null)
                {
                    throw SensusException.Report("Failed to find new path.");
                }
            }
        }
        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());
        }
예제 #14
0
        public override bool ValueMatches(object conditionValue, bool conjunctive)
        {
            // if a list is passed, compare values
            if (conditionValue is List <object> )
            {
                List <object> selectedValueList  = Value as List <object>;
                List <object> conditionValueList = conditionValue as List <object>;

                // if the matching condition is conjunctive, then the two lists must be identical.
                if (conjunctive)
                {
                    return(selectedValueList.OrderBy(o => o).SequenceEqual(conditionValueList.OrderBy(o => o)));
                }
                // if the matching condiction is disjunctive, then any of the condition values may be selected.
                else
                {
                    return(conditionValueList.Any(o => selectedValueList.Contains(o)));
                }
            }
            else
            {
                SensusException.Report("Called ItemPickerPageInput.ValueMatches with conditionValue that is not a List<object>.");
                return(false);
            }
        }
예제 #15
0
        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 to Insights 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);

                IiOSCallbackScheduler callbackScheduler = SensusContext.Current.CallbackScheduler as IiOSCallbackScheduler;
                if (callbackScheduler == null)
                {
                    SensusException.Report("Invalid callback scheduler.");
                }
                else
                {
                    // service the callback.
                    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 the user opened the notification,
                    // display the page associated with the notification (if there is one).
                    if (application.ApplicationState != UIApplicationState.Active)
                    {
                        callbackScheduler.OpenDisplayPage(notification.UserInfo);
                    }
                }
            }
        }
예제 #16
0
        public void GetActivityResultAsync(Intent intent, AndroidActivityResultRequestCode requestCode, Action <Tuple <Result, Intent> > callback)
        {
            Task.Run(() =>
            {
                lock (_locker)
                {
                    _activityResultRequestCode = requestCode;
                    _activityResult            = null;

                    _activityResultWait.Reset();

                    try
                    {
                        StartActivityForResult(intent, (int)requestCode);
                    }
                    catch (Exception ex)
                    {
                        SensusException.Report(ex);
                        _activityResultWait.Set();
                    }

                    _activityResultWait.WaitOne();

                    callback(_activityResult);
                }
            });
        }
예제 #17
0
        public override void OnActivated(UIApplication uiApplication)
        {
            System.Threading.Tasks.Task.Run(async() =>
            {
                try
                {
                    // ensure service helper is running
                    await SensusServiceHelper.Get().StartAsync();

                    // update/run all callbacks
                    await(SensusContext.Current.CallbackScheduler as IiOSCallbackScheduler).UpdateCallbacksAsync();

#if UI_TESTING
                    // load and run the UI testing protocol
                    string filePath = NSBundle.MainBundle.PathForResource("UiTestingProtocol", "json");
                    using (Stream file = new FileStream(filePath, FileMode.Open, FileAccess.Read))
                    {
                        await Protocol.RunUiTestingProtocolAsync(file);
                    }
#endif
                }
                catch (Exception ex)
                {
                    SensusException.Report("Failed in OnActivated.", ex);
                }
            });

            base.OnActivated(uiApplication);
        }
        public AndroidPowerConnectionChangeListener()
        {
            AndroidPowerConnectionChangeBroadcastReceiver.POWER_CONNECTION_CHANGED += async(sender, connected) =>
            {
                try
                {
                    bool listenOnACPower = SensusServiceHelper.Get().GetRunningProtocols().SelectMany(x => x.Probes.OfType <ListeningProbe>()).Any(x => x.KeepDeviceAwakeOnAcPower);

                    if (connected && listenOnACPower)
                    {
                        await SensusServiceHelper.Get().KeepDeviceAwakeAsync();
                    }
                    else
                    {
                        await SensusServiceHelper.Get().LetDeviceSleepAsync();
                    }

                    PowerConnectionChanged?.Invoke(sender, connected);
                }
                catch (Exception ex)
                {
                    SensusException.Report("Failed to process power connection change.", ex);
                }
            };
        }
예제 #19
0
        private async Task StartNewFileAsync(CancellationToken cancellationToken)
        {
            // start a new file within a lock to ensure that anyone with the lock will have a valid file
            string unpreparedPath = null;

            lock (_fileLocker)
            {
                try
                {
                    unpreparedPath = CloseFile();
                }
                catch (Exception closeException)
                {
                    SensusException.Report("Exception while closing file:  " + closeException.Message, closeException);
                }

                if (Running)
                {
                    try
                    {
                        OpenFile();
                    }
                    catch (Exception openException)
                    {
                        SensusException.Report("Exception while opening file:  " + openException.Message, openException);
                    }
                }
            }

            await PreparePathForRemoteAsync(unpreparedPath, cancellationToken);
        }
예제 #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;

            // 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);
        }
예제 #21
0
        public async override void OnMessageReceived(RemoteMessage message)
        {
            AndroidSensusServiceHelper serviceHelper = null;

            try
            {
                // based on log messages, it looks like the os might destroy the service component of the application
                // but leave the rest of the application (e.g., the service helper) intact and resident in memory.
                // if this happens then the serivce helper will be present, but the service itself will be destroyed.
                // this may also mean that the protocols are stopped. regardless, we desire for the service to always
                // be running, as this ensures that the app will continue as a foreground service. so, ask the os to
                // start the service any time a push notification is received. this should be a no-op if the service
                // is already running. don't ask for the service to be stopped in case no protocols are running, as
                // it could just be the case that a push notification arrives late after the user has stopped protocols.
                AndroidSensusService.Start(false);

                serviceHelper = SensusServiceHelper.Get() as AndroidSensusServiceHelper;

                // if we just started the service above, then it's likely that the service helper will not yet be
                // initialized (it must be deserialized, which is slow). in this case, just bail out and wait for
                // the next push notification to arrive, at which time the service helper will hopefully be ready.
                if (serviceHelper == null)
                {
                    SensusServiceHelper.Get().Logger.Log("Service helper not initialized following receipt of push notification and service start.", LoggingLevel.Normal, GetType());
                    return;
                }

                // acquire wake lock before this method returns to ensure that the device does not sleep prematurely,
                // interrupting any execution requested by the push notification. the service
                serviceHelper.KeepDeviceAwakeAsync().Wait();

                PushNotification pushNotification = new PushNotification
                {
                    Id         = message.Data["id"],
                    ProtocolId = message.Data["protocol"],
                    Update     = bool.Parse(message.Data["update"]),
                    Title      = message.Data["title"],
                    Body       = message.Data["body"],
                    Sound      = message.Data["sound"]
                };

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

                await SensusContext.Current.Notifier.ProcessReceivedPushNotificationAsync(pushNotification, CancellationToken.None);
            }
            catch (Exception ex)
            {
                SensusException.Report("Exception while processing remote notification:  " + ex.Message, ex);
            }
            finally
            {
                serviceHelper?.LetDeviceSleepAsync().Wait();
            }
        }
예제 #22
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.");
                                }
                            }
                        });
                    });
                }
            }
        }
 public override void OnChange(bool selfChange)
 {
     try
     {
         OnChange(selfChange, global::Android.Net.Uri.Parse("content://sms"));
     }
     catch (Exception ex)
     {
         SensusException.Report("Exception in OnChange:  " + ex.Message, ex);
     }
 }
 public override void OnReceive(global::Android.Content.Context context, Intent intent)
 {
     // this method is usually called on the UI thread and can crash the app if it throws an exception
     try
     {
         AndroidSensusService.Start(true);
     }
     catch (Exception ex)
     {
         SensusException.Report("Exception in boot-start broadcast receiver:  " + ex.Message, ex);
     }
 }
예제 #25
0
        public FacebookPermission(string name, string edge, string field)
            : base()
        {
            _name  = name;
            _edge  = edge;
            _field = field;

            if (_edge == null && _field == null)
            {
                throw SensusException.Report("Facebook permission edge and field are both null for permission \"" + _name + "\".");
            }
        }
예제 #26
0
        private void OpenFile()
        {
            lock (_locker)
            {
                // it's possible to stop the datastore before entering this lock.
                if (!Running)
                {
                    return;
                }

                // open new file
                _path = null;
                Exception mostRecentException = null;
                for (int i = 0; _path == null && i < 5; ++i)
                {
                    try
                    {
                        _path = System.IO.Path.Combine(StorageDirectory, Guid.NewGuid().ToString());
                        Stream file = new FileStream(_path, FileMode.CreateNew, FileAccess.Write);

                        // add gzip stream if doing compression
                        if (_compressionLevel != CompressionLevel.NoCompression)
                        {
                            file = new GZipStream(file, _compressionLevel, false);
                        }

                        // use buffering for compression and runtime performance
                        _file = new BufferedStream(file, _bufferSizeBytes);
                        _bytesWrittenToCurrentFile = 0;
                        _dataWrittenToCurrentFile  = 0;
                        _filesOpened++;
                    }
                    catch (Exception ex)
                    {
                        mostRecentException = ex;
                        _path = null;
                    }
                }

                // we could not open a file to write, so we cannot proceed. report the most recent exception and bail.
                if (_path == null)
                {
                    throw SensusException.Report("Failed to open file for local data store.", mostRecentException);
                }
                else
                {
                    // open the JSON array
                    byte[] jsonBeginArrayBytes = Encoding.UTF8.GetBytes("[");
                    _file.Write(jsonBeginArrayBytes, 0, jsonBeginArrayBytes.Length);
                }
            }
        }
예제 #27
0
        public SensusMasterDetailPage()
        {
            _masterPage = new SensusMasterPage();

            _masterPage.MasterPageItemsListView.ItemSelected += (sender, e) =>
            {
                try
                {
                    SensusDetailPageItem selectedDetailPageItem = e.SelectedItem as SensusDetailPageItem;

                    if (selectedDetailPageItem != null)
                    {
                        if (selectedDetailPageItem.TargetType == null)
                        {
                            selectedDetailPageItem.Action?.Invoke();
                        }
                        else
                        {
                            Detail      = new NavigationPage((Page)Activator.CreateInstance(selectedDetailPageItem.TargetType));
                            IsPresented = false;
                        }

                        _masterPage.MasterPageItemsListView.SelectedItem = null;
                    }
                }
                catch (Exception ex)
                {
                    SensusException.Report("Exception while handling master-detail menu selection:  " + ex.Message, ex);
                }
            };

            Master = _masterPage;

            // the SensusServiceHelper is not yet loaded when this page is constructed. as a result, we cannot assign the
            // ProtocolsPage to the Detail property. instead, just assign a blank content page and show the user the master
            // detail list. by the time the user selects from the list, the service helper will be available and the protocols
            // page will be ready to go.
            Detail = new NavigationPage(new ContentPage
            {
                Content = new Label
                {
                    Text                    = "Welcome to Sensus." + Environment.NewLine + "Please select from the menu above.",
                    FontSize                = 30,
                    HorizontalOptions       = LayoutOptions.CenterAndExpand,
                    VerticalOptions         = LayoutOptions.CenterAndExpand,
                    VerticalTextAlignment   = TextAlignment.Center,
                    HorizontalTextAlignment = TextAlignment.Center
                }
            });

            IsPresented = true;
        }
예제 #28
0
 public override void OnAccessibilityEvent(AccessibilityEvent e)
 {
     try {
         if (e.Text.Count > 0)
         {
             AccessibilityBroadcast?.Invoke(this, new KeystrokeDatum(DateTimeOffset.UtcNow, e.Text[0].ToString(), e.PackageName));
         }
     }
     catch (Exception ex)
     {
         SensusException.Report("Exception in Kesystroke service:  " + ex.Message, ex);
     }
 }
예제 #29
0
 public bool CallbackIsScheduled(string callbackId)
 {
     // we should never get a null callback id, but it seems that we are from android.
     if (callbackId == null)
     {
         SensusException.Report("Received null callback id.");
         return(false);
     }
     else
     {
         return(_idCallback.ContainsKey(callbackId));
     }
 }
예제 #30
0
        protected ScheduledCallback TryGetCallback(string id)
        {
            ScheduledCallback callback;

            _idCallback.TryGetValue(id, out callback);

            if (callback == null)
            {
                SensusException.Report("Failed to retrieve callback " + id + ".");
            }

            return(callback);
        }