Advertisement
Scroll to top
Read Time: 11 min
This post is part of a series called Google Fit for Android.
Google Fit for Android: Recording API
Google Fit for Android: Sessions API

Google Fit is a platform that allows developers to build applications that are focused on user fitness data. One of the tools Google has provided is Google Fit for Android, which is available as a package in Google Play Services.

In a previous tutorial, we explored how to use the Google Fit Recording API to store fitness data through Google Play Services. This tutorial expands on the topic by exploring how to access and update data stored in Google Fit using the History API.

What makes Google Fit powerful is that fitness data is stored through Google Cloud Services, which allows it to be accessed and analyzed at a later time. While the Sensors and Recording APIs are focused on gathering fitness data from an Android device, the History API is intended to give developers easy access to that stored data.

This tutorial walks you through a sample project that demonstrates how to work with the History API. The finished product can be downloaded from GitHub.

1. Project Setup

Step 1: Set Up the Developer Console

To use Google Fit, you need to create an OAuth 2.0 client ID and register your application through the Google Developer Console. You can find a detailed explanation of how to set up a project in the Google Developer Console in my tutorial about the Google Fit Sensors API.

Step 2: Create the Android Project

Once you have your application configured for authentication in the Google Developer Console, use the package name you registered to create a new Android application with a minimum SDK of 9 and an empty Activity.

With the base Android app created, open build.gradle and include Google Play Services under the dependencies node and sync your app.

1
compile 'com.google.android.gms:play-services-fitness:8.4.0'

You should now be able to include the necessary Google Play Services classes into your application. Before we dive into the Java code for this tutorial, open activity_main.xml and modify it so that it includes five Button items that we will use to demonstrate some of the functionality of the History API.

1
<?xml version="1.0" encoding="utf-8"?>
2
<LinearLayout xmlns:android="https://schemas.android.com/apk/res/android"
3
    android:layout_width="match_parent"
4
    android:layout_height="match_parent"
5
    android:paddingBottom="@dimen/activity_vertical_margin"
6
    android:paddingLeft="@dimen/activity_horizontal_margin"
7
    android:paddingRight="@dimen/activity_horizontal_margin"
8
    android:paddingTop="@dimen/activity_vertical_margin"
9
    android:orientation="vertical">
10
11
    <Button
12
        android:id="@+id/btn_view_week"
13
        android:layout_width="match_parent"
14
        android:layout_height="wrap_content"
15
        android:text="View this weeks steps" />
16
17
    <Button
18
        android:id="@+id/btn_view_today"
19
        android:layout_width="match_parent"
20
        android:layout_height="wrap_content"
21
        android:text="View today's steps"/>
22
23
    <Button
24
        android:id="@+id/btn_add_steps"
25
        android:layout_width="match_parent"
26
        android:layout_height="wrap_content"
27
        android:text="Add step data for today" />
28
29
    <Button
30
        android:id="@+id/btn_update_steps"
31
        android:layout_width="match_parent"
32
        android:layout_height="wrap_content"
33
        android:text="Update step data for yesterday" />
34
35
    <Button
36
        android:id="@+id/btn_delete_steps"
37
        android:layout_width="match_parent"
38
        android:layout_height="wrap_content"
39
        android:text="Delete step data for yesterday" />
40
41
</LinearLayout>

When you run your application, the user interface should look like this:

To continue setting up your project, open MainActivity.java and implement the following callbacks and required methods:

  • GoogleApiClient.ConnectionCallbacks
  • GoogleAPiClient.OnConnectionFailedListener
  • View.OnClickListener

You also need to add a member variable for the GoogleApiClient and a set of references to your Button objects, as shown below.

1
public class MainActivity extends AppCompatActivity implements
2
    GoogleApiClient.ConnectionCallbacks,
3
    GoogleApiClient.OnConnectionFailedListener,
4
    View.OnClickListener {
5
 
6
    private Button mButtonViewWeek;
7
    private Button mButtonViewToday;
8
    private Button mButtonAddSteps;
9
    private Button mButtonUpdateSteps;
10
    private Button mButtonDeleteSteps;
11
12
    private GoogleApiClient mGoogleApiClient;
13
 
14
    @Override
15
    protected void onCreate(Bundle savedInstanceState) {
16
        super.onCreate(savedInstanceState);
17
        setContentView(R.layout.activity_main);
18
        
19
        mButtonViewWeek = (Button) findViewById(R.id.btn_view_week);
20
        mButtonViewToday = (Button) findViewById(R.id.btn_view_today);
21
        mButtonAddSteps = (Button) findViewById(R.id.btn_add_steps);
22
        mButtonUpdateSteps = (Button) findViewById(R.id.btn_update_steps);
23
        mButtonDeleteSteps = (Button) findViewById(R.id.btn_delete_steps);
24
25
        mButtonViewWeek.setOnClickListener(this);
26
        mButtonViewToday.setOnClickListener(this);
27
        mButtonAddSteps.setOnClickListener(this);
28
        mButtonUpdateSteps.setOnClickListener(this);
29
        mButtonDeleteSteps.setOnClickListener(this);
30
    }
31
 
32
    @Override
33
    public void onConnectionSuspended(int i) {
34
        Log.e("HistoryAPI", "onConnectionSuspended");
35
    }
36
37
    @Override
38
    public void onConnectionFailed(@NonNull ConnectionResult connectionResult) {
39
        Log.e("HistoryAPI", "onConnectionFailed");
40
    }
41
 
42
    public void onConnected(@Nullable Bundle bundle) {
43
        Log.e("HistoryAPI", "onConnected");
44
    }
45
46
 
47
    @Override
48
    public void onClick(View v) {
49
    }
50
}

The final thing you need to do to set up your sample project is connect to Google Play Services. This can be done at the end of the onCreate() method.

1
mGoogleApiClient = new GoogleApiClient.Builder(this)
2
        .addApi(Fitness.HISTORY_API)
3
        .addScope(new Scope(Scopes.FITNESS_ACTIVITY_READ_WRITE))
4
        .addConnectionCallbacks(this)
5
        .enableAutoManage(this, 0, this)
6
        .build();

With the above code, we connect to Google Play Services and request access to the History API in Google Fit, as well as permission to read and write activity data. By adding the enableAutoManage property, Google Play Services manages connecting and disconnecting properly.

2. Using the Google Fit History API

Using the History API, you can retrieve and aggregate data, insert values that you have gathered in your app or delete data that may be stored. One important thing to note is that all of the History API operations in this tutorial must be done off of the main thread. In other words, each operation takes place in an AsyncTask:

1
private class ViewWeekStepCountTask extends AsyncTask<Void, Void, Void> {
2
    protected Void doInBackground(Void... params) {
3
        displayLastWeeksData();
4
        return null;
5
    }
6
}

Step 1: Displaying Data Over Time

One task that you may want to do is use or display data that has been collected over time. You can do this by creating a new DataReadRequest for a specific type of data that fits some query parameters.

You can group this data together in Bucket objects by time or sessions. For this example, you request aggregated step data for the last week and bucket that data by day.

1
Calendar cal = Calendar.getInstance();
2
Date now = new Date();
3
cal.setTime(now);
4
long endTime = cal.getTimeInMillis();
5
cal.add(Calendar.WEEK_OF_YEAR, -1);
6
long startTime = cal.getTimeInMillis();
7
8
java.text.DateFormat dateFormat = DateFormat.getDateInstance();
9
Log.e("History", "Range Start: " + dateFormat.format(startTime));
10
Log.e("History", "Range End: " + dateFormat.format(endTime));
11
12
//Check how many steps were walked and recorded in the last 7 days

13
DataReadRequest readRequest = new DataReadRequest.Builder()
14
        .aggregate(DataType.TYPE_STEP_COUNT_DELTA, DataType.AGGREGATE_STEP_COUNT_DELTA)
15
        .bucketByTime(1, TimeUnit.DAYS)
16
        .setTimeRange(startTime, endTime, TimeUnit.MILLISECONDS)
17
        .build();

Once you have your DataReadRequest created, you can request data from the GoogleApiClient using the Fitness.HistoryApi.readData command. You also call await on the API client so that the thread stalls for up to a minute while waiting for the query. If something goes wrong, the thread continues after that minute.

1
DataReadResult dataReadResult = Fitness.HistoryApi.readData(mGoogleApiClient, readRequest).await(1, TimeUnit.MINUTES);

If the call returns successfully, you are able to access the user's fitness data from the DataReadResult object. Your step data request is returned in Bucket objects, though other types of data are returned as a List of DataSet objects. Depending on the type of data returned, you can access it with the following code:

1
//Used for aggregated data

2
if (dataReadResult.getBuckets().size() > 0) {
3
    Log.e("History", "Number of buckets: " + dataReadResult.getBuckets().size());
4
    for (Bucket bucket : dataReadResult.getBuckets()) {
5
        List<DataSet> dataSets = bucket.getDataSets();
6
        for (DataSet dataSet : dataSets) {
7
            showDataSet(dataSet);
8
        }
9
    }
10
}
11
//Used for non-aggregated data

12
else if (dataReadResult.getDataSets().size() > 0) {
13
    Log.e("History", "Number of returned DataSets: " + dataReadResult.getDataSets().size());
14
    for (DataSet dataSet : dataReadResult.getDataSets()) {
15
        showDataSet(dataSet);
16
    }
17
}

You can then retrieve each DataPoint from the DataSet objects that have been returned.

1
private void showDataSet(DataSet dataSet) {
2
    Log.e("History", "Data returned for Data type: " + dataSet.getDataType().getName());
3
    DateFormat dateFormat = DateFormat.getDateInstance();
4
    DateFormat timeFormat = DateFormat.getTimeInstance();
5
6
    for (DataPoint dp : dataSet.getDataPoints()) {
7
        Log.e("History", "Data point:");
8
        Log.e("History", "\tType: " + dp.getDataType().getName());
9
        Log.e("History", "\tStart: " + dateFormat.format(dp.getStartTime(TimeUnit.MILLISECONDS)) + " " + timeFormat.format(dp.getStartTime(TimeUnit.MILLISECONDS)));
10
        Log.e("History", "\tEnd: " + dateFormat.format(dp.getEndTime(TimeUnit.MILLISECONDS)) + " " + timeFormat.format(dp.getStartTime(TimeUnit.MILLISECONDS)));
11
        for(Field field : dp.getDataType().getFields()) {
12
            Log.e("History", "\tField: " + field.getName() +
13
                    " Value: " + dp.getValue(field));
14
        }
15
    }
16
}

If an app on the user's device has turned on the Recording API within the last week, then you receive data for each day that it was active. The logs from the above code should look similar to this:

1
E/History: Range Start: Feb 17, 2016
2
E/History: Range End: Feb 24, 2016
3
E/History: Number of buckets: 7
4
E/History: Data returned for Data type: com.google.step_count.delta
5
E/History: Data point:
6
E/History:  Type: com.google.step_count.delta
7
E/History:  Start: Feb 17, 2016 11:01:46 PM
8
E/History:  End: Feb 17, 2016 11:01:46 PM
9
E/History:  Field: steps Value: 9360
10
E/History: Data returned for Data type: com.google.step_count.delta
11
E/History: Data returned for Data type: com.google.step_count.delta
12
E/History: Data returned for Data type: com.google.step_count.delta
13
E/History: Data point:
14
E/History:  Type: com.google.step_count.delta
15
E/History:  Start: Feb 21, 2016 9:58:03 AM
16
E/History:  End: Feb 21, 2016 9:58:03 AM
17
E/History:  Field: steps Value: 10041
18
E/History: Data returned for Data type: com.google.step_count.delta
19
E/History: Data point:
20
E/History:  Type: com.google.step_count.delta
21
E/History:  Start: Feb 21, 2016 11:22:53 PM
22
E/History:  End: Feb 22, 2016 11:22:53 PM
23
E/History:  Field: steps Value: 10786
24
E/History: Data returned for Data type: com.google.step_count.delta
25
E/History: Data point:
26
E/History:  Type: com.google.step_count.delta
27
E/History:  Start: Feb 22, 2016 11:06:31 PM
28
E/History:  End: Feb 23, 2016 11:06:31 PM
29
E/History:  Field: steps Value: 13099
30
E/History: Data returned for Data type: com.google.step_count.delta
31
E/History: Data point:
32
E/History:  Type: com.google.step_count.delta
33
E/History:  Start: Feb 23, 2016 11:02:31 PM
34
E/History:  End: Feb 24, 2016 11:02:31 PM
35
E/History:  Field: steps Value: 9765

Step 2: Displaying Data for Today

You now know how to retrieve Google Fit data based on a timeframe. One common use case developers have is needing data for the current day. Luckily, Google has made this easy by adding the Fitness.HistoryApi.readDailyTotal call, which creates a DailyTotalResult object containing data collected for the day.

1
private void displayStepDataForToday() {
2
    DailyTotalResult result = Fitness.HistoryApi.readDailyTotal( mGoogleApiClient, DataType.TYPE_STEP_COUNT_DELTA ).await(1, TimeUnit.MINUTES);
3
    showDataSet(result.getTotal());
4
}

The above code logs out the user's step data for the day.

1
E/History: Data returned for Data type: com.google.step_count.delta
2
E/History: Data point:
3
E/History:     Type: com.google.step_count.delta
4
E/History:     Start: Feb 24, 2016 8:39:59 AM
5
E/History:     End: Feb 24, 2016 8:39:59 AM
6
E/History:     Field: steps Value: 9474

Step 3: Inserting Data Into Google Fit

While being able to read data from Google Fit is important, there may be times where you need to add your own collected data to the Google Fit data store. To do this, you select a time range for the data and create a DataSource object that represents the kind of information that you put into Google Fit. Next, you create a DataSet object and place a new DataPoint into it for the insert operation.

For this example, we are going to assume the user has walked one million steps, or roughly 500 miles, and insert that raw step data into the last hour of the user's activity.

It is important to note that if you select a time range that spans multiple days, the number of steps that you insert is evenly distributed among those days.

1
Calendar cal = Calendar.getInstance();
2
Date now = new Date();
3
cal.setTime(now);
4
long endTime = cal.getTimeInMillis();
5
cal.add(Calendar.HOUR_OF_DAY, -1);
6
long startTime = cal.getTimeInMillis();
7
8
DataSource dataSource = new DataSource.Builder()
9
    .setAppPackageName(this)
10
    .setDataType(DataType.TYPE_STEP_COUNT_DELTA)
11
    .setName("Step Count")
12
    .setType(DataSource.TYPE_RAW)
13
    .build();
14
    
15
int stepCountDelta = 1000000;
16
DataSet dataSet = DataSet.create(dataSource);
17
18
DataPoint point = dataSet.createDataPoint()
19
        .setTimeInterval(startTime, endTime, TimeUnit.MILLISECONDS);
20
point.getValue(Field.FIELD_STEPS).setInt(stepCountDelta);
21
dataSet.add(point);

After creating the DataSet object, you can insert it into the Google Fit data store using the History API's insertData call.

1
Fitness.HistoryApi.insertData(mGoogleApiClient, dataSet).await(1, TimeUnit.MINUTES);

If you insert the user's steps and then read the activity log, you should see something similar to the following:

1
E/History: Data returned for Data type: com.google.step_count.delta
2
E/History: Data point:
3
E/History:     Type: com.google.step_count.delta
4
E/History: 	Start: Feb 27, 2016 10:05:18 PM
5
E/History: 	End: Feb 28, 2016 10:05:18 PM
6
E/History: 	Field: steps Value: 1008747

Step 4: Update Data on Google Fit

Once you have inserted data into Google Fit, you are unable to insert any more data of that type if it overlaps with existing data. This could cause a problem if the user had walked 500 miles and then walked 500 more.

To solve this, you need to use the updateData call. Luckily, updating data is almost identical to inserting new data, except that you create a DataUpdateRequest instead.

1
Calendar cal = Calendar.getInstance();
2
Date now = new Date();
3
cal.setTime(now);
4
long endTime = cal.getTimeInMillis();
5
cal.add(Calendar.HOUR_OF_DAY, -1);
6
long startTime = cal.getTimeInMillis();
7
8
DataSource dataSource = new DataSource.Builder()
9
        .setAppPackageName(this)
10
        .setDataType(DataType.TYPE_STEP_COUNT_DELTA)
11
        .setName("Step Count")
12
        .setType(DataSource.TYPE_RAW)
13
        .build();
14
15
int stepCountDelta = 2000000;
16
DataSet dataSet = DataSet.create(dataSource);
17
18
DataPoint point = dataSet.createDataPoint()
19
        .setTimeInterval(startTime, endTime, TimeUnit.MILLISECONDS);
20
point.getValue(Field.FIELD_STEPS).setInt(stepCountDelta);
21
dataSet.add(point);
22
23
DataUpdateRequest updateRequest = new DataUpdateRequest.Builder().setDataSet(dataSet).setTimeInterval(startTime, endTime, TimeUnit.MILLISECONDS).build();
24
Fitness.HistoryApi.updateData(mGoogleApiClient, updateRequest).await(1, TimeUnit.MINUTES);

If you were to log out the user's steps for the day after running the above, you should see over two million steps.

1
E/History: Data returned for Data type: com.google.step_count.delta
2
E/History: Data point:
3
E/History:     Type: com.google.step_count.delta
4
E/History: 	Start: Feb 27, 2016 10:05:18 PM
5
E/History: 	End: Feb 28, 2016 10:05:18 PM
6
E/History: 	Field: steps Value: 2008747

Step 5: Deleting Data From Google Fit

The final operation available through the Google Fit History API is the ability to delete data that you have stored in Google Fit. This can be done by selecting a date range, creating a DataDeleteRequest object, and calling deleteData from the History API. While you can delete your own inserted data, data stored by Google Fit is not removed.

1
Calendar cal = Calendar.getInstance();
2
Date now = new Date();
3
cal.setTime(now);
4
long endTime = cal.getTimeInMillis();
5
cal.add(Calendar.DAY_OF_YEAR, -1);
6
long startTime = cal.getTimeInMillis();
7
8
DataDeleteRequest request = new DataDeleteRequest.Builder()
9
        .setTimeInterval(startTime, endTime, TimeUnit.MILLISECONDS)
10
        .addDataType(DataType.TYPE_STEP_COUNT_DELTA)
11
        .build();
12
13
Fitness.HistoryApi.deleteData(mGoogleApiClient, request).await(1, TimeUnit.MINUTES);

Conclusion

As you have seen, the History API is an incredibly powerful tool for accessing and manipulating the Google Fit data store. While other Google Fit APIs are intended to gather data, the History API allows you to maintain and access data for analysis. You should now be able to create amazing apps that take advantage of this feature.

Advertisement
Did you find this post useful?
Want a weekly email summary?
Subscribe below and we’ll send you a weekly email summary of all new Code tutorials. Never miss out on learning about the next big thing.
Advertisement
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.