How To Develop Your First Google Fit App

google-fit

Google recently unveiled its Google Health initiative, Google Fit, the company’s response to Apple Health, which was showcased earlier at Google IO.

Google Fit relies on sensors built on Google wearables and phones to track various activities--such as running, walking and cycling--as well as to track personal information such as weight over time. Activities are tracked against the user’s fitness goals to provide insights into their fitness. Google has partnered up with vendors such as Nike, HTC, LG, Withings, Motorola, RunKeeper and Polar for greater device compatibilty.

Google has also released a consumer app, which is available on the Play Store; it comes pre-installed and is compatible with all Google Android Wear watches.

In this article, we will give you an introduction to Google Fit and provide advice on how to get started coding for it.

Why join the fitness revolution?

Google's previous health platform, Google Health, was discontinued at the end of 2012 because it did not get much traction. Timing is everything, and it took a few years for the fitness market to mature, thanks to various hardware accessories like FitBit and Jawbone, as well as Apple’s forey into Health with iOS 8.

tesco

People are arguably more aware of their health these days (and with their ability to track it using their devices and apps), and with Google Watch Wearables already in the hand of users, it made business sense for Google to reboot its health initiative. This time the market is ripe for companies with clout, like Apple and Google, to double down on health.

Architectural Overview

image

Google Fit consists of the Fitness Store, the persistance data cloud layer that stores data from disparate devices and sources; Sensor Framework, which is an abstract representation of sensors and fitness types (such as step count and heart rate) with the ability to query and interact with the database; and Permissions/User Controls, which manages user permissions and consent.

Google Fit APIs

Google Fit is available as an Android SDK as well as a REST API; for this article we worked with the latter. If you were curious to take the Android route, you can check out Google’s documentation.

Restful API

With REST acess you are offered the freedom of interacting with Google Fit through Web apps, as well as through iOS apps (although you would probably use the Android SDK for native Android apps).
 

google-fit-store

Google Fit’s REST API provides access to query:

  • data sources consisting of sensor data/sources (sensor framework)
  • datasets consisting of data-points from a data source
  • data points and sessions

oAuth Playground

For this exercise, we will be interacting with Google Fit’s REST API through Google’s oAuth Playground. But, before doing that, you'll need to create a client ID to access oAuth, recording the client id and client secret for later use.

image

WIthin the oAuth Playground, select the gear icon on the top right then select use your own credentials. You will then enter the client id and client secret values you obtained previously.

Finally in the input your own scopes field, enter:

https://www.googleapis.com/auth/fitness.activity.write and authorize API.

You should now be ready to interact wtih the REST API, so let’s begin experimenting with some HTTP calls.

Taking Google Fit APIs for a spin

Using the Google REST API as a guide, we can start using some of the listed methods to make a call. We'll start by creating a data source, which requires adding userid as a parameter, as per the documentation:

https://www.googleapis.com/fitness/v1/users/me/dataSources

Sending the request gets you the following response:

    GET /fitness/v1/users/me/dataSources HTTP/1.1
    Host: www.googleapis.com
    Content-length: 0
    Authorization: Bearer ya29.BAHJjY4wVJvZlbDd7XK2MEaFIbdE62F3_Jky5MaEQKdbWwiIjsuqxMF-enw9HV651bPtW_vVebIfSw
    HTTP/1.1 200 OK
    Content-length: 3
    X-xss-protection: 1; mode=block
    Content-location: https://www.googleapis.com/fitness/v1/users/me/dataSources
    X-content-type-options: nosniff
    Expires: Fri, 01 Jan 1990 00:00:00 GMT
    Vary: Origin,X-Origin
    Server: GSE
    Etag: "hLUJCvL5ZSSqXM3VrwyiV-fn_Cc/vyGp6PvFo4RvsFtPoIWeCReyIC8"
    Pragma: no-cache
    Cache-control: no-cache, no-store, max-age=0, must-revalidate
    Date: Fri, 23 Jan 2015 20:35:39 GMT
    X-frame-options: SAMEORIGIN
    Content-type: application/json; charset=UTF-8
    {}

In this case, I don’t have any data sources, so let’s go ahead and create a data source. The API endpoint we will call is:

https://www.googleapis.com/fitness/v1/users/me/dataSources

Add to the body section the following:

    {
  "dataStreamName": "programmableweb",
  "name": "test device",
  "type": "derived",
  "application": {
    "detailsUrl": "http://programmableweb.com",
    "name": "PW Example App",
    "version": "1"
  },
  "dataType": {
    "field": [
      {
        "name": "steps",
        "format": "integer"
      }
    ],
    "name": "com.google.step_count.delta"
  },
  "device": {
    "manufacturer": "ProgrammableWeb",
    "model": "Secret device",
    "type": "tablet",
    "uid": "1000001",
    "version": "1.0"
  }
}

And alas, our response:

    POST /fitness/v1/users/me/dataSources HTTP/1.1
    Host: www.googleapis.com
    Content-length: 516
    Content-type: application/json
    Authorization: Bearer ya29.BAG9HPMVOCfnEHzSW0QMP7OP4TZKONV-ro3zYurVLgdOiZYiE8dtS32V5ifKZiHLPGmb_MUjE58hwA
    {
      "dataStreamName": "programmableweb",
      "name": "test device",
      "type": "derived",
      "application": {
        "detailsUrl": "http://programmableweb.com",
        "name": "PW Example App",
        "version": "1"
      },
      "dataType": {
        "field": [
          {
            "name": "steps",
            "format": "integer"
          }
        ],
        "name": "com.google.step_count.delta"
      },
      "device": {
        "manufacturer": "ProgrammableWeb",
        "model": "Secret device",
        "type": "tablet",
        "uid": "1000001",
        "version": "1.0"
      }
    }
    HTTP/1.1 200 OK
    Content-length: 594
    X-xss-protection: 1; mode=block
    X-content-type-options: nosniff
    Expires: Fri, 01 Jan 1990 00:00:00 GMT
    Vary: Origin,X-Origin
    Server: GSE
    Etag: "hLUJCvL5ZSSqXM3VrwyiV-fn_Cc/PTNatUmfX6mKwP-srPlu9zAxA6o"
    Pragma: no-cache
    Cache-control: no-cache, no-store, max-age=0, must-revalidate
    Date: Fri, 23 Jan 2015 20:47:25 GMT
    X-frame-options: SAMEORIGIN
    Content-type: application/json; charset=UTF-8
    {
      "name": "test device", 
      "dataStreamName": "programmableweb", 
      "dataType": {
        "field": [
          {
            "name": "steps", 
            "format": "integer"
          }
        ], 
        "name": "com.google.step_count.delta"
      }, 
      "application": {
        "version": "1", 
        "name": "PW Example App", 
        "detailsUrl": "http://programmableweb.com"
      }, 
      "device": {
        "model": "Secret device", 
        "version": "1.0", 
        "type": "tablet", 
        "uid": "1000001", 
        "manufacturer": "ProgrammableWeb"
      }, 
      "dataStreamId": "derived:com.google.step_count.delta:336887542584:ProgrammableWeb:Secret device:1000001:programmableweb", 
      "type": "derived"
    }  

We can now go back and call the method to list our data sources again, just to confirm that we do have a new data source, and we get that data source we just created:

{
  "dataSource": [
    {
      "name": "test device", 
      "dataStreamName": "programmableweb", 
      "dataType": {
        "field": [
          {
            "name": "steps", 
            "format": "integer"
          }
        ], 
        "name": "com.google.step_count.delta"
      }, 
      "application": {
        "version": "1", 
        "name": "PW Example App", 
        "detailsUrl": "http://programmableweb.com"
      }, 
      "device": {
        "model": "Secret device", 
        "version": "1.0", 
        "type": "tablet", 
        "uid": "1000001", 
        "manufacturer": "ProgrammableWeb"
      }, 
      "dataStreamId": "derived:com.google.step_count.delta:336887542584:ProgrammableWeb:Secret device:1000001:programmableweb", 
      "type": "derived"
    }
  ]
}     

Now, let’s look at working with data sets. Enter the following call, using PATCH method:

https://www.googleapis.com/fitness/v1/users/me/dataSourcaes/derived:com.google.step_count.delta:336887542584:ProgrammableWeb:Secret device:1000001:programmableweb/datasets/1397513334728708316-1397515179728708316

In the body, enter:

{
  "dataSourceId":
      "derived:com.google.step_count.delta:336887542584:ProgrammableWeb:Secret device:1000001:programmableweb",
  "maxEndTimeNs": 1397515179728708316,
  "minStartTimeNs": 1397513334728708316,
  "point": [
    {
      "dataTypeName": "com.google.step_count.delta",
      "endTimeNanos": 1397513365565713993,
      "originDataSourceId": "",
      "startTimeNanos": 1397513334728708316,
      "value": [
        {
          "intVal": 8
        }
      ]
    },
    {
      "dataTypeName": "com.google.step_count.delta",
      "endTimeNanos": 1397513675197854515,
      "originDataSourceId": "",
      "startTimeNanos": 1397513530098955298,
      "value": [
        {
          "intVal": 3
        }
      ]
    }
  ]
}

The subsequent RESTful response is:

    {
      "minStartTimeNs": "1397513334728708316", 
      "maxEndTimeNs": "1397515179728708316", 
      "dataSourceId": "derived:com.google.step_count.delta:336887542584:ProgrammableWeb:Secret device:1000001:programmableweb", 
      "point": [
        {
          "startTimeNanos": "1397513334728708316", 
          "originDataSourceId": "", 
          "endTimeNanos": "1397513365565713993", 
          "value": [
            {
              "intVal": 8
            }
          ], 
          "dataTypeName": "com.google.step_count.delta"
        }, 
        {
          "startTimeNanos": "1397513530098955298", 
          "originDataSourceId": "", 
          "endTimeNanos": "1397513675197854515", 
          "value": [
            {
              "intVal": 3
            }
          ], 
          "dataTypeName": "com.google.step_count.delta"
        }
      ]
    }

So, that’s it--we created our first data point entering time as well as interval as a step_count. The idea is that you would have a device that would publish various data-points, which you would then push to Google Fit, conforming to the Json body style demonstrated above.

You can now take a look at the Google Fit Reference Guide to see how to work with other data-set methods. Remember to authorize the correct API permissions to access the appropriate resource endpoint.

Resources

Doron Katz A keen passion for emerging technologies, practices and methodologies, Doron embraces the vision of lean development with continuous customer development. Consultant for various startups, as a Project and Product Manager, with a mobile engineering background in iOS, and over 10 years of professional web development experience.

Comments