Work with the Fitness History

The History API enables your app to perform bulk operations on the fitness store: reading, inserting, updating, and deleting fitness data. Use the History API to:

  • Read fitness data that was inserted or recorded using other apps.
  • Import batch data into Google Fit.
  • Update data in Google Fit.
  • Delete data that your app previously stored in the fitness history.

To insert fitness data with session metadata, you can use the Sessions API.

Read data

Read detailed and aggregate data

To read data from the fitness history, first create a DataReadRequest instance:

// Setting a start and end date using a range of 1 week before this moment.
Calendar cal = Calendar.getInstance();
Date now = new Date();
cal.setTime(now);
long endTime = cal.getTimeInMillis();
cal.add(Calendar.WEEK_OF_YEAR, -1);
long startTime = cal.getTimeInMillis();

java.text.DateFormat dateFormat = getDateInstance();
Log.i(TAG, "Range Start: " + dateFormat.format(startTime));
Log.i(TAG, "Range End: " + dateFormat.format(endTime));

DataReadRequest readRequest = new DataReadRequest.Builder()
        // The data request can specify multiple data types to return, effectively
        // combining multiple data queries into one call.
        // In this example, it's very unlikely that the request is for several hundred
        // datapoints each consisting of a few steps and a timestamp.  The more likely
        // scenario is wanting to see how many steps were walked per day, for 7 days.
        .aggregate(DataType.TYPE_STEP_COUNT_DELTA, DataType.AGGREGATE_STEP_COUNT_DELTA)
                // Analogous to a "Group By" in SQL, defines how data should be aggregated.
                // bucketByTime allows for a time span, whereas bucketBySession would allow
                // bucketing by "sessions", which would need to be defined in code.
        .bucketByTime(1, TimeUnit.DAYS)
        .setTimeRange(startTime, endTime, TimeUnit.MILLISECONDS)
        .build();

The data request can specify multiple data types to return, effectively combining multiple data queries into one call. This example only specifies the TYPE_STEP_COUNT_DELTA data type. The data request can also indicate whether to return time-series data points or aggregated data points. This example uses aggregated data points where each DataPoint represents the number of steps walked in a day. For this particular use case, aggregated data points have two advantages:

  • Your app and the fitness store exchange smaller amounts of data.
  • Your app does not have to aggregate the data manually.

Your app can use data requests to retrieve lots of different types of data. The following example shows how to create a DataReadRequest to get calories burned for each activity performed within the specified time range. The resulting data matches the calories per activity as reported in the Google Fit app, with each activity getting its own bucket of calorie data.

DataReadRequest readRequest = new DataReadRequest.Builder()
                .aggregate(DataType.TYPE_CALORIES_EXPENDED, DataType.AGGREGATE_CALORIES_EXPENDED)
                .bucketByActivityType(1, TimeUnit.SECONDS)
                .setTimeRange(startTime, endTime, TimeUnit.MILLISECONDS)
                .build();

After you create a DataReadRequest instance, use the HistoryApi.readData() method and wait synchronously or provide a callback method to process the data from the fitness history.

// Invoke the History API to fetch the data with the query and await the result of
// the read request.
DataReadResult dataReadResult =
        Fitness.HistoryApi.readData(mClient, readRequest).await(1, TimeUnit.MINUTES);

The following example demonstrates how to obtain the DataPoint instances from a DataSet:

private static void dumpDataSet(DataSet dataSet) {
    Log.i(TAG, "Data returned for Data type: " + dataSet.getDataType().getName());
    DateFormat dateFormat = getTimeInstance();

    for (DataPoint dp : dataSet.getDataPoints()) {
        Log.i(TAG, "Data point:");
        Log.i(TAG, "\tType: " + dp.getDataType().getName());
        Log.i(TAG, "\tStart: " + dateFormat.format(dp.getStartTime(TimeUnit.MILLISECONDS)));
        Log.i(TAG, "\tEnd: " + dateFormat.format(dp.getEndTime(TimeUnit.MILLISECONDS)));
        for(Field field : dp.getDataType().getFields()) {
            Log.i(TAG, "\tField: " + field.getName() +
                    " Value: " + dp.getValue(field));
        }
    }
}

Read daily total data

Google Fit also provides simple access to the daily total of a specified data type. Use the HistoryApi.readDailyTotal() method to retrieve the data type that you specify as of midnight of the current day in the device's current timezone. For example, pass in the TYPE_STEP_COUNT_DELTA data type to this method to retrieve the daily total steps. You may pass in an instantaneous data type that has an aggregate daily total. For more information on the supported data types, see AGGREGATE_INPUT_TYPES.

Google Fit does not require authorization to subscribe to TYPE_STEP_COUNT_DELTA updates from the HistoryApi.readDailyTotal() method when this method is called using the default account and no scopes are specified. This can be useful if you require step data for use in areas where you are unable to show the permissions panel (for example, Android Wear watch faces).

Users prefer to see consistent step counts across the Google Fit app, other fitness apps, and Android Wear watch faces, as this provides them with a consistent and reliable experience. To keep step counts consistent, subscribe to steps in the Google Fit platform from your fitness app or watch face, and then call this method every 30 seconds in interactive mode, and every 60 seconds in ambient mode. For more information on how to use this data in a watch face, see Showing Information in Watch Faces and the Android Watch Face sample application.

Insert data

To insert data into the fitness history, first create a DataSet instance:

// Set a start and end time for our data, using a start time of 1 hour before this moment.
Calendar cal = Calendar.getInstance();
Date now = new Date();
cal.setTime(now);
long endTime = cal.getTimeInMillis();
cal.add(Calendar.HOUR_OF_DAY, -1);
long startTime = cal.getTimeInMillis();

// Create a data source
DataSource dataSource = new DataSource.Builder()
        .setAppPackageName(this)
        .setDataType(DataType.TYPE_STEP_COUNT_DELTA)
        .setStreamName(TAG + " - step count")
        .setType(DataSource.TYPE_RAW)
        .build();

// Create a data set
int stepCountDelta = 950;
DataSet dataSet = DataSet.create(dataSource);
// For each data point, specify a start time, end time, and the data value -- in this case,
// the number of new steps.
DataPoint dataPoint = dataSet.createDataPoint()
        .setTimeInterval(startTime, endTime, TimeUnit.MILLISECONDS);
dataPoint.getValue(Field.FIELD_STEPS).setInt(stepCountDelta);
dataSet.add(dataPoint);

After you create a DataSet instance, use the HistoryApi.insertData method and wait synchronously or provide a callback method to check the status of the insertion.

// Then, invoke the History API to insert the data and await the result, which is
// possible here because of the {@link AsyncTask}. Always include a timeout when calling
// await() to prevent hanging that can occur from the service being shutdown because
// of low memory or other conditions.
Log.i(TAG, "Inserting the dataset in the History API.");
com.google.android.gms.common.api.Status insertStatus =
        Fitness.HistoryApi.insertData(mClient, dataSet)
                .await(1, TimeUnit.MINUTES);

// Before querying the data, check to see if the insertion succeeded.
if (!insertStatus.isSuccess()) {
    Log.i(TAG, "There was a problem inserting the dataset.");
    return null;
}

// At this point, the data has been inserted and can be read.
Log.i(TAG, "Data insert was successful!");

Manage conflicting data points

Each DataPoint in your app's DataSet must have a startTime and an endTime that defines a unique interval within that DataSet, with no overlap between DataPoint instances.

If your app attepts to insert a new DataPoint that conflicts with an existing DataPoint instance, the new DataPoint is discarded. To insert a new DataPoint that may overlap existing data points, use the HistoryApi.updateData method described in Update data.

Update data

Google Fit enables your app to update fitness data that it previously inserted into the fitness history. To add data to the fitness history for a new DataSet, or to add new DataPoint instances that do not conflict with existing data points as described in Manage conflicting data points, your app should use the HistoryApi.insertData method.

To update fitness history, use the HistoryApi.updateData method. This method deletes any existing DataPoint instances that overlap with DataPoint instances added using this method.

To update fitness history data, first create a DataSet instance:

// Set a start and end time for the data that fits within the time range
// of the original insertion.
Calendar cal = Calendar.getInstance();
Date now = new Date();
cal.setTime(now);
cal.add(Calendar.MINUTE, 0);
long endTime = cal.getTimeInMillis();
cal.add(Calendar.MINUTE, -50);
long startTime = cal.getTimeInMillis();

// Create a data source
DataSource dataSource = new DataSource.Builder()
        .setAppPackageName(this)
        .setDataType(DataType.TYPE_STEP_COUNT_DELTA)
        .setStreamName(TAG + " - step count")
        .setType(DataSource.TYPE_RAW)
        .build();

// Create a data set
int stepCountDelta = 1000;
DataSet dataSet = DataSet.create(dataSource);
// For each data point, specify a start time, end time, and the data value -- in this case,
// the number of new steps.
DataPoint dataPoint = dataSet.createDataPoint()
        .setTimeInterval(startTime, endTime, TimeUnit.MILLISECONDS);
dataPoint.getValue(Field.FIELD_STEPS).setInt(stepCountDelta);
dataSet.add(dataPoint);

Then, use DataUpdateRequest.Builder() to create a new data update request, and use the HistoryApi.updateData method to make the request.

Log.i(TAG, "Updating the dataset in the History API.");


DataUpdateRequest request = new DataUpdateRequest.Builder()
        .setDataSet(dataSet)
        .setTimeInterval(startTime, endTime, TimeUnit.MILLISECONDS)
        .build();

com.google.android.gms.common.api.Status updateStatus =
        Fitness.HistoryApi.updateData(mClient, request)
                .await(1, TimeUnit.MINUTES);

// Before querying the data, check to see if the update succeeded.
if (!updateStatus .isSuccess()) {
    Log.i(TAG, "There was a problem updating the dataset.");
    return null;
}

// At this point the data has been updated and can be read.
Log.i(TAG, "Data update was successful.");

Delete data

Google Fit enables your app to delete fitness data that it inserted into the fitness history.

To delete fitness data from the fitness history, use the HistoryApi.deleteData method:

// Set a start and end time for our data, using a start time of 1 day before this moment.
Calendar cal = Calendar.getInstance();
Date now = new Date();
cal.setTime(now);
long endTime = cal.getTimeInMillis();
cal.add(Calendar.DAY_OF_YEAR, -1);
long startTime = cal.getTimeInMillis();

//  Create a delete request object, providing a data type and a time interval
DataDeleteRequest request = new DataDeleteRequest.Builder()
        .setTimeInterval(startTime, endTime, TimeUnit.MILLISECONDS)
        .addDataType(DataType.TYPE_STEP_COUNT_DELTA)
        .build();

// Invoke the History API with the Google API client object and delete request, and then
// specify a callback that will check the result.
Fitness.HistoryApi.deleteData(mClient, request)
        .setResultCallback(new ResultCallback<Status>() {
            @Override
            public void onResult(Status status) {
                if (status.isSuccess()) {
                    Log.i(TAG, "Successfully deleted today's step count data.");
                } else {
                    // The deletion will fail if the requesting app tries to delete data
                    // that it did not insert.
                    Log.i(TAG, "Failed to delete today's step count data.");
                }
            }
        });

Apps can provide specific sessions or delete all data. For more information, see the API reference for DataDeleteRequest.

Send feedback about...