We use essential cookies for the website to function, as well as analytics cookies for analyzing and creating statistics of the website performance. To agree to the use of analytics cookies, click "Accept All". You can manage your preferences at any time by clicking "Cookie Settings" on the footer. More Information.

Only Essential Cookies
Accept All

Managing Exercise Records

After integrating Health Service Kit, you can manage users' exercise records by calling APIs in the ActivityRecordsController class.

First, check whether your app has been granted the following scopes to operate on the exercise record data:

  • Read: Scopes.HEALTHKIT_ACTIVITY_RECORD_READ
  • Write: Scopes.HEALTHKIT_ACTIVITY_RECORD_WRITE

If your app has obtained the preceding scopes, you will be able to perform the following operations on user data:

NOTE
  1. Before calling APIs provided by the Health Service Kit SDK for Android, make sure that the phone screen is unlocked and remains on. Otherwise, API calling may fail with the error code DISABLE_DATA_OPERATION. For details, please refer to error codes.
  2. Before calling APIs provided by the Health Service Kit SDK, make sure that your app runs in the foreground, or is bound with a foreground app. Otherwise, API calling may fail with the error code APPLICATION_NOT_FORGROUND. For details, please refer to error codes.
  3. If the Activity object (called in scenarios where app UIs are included) is passed when HuaweiHiHealth.getActivityRecordsController() is used to obtain the ActivityRecordsController object, the HMS Core version can be updated forcibly. If the Context object (called in scenarios where app UIs are not included) is passed, the HMS Core version cannot be updated forcibly.
  4. Learn how to call APIs provided by the Health Service Kit SDK for Android by referring to Example of Calling APIs of the Health Service Kit SDK for Android before API calling.

Writing Exercise Records to Health Service Kit

You can write exercise records to Health Service Kit via ActivityRecordsController. The detailed operations are as follows:

  1. Create an ActivityRecord object with a specified workout type, start time, end time, workout statistics, time zone, and other necessary information.
  2. Create an ActivityRecordInsertOptions object using ActivityRecord and available sampling datasets or polymerized sampling points.
  3. Insert ActivityRecordInsertOptions into Health Service Kit using the ActivityRecordsController.addActivityRecord method.
    NOTE

    The start time and end time passed for inserting exercise records must be later than the UNIX timestamp corresponding to January 1, 2014.

Sample code for writing exercise records to Health Service Kit:

// Note:
// 1. this refers to an Activity object.
// 2. Initialize objects of the ActivityRecordsController class each time before API calling. Otherwise, data reading may fail due to expired Activity or Context.
final ActivityRecordsController activityRecordsController = HuaweiHiHealth.getActivityRecordsController(this);
Context context = getApplicationContext();

// Build the time range of the request object: start time and end time.
// Note that the start time and end time must be later than the UNIX timestamp corresponding to January 1, 2014.
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();

// Build the PaceSummary object.
// PaceSummary in ActivitySummary is used to carry pace statistics.
ActivitySummary activitySummary = new ActivitySummary();
PaceSummary paceSummary = new PaceSummary();
paceSummary.setAvgPace(247.27626);
paceSummary.setBestPace(212.0);
paceSummary.setPartTimeMap((Map<String, Double>) new HashMap<>().put(1.0, 456));
paceSummary.setPaceMap((Map<String, Double>) new HashMap<>().put(1.0, 263));
activitySummary.setPaceSummary(paceSummary);

// Build sampling points for statistical data based on the data collector and add the sampling points to the DataSummary object.
// Create a sampling point for distance statistics.
SamplePoint samplePointDistance = new SamplePoint.Builder(DataType.DT_CONTINUOUS_DISTANCE_TOTAL).build();
samplePointDistance.setTimeInterval(startTime + 1L, startTime + 300000L, TimeUnit.MILLISECONDS);
samplePointDistance.getFieldValue(Field.FIELD_DISTANCE).setDoubleValue(352.0d);

// Create a sampling point for speed statistics.
SamplePoint samplePointSpeed = new SamplePoint.Builder(DataType.POLYMERIZE_CONTINUOUS_SPEED_STATISTICS).build();
samplePointSpeed.setTimeInterval(startTime + 1L, startTime + 300000L, TimeUnit.MILLISECONDS);
samplePointSpeed.getFieldValue(Field.FIELD_AVG).setDoubleValue(6.0d);
samplePointSpeed.getFieldValue(Field.FIELD_MIN).setDoubleValue(4.0d);
samplePointSpeed.getFieldValue(Field.FIELD_MAX).setDoubleValue(8.0d);

// DataSummary in ActivitySummary is used to carry statistical data.
List<SamplePoint> dataSummary = new ArrayList<>();
dataSummary.add(samplePointDistance);
dataSummary.add(samplePointSpeed);
activitySummary.setDataSummary(dataSummary);

// Build SampleSection and add it to SectionSummary.
SamplePoint samplePointSectionDistanceSamplePoint = new SamplePoint.Builder(DataType.DT_CONTINUOUS_DISTANCE_TOTAL).build();
samplePointSectionDistanceSamplePoint.getFieldValue(Field.FIELD_DISTANCE).setDoubleValue(400d);
SamplePoint samplePointSectionSpeedSamplePoint = new SamplePoint.Builder(DataType.POLYMERIZE_CONTINUOUS_SPEED_STATISTICS).build();
samplePointSectionSpeedSamplePoint.getFieldValue(Field.FIELD_AVG).setDoubleValue(60.0d);
samplePointSectionSpeedSamplePoint.getFieldValue(Field.FIELD_MIN).setDoubleValue(40.0d);
samplePointSectionSpeedSamplePoint.getFieldValue(Field.FIELD_MAX).setDoubleValue(80.0d);
SampleSection sampleSection = new SampleSection.Builder()
    .setSectionNum(1)
    .setSectionTime(60 * 5L, TimeUnit.SECONDS)
    .setStartTime(startTime + 1L, TimeUnit.MILLISECONDS)
    .setEndTime(startTime + 5 * 60 * 1000L, TimeUnit.MILLISECONDS)
    .setSectionDataList(Arrays.asList(samplePointSectionDistanceSamplePoint, samplePointSectionSpeedSamplePoint))
    .build();
activitySummary.setSectionSummary(Collections.singletonList(sampleSection));

// Build the exercise record and set the ActivitySummary object of the statistical data.
ActivityRecord activityRecord = new ActivityRecord.Builder()
    .setName("AddActivityRecord")
    .setDesc("This is ActivityRecord add test!")
    .setId("MyAddActivityRecordId")
    .setActivityTypeId(HiHealthActivities.RUNNING)
    .setStartTime(startTime, TimeUnit.MILLISECONDS)
    .setEndTime(endTime, TimeUnit.MILLISECONDS)
    .setActivitySummary(activitySummary)
    .setTimeZone("+0800")
    .setMetadata("this is my test") 
    .build();

// Create a data collector for detailed data.
DataCollector dataCollector = new com.huawei.hms.hihealth.data.DataCollector.Builder()
    .setDataType(DataType.DT_INSTANTANEOUS_STEPS_RATE)
    .setDataGenerateType(DataCollector.DATA_TYPE_RAW)
    .setPackageName(getApplicationContext())
    .setDataCollectorName("AddActivityRecord")
    .build();
// Build a sampling dataset based on the data collector.
SampleSet sampleSet = SampleSet.create(dataCollector);

// Build a sampling point object for the detailed incremental cadence data (DT_INSTANTANEOUS_STEPS_RATE) and add it to the detailed sampling dataset.
SamplePoint samplePointStepDelta = sampleSet.createSamplePoint().setTimeInterval(startTime, endTime, TimeUnit.MILLISECONDS);
samplePointStepDelta.getFieldValue(Field.FIELD_STEP_RATE).setDoubleValue(100.0d);
sampleSet.addSample(samplePointStepDelta);

// Build an exercise record request and set the activityRecord object and the associated detailed sampling dataset.
ActivityRecordInsertOptions insertOption =
    new ActivityRecordInsertOptions.Builder().setActivityRecord(activityRecord).addSampleSet(sampleSet).build();

// Call addActivityRecord to add an exercise record.
Task<Void> addActivityRecordTask = activityRecordsController.addActivityRecord(insertOption);
addActivityRecordTask.addOnSuccessListener(new OnSuccessListener<Void>() {
    @Override
    public void onSuccess(Void aVoid) {
        Log.i("ActivityRecords","ActivityRecord was added successfully!");
    }
}).addOnFailureListener(new OnFailureListener() {
    @Override
    public void onFailure(Exception e) {
        Log.i("ActivityRecord","Failed to add the ActivityRecord:" + e.getMessage());
    }
});

Deleting Exercise Records from Health Service Kit

You can delete exercise records through ActivityRecordsController as follows:

  1. Create an ActivityRecordDeleteOptions object based on activityRecordIds or the workout start and end times.
  2. Call ActivityRecordsController.deleteActivityRecord to delete exercise record data matching ActivityRecordDeleteOptions from Health Service Kit.
    NOTE
    1. The start time and end time passed for deleting exercise records must be later than the UNIX timestamp corresponding to January 1, 2014.
    2. If activityRecordIds is not empty, it will be used to delete exercise record data. Otherwise, exercise record data will be deleted based on the start and end times.

Sample code for deleting exercise records from Health Service Kit:

// Note:
// 1. this refers to an Activity object.
// 2. Initialize objects of the ActivityRecordsController class each time before API calling. Otherwise, data reading may fail due to expired Activity or Context.
final ActivityRecordsController activityRecordsController = HuaweiHiHealth.getActivityRecordsController(this);

// Build the time range of the request object: start time and end time.
// Note that the start time and end time must be later than the UNIX timestamp corresponding to January 1, 2014.
Calendar cal = Calendar.getInstance();
Date now = new Date();
cal.setTime(now);
long endTime = cal.getTimeInMillis();
cal.add(Calendar.DAY_OF_YEAR, -2);
long startTime = cal.getTimeInMillis();
// Build subDataTypeList for the request object.
List<DataType> subDataTypeList = Collections.singletonList(DataType.DT_CONTINUOUS_STEPS_DELTA);
// Build activityRecordIds for the request object.
List<String> activityRecordIds = Collections.singletonList("MyAddActivityRecordId");

// Build the request body with the deletion object.
ActivityRecordDeleteOptions deleteRequest = new ActivityRecordDeleteOptions.Builder()
    .setSubDataTypeList(subDataTypeList)
    .setTimeInterval(startTime, endTime, TimeUnit.MILLISECONDS)
    .setActivityRecordIds(activityRecordIds)
    .build();

// Call deleteActivityRecord to delete the exercise record.
Task<Void> deleteTask = activityRecordsController.deleteActivityRecord(deleteRequest);
deleteTask.addOnSuccessListener(new OnSuccessListener<Void>() {
    @Override
    public void onSuccess(Void aVoid) {
        Log.i("ActivityRecords","ActivityRecord was deleted successfully!");
    }
}).addOnFailureListener(new OnFailureListener() {
    @Override
    public void onFailure(Exception e) {
        Log.i("ActivityRecord","Failed to delete the ActivityRecord:" + e.getMessage());
    }
});

Reading Exercise Records from Health Service Kit

You can obtain exercise records that meet certain criteria and data associated with the records from Health Service Kit, for example, exercise records of specific data types included in a specific time period, or exercise records created by other apps. activitySummary in ActivityRecord enables you to obtain the statistical data of the corresponding exercise record. You can find the detailed data in sampleSet of ActivityRecordReply.

  1. To obtain a list of exercise records that meet specific conditions, create an ActivityRecordReadOptions instance first.
    NOTE
    1. The start time and end time for querying exercise records must be later than the UNIX timestamp corresponding to January 1, 2014.
    2. The start time and end time for querying cannot span over 31 days.
    3. The data type carried in the read method of the ActivityRecordReadOptions instance can be obtained from Overview. If no data type is carried, the exercise records may fail to be read from the Huawei Health app.
  2. Call the ActivityRecordsController.getActivityRecord method to obtain the data.
    // Note:
    // 1. this refers to an Activity object.
    // 2. Initialize objects of the ActivityRecordsController class each time before API calling. Otherwise, data reading may fail due to expired Activity or Context.
    final ActivityRecordsController activityRecordsController = HuaweiHiHealth.getActivityRecordsController(this);
    
    // 1. Build the time range of the request object: start time and end time.
    // Note that the start time and end time must be later than the UNIX timestamp corresponding to January 1, 2014.
    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();
    
    // 2. Build the request body for reading exercise records and set the request time range.
    // The following example shows how to read the exercise records within a specified period of time and the cadence data during the workout.
    // You can add other types of detailed workout data you want to read.
    ActivityRecordReadOptions readOption =
        new ActivityRecordReadOptions.Builder().setTimeInterval(startTime, endTime, TimeUnit.MILLISECONDS)
            .readActivityRecordsFromAllApps()
            .read(DataType.DT_INSTANTANEOUS_STEPS_RATE)
            .build();
    
    // 3. Call getActivityRecord to obtain the exercise records from Health Service Kit based on the request body.
    Task<ActivityRecordReply> getTask = activityRecordsController.getActivityRecord(readOption);
    getTask.addOnSuccessListener(new OnSuccessListener<ActivityRecordReply>() {
        @Override
        public void onSuccess(ActivityRecordReply activityRecordReply) {
            Log.i("ActivityRecords","Obtaining activity records succeeded.!");
            // Print the list of obtained exercise records.
            List<ActivityRecord> activityRecordList = activityRecordReply.getActivityRecords();
            for (ActivityRecord activityRecord : activityRecordList) {
                if (activityRecord == null) {
                    continue;
                }
                DateFormat dateFormat = DateFormat.getDateInstance();
                DateFormat timeFormat = DateFormat.getTimeInstance();
                Log.i("ActivityRecordSample",
                    "Returned for ActivityRecord: " + activityRecord.getName() + "\n\tActivityRecord Identifier is "
                    + activityRecord.getId() + "\n\tActivityRecord created by app is "
                    + activityRecord.getPackageName() + "\n\tDescription: " + activityRecord.getDesc()
                    + "\n\tStart: " + dateFormat.format(activityRecord.getStartTime(TimeUnit.MILLISECONDS))
                    + " " + timeFormat.format(activityRecord.getStartTime(TimeUnit.MILLISECONDS)) + "\n\tEnd: "
                    + dateFormat.format(activityRecord.getEndTime(TimeUnit.MILLISECONDS)) + "\n\tActivity:"
                    + activityRecord.getActivityType() + "\n\tTimeZone:" + activityRecord.getTimeZone() 
                    + "\n\tmetadata:" + activityRecord.getMetadata());
                // Print ActivitySummary in each exercise record. printActivitySummary is a custom output function.
                if (activityRecord.getActivitySummary() != null) {
                    printActivitySummary(activityRecord.getActivitySummary());
                }
                // Print the detailed data points corresponding to each exercise record.
                for (SampleSet sampleSet : activityRecordReply.getSampleSet(activityRecord)) {
                    Log.i("ActivityRecordSample",
                        "Returned for SamplePoint and Data type: " + sampleSet.getDataType().getName());
                    for (SamplePoint dp : sampleSet.getSamplePoints()) {
                        Log.i("ActivityRecordSample", "SamplePoint:");
                        Log.i("ActivityRecordSample", "DataCollector:" + dp.getDataCollector().toString());
                        Log.i("ActivityRecordSample", "\tType: " + dp.getDataType().getName());
                        Log.i("ActivityRecordSample",
                            "\tStart: " + dateFormat.format(dp.getStartTime(TimeUnit.MILLISECONDS)));
                        Log.i("ActivityRecordSample",
                            "\tEnd: " + dateFormat.format(dp.getEndTime(TimeUnit.MILLISECONDS)));
                        for (Field field : dp.getDataType().getFields()) {
                            Log.i("ActivityRecordSample",
                                "\tField: " + field.toString() + " Value: " + dp.getFieldValue(field));
                        }
                    }
                }
            }
        }
    }).addOnFailureListener(new OnFailureListener() {
        @Override
        public void onFailure(Exception e) {
            Log.i("ActivityRecord","Failed to read the ActivityRecord:" + e.getMessage());
        }
    });
NOTE

In the preceding code snippet, the following helper functions are used:

  1. Data display function printActivitySummary(ActivitySummary activitySummary) for Health Service Kit to return ActivitySummary. The sample code for the function is as follows:
    // Customize the function for outputting ActivitySummary.
    public void printActivitySummary(ActivitySummary activitySummary) {
        List<SamplePoint> dataSummary = activitySummary.getDataSummary();
        Log.i("ActivityRecordSample", "\nPrint statistical data: ");
        Log.i("ActivityRecordSample", "\nActivitySummary\n\t DataSummary: ");
        // Print statistical data points.
        for (SamplePoint samplePoint : dataSummary) {
            Log.i("ActivityRecordSample", "\n\t samplePoint: \n\t DataCollector" + samplePoint.getDataCollector() + "\n\t DataType" 
                + samplePoint.getDataType() + "\n\t StartTime" + samplePoint.getStartTime(TimeUnit.MILLISECONDS) + "\n\t EndTime"
                + samplePoint.getEndTime(TimeUnit.MILLISECONDS) + "\n\t SamplingTime" + samplePoint.getSamplingTime(TimeUnit.MILLISECONDS)
                + "\n\t FieldValues" + samplePoint.getFieldValues());
        }
        // Print the pace information.
        PaceSummary paceSummary = activitySummary.getPaceSummary();
        Log.i("ActivityRecordSample", "\n\t PaceSummary: \n\t AvgPace" + paceSummary.getAvgPace() + "\n\t BestPace" 
            + paceSummary.getBestPace() + "\n\t PaceMap" + paceSummary.getPaceMap() + "\n\t PartTimeMap"
            + paceSummary.getPartTimeMap() + "\n\t SportHealthPaceMap" + paceSummary.getSportHealthPaceMap());
        printSectionSummary(activitySummary.getSectionSummary());
    }
  2. Data display function printSectionSummary(List<SampleSection> sectionSummary) for Health Service Kit to return data. The sample code for the function is as follows:
    // Customize the function for displaying the returned data.
    private void printSectionSummary(List<SampleSection> sectionSummary) {
        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    
        for (SampleSection sampleSection : sectionSummary) {
            Log.i("ActivityRecordSample", "SampleSection.SectionNum: " + sampleSection.getSectionNum());
            Log.i("ActivityRecordSample", "SampleSection.sectionTime: " + sampleSection.getSectionTime(TimeUnit.SECONDS));
            Log.i("ActivityRecordSample", "SampleSection.startTime: " + dateFormat.format(new Date(sampleSection.getStartTime(TimeUnit.MILLISECONDS))));
            Log.i("ActivityRecordSample", "SampleSection.endTime: " + dateFormat.format(new Date(sampleSection.getEndTime(TimeUnit.MILLISECONDS))));
            Log.i("ActivityRecordSample", "SampleSection.sectionDataList:");
            for (SamplePoint samplePoint : sampleSection.getSectionDataList()) {
                for (Field field : samplePoint.getDataType().getFields()) {
                    Log.i("ActivityRecordSample", "Field: " + field.getName() + " Value: " + samplePoint.getFieldValue(field));
                }
            }
        }
    }

Creating Exercise Records Based on Real-time Data

Create ActivityRecord for ongoing exercises. Real-time fitness data is implicitly associated with ActivityRecord in Health Service Kit.

  • When the user starts a workout, use the ActivityRecordsController.beginActivityRecord method to start an exercise record.
  • When the user ends a workout, use the ActivityRecordsController.endActivityRecord method to stop the exercise record.
  1. Start ActivityRecord.

    Call ActivityRecordsController.beginActivityRecord to start an exercise record as follows:

    // Note:
    // 1. this refers to an Activity object.
    // 2. Initialize objects of the ActivityRecordsController class each time before API calling. Otherwise, data reading may fail due to expired Activity or Context.
    final ActivityRecordsController activityRecordsController = HuaweiHiHealth.getActivityRecordsController(this);
    
    // Build the start time of the workout.
    long startTime = Calendar.getInstance().getTimeInMillis();
    // Build the ActivityRecord object and set the start time of the exercise record.
    ActivityRecord activityRecord = new ActivityRecord.Builder()
        .setId("MyBeginActivityRecordId")
        .setName("BeginActivityRecord")
        .setDesc("This is ActivityRecord begin test!")
        .setActivityTypeId(HiHealthActivities.RUNNING)
        .setStartTime(startTime, TimeUnit.MILLISECONDS)
        .build();
    
    // Call beginActivityRecord to start a new exercise record.
    Task<Void> task1 = activityRecordsController.beginActivityRecord(activityRecord);
    // Add a listener for the ActivityRecord start success.
    task1.addOnSuccessListener(new OnSuccessListener<Void>() {
        @Override
        public void onSuccess(Void aVoid) {
            Log.i("ActivityRecords","MyActivityRecord started successfully.");
        }
    // Add a listener for the ActivityRecord start failure.
    }).addOnFailureListener(new OnFailureListener() {
        @Override
        public void onFailure(Exception e) {
            Log.i("ActivityRecords","Failed to begin MyActivityRecord:" +  e.getMessage());
        }
    });

  2. Stop ActivityRecord.

    Call ActivityRecordsController.endActivityRecord to stop the exercise record as follows:

        // Note that this refers to an Activity object.
    final ActivityRecordsController activityRecordsController = HuaweiHiHealth.getActivityRecordsController(this);
    
    // Call endActivityRecord to stop the exercise record. The input parameter can be null or the ID string of ActivityRecord.
    // Stop an exercise record in your app by specifying the ID string as the input parameter.
    // Stop all exercise records in your app by specifying null as the input parameter.
    Task<List<ActivityRecord>> endTask = activityRecordsController.endActivityRecord("MyBeginActivityRecordId");
    endTask.addOnSuccessListener(new OnSuccessListener<List<ActivityRecord>>() {
        @Override
        public void onSuccess(List<ActivityRecord> activityRecords) {
            Log.i("ActivityRecords","MyActivityRecord End success");
            // Return the list of exercise records that have stopped.
            if (activityRecords.size() > 0) {
                for (ActivityRecord activityRecord : activityRecords) {
                    DateFormat dateFormat = DateFormat.getDateInstance();
                    DateFormat timeFormat = DateFormat.getTimeInstance();
                    Log.i("ActivityRecords", "Returned for ActivityRecord: " + activityRecord.getName() + "\n\tActivityRecord Identifier is "
                        + activityRecord.getId() + "\n\tActivityRecord created by app is " + activityRecord.getPackageName()
                        + "\n\tDescription: " + activityRecord.getDesc() + "\n\tStart: "
                        + dateFormat.format(activityRecord.getStartTime(TimeUnit.MILLISECONDS)) + " "
                        + timeFormat.format(activityRecord.getStartTime(TimeUnit.MILLISECONDS)) + "\n\tEnd: "
                        + dateFormat.format(activityRecord.getEndTime(TimeUnit.MILLISECONDS)) + " "
                        + timeFormat.format(activityRecord.getEndTime(TimeUnit.MILLISECONDS)) + "\n\tActivity:"
                        + activityRecord.getActivityType());
                }
            } else {
                // null will be returned if the exercise record fails to stop.
                Log.i("ActivityRecords","MyActivityRecord End response is null");
            }
        }
    }).addOnFailureListener(new OnFailureListener() {
        @Override
        public void onFailure(Exception e) {
            Log.i("ActivityRecords","MyActivityRecord End fail:" + e.getMessage());     
        }
    });