Thursday, February 12, 2015

How long did I sleep last night: using Cordova, HealthKit, and JavaScript, and a handful of Promises

Now that we can know how much activity I did yesterday, we can look at whether I am getting enough sleep.

A quick look at the Apple documentation lets us see that HKCategoryTypeIdentifierSleepAnalysis is the right measure to be using. This is a subtype of SampleType so we can use the querySampleType function.

First of all here is the same boilerplate code, with the minor modification to allow for access to the sleep data rather than the step data we used before. You could of course combine the two.

var wearable = {

    avaliable: _.once(function() {

        return new Promise(function(resolve, reject) {
        
            if (window.plugins.healthkit) {
                window.plugins.healthkit.available(
                        function() {
                            console.log("Healthkit is avaliable");
                            resolve(true);
                        },
                        function() {
                            console.log("Healthkit is not avaliable");
                            reject(Error(false));
                        }
                );
            } else {
                reject(Error("HealthKit Not Available"));
            }
        });
    }),


    getHealthKit : _.once(function() {

        return wearable.avaliable().then(function() {

            return new Promise(function(resolve, reject) {
                window.plugins.healthkit.requestAuthorization(
                        {
                            'readTypes': ['HKCategoryTypeIdentifierSleepAnalysis'],
                            'writeTypes': []
                        },
                function() {
                    console.log("HealthKit authorisation accepted");
                    resolve(window.plugins.healthkit);
                },
                function() {
                    reject(Error("HealthKit authorisation rejected"));
                });
            });
        });
    }),

    ...

}



Then it is s simple matter of querying HeathKit with a useful date range. Note I use limit and ascending to access the last data point - it is possible that different tools will report multiple entires for a single night. (In particular new parents) I need to do a little bit more investigation here.

wearable = {

    ... 

    querySleep: function() {

        wearable.getHealthKit().then(function(healthkit) {

                var startDate = moment().subtract('d', 1).toDate();

                healthkit.querySampleType({
                        'startDate': startDate,
                        'endDate': moment().toDate(),
                        'sampleType': "HKCategoryTypeIdentifierSleepAnalysis",
                        'ascending': "NO",
                        'limit': 1
                    },
                    function(value) {
                        console.log("Success for runing sleep query");

                        // Debug output for the momment
                        value.forEach(function(next) {

                            console.dir(next);
                            var measure = next.value === 1 ? "Sleeping" : "In Bed";
                            var minutesSleep = moment(next.endDate).diff(next.startDate, "minutes");
                            var hoursSleep = moment(next.endDate).diff(next.startDate, "hours");
                            console.log("Entry got " + minutesSleep + " minutes " + measure);
                            console.log("Entry got " + hoursSleep + " hours " + measure);

                        });

                        // Use data in some way
                    });

            }
        }
    },
}


Finally it would be really useful to be able to be told when data is added to HealthKit by another app for example the client app for a wearable. You can do this using the monitorSampleType function. Ideally this would code would then use something called an anchored query so you only access the most recently added data; but I haven't had time to implement this yet so we simply call querySleep().

Now making sure you app wakes up in the background to receive this data is a whole other blog....

var wearable = {

    ...

    monitorSleep: _.once(function() {

        console.log("Starting to monitor sleep");


        wearable.getHealthKit().then(function(healthkit) {
            healthkit.monitorSampleType({
                    'sampleType': "HKCategoryTypeIdentifierSleepAnalysis"
                },
                _.debounce(function(value) {
                    console.log("Sleep data has been updated, lets see if anything interesting has been added");
                    wearable.querySleep();
                }, 2000),
                function() {
                    console.log("Failed to monitor sample data");
                    console.dir(arguments);
                });

        });
    })
}




Tuesday, February 10, 2015

Per client cookie handling with Jersey

A lot of REST services will use cookies as part of the authentication / authorisation scheme. This is a problem because by default the old Jersey client will use the singleton CookieHandler.getDefault which is most cases will be null and if not null will not likely work in a multithreaded server environment. (This is because in the background the default Jersey client will use URL.openConnection)

Now you can work around this by using the Apache HTTP Client adapter for Jersey; but this is not always available. So if you want to use the Jersey client with cookies in a server environment you need to do a little bit of reflection to ensure you use your own private cookie jar.

final CookieHandler ch = new CookieManager();
    
    Client client = new Client(new URLConnectionClientHandler(
       new HttpURLConnectionFactory() {

        @Override
        public HttpURLConnection getHttpURLConnection(URL uRL) throws IOException {
            HttpURLConnection connect = (HttpURLConnection) uRL.openConnection();
        
            try {
                
                Object toModify = connect;
                
                if (!(toModify instanceof sun.net.www.protocol.http.HttpURLConnection)) {
        
                    Field delegateField = connect.getClass().getDeclaredField("delegate");
                    delegateField.setAccessible(true);
                    toModify = MethodHandles.lookup().unreflectGetter(delegateField)
                        .bindTo(toModify).invoke();
                    
                }
        
                Field cookieField = sun.net.www.protocol.http.HttpURLConnection.class.getDeclaredField("cookieHandler");
                cookieField.setAccessible(true);
                MethodHandle mh = MethodHandles.lookup().unreflectSetter(cookieField);
                mh.bindTo(toModify).invoke(ch);
            } catch (Throwable e) {
                e.printStackTrace();
            }
        
            return connect;

        }
    }));
    

This will only work if your environment is using the internal implementation of sun.net.www.protocol.http.HttpURLConnection that comes with the JDK. This appears to be the case for modern versions of WLS.

For JAX-RS 2.0 you can do a similar change using Jersey 2.x specific ClientConfig class and HttpUrlConnectorProvider.

final CookieHandler ch = new CookieManager();


    Client client =
        ClientBuilder.newClient(new ClientConfig().connectorProvider(new HttpUrlConnectorProvider().connectionFactory(new HttpUrlConnectorProvider.ConnectionFactory() {
            @Override
            public HttpURLConnection getConnection(URL uRL) throws IOException {
                HttpURLConnection connect = (HttpURLConnection) uRL.openConnection();
            
                try {
                    
                    Object toModify = connect;
                    
                    if (!(toModify instanceof sun.net.www.protocol.http.HttpURLConnection)) {
            
                        Field delegateField = connect.getClass().getDeclaredField("delegate");
                        delegateField.setAccessible(true);
                        toModify = MethodHandles.lookup().unreflectGetter(delegateField)
                            .bindTo(toModify).invoke();
                        
                    }
            
                    Field cookieField = sun.net.www.protocol.http.HttpURLConnection.class.getDeclaredField("cookieHandler");
                    cookieField.setAccessible(true);
                    MethodHandle mh = MethodHandles.lookup().unreflectSetter(cookieField);
                    mh.bindTo(toModify).invoke(ch);
                } catch (Throwable e) {
                    e.printStackTrace();
                }
            
                return connect;

            }
        })));



Update 11th Feb 2015: It seems in some cases, in particular using https, I have seen the HttpURLConnection wrapped in another class, to work around this just use reflection to access the value of the delegate field. I have updated the code examples to reflect this issue.

Sunday, February 1, 2015

How many steps did I walk yesterday: using Cordova, HealthKit, and JavaScript, and a handful of Promises

As people who know me might know I have been playing around with various wearables for some years now, starting with a Nike Fuel Band, and also trying out products from Misfit and UP along the way. I have mostly just watched with interest as my exercise levels, and indeed sleep now we have a little one in the house, has gone up and down over time.

More recently with the arrival of HealthKit there comes a standard method of accessing this information from multiple products on the iPhone, I needed to brush up on my JavaScript for work so I decided to have a play in Cordova to build something in my spare time. One of the cool things about that Cordova community when compared to other app development frameworks is the large and healthy plugin community - and quick google and I found what I needed to support HealthKit HealthKit. This took only a small amount of patching from me to get at the information I wanted. In this blog I will just look at number of steps per day as a proxy for activity.

I am going to assume the rest of the Cordova App is in place and start with a simple wearable object which exposes a promise that allows you to check whether HealthKit is available on this platform, you might have iOS 8 but HealthKit is iPhone only. Remember that most of Cordova is interacting with a bunch of asynchronous native systems so you have to make sure you consider this as you work with it. Hence the liberal use of promises. (If you are wanting to support < iOS 8 then you will need to use a different promise library / shim) In this case I am also using the underscorejs library to create a promise who's result won't be calculated until it is invoked for the first time.

var wearable = {

    avaliable: _.once(function() {

        return new Promise(function(resolve, reject) {
        
            if (window.plugins.healthkit) {
                window.plugins.healthkit.available(
                        function() {
                            console.log("Healthkit is avaliable");
                            resolve(true);
                        },
                        function() {
                            console.log("Healthkit is not avaliable");
                            reject(Error(false));
                        }
                );
            } else {
                reject(Error("HealthKit Not Available"));
            }
        });
    }),

...

}

The next stage of the code, once you are sure that the system is available, is to get verify that you can access the data you want to access. Now Apple is clear in there developer guide that you should only request the data you might want to access once. So no asking for step data one time then heath rate data in a different part of the UI. The Cordova HealthKit Plugin will just return with a success if those values are already authorised, so we will use another promise as a gateway to actually working with the data to make one consistent entry point for authorisation.

var wearable = {

...


    getHealthKit : _.once(function() {

        return wearable.avaliable().then(function() {

            return new Promise(function(resolve, reject) {
                window.plugins.healthkit.requestAuthorization(
                        {
                            'readTypes': ['HKQuantityTypeIdentifierStepCount'],
                            'writeTypes': []
                        },
                function() {
                    console.log("HealthKit authorisation accepted");
                    resolve(window.plugins.healthkit);
                },
                function() {
                    reject(Error("HealthKit authorisation rejected"));
                });
            });
        });
    }),

...

}

Finally now we have all the ground work in place, lets put in place a simple method to work out how many steps were recorded yesterday. I am then using special function called sumQuantityType that performs some rather clever statistical leg work for you. Consider if you have a iPhone 6 along with step monitoring wrist band of some kind like a Apple Watch, in HealthKit you will have two lots of data that inconsistently overlaps. HealthKit provides methods to deal with this, more information can be found in the WWDC presentation from 2014, but basically it works out for each time segment which data source(s) to use to get the value for the time range requested.

var wearable = {

...


    querySteps : function() {
        
        return wearable.getHealthKit().then(function (healthkit)
        {
            return new Promise(function(resolve, reject) {

                var m = moment().startOf('day');
                var endDate = m.toDate();
                var startDate = moment(m).subtract('d', 1).toDate();

                healthkit.sumQuantityType({
                        'startDate': startDate,
                        'endDate': endDate,
                        'sampleType': "HKQuantityTypeIdentifierStepCount"
                    },
                    function(value)
                    {
                        resolve(value)
                    },
                    function()
                    {
                        reject(Error("Problem queuing steps"));
                    });
        });
    }

}

Now we have this object, we can access the data and show it to the user. Note that the promise objects chain so the catch function will tell you if something went wrong at any stage of the process, if you had tried to wire this up with event handlers you would have had an ungodly mess of code and callbacks.

wearable.querySteps()
       .then(function(value){
           console.log("Number of steps "  + value);
           ... update UI
       })
       .catch(function (error) {
           ... update UI
       };


This is of course just the start, there is a lot more interesting stuff you can get from this API; but that is for another day. I like HealthKit because unlike GoogleFit all your health data is stored locally by default. There is a lot of potential here for doing interesting stuff.

Finally a quick shout out of Eddy Verbruggen who created HealthKit plugin along with a whole host of other interesting libraries.

I am going to walk around for a bit now, gotta meet those targets. :-)