“Think: mHealth as personal health reform.”
Having an accessible and programmable health record sets HealthVault apart. It enables a rich ecosystem of devices and mobile and web applications. Chapter 3 focused on introducing the HealthVault API, and Chapter 4 gave a good overview of HealthVault data types using a data-intensive Quantified Self application. This chapter takes a closer look at building mobile applications for HealthVault.
We will look at an end-to-end example of building a mood-tracking application on top of mobile platforms. The chapter will cover elements of mobile client programming using code samples for Windows Phone 7 (C#). Similar interfaces are available for Android (Java) and iOS (Objective-C).
In Chapter 3, we built an end-to-end web application that enables a user to track several kinds of data and use that data to help with self-experimentation. Many elements of self-tracking data, such as sleep, weight, and exercise, have the capability to be measured through devices; however, it’s very hard to measure elements of happiness, such as mood and stress, automatically.
In recent years, we have seen a surge in mobile smartphone devices. Mobile devices offer a very effective tool for efficient data entry and are an ideal platform to build data collection tools. So our manual “mood tracking” need could be served by an application that makes it easy and engaging for a user to track mood using a smartphone. For the purposes of our example, let’s build the application on the Windows Phone 7 platform.
The application will allow the user to input his mood, well-being, and stress level; present a way to look at the history of the data; and add a bit of zest using a “mood plant” avatar. The mood plant summarizes the user’s emotional state over time. When the user is happy, stress-free, and fit for a long time, the plant thrives, showing a happy face (☺), and in the case of depression and stress, it shows the effects of bad health (☹).
Figure 5-1 is a sketch of what the app might look like.
The first question we need to answer is what kind of HealthVault connectivity this application requires. We discussed several models of connecting with HealthVault in Chapter 3. As this application is only for a client device, we will use a client application model and the HealthVault Windows Phone 7 client library. Having a client application allows you to provide a rich interface and the potential capability to store the readings locally.
The next question we should solve is which HealthVault data types to use. We discussed HealthVault data types in detail in Chapter 4. Various data types could apply in this context, but browsing the HealthVault data types reveals one relevant data type in particular: Emotional State.
On further analysis, it turns out that this type is almost perfect for our use. Mood, stress, and well-being are rated on a scale of 1–5. We do a further reading of associations for each of these values, and add appropriate textual elements for each of the values (mood, stress, and well-being).
I assume you have Visual Studio installed with Window Phone 7 (WP7) tools. If not, you can get them from http://create.msdn.com/en-us/home/getting_started.
Next, go over to CodePlex at http://healthvaultwp7.codeplex.com/ and download the HealthVault library with sample applications.
I extracted the library to my desktop, and the folder structure looks like Figure 5-2. HvMobileRegular has the relevant C# code to abstract for working with the HealthVault web service, and HvMobilePhone uses the code in HvMobileRegular to build a library that works with Windows Phone 7 platform. The TestRegular directory has a unit test for the HealthVault mobile Windows Phone 7 (WP7) library. WeightTrackerDemo is a sample application that shows use cases of the library for a Weight Tracking application.
If you open the MobileSDK solution in Visual Studio and press F5, the library compiles and the WeightTracker demo starts. Figure 5-3 shows this application in action; we will use this application as a template for building ours.
Without further ado, let’s create our new Silverlight for Windows Phone project. We can create a solution for MoodTracker and reference the HVMobilePhone library in that project. You can also use the existing project, MobileSDK, and associate a new application in it; in the source code associated with this chapter, I created a new project called MoodTracker (Figure 5-4).
First things first: let’s set up the application to talk to HealthVault. In the App.xaml.cs class, add a reference to the HealthVault Service and HealthVault Shell. We also need to make sure we get a unique application ID in the developer environment of HealthVault. To do that, we head over to the HealthVault Application Configuration Center and create a new application by clicking on the “Create a new application” button (Figure 5-5). Note that in Chapter 3 we used the Application Manager utility to create a web application, but in this chapter we use an alternative method that allows us to create client applications as well as web applications.
We create an application of type Software on Device Auth (SODA), which is an authentication mechanism for client applications, and pick the name Mood Tracker for it, as shown in Figure 5-6.
Once the application is created, we need to assign appropriate authorization rules for the data types that the application will access. To do that, click on the app’s link and assign appropriate data types for the application, as shown in Figure 5-7.
Having created the client application and assigned data type authorization rules, we are all set! Now let’s configure the base page to work with the HealthVault preproduction environment (PPE). The PPE is the development environment publicly available for all HealthVault developers. The HealthVault platform in this environment is available at https://platform.healthvault-ppe.com/platform/wildcat.ashx, and the HealthVault shell in this environment is available at https://account.healthvault.com. Chapter 6 will show how to deploy your app to the general public after you have developed and tested it.
Example 5-1 shows the
initial code for configuring the application. In Line , we assign the
platformUrl
; in Line , we assign the
shellUrl
; and Line is the application identifier that we created
using the Application Configuration Center. The
HealthVaultService
object initialized the
HealthVault Windows Phone 7 library with appropriate configuration
variables. Using this object, we can make all the relevant HealthVault
web service requests.
Example 5-1. Configuring the client application
namespace MoodTracker { public partial class App : Application { public static string SettingsFilename = "Settings"; public static HealthVaultService HealthVaultService { get; set; } public static string HealthVaultShellUrl { get; set; } static string platformUrl = @"https://platform.healthvault-ppe.com/platform/wildcat.ashx";static string shellUrl = @"https://account.healthvault-ppe.com";
static string masterAppId = "83bf507d-9186-407f-a6cd-b2d65f558690";
// Code to execute when the application is launching (eg, from Start) // This code will not execute when the application is reactivated private void Application_Launching(object sender, LaunchingEventArgs e) { HealthVaultService = new HealthVaultService (platformUrl, shellUrl, new Guid(masterAppId)); } // Code to execute when the application is activated (brought to foreground) // This code will not execute when the application is first launched private void Application_Activated(object sender, ActivatedEventArgs e) { HealthVaultService = new HealthVaultService (platformUrl, shellUrl, new Guid(masterAppId)); }
We can make this project a startup project, press F5, and get to the first page of our application. We’re in business!
In order for the Mood Tracker application to work with HealthVault, we will get appropriate application creation credentials from the HealthVault Service. We must also set up a method by which the user can authorize the application using the HealthVault Shell.
To get the credentials from the HealthVault Service, the application contacts the HealthVault service to get an application creation URL. The code for that is outlined in MyMood.xaml.cs (Example 5-2).
Example 5-2. Authenticating the application with HealthVaultService
void MainPage_Loaded(object sender, RoutedEventArgs e) { App.HealthVaultService.LoadSettings(App.SettingsFilename); App.HealthVaultService.BeginAuthenticationCheck(AuthenticationCompleted, DoShellAuthentication); SetProgressBarVisibility(true); } void DoShellAuthentication(object sender, HealthVaultResponseEventArgs e) { SetProgressBarVisibility(false); App.HealthVaultService.SaveSettings(App.SettingsFilename); string url; if (_addingRecord) { url = App.HealthVaultService.GetUserAuthorizationUrl(); } else { url = App.HealthVaultService.GetApplicationCreationUrl(); }
The application creation needs to be validated on behalf of the user.
The best mechanism to achieve this is by having a page with a hosted browser that redirects appropriately to HealthVault, and then closes the browser and navigates back to the application page after a successful authorization.
Example 5-3 is the relevant code in HostedBrowserPage.xaml.
Example 5-3. Using a hosted browser to show HealthVault user authentication
void c_webBrowser_Navigated (object sender, System.Windows.Navigation.NavigationEventArgs e) { if (e.Uri.OriginalString.Contains("target=AppAuthSuccess")) { Uri pageUri = new Uri("/MyMood.xaml", UriKind.RelativeOrAbsolute); Deployment.Current.Dispatcher.BeginInvoke(() => { NavigationService.Navigate(pageUri); }); } } void HealthVaultWebPage_Loaded(object sender, RoutedEventArgs e) { string url = App.HealthVaultShellUrl; c_webBrowser.Navigate(new Uri(url)); }
Note that on success, the application is redirected to MyMood.xaml, which is our application’s landing page.
Figure 5-8 shows the flow of how the authentication described here works.
The data type we settled on for our application was Emotional State. Our first goal is to be able to read data for this type and display it in our application. To do this, we need test data for emotional state. Add test information into the test or developer account for this application from the list of type samples associated with the Emotional State type in the HealthVault Developer Center (http://developer.healthvault.com/pages/types/types.aspx), as shown in Figure 5-9. An important thing to note is that you need to be signed into http://developer.healthvault.com to add the sample; otherwise, this application gives an error.
We can verify that this sample is added to our record by viewing the information in the HealthVault PPE shell interface (https://account.healthvault-ppe.com), as shown in Figure 5-10.
Chapter 2 explained the HealthVault
GetThings
method. This method enables
an application to read data from the user’s health record. A read
request for health data can be performed using various querying
mechanisms. For the purposes of this application, we will retrieve the
last active item for the user’s Emotional State data type (Example 5-4).
Example 5-4. Using GetThings
HealthVaultMethods.GetThings (EmotionalStateModel.TypeId, 1, null, null, GetThingsCompleted);
To make it easier to work with GetThings, I implemented a simple
abstraction on the method in the HealthVaultMethods
class. Example 5-5 shows the code for this abstraction.
It allows the construction of a GetThings query for one type ID, with
the maximum items returned and with the appropriate minimum and maximum
effective dates for these health items. Chapter 4 explains the XML
query sent by the GetThings
method in
detail.
Example 5-5. GetThings abstraction
public static void GetThings(string typeId, int? maxItems, DateTime? effDateMin, DateTime? effDateMax, EventHandler<HealthVaultResponseEventArgs> responseCallback) { string thingXml = @" <info> <group {0}> <filter> <type-id>{1}</type-id> <thing-state>Active</thing-state> {2} {3} </filter> <format> <section>core</section> <xml/> <type-version-format>{1}</type-version-format> </format> </group> </info>"; XElement info = XElement.Parse(string.Format (thingXml, MaxItemsXml(maxItems), typeId, EffDateMinXml(effDateMin), EffDateMaxXml(effDateMax))); HealthVaultRequest request = new HealthVaultRequest ("GetThings", "3", info, responseCallback); App.HealthVaultService.BeginSendRequest(request); }
Now, once we can get Emotional State things, we need to perform two action things on the client side.
First, pick the item we are interested in from the GetThings
response. To choose the appropriate item, LINQ to XML comes in very
handy, offering a SQL-like select
clause for XML data, as shown in Example 5-6. LINQ stands for
Language Integrated Querying, and it allows for making queries natively
from C#.
Example 5-6. Choosing things from a GetThings response
// using LINQ to get the latest reading of emotional state XElement latestEmotion = (from thingNode in responseNode.Descendants("thing") orderby Convert.ToDateTime(thingNode.Element ("eff-date").Value) descending select thingNode).FirstOrDefault<XElement>();
Second, parse the items returned for mood, stress, and well-being
data. We can achieve this by creating a model for Emotional State. This
model is available for review in the EmotionalStateModel.cs file. The parse method
in this model parses the appropriate elements in thingXml
. Chapter 4 details the format
of this XML. Notice that in Line of Example 5-7, we parse the
common
element to fetch the note
data for the Emotional State type. In
Line , we are setting the
When
date of the instance to the eff-date
element. We have created enumerations
for Mood
, Stress
, and Wellbeing
values, and we can parse the
integers for those values using the Enum.Parse
method.
Example 5-7. Parsing a thing for the Emotion State data type
public void Parse(XElement thingXml) { this.Mood = Mood.None; this.Stress = Stress.None; this.Wellbeing = Wellbeing.None; XElement emotionalState = thingXml.Descendants ("data-xml").Descendants("emotion").First(); this.When = Convert.ToDateTime(thingXml.Element("eff-date").Value);if (thingXml.Descendants("common") != null &&
(thingXml.Descendants("common").Descendants("note").Count() != 0)) { this.Note = thingXml.Descendants("common").Descendants("note").First().Value; } if (emotionalState.Element("mood") != null) { try { this.Mood = (Mood)System.Enum.Parse(typeof(Mood), ((XElement)emotionalState.Element("mood")).Value, true); } catch (Exception) { } } if (emotionalState.Element("stress") != null) { try { this.Stress = (Stress)System.Enum.Parse(typeof(Stress), ((XElement)emotionalState.Element("stress")).Value, true); } catch (Exception) { } } if (emotionalState.Element("wellbeing") != null) { try { this.Wellbeing = (Wellbeing)System.Enum.Parse(typeof(Wellbeing), ((XElement)emotionalState.Element("wellbeing")).Value, true); } catch (Exception) { } } }
After retrieving the data in our Emotional State model, we can use XAML to view it in our application. XAML is the user interface markup technology for Windows Phone 7. For the purposes of this book, we won’t go into the details of XAML. Figure 5-11 shows the display of the latest emotional state reading from HealthVault.
In the previous section we discussed how one can display the data retrieved from the HealthVault Emotional State data type. Before we get to the topic of this section and discuss how we can put new items into HealthVault, Figure 5-12 shows a screenshot of how the application looks once we have enabled the put.
For each of the emotional states—mood, stress, and well-being—we have a slider that lets users capture their emotional state. They can also add a note pertaining to their moods using a text box. We want this information to be uploaded with the current time stamp once the user hits the Save Now! button.
Example 5-8 shows how the
save button submits information to HealthVault. Note that in Line we are calling an abstraction for the
PutThings
method.
Example 5-8. Saving new data to HealthVault
// Save the reading to HealthVault private void Btn_SaveReadingToHealthVault_Click(object sender, RoutedEventArgs e) { EmotionalStateModel model = new EmotionalStateModel(); model.Mood = (Mood)c_MoodSlider.Value; model.Stress = (Stress)c_StressSlider.Value; model.Wellbeing = (Wellbeing)c_WellbeingSlider.Value; model.When = DateTime.Now; model.Note = GetNote(); HealthVaultMethods.PutThings(model, PutThingsCompleted);SetProgressBarVisibility(true); }
In Chapter 3, we looked at
the PutThings
method in detail. This
method enables an application to add or update health items in a user’s
record. As the first line in Example 5-9
shows, our abstraction fetches the relevant information from the base
health record item object and submits that to HealthVault using the
PutThings version 2 API. The response for this request is handled by the
responseCallback
function, which in
turn can check for various return codes from the service.
Example 5-9. PutThings abstraction
public static void PutThings(HealthRecordItemModel item, EventHandler<HealthVaultResponseEventArgs> responseCallback) { XElement info = XElement.Parse(item.GetXml()); HealthVaultRequest request = new HealthVaultRequest ("PutThings", "2", info, responseCallback); App.HealthVaultService.BeginSendRequest(request); }
Now that we are able to write data to HealthVault, we have a mobile application that can read and update information from and to HealthVault!
In the last section, we enabled Mood Tracker (http://healthblog.vitraag.com/2011/06/entering-new-data-with-mood-tracker-5/) to enter new data in HealthVault. We want to be able to discover patterns in mood, stress, and well-being, and graphing them over time is a great mechanism by which to achieve this goal. Let’s start with a simplistic approach, showing the Emotional State readings for mood, stress, and well-being over a week. As Figure 5-13 shows, a user can browse mood readings based on a weekly margin and move forward or backward a week at time.
In order to get data from HealthVault for a specific time period,
the GetThings
method (https://github.com/vaibhavb/moodtracker/blob/master/MoodTracker/HealthVaultMethods.cs)
needs to have the effective date filter enabled to look for appropriate
readings. Line in Example 5-10 shows how the
GetThings abstraction is configured to return elements for the last
seven days only.
Example 5-10. Fetching readings for the last seven days
void RefreshGraph() { this.EmotionList.Clear(); this.GraphLabel.Text = string.Format("Readings for last 7 Days from {0}", BaseTimeForGraph.ToString("MMM dd, yyyy")); // Get the last emotional state info and try to plot a graph HealthVaultMethods.GetThings(EmotionalStateModel.TypeId, null, BaseTimeForGraph.Subtract(new TimeSpan(7, 0, 0, 0)),BaseTimeForGraph, GetThingsCompleted); }
Note that the eff-date-min
element, as implemented in the GetThings class in HealthVautlMethods.cs, must be formatted in
the ISO 8601 format. Line in Example 5-11 shows how we do
the formatting.
Once we can selectively get information from HealthVault, we can
use a graphing library to show the readings. In our case, I chose the
open source graphing library amCharts based on its ease of use. In fact,
I added it to the project with one click using the NuGet package manager
(http://www.nuget.org/packages/amChartsQuickCharts). Example 5-12 shows a snippet of the
configuration code showing how the graph is set up for mood, stress, and
well-being using a serial chart. Note that the series values are bound
in Line using a
DataSource
called EmotionList
; it is a list of
observable emotional states.
Example 5-12. Graphing emotional state
<amq:SerialChart x:Name="EmotionsChart" BorderThickness="1" DataSource="{Binding EmotionList}"CategoryValueMemberPath="FormattedWhen" AxisForeground="White" PlotAreaBackground="Black" GridStroke="DarkGray" Height="463" Width="450"> <amq:SerialChart.Graphs> <amq:LineGraph ValueMemberPath="Mood" Title="Mood" Brush="Blue" StrokeThickness="6" BorderBrush="Cornsilk"/> <amq:LineGraph ValueMemberPath="Stress" Title="Stress" Brush="#8000FF00" StrokeThickness="8" /> <amq:LineGraph ValueMemberPath="Wellbeing" Title="Wellbeing" StrokeThickness="2" Brush="#80FF0000"/> </amq:SerialChart.Graphs> </amq:SerialChart>
We want a user to engage with the emotional state readings, and a good way to achieve this goal is by providing a zestful visualization for their emotional state. We use a mood plant as a mechanism to gauge a user’s emotional state over the recent past.
The flower of the plant represents the average mood, the leaves represent average stress, and the roots represent the average well-being. Individual flower, leaf, and root ligatures map the values 0 through 5 to mood, stress, and well-being. The final mood plant is a result of superimposing these values. Figure 5-14 shows an instance of a mood plant with mood 3, stress 3, and well-being 3.
So how do we find average mood, stress, or well-being? Various correlations and algorithms can be used to express the average emotional state over time. We will start with a simple function that creates an average mood, stress, or well-being score based on a weighted average of the values. The function takes the readings for mood, stress, or well-being for the past month and assigns a 50% weight to the most recent week, 30% to week 3, and 20% to weeks 1 and 2 of the month’s readings. It is left as an exercise to the reader to evaluate and try different functions. Example 5-13 shows the code for the weighting algorithm.
Example 5-13. Algorithm for calculating mood, stress, and well-being over the past month
/* * Algorithm * 1. Read last 1 month's readings * 2. Weight 50% to 4th week * 3. Weight 25% to 1-3 week * 4. Weight 25% to how many readings (Good is 4/wk) */ DateTime time50p = baseTime.Subtract(new TimeSpan(7,0,0,0)); DateTime time30p = baseTime.Subtract(new TimeSpan(14, 0, 0, 0)); int m = 0; int s = 0; int w = 0; int c50 = 0; int c30 = 0; int c20 = 0; foreach (EmotionalStateModel emotion in emotionList) { if (emotion.When >= time50p) { m += (int)emotion.Mood * 50 ; s += (int)emotion.Stress * 50 ; w += (int)emotion.Wellbeing * 50; c50++; } else if (emotion.When >= time30p) { m += (int)emotion.Mood * 30; s += (int)emotion.Stress * 30 ; w += (int)emotion.Wellbeing * 30; c30++; } else { m += (int)emotion.Mood * 20; s += (int)emotion.Stress * 20; w += (int)emotion.Wellbeing * 20; c20++; } } // Final numbers int c = 50 * c50 + 30 * c30 + 20 * c20; m = m / c; s = s /c; w = w / c;