예제 #1
0
        public ScriptTriggersPage(ScriptProbe probe)
        {
            Title = "Script Triggers";

            ScriptProbe scriptProbe = probe as ScriptProbe;

            ListView triggerList = new ListView();

            triggerList.ItemTemplate = new DataTemplate(typeof(TextCell));
            triggerList.ItemTemplate.SetBinding(TextCell.TextProperty, new Binding(".", stringFormat: "{0}"));
            triggerList.ItemsSource = scriptProbe.Triggers;

            Content = triggerList;

            ToolbarItems.Add(new ToolbarItem("+", null, async() =>
            {
                if (scriptProbe.Protocol.Probes.Where(p => p != scriptProbe && p.Enabled).Count() > 0)
                {
                    await Navigation.PushAsync(new AddScriptProbeTriggerPage(scriptProbe));
                }
                else
                {
                    UiBoundSensusServiceHelper.Get(true).FlashNotificationAsync("You must enable probes before adding triggers.");
                }
            }));

            ToolbarItems.Add(new ToolbarItem("-", null, async() =>
            {
                if (triggerList.SelectedItem != null && await DisplayAlert("Confirm Delete", "Are you sure you want to delete the selected trigger?", "Yes", "Cancel"))
                {
                    scriptProbe.Triggers.Remove(triggerList.SelectedItem as SensusService.Probes.User.Trigger);
                    triggerList.SelectedItem = null;
                }
            }));
        }
예제 #2
0
        public SensusMainPage()
        {
            Title = "Sensus";

            StackLayout contentLayout = new StackLayout
            {
                Orientation     = StackOrientation.Vertical,
                VerticalOptions = LayoutOptions.FillAndExpand
            };

            Button viewProtocolsButton = new Button
            {
                Text     = "View Protocols",
                FontSize = 20
            };

            viewProtocolsButton.Clicked += async(o, e) =>
            {
                await Navigation.PushAsync(new ProtocolsPage());
            };

            contentLayout.Children.Add(viewProtocolsButton);

            Button viewLogButton = new Button
            {
                Text     = "View Log",
                FontSize = 20
            };

            viewLogButton.Clicked += async(o, e) =>
            {
                await Navigation.PushAsync(new ViewTextLinesPage("Log", UiBoundSensusServiceHelper.Get(true).Logger.Read(int.MaxValue), () => UiBoundSensusServiceHelper.Get(true).Logger.Clear()));
            };

            contentLayout.Children.Add(viewLogButton);

            Button stopSensusButton = new Button
            {
                Text     = "Stop Sensus",
                FontSize = 20
            };

            stopSensusButton.Clicked += async(o, e) =>
            {
                if (await DisplayAlert("Stop Sensus?", "Are you sure you want to stop Sensus?", "OK", "Cancel"))
                {
                    UiBoundSensusServiceHelper.Get(true).StopAsync();
                }
            };

            contentLayout.Children.Add(stopSensusButton);

            Content = new ScrollView
            {
                Content = contentLayout
            };
        }
예제 #3
0
        protected override void OnSleep()
        {
            base.OnSleep();

            SensusServiceHelper serviceHelper = UiBoundSensusServiceHelper.Get(false);  // OnSleep can be called before the activity has actually had a chance to start up and bind to the service.

            if (serviceHelper != null)
            {
                serviceHelper.Save();
            }
        }
예제 #4
0
        public void Bind()
        {
            _protocolsList.ItemsSource = null;

            // don't wait for service helper -- it might get disconnected before we get the OnDisappearing event that calls Bind
            SensusServiceHelper serviceHelper = UiBoundSensusServiceHelper.Get(false);

            if (serviceHelper != null)
            {
                _protocolsList.ItemsSource = serviceHelper.RegisteredProtocols;
            }
        }
예제 #5
0
        /// <summary>
        /// Initializes a new instance of the <see cref="SensusUI.ProximityTriggersPage"/> class.
        /// </summary>
        /// <param name="proximityProbe">Proximity probe to display triggers for.</param>
        public ProximityTriggersPage(IPointsOfInterestProximityProbe proximityProbe)
        {
            Title = "Proximity Triggers";

            ListView triggerList = new ListView();

            triggerList.ItemTemplate = new DataTemplate(typeof(TextCell));
            triggerList.ItemTemplate.SetBinding(TextCell.TextProperty, new Binding(".", stringFormat: "{0}"));
            triggerList.ItemsSource = proximityProbe.Triggers;
            triggerList.ItemTapped += async(o, e) =>
            {
                if (triggerList.SelectedItem == null)
                {
                    return;
                }

                PointOfInterestProximityTrigger selectedTrigger = triggerList.SelectedItem as PointOfInterestProximityTrigger;

                string selectedAction = await DisplayActionSheet(selectedTrigger.ToString(), "Cancel", null, "Delete");

                if (selectedAction == "Delete")
                {
                    if (await DisplayAlert("Delete " + selectedTrigger + "?", "This action cannot be undone.", "Delete", "Cancel"))
                    {
                        proximityProbe.Triggers.Remove(selectedTrigger);
                        triggerList.SelectedItem = null;  // reset manually since it isn't done automatically
                        UiBoundSensusServiceHelper.Get(true).SaveAsync();
                    }
                }
            };

            Content = triggerList;

            ToolbarItems.Add(new ToolbarItem(null, "plus.png", async() =>
            {
                if (UiBoundSensusServiceHelper.Get(true).PointsOfInterest.Union(proximityProbe.Protocol.PointsOfInterest).Count() > 0)
                {
                    await Navigation.PushAsync(new AddProximityTriggerPage(proximityProbe));
                }
                else
                {
                    UiBoundSensusServiceHelper.Get(true).FlashNotificationAsync("You must define points of interest before adding triggers.");
                }
            }));
        }
예제 #6
0
        /// <summary>
        /// Initializes a new instance of the <see cref="SensusUI.ScriptTriggersPage"/> class.
        /// </summary>
        /// <param name="scriptRunner">Script runner to display.</param>
        public ScriptTriggersPage(ScriptRunner scriptRunner)
        {
            Title = "Script Triggers";

            ListView triggerList = new ListView();

            triggerList.ItemTemplate = new DataTemplate(typeof(TextCell));
            triggerList.ItemTemplate.SetBinding(TextCell.TextProperty, new Binding(".", stringFormat: "{0}"));
            triggerList.ItemsSource = scriptRunner.Triggers;
            triggerList.ItemTapped += async(o, e) =>
            {
                if (triggerList.SelectedItem == null)
                {
                    return;
                }

                SensusService.Probes.User.Trigger selectedTrigger = triggerList.SelectedItem as SensusService.Probes.User.Trigger;

                string selectedAction = await DisplayActionSheet(selectedTrigger.ToString(), "Cancel", null, "Delete");

                if (selectedAction == "Delete")
                {
                    if (await DisplayAlert("Confirm Delete", "Are you sure you want to delete the selected trigger?", "Yes", "Cancel"))
                    {
                        scriptRunner.Triggers.Remove(selectedTrigger);
                        triggerList.SelectedItem = null;  // must reset this, since it isn't reset automatically
                    }
                }
            };

            Content = triggerList;

            ToolbarItems.Add(new ToolbarItem(null, "plus.png", async() =>
            {
                if (scriptRunner.Probe.Protocol.Probes.Where(p => p != scriptRunner.Probe && p.Enabled).Count() > 0)
                {
                    await Navigation.PushAsync(new AddScriptTriggerPage(scriptRunner));
                }
                else
                {
                    UiBoundSensusServiceHelper.Get(true).FlashNotificationAsync("You must enable other probes before adding triggers.");
                }
            }));
        }
예제 #7
0
        private void ExecuteActionUponProtocolAuthentication(Protocol protocol, Action action)
        {
            if (protocol.LockPasswordHash == "")
            {
                action();
            }
            else
            {
                UiBoundSensusServiceHelper.Get(true).PromptForInputAsync(

                    "Authenticate",

                    new TextInput("Protocol Password:"******"The password you entered was not correct.");
                    }
                });
            }
        }
예제 #8
0
        /// <summary>
        /// Initializes a new instance of the <see cref="SensusUI.ShareLocalDataStorePage"/> class.
        /// </summary>
        /// <param name="localDataStore">Local data store to display.</param>
        public ShareLocalDataStorePage(LocalDataStore localDataStore)
        {
            _cancellationTokenSource = new CancellationTokenSource();

            Title = "Sharing Local Data Store";

            StackLayout contentLayout = new StackLayout
            {
                Orientation     = StackOrientation.Vertical,
                VerticalOptions = LayoutOptions.FillAndExpand
            };

            Label statusLabel = new Label
            {
                FontSize          = 20,
                HorizontalOptions = LayoutOptions.FillAndExpand
            };

            contentLayout.Children.Add(statusLabel);

            ProgressBar progressBar = new ProgressBar
            {
                Progress          = 0,
                HorizontalOptions = LayoutOptions.FillAndExpand
            };

            contentLayout.Children.Add(progressBar);

            Button cancelButton = new Button
            {
                Text              = "Cancel",
                FontSize          = 20,
                HorizontalOptions = LayoutOptions.FillAndExpand
            };

            cancelButton.Clicked += async(o, e) =>
            {
                await Navigation.PopAsync();
            };

            contentLayout.Children.Add(cancelButton);

            new Thread(() =>
            {
                string sharePath           = UiBoundSensusServiceHelper.Get(true).GetSharePath(".json");
                bool errorWritingShareFile = false;
                try
                {
                    // step 1:  gather data.
                    Device.BeginInvokeOnMainThread(() => statusLabel.Text = "Gathering data...");
                    List <Datum> localData = localDataStore.GetDataForRemoteDataStore(_cancellationTokenSource.Token, progress =>
                    {
                        Device.BeginInvokeOnMainThread(() =>
                        {
                            progressBar.ProgressTo(progress, 250, Easing.Linear);
                        });
                    });

                    // step 2:  write gathered data to file.
                    if (!_cancellationTokenSource.IsCancellationRequested)
                    {
                        Device.BeginInvokeOnMainThread(() =>
                        {
                            progressBar.ProgressTo(0, 0, Easing.Linear);
                            statusLabel.Text = "Writing data to file...";
                        });

                        using (StreamWriter shareFile = new StreamWriter(sharePath))
                        {
                            shareFile.WriteLine("[");

                            int dataWritten = 0;
                            foreach (Datum localDatum in localData)
                            {
                                if (_cancellationTokenSource.IsCancellationRequested)
                                {
                                    break;
                                }

                                shareFile.Write((dataWritten++ == 0 ? "" : "," + Environment.NewLine) + localDatum.GetJSON(localDataStore.Protocol.JsonAnonymizer));

                                if (localData.Count >= 10 && (dataWritten % (localData.Count / 10)) == 0)
                                {
                                    Device.BeginInvokeOnMainThread(() => progressBar.ProgressTo(dataWritten / (double)localData.Count, 250, Easing.Linear));
                                }
                            }

                            shareFile.WriteLine(Environment.NewLine + "]");
                            shareFile.Close();
                        }
                    }
                }
                catch (Exception ex)
                {
                    errorWritingShareFile = true;
                    string message        = "Error writing share file:  " + ex.Message;
                    UiBoundSensusServiceHelper.Get(true).FlashNotificationAsync(message);
                    UiBoundSensusServiceHelper.Get(true).Logger.Log(message, LoggingLevel.Normal, GetType());
                }

                if (_cancellationTokenSource.IsCancellationRequested || errorWritingShareFile)
                {
                    // always delete the file on cancel / error
                    try
                    {
                        File.Delete(sharePath);
                    }
                    catch (Exception)
                    {
                    }

                    // the only way to get a cancellation event is to back out of the window, so only pop if there was an error
                    if (errorWritingShareFile)
                    {
                        Device.BeginInvokeOnMainThread(async() => await Navigation.PopAsync());
                    }
                }
                else
                {
                    Device.BeginInvokeOnMainThread(async() =>
                    {
                        await Navigation.PopAsync();
                        UiBoundSensusServiceHelper.Get(true).ShareFileAsync(sharePath, "Sensus Data");
                    });
                }
            }).Start();

            Content = new ScrollView
            {
                Content = contentLayout
            };
        }
예제 #9
0
        /// <summary>
        /// Initializes a new instance of the <see cref="SensusUI.AddProximityTriggerPage"/> class.
        /// </summary>
        /// <param name="proximityProbe">Proximity probe to add trigger to.</param>
        public AddProximityTriggerPage(IPointsOfInterestProximityProbe proximityProbe)
        {
            _proximityProbe = proximityProbe;

            Title = "Add Trigger";

            List <PointOfInterest> pointsOfInterest = UiBoundSensusServiceHelper.Get(true).PointsOfInterest.Union(_proximityProbe.Protocol.PointsOfInterest).ToList();

            if (pointsOfInterest.Count == 0)
            {
                Content = new Label
                {
                    Text     = "No points of interest defined. Please define one or more before creating triggers.",
                    FontSize = 20
                };

                return;
            }

            StackLayout contentLayout = new StackLayout
            {
                Orientation     = StackOrientation.Vertical,
                VerticalOptions = LayoutOptions.FillAndExpand
            };

            #region point of interest
            Label pointOfInterestLabel = new Label
            {
                Text     = "POI:",
                FontSize = 20
            };

            Picker pointOfInterestPicker = new Picker
            {
                Title             = "Select POI",
                HorizontalOptions = LayoutOptions.FillAndExpand
            };

            foreach (string poiDesc in pointsOfInterest.Select(poi => poi.ToString()))
            {
                pointOfInterestPicker.Items.Add(poiDesc);
            }

            pointOfInterestPicker.SelectedIndexChanged += (o, e) =>
            {
                if (pointOfInterestPicker.SelectedIndex < 0)
                {
                    _pointOfInterestName = _pointOfInterestType = null;
                }
                else
                {
                    PointOfInterest poi = pointsOfInterest[pointOfInterestPicker.SelectedIndex];
                    _pointOfInterestName = poi.Name;
                    _pointOfInterestType = poi.Type;
                }
            };

            contentLayout.Children.Add(new StackLayout
            {
                Orientation       = StackOrientation.Horizontal,
                HorizontalOptions = LayoutOptions.FillAndExpand,
                Children          = { pointOfInterestLabel, pointOfInterestPicker }
            });
            #endregion

            #region distance threshold
            Label distanceThresholdLabel = new Label
            {
                Text     = "Distance Threshold (Meters):",
                FontSize = 20
            };

            Entry distanceThresholdEntry = new Entry
            {
                HorizontalOptions = LayoutOptions.FillAndExpand,
                Keyboard          = Keyboard.Numeric
            };

            distanceThresholdEntry.TextChanged += (o, e) =>
            {
                if (!double.TryParse(distanceThresholdEntry.Text, out _distanceThresholdMeters))
                {
                    _distanceThresholdMeters = -1;
                }
                else if (_distanceThresholdMeters < GpsReceiver.Get().MinimumDistanceThreshold)
                {
                    UiBoundSensusServiceHelper.Get(true).FlashNotificationAsync("Distance threshold must be at least " + GpsReceiver.Get().MinimumDistanceThreshold + ".");
                }
            };

            contentLayout.Children.Add(new StackLayout
            {
                Orientation       = StackOrientation.Horizontal,
                HorizontalOptions = LayoutOptions.FillAndExpand,
                Children          = { distanceThresholdLabel, distanceThresholdEntry }
            });
            #endregion

            #region threshold direction
            Label thresholdDirectionLabel = new Label
            {
                Text     = "Threshold Direction:",
                FontSize = 20
            };

            ProximityThresholdDirection[] thresholdDirections = new ProximityThresholdDirection[] { ProximityThresholdDirection.Within, ProximityThresholdDirection.Outside };
            Picker thresholdDirectionPicker = new Picker
            {
                Title             = "Select Threshold Direction",
                HorizontalOptions = LayoutOptions.FillAndExpand
            };

            foreach (ProximityThresholdDirection thresholdDirection in thresholdDirections)
            {
                thresholdDirectionPicker.Items.Add(thresholdDirection.ToString());
            }

            thresholdDirectionPicker.SelectedIndexChanged += (o, e) =>
            {
                if (thresholdDirectionPicker.SelectedIndex < 0)
                {
                    _thresholdDirection = ProximityThresholdDirection.Within;
                }
                else
                {
                    _thresholdDirection = thresholdDirections[thresholdDirectionPicker.SelectedIndex];
                }
            };

            contentLayout.Children.Add(new StackLayout
            {
                Orientation       = StackOrientation.Horizontal,
                HorizontalOptions = LayoutOptions.FillAndExpand,
                Children          = { thresholdDirectionLabel, thresholdDirectionPicker }
            });
            #endregion

            Button okButton = new Button
            {
                Text     = "OK",
                FontSize = 20
            };

            okButton.Clicked += async(o, e) =>
            {
                try
                {
                    _proximityProbe.Triggers.Add(new PointOfInterestProximityTrigger(_pointOfInterestName, _pointOfInterestType, _distanceThresholdMeters, _thresholdDirection));
                    UiBoundSensusServiceHelper.Get(true).SaveAsync();
                    await Navigation.PopAsync();
                }
                catch (Exception ex)
                {
                    string message = "Failed to add trigger:  " + ex.Message;
                    UiBoundSensusServiceHelper.Get(true).FlashNotificationAsync(message);
                    UiBoundSensusServiceHelper.Get(true).Logger.Log(message, LoggingLevel.Normal, GetType());
                }
            };

            contentLayout.Children.Add(okButton);

            Content = new ScrollView
            {
                Content = contentLayout
            };
        }
예제 #10
0
        public ScriptInputsPage(InputGroup inputGroup)
        {
            _inputGroup = inputGroup;

            Title = "Inputs";

            _inputsList = new ListView();
            _inputsList.ItemTemplate = new DataTemplate(typeof(TextCell));
            _inputsList.ItemTemplate.SetBinding(TextCell.TextProperty, new Binding(".", stringFormat: "{0}"));
            _inputsList.ItemTapped += async(o, e) =>
            {
                if (_inputsList.SelectedItem == null)
                {
                    return;
                }

                Input selectedInput = _inputsList.SelectedItem as Input;
                int   selectedIndex = inputGroup.Inputs.IndexOf(selectedInput);

                List <string> actions = new string[] { "Edit", "Delete" }.ToList();

                if (selectedIndex < inputGroup.Inputs.Count - 1)
                {
                    actions.Insert(0, "Move Down");
                }

                if (selectedIndex > 0)
                {
                    actions.Insert(0, "Move Up");
                }

                string selectedAction = await DisplayActionSheet(selectedInput.Name, "Cancel", null, actions.ToArray());

                if (selectedAction == "Move Up")
                {
                    inputGroup.Inputs.Move(selectedIndex, selectedIndex - 1);
                }
                else if (selectedAction == "Move Down")
                {
                    inputGroup.Inputs.Move(selectedIndex, selectedIndex + 1);
                }
                else if (selectedAction == "Edit")
                {
                    ScriptInputPage inputPage = new ScriptInputPage(selectedInput);
                    inputPage.Disappearing += (oo, ee) =>
                    {
                        Bind();
                    };

                    await Navigation.PushAsync(inputPage);

                    _inputsList.SelectedItem = null;
                }
                else if (selectedAction == "Delete")
                {
                    if (await DisplayAlert("Delete " + selectedInput.Name + "?", "This action cannot be undone.", "Delete", "Cancel"))
                    {
                        _inputGroup.Inputs.Remove(selectedInput);
                        _inputsList.SelectedItem = null;  // manually reset, since it isn't done automatically.
                    }
                }
            };

            ToolbarItems.Add(new ToolbarItem(null, "plus.png", async() =>
            {
                List <Input> inputs = Assembly.GetExecutingAssembly()
                                      .GetTypes()
                                      .Where(t => !t.IsAbstract && t.IsSubclassOf(typeof(Input)))
                                      .Select(t => Activator.CreateInstance(t))
                                      .Cast <Input>()
                                      .OrderBy(i => i.Name)
                                      .ToList();

                string cancelButtonName = "Cancel";
                string selected         = await DisplayActionSheet("Select Input Type", cancelButtonName, null, inputs.Select((input, index) => (index + 1) + ") " + input.Name).ToArray());
                if (!string.IsNullOrWhiteSpace(selected) && selected != cancelButtonName)
                {
                    Input input = inputs[int.Parse(selected.Substring(0, selected.IndexOf(")"))) - 1];

                    if (input is VoiceInput && inputGroup.Inputs.Count > 0 || !(input is VoiceInput) && inputGroup.Inputs.Any(i => i is VoiceInput))
                    {
                        UiBoundSensusServiceHelper.Get(true).FlashNotificationAsync("Voice inputs must reside in groups by themselves.");
                    }
                    else
                    {
                        inputGroup.Inputs.Add(input);

                        ScriptInputPage inputPage = new ScriptInputPage(input);
                        inputPage.Disappearing   += (o, e) =>
                        {
                            Bind();
                        };

                        await Navigation.PushAsync(inputPage);
                    }
                }
            }));

            Bind();
            Content = _inputsList;
        }
예제 #11
0
        /// <summary>
        /// Initializes a new instance of the <see cref="SensusUI.ProbesPage"/> class.
        /// </summary>
        /// <param name="protocol">Protocol to display probes for.</param>
        public ProbesPage(Protocol protocol)
        {
            _protocol = protocol;

            Title = "Probes";

            _probesList = new ListView
            {
                IsPullToRefreshEnabled = true
            };

            _probesList.ItemTemplate = new DataTemplate(typeof(TextCell));
            _probesList.ItemTemplate.SetBinding(TextCell.TextProperty, new Binding(".", converter: new ProbeTextValueConverter()));
            _probesList.ItemTemplate.SetBinding(TextCell.TextColorProperty, new Binding(".", converter: new ProbeTextColorValueConverter()));
            _probesList.ItemTemplate.SetBinding(TextCell.DetailProperty, new Binding(".", converter: new ProbeDetailValueConverter()));
            _probesList.ItemTapped += async(o, e) =>
            {
                ProbePage probePage = new ProbePage(e.Item as Probe);
                probePage.Disappearing += (oo, ee) => { Bind(); };      // rebind the probes page to pick up changes in the probe
                await Navigation.PushAsync(probePage);

                _probesList.SelectedItem = null;
            };

            _probesList.Refreshing += (o, e) =>
            {
                Bind();
                _probesList.IsRefreshing = false;
            };

            Bind();

            ToolbarItems.Add(new ToolbarItem("All", null, async() =>
            {
                if (await DisplayAlert("Enable All Probes", "Are you sure you want to enable all probes?", "Yes", "No"))
                {
                    foreach (Probe probe in _protocol.Probes)
                    {
                        if (UiBoundSensusServiceHelper.Get(true).EnableProbeWhenEnablingAll(probe))
                        {
                            probe.Enabled = true;
                        }
                    }

                    Bind();
                }
            }));

            ToolbarItems.Add(new ToolbarItem("None", null, async() =>
            {
                if (await DisplayAlert("Disable All Probes", "Are you sure you want to disable all probes?", "Yes", "No"))
                {
                    foreach (Probe probe in _protocol.Probes)
                    {
                        probe.Enabled = false;
                    }

                    Bind();
                }
            }));

            Content = _probesList;
        }
예제 #12
0
        /// <summary>
        /// Initializes a new instance of the <see cref="SensusUI.PointsOfInterestPage"/> class.
        /// </summary>
        /// <param name="pointsOfInterest">Points of interest to display.</param>
        /// <param name="changeCallback">Called when a POI is added/deleted.</param>
        public PointsOfInterestPage(List <PointOfInterest> pointsOfInterest, Action changeCallback)
        {
            _pointsOfInterest = pointsOfInterest;

            Title = "Points of Interest";

            _pointsOfInterestList = new ListView();
            _pointsOfInterestList.ItemTemplate = new DataTemplate(typeof(TextCell));
            _pointsOfInterestList.ItemTemplate.SetBinding(TextCell.TextProperty, new Binding(".", stringFormat: "{0}"));
            _pointsOfInterestList.ItemTapped += async(o, e) =>
            {
                if (_pointsOfInterestList.SelectedItem == null)
                {
                    return;
                }

                PointOfInterest selectedPointOfInterest = _pointsOfInterestList.SelectedItem as PointOfInterest;

                string selectedAction = await DisplayActionSheet(selectedPointOfInterest.ToString(), "Cancel", null, "Delete");

                if (selectedAction == "Delete")
                {
                    if (await DisplayAlert("Delete " + selectedPointOfInterest.Name + "?", "This action cannot be undone.", "Delete", "Cancel"))
                    {
                        _pointsOfInterest.Remove(selectedPointOfInterest);
                        _pointsOfInterestList.SelectedItem = null;  // reset it manually, since it isn't done automatically.

                        if (changeCallback != null)
                        {
                            changeCallback();
                        }

                        Bind();
                    }
                }
            };

            Bind();

            Content = _pointsOfInterestList;

            ToolbarItems.Add(new ToolbarItem(null, "plus.png", () =>
            {
                UiBoundSensusServiceHelper.Get(true).PromptForInputsAsync(

                    "Define Point Of Interest",

                    new Input[]
                {
                    new TextInput("POI Name:"),
                    new TextInput("POI Type:"),
                    new TextInput("Address:"),
                    new YesNoInput("View Map:")
                },

                    null,

                    inputs =>
                {
                    if (inputs == null)
                    {
                        return;
                    }

                    string name    = inputs[0].Value as string;
                    string type    = inputs[1].Value as string;
                    string address = inputs[2].Value as string;
                    bool viewMap   = (bool)inputs[3].Value;

                    if (string.IsNullOrWhiteSpace(name) && string.IsNullOrWhiteSpace(type))
                    {
                        UiBoundSensusServiceHelper.Get(true).FlashNotificationAsync("You must enter either a name or type (or both).");
                    }
                    else
                    {
                        Action <List <Position> > addPOI = new Action <List <Position> >(poiPositions =>
                        {
                            Device.BeginInvokeOnMainThread(async() =>
                            {
                                if (poiPositions != null && poiPositions.Count > 0 && await DisplayAlert("Add POI?", "Would you like to add " + poiPositions.Count + " point(s) of interest?", "Yes", "No"))
                                {
                                    foreach (Position poiPosition in poiPositions)
                                    {
                                        _pointsOfInterest.Add(new PointOfInterest(name, type, poiPosition.ToGeolocationPosition()));

                                        if (changeCallback != null)
                                        {
                                            changeCallback();
                                        }

                                        Bind();
                                    }
                                }
                            });
                        });

                        string newPinName = name + (string.IsNullOrWhiteSpace(type) ? "" : " (" + type + ")");

                        if (string.IsNullOrWhiteSpace(address))
                        {
                            // cancel existing token source if we have one
                            if (_gpsCancellationTokenSource != null && !_gpsCancellationTokenSource.IsCancellationRequested)
                            {
                                _gpsCancellationTokenSource.Cancel();
                            }

                            _gpsCancellationTokenSource = new CancellationTokenSource();

                            Xamarin.Geolocation.Position gpsPosition = GpsReceiver.Get().GetReading(_gpsCancellationTokenSource.Token);

                            if (gpsPosition != null)
                            {
                                Position formsPosition = gpsPosition.ToFormsPosition();

                                if (viewMap)
                                {
                                    UiBoundSensusServiceHelper.Get(true).GetPositionsFromMapAsync(formsPosition, newPinName, addPOI);
                                }
                                else
                                {
                                    addPOI(new Position[] { formsPosition }.ToList());
                                }
                            }
                        }
                        else
                        {
                            UiBoundSensusServiceHelper.Get(true).GetPositionsFromMapAsync(address, newPinName, addPOI);
                        }
                    }
                });
            }));

            Disappearing += (o, e) =>
            {
                if (_gpsCancellationTokenSource != null && !_gpsCancellationTokenSource.IsCancellationRequested)
                {
                    _gpsCancellationTokenSource.Cancel();
                }
            };
        }
예제 #13
0
        /// <summary>
        /// Initializes a new instance of the <see cref="SensusUI.ProbePage"/> class.
        /// </summary>
        /// <param name="probe">Probe to display.</param>
        public ProbePage(Probe probe)
        {
            Title = "Probe";

            StackLayout contentLayout = new StackLayout
            {
                Orientation     = StackOrientation.Vertical,
                VerticalOptions = LayoutOptions.FillAndExpand
            };

            foreach (StackLayout stack in UiProperty.GetPropertyStacks(probe))
            {
                contentLayout.Children.Add(stack);
            }

            #region script probes
            if (probe is ScriptProbe)
            {
                ScriptProbe scriptProbe = probe as ScriptProbe;

                Button editScriptsButton = new Button
                {
                    Text     = "Edit Scripts",
                    FontSize = 20
                };

                contentLayout.Children.Add(editScriptsButton);

                editScriptsButton.Clicked += async(o, e) =>
                {
                    await Navigation.PushAsync(new ScriptRunnersPage(scriptProbe));
                };

                Button shareScriptButton = new Button
                {
                    Text     = "Share Definition",
                    FontSize = 20
                };

                contentLayout.Children.Add(shareScriptButton);

                shareScriptButton.Clicked += (o, e) =>
                {
                    string sharePath = UiBoundSensusServiceHelper.Get(true).GetSharePath(".json");

                    using (StreamWriter shareFile = new StreamWriter(sharePath))
                    {
                        shareFile.WriteLine(JsonConvert.SerializeObject(probe, SensusServiceHelper.JSON_SERIALIZER_SETTINGS));
                        shareFile.Close();
                    }

                    UiBoundSensusServiceHelper.Get(true).ShareFileAsync(sharePath, "Probe Definition");
                };
            }
            #endregion

            #region proximity probe
            if (probe is IPointsOfInterestProximityProbe)
            {
                Button editTriggersButton = new Button
                {
                    Text              = "Edit Triggers",
                    FontSize          = 20,
                    HorizontalOptions = LayoutOptions.FillAndExpand
                };

                contentLayout.Children.Add(editTriggersButton);

                editTriggersButton.Clicked += async(o, e) =>
                {
                    await Navigation.PushAsync(new ProximityTriggersPage(probe as IPointsOfInterestProximityProbe));
                };
            }
            #endregion

            #region anonymization
            List <PropertyInfo> anonymizableProperties = probe.DatumType.GetProperties().Where(property => property.GetCustomAttribute <Anonymizable>() != null).ToList();

            if (anonymizableProperties.Count > 0)
            {
                contentLayout.Children.Add(new Label
                {
                    Text              = "Anonymization",
                    FontSize          = 20,
                    FontAttributes    = FontAttributes.Italic,
                    TextColor         = Color.Accent,
                    HorizontalOptions = LayoutOptions.Center
                });

                List <StackLayout> anonymizablePropertyStacks = new List <StackLayout>();

                foreach (PropertyInfo anonymizableProperty in anonymizableProperties)
                {
                    Anonymizable anonymizableAttribute = anonymizableProperty.GetCustomAttribute <Anonymizable>(true);

                    Label propertyLabel = new Label
                    {
                        Text              = anonymizableAttribute.PropertyDisplayName ?? anonymizableProperty.Name + ":",
                        FontSize          = 20,
                        HorizontalOptions = LayoutOptions.Start
                    };

                    // populate a picker of anonymizers for the current property
                    Picker anonymizerPicker = new Picker
                    {
                        Title             = "Select Anonymizer",
                        HorizontalOptions = LayoutOptions.FillAndExpand
                    };

                    anonymizerPicker.Items.Add("Do Not Anonymize");
                    foreach (Anonymizer anonymizer in anonymizableAttribute.AvailableAnonymizers)
                    {
                        anonymizerPicker.Items.Add(anonymizer.DisplayText);
                    }

                    anonymizerPicker.SelectedIndexChanged += (o, e) =>
                    {
                        Anonymizer selectedAnonymizer = null;
                        if (anonymizerPicker.SelectedIndex > 0)
                        {
                            selectedAnonymizer = anonymizableAttribute.AvailableAnonymizers[anonymizerPicker.SelectedIndex - 1];  // subtract one from the selected index since the JsonAnonymizer's collection of anonymizers start after the "None" option within the picker.
                        }
                        probe.Protocol.JsonAnonymizer.SetAnonymizer(anonymizableProperty, selectedAnonymizer);
                    };

                    // set the picker's index to the current anonymizer (or "Do Not Anonymize" if there is no current)
                    Anonymizer currentAnonymizer = probe.Protocol.JsonAnonymizer.GetAnonymizer(anonymizableProperty);
                    int        currentIndex      = 0;
                    if (currentAnonymizer != null)
                    {
                        currentIndex = anonymizableAttribute.AvailableAnonymizers.IndexOf(currentAnonymizer) + 1;
                    }

                    anonymizerPicker.SelectedIndex = currentIndex;

                    StackLayout anonymizablePropertyStack = new StackLayout
                    {
                        Orientation       = StackOrientation.Horizontal,
                        HorizontalOptions = LayoutOptions.FillAndExpand,
                        Children          = { propertyLabel, anonymizerPicker }
                    };

                    anonymizablePropertyStacks.Add(anonymizablePropertyStack);
                }

                foreach (StackLayout anonymizablePropertyStack in anonymizablePropertyStacks.OrderBy(s => (s.Children[0] as Label).Text))
                {
                    contentLayout.Children.Add(anonymizablePropertyStack);
                }
            }
            #endregion

            Content = new ScrollView
            {
                Content = contentLayout
            };
        }
예제 #14
0
        public ViewTextLinesPage(string title, List <string> lines, Action clearCallback)
        {
            Title = title;

            ListView messageList = new ListView();

            messageList.ItemTemplate = new DataTemplate(typeof(TextCell));
            messageList.ItemTemplate.SetBinding(TextCell.TextProperty, new Binding(".", mode: BindingMode.OneWay));
            messageList.ItemsSource = new ObservableCollection <string>(lines);

            Button shareButton = new Button
            {
                Text              = "Share",
                FontSize          = 20,
                HorizontalOptions = LayoutOptions.FillAndExpand
            };

            shareButton.Clicked += (o, e) =>
            {
                string path = null;
                try
                {
                    path = UiBoundSensusServiceHelper.Get(true).GetSharePath(".txt");
                    using (StreamWriter file = new StreamWriter(path))
                    {
                        foreach (string line in lines)
                        {
                            file.WriteLine(line);
                        }

                        file.Close();
                    }
                }
                catch (Exception ex)
                {
                    UiBoundSensusServiceHelper.Get(true).Logger.Log("Failed to write lines to temp file for sharing:  " + ex.Message, SensusService.LoggingLevel.Normal, GetType());
                    path = null;
                }

                if (path != null)
                {
                    UiBoundSensusServiceHelper.Get(true).ShareFileAsync(path, title + ":  " + Path.GetFileName(path));
                }
            };

            Button clearButton = new Button
            {
                Text              = "Clear",
                FontSize          = 20,
                HorizontalOptions = LayoutOptions.FillAndExpand,
                IsEnabled         = clearCallback != null
            };

            if (clearCallback != null)
            {
                clearButton.Clicked += async(o, e) =>
                {
                    if (await DisplayAlert("Confirm", "Do you wish to clear the list? This cannot be undone.", "OK", "Cancel"))
                    {
                        clearCallback();
                        messageList.ItemsSource = null;
                    }
                }
            }
            ;

            StackLayout shareClearStack = new StackLayout
            {
                Orientation       = StackOrientation.Horizontal,
                HorizontalOptions = LayoutOptions.FillAndExpand,
                Children          = { shareButton, clearButton }
            };

            Content = new StackLayout
            {
                Orientation     = StackOrientation.Vertical,
                VerticalOptions = LayoutOptions.FillAndExpand,
                Children        = { messageList, shareClearStack }
            };
        }
    }
예제 #15
0
        public ShareLocalDataStorePage(LocalDataStore localDataStore)
        {
            _cancel = false;

            Title = "Sharing Local Data Store";

            StackLayout contentLayout = new StackLayout
            {
                Orientation     = StackOrientation.Vertical,
                VerticalOptions = LayoutOptions.FillAndExpand
            };

            Label statusLabel = new Label
            {
                FontSize          = 20,
                HorizontalOptions = LayoutOptions.FillAndExpand
            };

            contentLayout.Children.Add(statusLabel);

            ProgressBar progressBar = new ProgressBar
            {
                Progress          = 0,
                HorizontalOptions = LayoutOptions.FillAndExpand
            };

            contentLayout.Children.Add(progressBar);

            Button cancelButton = new Button
            {
                Text              = "Cancel",
                FontSize          = 20,
                HorizontalOptions = LayoutOptions.FillAndExpand
            };

            cancelButton.Clicked += async(o, e) =>
            {
                await Navigation.PopAsync();
            };

            contentLayout.Children.Add(cancelButton);

            new Thread(async() =>
            {
                string sharePath           = UiBoundSensusServiceHelper.Get(true).GetSharePath(".json");
                bool errorWritingShareFile = false;
                try
                {
                    Device.BeginInvokeOnMainThread(() => statusLabel.Text = "Gathering data...");
                    List <Datum> localData = localDataStore.GetDataForRemoteDataStore(progress =>
                    {
                        Device.BeginInvokeOnMainThread(() =>
                        {
                            progressBar.ProgressTo(progress, 250, Easing.Linear);
                        });
                    }, () =>
                    {
                        return(_cancel);
                    });

                    Device.BeginInvokeOnMainThread(() =>
                    {
                        progressBar.ProgressTo(0, 0, Easing.Linear);
                        statusLabel.Text = "Writing data to file...";
                    });

                    using (StreamWriter shareFile = new StreamWriter(sharePath))
                    {
                        int dataWritten = 0;
                        foreach (Datum datum in localData)
                        {
                            shareFile.WriteLine(datum.GetJSON(localDataStore.Protocol.JsonAnonymizer));

                            if ((++dataWritten % (localData.Count / 10)) == 0)
                            {
                                Device.BeginInvokeOnMainThread(() => progressBar.ProgressTo(dataWritten / (double)localData.Count, 250, Easing.Linear));
                            }
                        }

                        shareFile.Close();
                    }
                }
                catch (Exception ex)
                {
                    errorWritingShareFile = true;
                    string message        = "Error writing share file:  " + ex.Message;
                    SensusServiceHelper.Get().FlashNotificationAsync(message);
                    SensusServiceHelper.Get().Logger.Log(message, LoggingLevel.Normal, GetType());
                    await Navigation.PopAsync();
                }

                if (!_cancel && !errorWritingShareFile)
                {
                    Device.BeginInvokeOnMainThread(async() => await Navigation.PopAsync());
                    SensusServiceHelper.Get().ShareFileAsync(sharePath, "Sensus Data");
                }
            }).Start();

            Content = new ScrollView
            {
                Content = contentLayout
            };
        }
예제 #16
0
        public DataStorePage(Protocol protocol, DataStore dataStore, bool local)
        {
            Title = (local ? "Local" : "Remote") + " Data Store";

            List <StackLayout> stacks = UiProperty.GetPropertyStacks(dataStore);

            StackLayout buttonStack = new StackLayout
            {
                Orientation     = StackOrientation.Vertical,
                VerticalOptions = LayoutOptions.FillAndExpand
            };

            stacks.Add(buttonStack);

            if (dataStore.Clearable)
            {
                Button clearButton = new Button
                {
                    Text = "Clear",
                    HorizontalOptions = LayoutOptions.FillAndExpand,
                    FontSize          = 20
                };

                clearButton.Clicked += async(o, e) =>
                {
                    if (await DisplayAlert("Clear data from " + dataStore.Name + "?", "This action cannot be undone.", "Clear", "Cancel"))
                    {
                        dataStore.Clear();
                    }
                };

                buttonStack.Children.Add(clearButton);
            }

            if (local)
            {
                Button shareLocalDataButton = new Button
                {
                    Text = "Share",
                    HorizontalOptions = LayoutOptions.FillAndExpand,
                    FontSize          = 20
                };

                shareLocalDataButton.Clicked += async(o, e) =>
                {
                    LocalDataStore localDataStore = dataStore as LocalDataStore;

                    if (localDataStore.DataCount > 0)
                    {
                        await Navigation.PushAsync(new ShareLocalDataStorePage(dataStore as LocalDataStore));
                    }
                    else
                    {
                        UiBoundSensusServiceHelper.Get(true).FlashNotificationAsync("Local data store contains no data to share.");
                    }
                };

                buttonStack.Children.Add(shareLocalDataButton);
            }

            Button okayButton = new Button
            {
                Text = "OK",
                HorizontalOptions = LayoutOptions.FillAndExpand,
                FontSize          = 20
            };

            okayButton.Clicked += async(o, e) =>
            {
                if (local)
                {
                    protocol.LocalDataStore = dataStore as LocalDataStore;
                }
                else
                {
                    protocol.RemoteDataStore = dataStore as RemoteDataStore;
                }

                await Navigation.PopAsync();
            };

            buttonStack.Children.Add(okayButton);

            StackLayout contentLayout = new StackLayout
            {
                Orientation     = StackOrientation.Vertical,
                VerticalOptions = LayoutOptions.FillAndExpand
            };

            foreach (StackLayout stack in stacks)
            {
                contentLayout.Children.Add(stack);
            }

            Content = new ScrollView
            {
                Content = contentLayout
            };
        }
예제 #17
0
        private MapPage(string newPinName)
        {
            _map = new MapExtend
            {
                IsShowingUser     = true,
                VerticalOptions   = LayoutOptions.FillAndExpand,
                HorizontalOptions = LayoutOptions.FillAndExpand,
            };

            ((ObservableCollection <Pin>)_map.Pins).CollectionChanged += (o, e) =>
            {
                // reset pin names to be the provided name
                if (e.NewItems != null)
                {
                    foreach (Pin pin in e.NewItems)
                    {
                        pin.Label = newPinName;
                    }
                }
            };

            #region search
            Label searchLabel = new Label
            {
                Text     = "Search:",
                FontSize = 20
            };

            _searchEntry = new Entry
            {
                HorizontalOptions = LayoutOptions.FillAndExpand
            };

            Button searchGoButton = new Button
            {
                Text     = "Go",
                FontSize = 20
            };

            searchGoButton.Clicked += (o, e) =>
            {
                if (!string.IsNullOrWhiteSpace(_searchEntry.Text))
                {
                    try
                    {
                        _map.SearchAdress(_searchEntry.Text);
                    }
                    catch (Exception ex)
                    {
                        try
                        {
                            string errorMessage = "Failed to search for address:  " + ex.Message;
                            UiBoundSensusServiceHelper.Get(true).Logger.Log(errorMessage, SensusService.LoggingLevel.Normal, GetType());
                            UiBoundSensusServiceHelper.Get(true).FlashNotificationAsync(errorMessage);
                        }
                        catch (Exception)
                        {
                        }
                        finally
                        {
                            Insights.Report(ex, Insights.Severity.Warning);
                        }
                    }
                }
            };
            #endregion

            Button clearPinsButton = new Button
            {
                Text              = "Clear Pins",
                FontSize          = 20,
                HorizontalOptions = LayoutOptions.FillAndExpand
            };

            clearPinsButton.Clicked += (o, e) =>
            {
                _map.Pins.Clear();
            };

            Button okButton = new Button
            {
                Text              = "OK",
                FontSize          = 20,
                HorizontalOptions = LayoutOptions.FillAndExpand
            };

            okButton.Clicked += async(o, e) =>
            {
                await Navigation.PopModalAsync();
            };

            Content = new StackLayout
            {
                Orientation     = StackOrientation.Vertical,
                VerticalOptions = LayoutOptions.FillAndExpand,
                Children        =
                {
                    new StackLayout
                    {
                        Orientation       = StackOrientation.Horizontal,
                        HorizontalOptions = LayoutOptions.FillAndExpand,
                        Children          = { searchLabel, _searchEntry, searchGoButton }
                    },
                    new StackLayout
                    {
                        Orientation       = StackOrientation.Horizontal,
                        HorizontalOptions = LayoutOptions.FillAndExpand,
                        Children          = { clearPinsButton, okButton }
                    },
                    _map,
                }
            };
        }
예제 #18
0
        public ProtocolsPage()
        {
            Title = "Protocols";

            _protocolsList = new ListView();
            _protocolsList.ItemTemplate = new DataTemplate(typeof(TextCell));
            _protocolsList.ItemTemplate.SetBinding(TextCell.TextProperty, "Name");
            _protocolsList.ItemTapped += async(o, e) =>
            {
                if (_protocolsList.SelectedItem == null)
                {
                    return;
                }

                Protocol selectedProtocol = _protocolsList.SelectedItem as Protocol;

                string selectedAction = await DisplayActionSheet(selectedProtocol.Name, "Cancel", null, selectedProtocol.Running? "Stop" : "Start", "Edit", "Status", "Share", "Delete");

                if (selectedAction == "Start")
                {
                    selectedProtocol.StartWithUserAgreement(null);
                }
                else if (selectedAction == "Stop")
                {
                    if (await DisplayAlert("Confirm Stop", "Are you sure you want to stop " + selectedProtocol.Name + "?", "Yes", "No"))
                    {
                        selectedProtocol.Running = false;
                    }
                }
                else if (selectedAction == "Edit")
                {
                    Action editAction = new Action(async() =>
                    {
                        ProtocolPage protocolPage  = new ProtocolPage(selectedProtocol);
                        protocolPage.Disappearing += (oo, ee) => Bind();      // rebind to pick up name changes
                        await Navigation.PushAsync(protocolPage);
                        _protocolsList.SelectedItem = null;
                    });

                    ExecuteActionUponProtocolAuthentication(selectedProtocol, editAction);
                }
                else if (selectedAction == "Status")
                {
                    if (UiBoundSensusServiceHelper.Get(true).ProtocolShouldBeRunning(selectedProtocol))
                    {
                        selectedProtocol.TestHealthAsync(true, () =>
                        {
                            Device.BeginInvokeOnMainThread(async() =>
                            {
                                if (selectedProtocol.MostRecentReport == null)
                                {
                                    await DisplayAlert("No Report", "Status check failed.", "OK");
                                }
                                else
                                {
                                    await Navigation.PushAsync(new ViewTextLinesPage("Protocol Report", selectedProtocol.MostRecentReport.ToString().Split(new string[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries).ToList(), null));
                                }
                            });
                        });
                    }
                    else
                    {
                        await DisplayAlert("Protocol Not Running", "Cannot check status of protocol when protocol is not running.", "OK");
                    }
                }
                else if (selectedAction == "Share")
                {
                    Action shareAction = new Action(() =>
                    {
                        string path = null;
                        try
                        {
                            path = UiBoundSensusServiceHelper.Get(true).GetSharePath(".sensus");
                            selectedProtocol.Save(path);
                        }
                        catch (Exception ex)
                        {
                            UiBoundSensusServiceHelper.Get(true).Logger.Log("Failed to save protocol to file for sharing:  " + ex.Message, LoggingLevel.Normal, GetType());
                            path = null;
                        }

                        if (path != null)
                        {
                            UiBoundSensusServiceHelper.Get(true).ShareFileAsync(path, "Sensus Protocol:  " + selectedProtocol.Name);
                        }
                    });

                    // don't authenticate if the protocol was declared shareable -- participants might require the ability to share without the password.
                    if (selectedProtocol.Shareable)
                    {
                        shareAction();
                    }
                    // if the protocol isn't declared shareable, require authentication, since sharing is equivalent to editing the protocol.
                    else
                    {
                        ExecuteActionUponProtocolAuthentication(selectedProtocol, shareAction);
                    }
                }
                else if (selectedAction == "Delete")
                {
                    if (await DisplayAlert("Delete " + selectedProtocol.Name + "?", "This action cannot be undone.", "Delete", "Cancel"))
                    {
                        selectedProtocol.StopAsync(() =>
                        {
                            UiBoundSensusServiceHelper.Get(true).UnregisterProtocol(selectedProtocol);

                            try
                            {
                                Directory.Delete(selectedProtocol.StorageDirectory, true);
                            }
                            catch (Exception ex)
                            {
                                UiBoundSensusServiceHelper.Get(true).Logger.Log("Failed to delete protocol storage directory \"" + selectedProtocol.StorageDirectory + "\":  " + ex.Message, LoggingLevel.Normal, GetType());
                            }

                            Device.BeginInvokeOnMainThread(() =>
                            {
                                _protocolsList.SelectedItem = null;          // must reset this manually, since it isn't reset automatically
                            });
                        });
                    }
                }
            };

            Bind();

            Content = _protocolsList;

            ToolbarItems.Add(new ToolbarItem(null, "plus.png", () =>
            {
                Protocol.CreateAsync("New Protocol", protocol =>
                {
                    UiBoundSensusServiceHelper.Get(true).RegisterProtocol(protocol);
                });
            }));
        }
예제 #19
0
 public void Bind()
 {
     _protocolsList.ItemsSource = null;
     _protocolsList.ItemsSource = UiBoundSensusServiceHelper.Get(true).RegisteredProtocols;
 }
예제 #20
0
        public ParticipationReportPage(Protocol protocol)
        {
            Title = protocol.Name;

            #if __IOS__
            string howToIncreaseScore = "You can increase your score by opening Sensus more often and responding to questions that Sensus asks you.";
            #elif __ANDROID__
            string howToIncreaseScore = "You can increase your score by allowing Sensus to run continuously and responding to questions that Sensus asks you.";
            #elif WINDOWS_PHONE
            string userNotificationMessage = null; // TODO:  How to increase score?
            #else
            #error "Unrecognized platform."
            #endif

            Button helpButton = null;
            if (!string.IsNullOrWhiteSpace(protocol.ContactEmail))
            {
                helpButton = new Button
                {
                    Text     = "Email Study Manager",
                    FontSize = 20
                };

                helpButton.Clicked += (o, e) =>
                {
                    UiBoundSensusServiceHelper.Get(true).SendEmailAsync(protocol.ContactEmail, "Help with study:  " + protocol.Name, "Hello - " + Environment.NewLine + Environment.NewLine + "I am having the following problem:" + Environment.NewLine + Environment.NewLine + "[DESCRIBE YOUR PROBLEM HERE]");
                };
            }

            StackLayout contentLayout = new StackLayout
            {
                Orientation     = StackOrientation.Vertical,
                VerticalOptions = LayoutOptions.FillAndExpand,
                Padding         = new Thickness(0, 50, 0, 0),
                Children        =
                {
                    new Label
                    {
                        Text              = "Participation Level",
                        FontSize          = 20,
                        HorizontalOptions = LayoutOptions.CenterAndExpand
                    },
                    new Label
                    {
                        Text              = Math.Round(protocol.OverallParticipationLevel * 100, 0) + "%",
                        FontSize          = 75,
                        HorizontalOptions = LayoutOptions.CenterAndExpand
                    },
                    new Label
                    {
                        Text              = "This score reflects your overall participation level in the \"" + protocol.Name + "\" study over the past " + (protocol.ParticipationHorizonDays == 1 ? "day" : protocol.ParticipationHorizonDays + " days") + ". " + howToIncreaseScore + (helpButton == null ? "" : " If you have questions, please click the button below to email the study manager."),
                        FontSize          = 20,
                        HorizontalOptions = LayoutOptions.CenterAndExpand
                    }
                }
            };

            if (helpButton != null)
            {
                contentLayout.Children.Add(helpButton);
            }

            Content = new ScrollView
            {
                Content = contentLayout
            };
        }
예제 #21
0
        public ProtocolPage(Protocol protocol)
        {
            _protocol = protocol;

            Title = "Protocol";

            List <View> views = new List <View>();

            #region on/off
            Label onOffLabel = new Label
            {
                Text              = "Status:",
                FontSize          = 20,
                HorizontalOptions = LayoutOptions.Start
            };

            Switch onOffSwitch = new Switch
            {
                IsToggled = _protocol.Running
            };

            onOffSwitch.Toggled += (o, e) => _protocol.Running = e.Value;

            views.Add(new StackLayout
            {
                Orientation       = StackOrientation.Horizontal,
                HorizontalOptions = LayoutOptions.FillAndExpand,
                Children          = { onOffLabel, onOffSwitch }
            });
            #endregion

            views.AddRange(UiProperty.GetPropertyStacks(_protocol));

            #region data stores
            Button editLocalDataStoreButton = new Button
            {
                Text              = "Local Data Store",
                FontSize          = 20,
                HorizontalOptions = LayoutOptions.FillAndExpand,
                IsEnabled         = !_protocol.Running
            };

            editLocalDataStoreButton.Clicked += async(o, e) =>
            {
                if (_protocol.LocalDataStore != null)
                {
                    await Navigation.PushAsync(new DataStorePage(_protocol, _protocol.LocalDataStore.Copy(), true));
                }
            };

            Button createLocalDataStoreButton = new Button
            {
                Text              = "+",
                FontSize          = 20,
                HorizontalOptions = LayoutOptions.End,
                IsEnabled         = !_protocol.Running
            };

            createLocalDataStoreButton.Clicked += (o, e) => CreateDataStore(true);

            StackLayout localDataStoreStack = new StackLayout
            {
                Orientation       = StackOrientation.Horizontal,
                HorizontalOptions = LayoutOptions.FillAndExpand,
                Children          = { editLocalDataStoreButton, createLocalDataStoreButton }
            };

            views.Add(localDataStoreStack);

            Button editRemoteDataStoreButton = new Button
            {
                Text              = "Remote Data Store",
                FontSize          = 20,
                HorizontalOptions = LayoutOptions.FillAndExpand,
                IsEnabled         = !_protocol.Running
            };

            editRemoteDataStoreButton.Clicked += async(o, e) =>
            {
                if (_protocol.RemoteDataStore != null)
                {
                    await Navigation.PushAsync(new DataStorePage(_protocol, _protocol.RemoteDataStore.Copy(), false));
                }
            };

            Button createRemoteDataStoreButton = new Button
            {
                Text              = "+",
                FontSize          = 20,
                HorizontalOptions = LayoutOptions.End,
                IsEnabled         = !_protocol.Running
            };

            createRemoteDataStoreButton.Clicked += (o, e) => CreateDataStore(false);

            StackLayout remoteDataStoreStack = new StackLayout
            {
                Orientation       = StackOrientation.Horizontal,
                HorizontalOptions = LayoutOptions.FillAndExpand,
                Children          = { editRemoteDataStoreButton, createRemoteDataStoreButton }
            };

            views.Add(remoteDataStoreStack);
            #endregion

            #region view probes
            Button viewProbesButton = new Button
            {
                Text     = "Probes",
                FontSize = 20
            };

            viewProbesButton.Clicked += async(o, e) =>
            {
                await Navigation.PushAsync(new ProbesPage(_protocol));
            };

            views.Add(viewProbesButton);
            #endregion

            _protocolRunningChangedAction = (o, running) =>
            {
                Device.BeginInvokeOnMainThread(() =>
                {
                    onOffSwitch.IsToggled = running;
                    editLocalDataStoreButton.IsEnabled = createLocalDataStoreButton.IsEnabled = editRemoteDataStoreButton.IsEnabled = createRemoteDataStoreButton.IsEnabled = !running;
                });
            };

            StackLayout stack = new StackLayout
            {
                Orientation     = StackOrientation.Vertical,
                VerticalOptions = LayoutOptions.FillAndExpand
            };

            foreach (View view in views)
            {
                stack.Children.Add(view);
            }

            Button lockButton = new Button
            {
                Text              = _protocol.LockPasswordHash == "" ? "Lock" : "Unlock",
                FontSize          = 20,
                HorizontalOptions = LayoutOptions.FillAndExpand
            };

            lockButton.Clicked += (o, e) =>
            {
                if (lockButton.Text == "Lock")
                {
                    UiBoundSensusServiceHelper.Get(true).PromptForInputAsync("Create password to lock protocol:", false, password =>
                    {
                        if (password == null)
                        {
                            return;
                        }
                        else if (string.IsNullOrWhiteSpace(password))
                        {
                            UiBoundSensusServiceHelper.Get(true).FlashNotificationAsync("Please enter a non-empty password.");
                        }
                        else
                        {
                            _protocol.LockPasswordHash = UiBoundSensusServiceHelper.Get(true).GetMd5Hash(password);
                            Device.BeginInvokeOnMainThread(() => lockButton.Text = "Unlock");
                        }
                    });
                }
                else if (lockButton.Text == "Unlock")
                {
                    _protocol.LockPasswordHash = "";
                    lockButton.Text            = "Lock";
                }
            };

            stack.Children.Add(lockButton);

            Content = new ScrollView
            {
                Content = stack
            };

            #region toolbar
            ToolbarItems.Add(new ToolbarItem("Ping", null, async() =>
            {
                if (SensusServiceHelper.Get().ProtocolShouldBeRunning(_protocol))
                {
                    _protocol.TestHealthAsync(() =>
                    {
                        Device.BeginInvokeOnMainThread(async() =>
                        {
                            if (_protocol.MostRecentReport == null)
                            {
                                await DisplayAlert("No Report", "Ping failed.", "OK");
                            }
                            else
                            {
                                await Navigation.PushAsync(new ViewTextLinesPage("Protocol Report", _protocol.MostRecentReport.ToString().Split(new string[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries).ToList(), null));
                            }
                        });
                    });
                }
                else
                {
                    await DisplayAlert("Protocol Not Running", "Cannot ping protocol when it is not running.", "OK");
                }
            }));

            ToolbarItems.Add(new ToolbarItem("Share", null, () =>
            {
                string path = null;
                try
                {
                    path = UiBoundSensusServiceHelper.Get(true).GetSharePath(".sensus");
                    _protocol.Save(path);
                }
                catch (Exception ex)
                {
                    UiBoundSensusServiceHelper.Get(true).Logger.Log("Failed to save protocol to file for sharing:  " + ex.Message, LoggingLevel.Normal, GetType());
                    path = null;
                }

                if (path != null)
                {
                    UiBoundSensusServiceHelper.Get(true).ShareFileAsync(path, "Sensus Protocol:  " + _protocol.Name);
                }
            }));
            #endregion
        }
예제 #22
0
        /// <summary>
        /// Initializes a new instance of the <see cref="SensusUI.AddScriptTriggerPage"/> class.
        /// </summary>
        /// <param name="scriptRunner">Script runner to add trigger to.</param>
        public AddScriptTriggerPage(ScriptRunner scriptRunner)
        {
            _scriptRunner = scriptRunner;

            Title = "Add Trigger";

            List <Probe> enabledProbes = _scriptRunner.Probe.Protocol.Probes.Where(p => p != _scriptRunner.Probe && p.Enabled).ToList();

            if (enabledProbes.Count == 0)
            {
                Content = new Label
                {
                    Text     = "No enabled probes. Please enable them before creating triggers.",
                    FontSize = 20
                };

                return;
            }

            StackLayout contentLayout = new StackLayout
            {
                Orientation     = StackOrientation.Vertical,
                VerticalOptions = LayoutOptions.FillAndExpand
            };

            Label probeLabel = new Label
            {
                Text     = "Probe:",
                FontSize = 20
            };

            Picker probePicker = new Picker {
                Title = "Select Probe", HorizontalOptions = LayoutOptions.FillAndExpand
            };

            foreach (Probe enabledProbe in enabledProbes)
            {
                probePicker.Items.Add(enabledProbe.DisplayName);
            }

            contentLayout.Children.Add(new StackLayout
            {
                Orientation       = StackOrientation.Horizontal,
                HorizontalOptions = LayoutOptions.FillAndExpand,
                Children          = { probeLabel, probePicker }
            });

            StackLayout triggerDefinitionLayout = new StackLayout
            {
                Orientation     = StackOrientation.Vertical,
                VerticalOptions = LayoutOptions.FillAndExpand
            };

            contentLayout.Children.Add(triggerDefinitionLayout);

            Switch     changeSwitch           = new Switch();
            Switch     regexSwitch            = new Switch();
            Switch     fireRepeatedlySwitch   = new Switch();
            Switch     ignoreFirstDatumSwitch = new Switch();
            TimePicker startTimePicker        = new TimePicker {
                HorizontalOptions = LayoutOptions.FillAndExpand
            };
            TimePicker endTimePicker = new TimePicker {
                HorizontalOptions = LayoutOptions.FillAndExpand
            };

            probePicker.SelectedIndexChanged += (o, e) =>
            {
                _selectedProbe         = null;
                _selectedDatumProperty = null;
                _conditionValue        = null;

                triggerDefinitionLayout.Children.Clear();

                if (probePicker.SelectedIndex < 0)
                {
                    return;
                }

                _selectedProbe = enabledProbes[probePicker.SelectedIndex];

                PropertyInfo[] datumProperties = _selectedProbe.DatumType.GetProperties(BindingFlags.Public | BindingFlags.Instance).Where(p => p.GetCustomAttributes <ProbeTriggerProperty>().Count() > 0).ToArray();
                if (datumProperties.Length == 0)
                {
                    return;
                }

                #region datum property picker
                Label datumPropertyLabel = new Label
                {
                    Text     = "Property:",
                    FontSize = 20
                };

                Picker datumPropertyPicker = new Picker {
                    Title = "Select Datum Property", HorizontalOptions = LayoutOptions.FillAndExpand
                };
                foreach (PropertyInfo datumProperty in datumProperties)
                {
                    ProbeTriggerProperty triggerProperty = datumProperty.GetCustomAttributes <ProbeTriggerProperty>().First();
                    datumPropertyPicker.Items.Add(triggerProperty.Name ?? datumProperty.Name);
                }

                triggerDefinitionLayout.Children.Add(new StackLayout
                {
                    Orientation       = StackOrientation.Horizontal,
                    HorizontalOptions = LayoutOptions.FillAndExpand,
                    Children          = { datumPropertyLabel, datumPropertyPicker }
                });
                #endregion

                #region condition picker (same for all datum types)
                Label conditionLabel = new Label
                {
                    Text     = "Condition:",
                    FontSize = 20
                };

                Picker conditionPicker = new Picker {
                    Title = "Select Condition", HorizontalOptions = LayoutOptions.FillAndExpand
                };
                TriggerValueCondition[] conditions = Enum.GetValues(typeof(TriggerValueCondition)) as TriggerValueCondition[];
                foreach (TriggerValueCondition condition in conditions)
                {
                    conditionPicker.Items.Add(condition.ToString());
                }

                conditionPicker.SelectedIndexChanged += (oo, ee) =>
                {
                    if (conditionPicker.SelectedIndex < 0)
                    {
                        return;
                    }

                    _selectedCondition = conditions[conditionPicker.SelectedIndex];
                };

                triggerDefinitionLayout.Children.Add(new StackLayout
                {
                    Orientation       = StackOrientation.Horizontal,
                    HorizontalOptions = LayoutOptions.FillAndExpand,
                    Children          = { conditionLabel, conditionPicker }
                });
                #endregion

                #region condition value for comparison, based on selected datum property -- includes change calculation (for double datum) and regex (for string datum)
                StackLayout conditionValueStack = new StackLayout
                {
                    Orientation       = StackOrientation.Vertical,
                    HorizontalOptions = LayoutOptions.FillAndExpand
                };

                triggerDefinitionLayout.Children.Add(conditionValueStack);

                datumPropertyPicker.SelectedIndexChanged += (oo, ee) =>
                {
                    _selectedDatumProperty = null;
                    _conditionValue        = null;

                    conditionValueStack.Children.Clear();

                    if (datumPropertyPicker.SelectedIndex < 0)
                    {
                        return;
                    }

                    _selectedDatumProperty = datumProperties[datumPropertyPicker.SelectedIndex];

                    ProbeTriggerProperty datumTriggerAttribute = _selectedDatumProperty.GetCustomAttribute <ProbeTriggerProperty>();

                    View conditionValueStackView = null;
                    bool allowChangeCalculation  = false;
                    bool allowRegularExpression  = false;

                    if (datumTriggerAttribute is ListProbeTriggerProperty)
                    {
                        Picker conditionValuePicker = new Picker {
                            Title = "Select Condition Value", HorizontalOptions = LayoutOptions.FillAndExpand
                        };
                        object[] items = (datumTriggerAttribute as ListProbeTriggerProperty).Items;
                        foreach (object item in items)
                        {
                            conditionValuePicker.Items.Add(item.ToString());
                        }

                        conditionValuePicker.SelectedIndexChanged += (ooo, eee) =>
                        {
                            if (conditionValuePicker.SelectedIndex < 0)
                            {
                                return;
                            }

                            _conditionValue = items[conditionValuePicker.SelectedIndex];
                        };

                        conditionValueStackView = conditionValuePicker;
                    }
                    else if (datumTriggerAttribute is NumberProbeTriggerProperty)
                    {
                        Entry entry = new Entry
                        {
                            Keyboard          = Keyboard.Numeric,
                            HorizontalOptions = LayoutOptions.FillAndExpand
                        };

                        entry.TextChanged += (ooo, eee) =>
                        {
                            double value;
                            if (double.TryParse(eee.NewTextValue, out value))
                            {
                                _conditionValue = value;
                            }
                        };

                        conditionValueStackView = entry;
                        allowChangeCalculation  = true;
                    }
                    else if (datumTriggerAttribute is TextProbeTriggerProperty)
                    {
                        Entry entry = new Entry
                        {
                            Keyboard          = Keyboard.Default,
                            HorizontalOptions = LayoutOptions.FillAndExpand
                        };

                        entry.TextChanged += (ooo, eee) => _conditionValue = eee.NewTextValue;

                        conditionValueStackView = entry;
                        allowRegularExpression  = true;
                    }
                    else if (datumTriggerAttribute is BooleanProbeTriggerProperty)
                    {
                        Switch booleanSwitch = new Switch();

                        booleanSwitch.Toggled += (ooo, eee) => _conditionValue = eee.Value;

                        conditionValueStackView = booleanSwitch;
                    }

                    Label conditionValueStackLabel = new Label
                    {
                        Text     = "Value:",
                        FontSize = 20
                    };

                    conditionValueStack.Children.Add(new StackLayout
                    {
                        Orientation       = StackOrientation.Horizontal,
                        HorizontalOptions = LayoutOptions.FillAndExpand,
                        Children          = { conditionValueStackLabel, conditionValueStackView }
                    });

                    #region change calculation
                    if (allowChangeCalculation)
                    {
                        Label changeLabel = new Label
                        {
                            Text     = "Change:",
                            FontSize = 20
                        };

                        changeSwitch.IsToggled = false;

                        conditionValueStack.Children.Add(new StackLayout
                        {
                            Orientation       = StackOrientation.Horizontal,
                            HorizontalOptions = LayoutOptions.FillAndExpand,
                            Children          = { changeLabel, changeSwitch }
                        });
                    }
                    #endregion

                    #region regular expression
                    if (allowRegularExpression)
                    {
                        Label regexLabel = new Label
                        {
                            Text     = "Regular Expression:",
                            FontSize = 20
                        };

                        regexSwitch.IsToggled = false;

                        conditionValueStack.Children.Add(new StackLayout
                        {
                            Orientation       = StackOrientation.Horizontal,
                            HorizontalOptions = LayoutOptions.FillAndExpand,
                            Children          = { regexLabel, regexSwitch }
                        });
                    }
                    #endregion
                };

                datumPropertyPicker.SelectedIndex = 0;
                #endregion

                #region fire repeatedly
                Label fireRepeatedlyLabel = new Label
                {
                    Text     = "Fire Repeatedly:",
                    FontSize = 20
                };

                fireRepeatedlySwitch.IsToggled = false;

                triggerDefinitionLayout.Children.Add(new StackLayout
                {
                    Orientation       = StackOrientation.Horizontal,
                    HorizontalOptions = LayoutOptions.FillAndExpand,
                    Children          = { fireRepeatedlyLabel, fireRepeatedlySwitch }
                });
                #endregion

                #region ignore first datum
                Label ignoreFirstDatumLabel = new Label
                {
                    Text     = "Ignore First Datum:",
                    FontSize = 20
                };

                ignoreFirstDatumSwitch.IsToggled = false;

                triggerDefinitionLayout.Children.Add(new StackLayout
                {
                    Orientation       = StackOrientation.Horizontal,
                    HorizontalOptions = LayoutOptions.FillAndExpand,
                    Children          = { ignoreFirstDatumLabel, ignoreFirstDatumSwitch }
                });
                #endregion

                #region start/end times
                Label startTimeLabel = new Label
                {
                    Text     = "Start Time:",
                    FontSize = 20
                };

                startTimePicker.Time = new TimeSpan(8, 0, 0);

                triggerDefinitionLayout.Children.Add(new StackLayout
                {
                    Orientation       = StackOrientation.Horizontal,
                    HorizontalOptions = LayoutOptions.FillAndExpand,
                    Children          = { startTimeLabel, startTimePicker }
                });

                Label endTimeLabel = new Label
                {
                    Text     = "End Time:",
                    FontSize = 20
                };

                endTimePicker.Time = new TimeSpan(21, 0, 0);

                triggerDefinitionLayout.Children.Add(new StackLayout
                {
                    Orientation       = StackOrientation.Horizontal,
                    HorizontalOptions = LayoutOptions.FillAndExpand,
                    Children          = { endTimeLabel, endTimePicker }
                });
                #endregion
            };

            probePicker.SelectedIndex = 0;

            Button okButton = new Button
            {
                Text     = "OK",
                FontSize = 20
            };

            okButton.Clicked += async(o, e) =>
            {
                try
                {
                    _scriptRunner.Triggers.Add(new SensusService.Probes.User.Trigger(_selectedProbe, _selectedDatumProperty, _selectedCondition, _conditionValue, changeSwitch.IsToggled, fireRepeatedlySwitch.IsToggled, regexSwitch.IsToggled, ignoreFirstDatumSwitch.IsToggled, startTimePicker.Time, endTimePicker.Time));
                    await Navigation.PopAsync();
                }
                catch (Exception ex)
                {
                    string message = "Failed to add trigger:  " + ex.Message;
                    UiBoundSensusServiceHelper.Get(true).FlashNotificationAsync(message);
                    UiBoundSensusServiceHelper.Get(true).Logger.Log(message, LoggingLevel.Normal, GetType());
                }
            };

            contentLayout.Children.Add(okButton);

            Content = new ScrollView
            {
                Content = contentLayout
            };
        }
예제 #23
0
        /// <summary>
        /// Initializes a new instance of the <see cref="SensusUI.ProtocolPage"/> class.
        /// </summary>
        /// <param name="protocol">Protocol to display.</param>
        public ProtocolPage(Protocol protocol)
        {
            _protocol = protocol;

            Title = "Protocol";

            List <View> views = new List <View>();

            views.AddRange(UiProperty.GetPropertyStacks(_protocol));

            #region data stores
            Button editLocalDataStoreButton = new Button
            {
                Text              = "Local Data Store",
                FontSize          = 20,
                HorizontalOptions = LayoutOptions.FillAndExpand,
                IsEnabled         = !_protocol.Running
            };

            editLocalDataStoreButton.Clicked += async(o, e) =>
            {
                if (_protocol.LocalDataStore != null)
                {
                    await Navigation.PushAsync(new DataStorePage(_protocol, _protocol.LocalDataStore.Copy(), true, false));
                }
            };

            Button createLocalDataStoreButton = new Button
            {
                Text              = "+",
                FontSize          = 20,
                HorizontalOptions = LayoutOptions.End,
                IsEnabled         = !_protocol.Running
            };

            createLocalDataStoreButton.Clicked += (o, e) => CreateDataStore(true);

            StackLayout localDataStoreStack = new StackLayout
            {
                Orientation       = StackOrientation.Horizontal,
                HorizontalOptions = LayoutOptions.FillAndExpand,
                Children          = { editLocalDataStoreButton, createLocalDataStoreButton }
            };

            views.Add(localDataStoreStack);

            Button editRemoteDataStoreButton = new Button
            {
                Text              = "Remote Data Store",
                FontSize          = 20,
                HorizontalOptions = LayoutOptions.FillAndExpand,
                IsEnabled         = !_protocol.Running
            };

            editRemoteDataStoreButton.Clicked += async(o, e) =>
            {
                if (_protocol.RemoteDataStore != null)
                {
                    await Navigation.PushAsync(new DataStorePage(_protocol, _protocol.RemoteDataStore.Copy(), false, false));
                }
            };

            Button createRemoteDataStoreButton = new Button
            {
                Text              = "+",
                FontSize          = 20,
                HorizontalOptions = LayoutOptions.End,
                IsEnabled         = !_protocol.Running
            };

            createRemoteDataStoreButton.Clicked += (o, e) => CreateDataStore(false);

            StackLayout remoteDataStoreStack = new StackLayout
            {
                Orientation       = StackOrientation.Horizontal,
                HorizontalOptions = LayoutOptions.FillAndExpand,
                Children          = { editRemoteDataStoreButton, createRemoteDataStoreButton }
            };

            views.Add(remoteDataStoreStack);
            #endregion

            #region points of interest
            Button pointsOfInterestButton = new Button
            {
                Text     = "Points of Interest",
                FontSize = 20
            };

            pointsOfInterestButton.Clicked += async(o, e) =>
            {
                await Navigation.PushAsync(new PointsOfInterestPage(
                                               _protocol.PointsOfInterest,
                                               () => UiBoundSensusServiceHelper.Get(true).SaveAsync()));
            };

            views.Add(pointsOfInterestButton);
            #endregion

            #region view probes
            Button viewProbesButton = new Button
            {
                Text     = "Probes",
                FontSize = 20
            };

            viewProbesButton.Clicked += async(o, e) =>
            {
                await Navigation.PushAsync(new ProbesPage(_protocol));
            };

            views.Add(viewProbesButton);
            #endregion

            _protocolRunningChangedAction = (o, running) =>
            {
                Device.BeginInvokeOnMainThread(() =>
                {
                    editLocalDataStoreButton.IsEnabled = createLocalDataStoreButton.IsEnabled = editRemoteDataStoreButton.IsEnabled = createRemoteDataStoreButton.IsEnabled = !running;
                });
            };

            StackLayout stack = new StackLayout
            {
                Orientation     = StackOrientation.Vertical,
                VerticalOptions = LayoutOptions.FillAndExpand
            };

            foreach (View view in views)
            {
                stack.Children.Add(view);
            }

            Button lockButton = new Button
            {
                Text              = _protocol.LockPasswordHash == "" ? "Lock" : "Unlock",
                FontSize          = 20,
                HorizontalOptions = LayoutOptions.FillAndExpand
            };

            lockButton.Clicked += (o, e) =>
            {
                if (lockButton.Text == "Lock")
                {
                    UiBoundSensusServiceHelper.Get(true).PromptForInputAsync(

                        "Lock Protocol",

                        new TextInput("Password:"******"Please enter a non-empty password.");
                        }
                        else
                        {
                            _protocol.LockPasswordHash = UiBoundSensusServiceHelper.Get(true).GetHash(password);
                            Device.BeginInvokeOnMainThread(() => lockButton.Text = "Unlock");
                        }
                    });
                }
                else if (lockButton.Text == "Unlock")
                {
                    _protocol.LockPasswordHash = "";
                    lockButton.Text            = "Lock";
                }
            };

            stack.Children.Add(lockButton);

            Content = new ScrollView
            {
                Content = stack
            };
        }
예제 #24
0
        /// <summary>
        /// Initializes a new instance of the <see cref="SensusUI.DataStorePage"/> class.
        /// </summary>
        /// <param name="protocol">Protocol to which data store is to be bound.</param>
        /// <param name="dataStore">Data store to display.</param>
        /// <param name="local">If set to <c>true</c>, the data store will be treated as a local data store.</param>
        /// <param name="newDataStore">If set to <c>true</c>, the data store will be treated as a new data store.</param>
        public DataStorePage(Protocol protocol, DataStore dataStore, bool local, bool newDataStore)
        {
            Title = (local ? "Local" : "Remote") + " Data Store";

            // property stacks all come from the data store passed in (i.e., a copy of the original on the protocol, if there is one)
            List <StackLayout> stacks = UiProperty.GetPropertyStacks(dataStore);

            StackLayout buttonStack = new StackLayout
            {
                Orientation     = StackOrientation.Vertical,
                VerticalOptions = LayoutOptions.FillAndExpand
            };

            stacks.Add(buttonStack);

            // clearing only applies to local data stores that already exist on protocols and are clearable. new local data stores don't have this option.
            if (local && !newDataStore && protocol.LocalDataStore.Clearable)
            {
                Button clearButton = new Button
                {
                    Text = "Clear",
                    HorizontalOptions = LayoutOptions.FillAndExpand,
                    FontSize          = 20
                };

                clearButton.Clicked += async(o, e) =>
                {
                    // display the name as it's currently shown on the form (i.e., in the passed-in data store rather than the protocols)
                    if (await DisplayAlert("Clear data from " + dataStore.Name + "?", "This action cannot be undone.", "Clear", "Cancel"))
                    {
                        protocol.LocalDataStore.Clear();  // clear the protocol's local data store
                    }
                };

                buttonStack.Children.Add(clearButton);
            }

            // sharing only applies to local data stores that already exist on protocols. new local data stores don't have this option.
            if (local && !newDataStore)
            {
                Button shareLocalDataButton = new Button
                {
                    Text = "Share",
                    HorizontalOptions = LayoutOptions.FillAndExpand,
                    FontSize          = 20
                };

                shareLocalDataButton.Clicked += async(o, e) =>
                {
                    // share the protocol's local data store if it has data in it
                    if (protocol.LocalDataStore.DataCount > 0)
                    {
                        await Navigation.PushAsync(new ShareLocalDataStorePage(protocol.LocalDataStore));
                    }
                    else
                    {
                        UiBoundSensusServiceHelper.Get(true).FlashNotificationAsync("Local data store contains no data to share.");
                    }
                };

                buttonStack.Children.Add(shareLocalDataButton);
            }

            Button okayButton = new Button
            {
                Text = "OK",
                HorizontalOptions = LayoutOptions.FillAndExpand,
                FontSize          = 20
            };

            okayButton.Clicked += async(o, e) =>
            {
                if (local)
                {
                    protocol.LocalDataStore = dataStore as LocalDataStore;
                }
                else
                {
                    protocol.RemoteDataStore = dataStore as RemoteDataStore;
                }

                await Navigation.PopAsync();
            };

            buttonStack.Children.Add(okayButton);

            StackLayout contentLayout = new StackLayout
            {
                Orientation     = StackOrientation.Vertical,
                VerticalOptions = LayoutOptions.FillAndExpand
            };

            foreach (StackLayout stack in stacks)
            {
                contentLayout.Children.Add(stack);
            }

            Content = new ScrollView
            {
                Content = contentLayout
            };
        }
예제 #25
0
        public SensusMainPage()
        {
            Title = "Sensus";

            StackLayout contentLayout = new StackLayout
            {
                Orientation     = StackOrientation.Vertical,
                VerticalOptions = LayoutOptions.FillAndExpand
            };

            Button protocolsButton = new Button
            {
                Text     = "Protocols",
                FontSize = 20
            };

            protocolsButton.Clicked += async(o, e) =>
            {
                await Navigation.PushAsync(new ProtocolsPage());
            };

            contentLayout.Children.Add(protocolsButton);

            Button studyParticipationButton = new Button
            {
                Text     = "Study Participation",
                FontSize = 20
            };

            studyParticipationButton.Clicked += async(o, e) =>
            {
                if (UiBoundSensusServiceHelper.Get(true).RegisteredProtocols.Count == 0)
                {
                    UiBoundSensusServiceHelper.Get(true).FlashNotificationAsync("You have not yet added any studies to Sensus.");
                }
                else
                {
                    string[] protocolNames        = UiBoundSensusServiceHelper.Get(true).RegisteredProtocols.Select((protocol, index) => (index + 1) + ") " + protocol.Name).ToArray();
                    string   cancelButtonName     = "Cancel";
                    string   selectedProtocolName = await DisplayActionSheet("Select Study", cancelButtonName, null, protocolNames);

                    if (!string.IsNullOrWhiteSpace(selectedProtocolName) && selectedProtocolName != cancelButtonName)
                    {
                        Protocol selectedProtocol = UiBoundSensusServiceHelper.Get(true).RegisteredProtocols[int.Parse(selectedProtocolName.Substring(0, selectedProtocolName.IndexOf(")"))) - 1];

                        if (selectedProtocol.Running)
                        {
                            await Navigation.PushAsync(new ParticipationReportPage(selectedProtocol));
                        }
                        else if (await DisplayAlert("Begin Study", "You are not currently participating in this study. Would you like to begin participating?", "Yes", "No"))
                        {
                            selectedProtocol.StartWithUserAgreement(null);
                        }
                    }
                }
            };

            contentLayout.Children.Add(studyParticipationButton);

            Button pointsOfInterestButton = new Button
            {
                Text     = "Points of Interest",
                FontSize = 20
            };

            pointsOfInterestButton.Clicked += async(o, e) =>
            {
                await Navigation.PushAsync(new PointsOfInterestPage(
                                               UiBoundSensusServiceHelper.Get(true).PointsOfInterest,
                                               () => UiBoundSensusServiceHelper.Get(true).SaveAsync()));
            };

            contentLayout.Children.Add(pointsOfInterestButton);

            Button logButton = new Button
            {
                Text     = "Log",
                FontSize = 20
            };

            logButton.Clicked += async(o, e) =>
            {
                await Navigation.PushAsync(new ViewTextLinesPage("Log", UiBoundSensusServiceHelper.Get(true).Logger.Read(int.MaxValue), () => UiBoundSensusServiceHelper.Get(true).Logger.Clear()));
            };

            contentLayout.Children.Add(logButton);

            Button stopSensusButton = new Button
            {
                Text     = "Stop Sensus",
                FontSize = 20
            };

            stopSensusButton.Clicked += async(o, e) =>
            {
                if (await DisplayAlert("Stop Sensus?", "Are you sure you want to stop Sensus?", "OK", "Cancel"))
                {
                    UiBoundSensusServiceHelper.Get(true).StopAsync();
                }
            };

            contentLayout.Children.Add(stopSensusButton);

            Content = new ScrollView
            {
                Content = contentLayout
            };
        }
예제 #26
0
        public ProtocolsPage()
        {
            Title = "Protocols";

            _protocolsList = new ListView();
            _protocolsList.ItemTemplate = new DataTemplate(typeof(TextCell));
            _protocolsList.ItemTemplate.SetBinding(TextCell.TextProperty, "Name");

            Bind();

            Content = _protocolsList;

            #region toolbar
            ToolbarItems.Add(new ToolbarItem("Open", null, () =>
            {
                if (_protocolsList.SelectedItem != null)
                {
                    Protocol protocol = _protocolsList.SelectedItem as Protocol;

                    Action openProtocol = new Action(async() =>
                    {
                        ProtocolPage protocolPage  = new ProtocolPage(protocol);
                        protocolPage.Disappearing += (o, e) => Bind();
                        await Navigation.PushAsync(protocolPage);
                        _protocolsList.SelectedItem = null;
                    });

                    if (protocol.LockPasswordHash == "")
                    {
                        openProtocol();
                    }
                    else
                    {
                        UiBoundSensusServiceHelper.Get(true).PromptForInputAsync("Enter protocol password to open:", false, password =>
                        {
                            if (password == null)
                            {
                                return;
                            }

                            if (UiBoundSensusServiceHelper.Get(true).GetMd5Hash(password) == protocol.LockPasswordHash)
                            {
                                Device.BeginInvokeOnMainThread(openProtocol);
                            }
                            else
                            {
                                UiBoundSensusServiceHelper.Get(true).FlashNotificationAsync("Incorrect password");
                            }
                        });
                    }
                }
            }));

            ToolbarItems.Add(new ToolbarItem("+", null, () =>
            {
                UiBoundSensusServiceHelper.Get(true).RegisterProtocol(new Protocol("New Protocol"));

                _protocolsList.ItemsSource = null;
                _protocolsList.ItemsSource = UiBoundSensusServiceHelper.Get(true).RegisteredProtocols;
            }));

            ToolbarItems.Add(new ToolbarItem("-", null, () =>
            {
                if (_protocolsList.SelectedItem != null)
                {
                    Protocol protocolToDelete = _protocolsList.SelectedItem as Protocol;

                    Action deleteProtocol = new Action(async() =>
                    {
                        if (await DisplayAlert("Delete " + protocolToDelete.Name + "?", "This action cannot be undone.", "Delete", "Cancel"))
                        {
                            protocolToDelete.StopAsync(() =>
                            {
                                UiBoundSensusServiceHelper.Get(true).UnregisterProtocol(protocolToDelete);

                                try { Directory.Delete(protocolToDelete.StorageDirectory, true); }
                                catch (Exception ex) { UiBoundSensusServiceHelper.Get(true).Logger.Log("Failed to delete protocol storage directory \"" + protocolToDelete.StorageDirectory + "\":  " + ex.Message, LoggingLevel.Normal, GetType()); }

                                Device.BeginInvokeOnMainThread(() =>
                                {
                                    _protocolsList.ItemsSource  = _protocolsList.ItemsSource.Cast <Protocol>().Where(p => p != protocolToDelete);
                                    _protocolsList.SelectedItem = null;
                                });
                            });
                        }
                    });

                    if (protocolToDelete.LockPasswordHash == "")
                    {
                        deleteProtocol();
                    }
                    else
                    {
                        UiBoundSensusServiceHelper.Get(true).PromptForInputAsync("Enter protocol password to delete:", false, password =>
                        {
                            if (password == null)
                            {
                                return;
                            }

                            if (UiBoundSensusServiceHelper.Get(true).GetMd5Hash(password) == protocolToDelete.LockPasswordHash)
                            {
                                Device.BeginInvokeOnMainThread(deleteProtocol);
                            }
                            else
                            {
                                UiBoundSensusServiceHelper.Get(true).FlashNotificationAsync("Incorrect password");
                            }
                        });
                    }
                }
            }));
            #endregion
        }