How to Monitor Health and Fitness Data During Home Workouts
Tags: java, programming
Published: 2022-06-08 02:26:01

As a busy developer, I can hardly spare the time to go to the gym, but I know that I should. Then I came across the videos of Pamela Reif, a popular fitness blogger, which gave me the idea of working out from home. I followed a home workout regimen, but found it hard to track my training load systematically, such through heart rate and calories burned. And that's exactly how my app, Fitness Manager came into being. I developed this app by harnessing the extended capabilities in HUAWEI Health Kit. Next, I'll show you how you can do the same!

## Demo
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/35cu5n5e0aqpzewldci7.gif)

## About Health Kit
Health Kit offers both basic and extended capabilities to be integrated. Its basic capabilities allow your app to add, delete, modify, and query user fitness and health data upon obtaining the user's authorization, so that you can provide a rich array of fitness and health services. Its extended capabilities open a greater range of real-time fitness and health data and solutions.
Fitness Manager was solely developed from the extended capabilities in Health Kit.

## Development Process

### Environment Requirements
**Android platform:**
- Android Studio: 3.X or later
- JDK 1.8.211 or later

**SDK and Gradle:**
- minSdkVersion 24
- targetSdkVersion 29
- compileSdkVersion 29
- Gradle: 4.6 or later
- Test device: You'll need a Huawei phone that runs Android 6.0 or later, and has installed the HUAWEI Health app.

### Development Procedure
Here I'll detail the entire process for developing an app using the extended capabilities mentioned above.
Before getting started, register and apply for the HUAWEI ID service, and then apply for the Health Kit service on HUAWEI Developers. You can skip this step if you have already created an app using the kit's basic capabilities. Then, apply for the data read and write scopes you need for your app. If you have any special needs, send an email to hihealth@huawei.com.
Now, integrate the SDK for the extended capabilities to your project in Android Studio. Before building the APK, make sure that you have configured the obfuscation script to prevent the HMS Core SDK from being obfuscated. Once the integration is complete, test your app against the test cases, and submit it for review. After passing the review, your app will obtain the formal scopes, and can be finally released.
Now, I'll show you how to implement some common features in your app using the kit's capabilities.

### Starting and Stopping a Workout
To control workouts and obtain real-time workout data, call the following APIs in sequence:
- **registerSportData**: Starts obtaining real-time workout data.
- **startSport**: Starts a workout.
- **stopSport**: Stops a workout.
- **unregisterSportData**: Stops obtaining real-time workout data.

**Key Code**
1) Starting obtaining real-time workout data
- Call the **registerSportData** method of the **HiHealthDataStore** object to start obtaining real-time workout data.
- Obtain the workout data through **HiSportDataCallback**.

```
HiHealthDataStore.registerSportData(context, new HiSportDataCallback() {
    @Override
    public void onResult(int resultCode) {
        // API calling result.
        Log.i(TAG, "registerSportData onResult resultCode:" + resultCode);
    }

    @Override
    public void onDataChanged(int state, Bundle bundle) {
        // Real-time data change callback.
        Log.i(TAG, "registerSportData onChange state: " + state);        
        StringBuffer stringBuffer = new StringBuffer("");              
        if (state == HiHealthKitConstant.SPORT_STATUS_RUNNING) {
            Log.i(TAG, "heart rate : " + bundle.getInt(HiHealthKitConstant.BUNDLE_KEY_HEARTRATE));
            Log.i(TAG, "distance : " + bundle.getInt(HiHealthKitConstant.BUNDLE_KEY_DISTANCE));
            Log.i(TAG, "duration : " + bundle.getInt(HiHealthKitConstant.BUNDLE_KEY_DURATION));
            Log.i(TAG, "calorie : " + bundle.getInt(HiHealthKitConstant.BUNDLE_KEY_CALORIE));
            Log.i(TAG, "totalSteps : " + bundle.getInt(HiHealthKitConstant.BUNDLE_KEY_TOTAL_STEPS));
            Log.i(TAG, "totalCreep : " + bundle.getInt(HiHealthKitConstant.BUNDLE_KEY_TOTAL_CREEP));
            Log.i(TAG, "totalDescent : " + bundle.getInt(HiHealthKitConstant.BUNDLE_KEY_TOTAL_DESCENT));
        }        
    }
});
```

2) Starting a workout
The following table lists supported workout constants.

| Open Data Type  | Constant                                  |
|-----------------|-------------------------------------------|
| Outdoor walking | HiHealthKitConstant.SPORT_TYPE_WALK       |
| Outdoor running | HiHealthKitConstant.SPORT_TYPE_RUN        |
| Outdoor cycling | HiHealthKitConstant.SPORT_TYPE_BIKE       |
| Indoor running  | HiHealthKitConstant.SPORT_TYPE_TREADMILL  |

- Call the **startSport** method of the **HiHealthDataStore** object to start a specific type of workout.
- Obtain the calling result through **ResultCallback**.

```
// Outdoor running.
int sportType = HiHealthKitConstant.SPORT_TYPE_RUN;
HiHealthDataStore.startSport(context, sportType, new ResultCallback() {
    @Override
    public void onResult(int resultCode, Object message) {
        if (resultCode == HiHealthError.SUCCESS) {
            Log.i(TAG, "start sport success");
        }
    }
});
```
3) Stopping a workout
- Call the **stopSport** method of the **HiHealthDataStore** object to stop a specific type of workout.
- Obtain the calling result through **ResultCallback**.

```
HiHealthDataStore.stopSport(context, new ResultCallback() {
    @Override
    public void onResult(int resultCode, Object message) {
        if (resultCode == HiHealthError.SUCCESS) {
            Log.i(TAG, "stop sport success");
        }
    }
});
```
4) Stopping obtaining real-time workout data
- Call the **unregisterSportData** method of the **HiHealthDataStore** object to stop obtaining the real-time workout data.
- Obtain the calling result through **HiSportDataCallback**.

```
HiHealthDataStore.unregisterSportData(context, new HiSportDataCallback() {
    @Override
    public void onResult(int resultCode) {
        // API calling result.
        Log.i(TAG, "unregisterSportData onResult resultCode:" + resultCode);
    }

    @Override
    public void onDataChanged(int state, Bundle bundle) {
       // The API is not called at the moment.
    }
});
```

### Querying Daily Activities
You can allow your users to query their daily activities in your app, such as step count details and statistics, distance, calories burned, and medium- and high-intensity activities. These data comes from Huawei phones or Huawei wearable devices. Before data query, you'll need to apply for the corresponding permissions, and obtain authorization from users. Otherwise, your API calling will fail.
1) Querying daily activity data by calling **execQuery**
- Call the **execQuery** method of the **HiHealthDataStore** object to query user's daily activities.
- Obtain the query result through **ResultCallback**.

The following takes querying step statistics as an example:

```
int timeout = 0;
// Query the step count of the current day.
Calendar currentDate = Calendar.getInstance();
currentDate.set(Calendar.HOUR_OF_DAY, 0);
currentDate.set(Calendar.MINUTE, 0);
currentDate.set(Calendar.SECOND, 0);
long startTime = currentDate.getTimeInMillis();
long endTime = System.currentTimeMillis();
// Query the step count.
HiHealthDataQuery hiHealthDataQuery = new HiHealthDataQuery(HiHealthPointType.DATA_POINT_STEP_SUM, startTime,
        endTime, new HiHealthDataQueryOption());
HiHealthDataStore.execQuery(context, hiHealthDataQuery, timeout, new ResultCallback() {
    @Override
    public void onResult(int resultCode, Object data) {
        Log.i(TAG, "query steps resultCode: " + resultCode);
        if (resultCode == HiHealthError.SUCCESS && data instanceof List) {
            List dataList = (ArrayList) data;
            for (Object obj : dataList) {
                HiHealthPointData pointData = (HiHealthPointData) obj;
                Log.i(TAG, "start time : " + pointData.getStartTime());
                Log.i(TAG, "query steps : " + String.valueOf(pointData.getValue()));
            }
        }
    }
});
```
Parameters required for query and the query results
<table>
<tr>
<th colspan="2">Open Data Type</th>
<th>Parameter for Query</th>
<th colspan="2">Method for Obtaining the Result</th>
<th>Result Value Type</th>
<th colspan="3">Result Description</th>
</tr>
<tr>
<td rowspan="5">Daily activities</td>
<td>Step count statistics</td>
<td>HiHealthPointType.DATA_POINT_STEP_SUM</td>
<td colspan="2" rowspan="5">HiHealthPointData.getValue()</td>
<td rowspan="5">int</td>
<td>Step count (unit: step). For the current day, the value is updated in real time. For each of the previous days, the value is the total step count of that day.</td>
</tr>
<tr>
<td>Step count details</td>
<td>HiHealthPointType.DATA_POINT_STEP</td>
<td>Step count per minute (unit: step).</td>
</tr>
<tr>
<td>Distance</td>
<td>HiHealthPointType.DATA_POINT_DISTANCE_SUM</td>
<td>Distance (unit: meter). For the current day, the value is updated in real time. For each of the previous days, the value is the total distance of that day.</td>
</tr>
<tr>
<td>Calories burned</td>
<td>HiHealthPointType.DATA_POINT_CALORIES_SUM</td>
<td>Calories burned (unit: kcal). For the current day, the value is updated in real time. For each of the previous days, the value is the total calories burned of that day.</td>
</tr>
<tr>
<td>Medium- and high-intensity activities</td>
<td>HiHealthPointType.DATA_POINT_EXERCISE_INTENSITY</td>
<td>Intensity (unit: minute). For the current day, the value is updated in real time. For each of the previous days, the value is the total intensity of that day.</td>
</tr>
</table>

### Querying Workout Records
The following is an example of querying workout records in the last 30 days:
- Call the **execQuery** method of the **HiHealthDataStore** object to query user's workout records.
- Obtain the query result through **ResultCallback**.

```
int timeout = 0;
 long endTime = System.currentTimeMillis();
// The time range for the query is the past 30 days.
 long startTime = endTime - 1000 * 60 * 60 * 24 * 30L;
// Query the running data.
 HiHealthDataQuery hiHealthDataQuery = new HiHealthDataQuery(HiHealthSetType.DATA_SET_RUN_METADATA, startTime,
         endTime, new HiHealthDataQueryOption());
 HiHealthDataStore.execQuery(context, hiHealthDataQuery, timeout, new ResultCallback() {
     @Override
     public void onResult(int resultCode, Object data) {
if (resultCode == HiHealthError.SUCCESS && data instanceof List){ 
            List dataList = (List) data;
            for (Object obj : dataList) {
                HiHealthSetData hiHealthData = (HiHealthSetData) obj;
                Map map = hiHealthData.getMap();
                Log.i(TAG, "start time : " + hiHealthData.getStartTime());
                Log.i(TAG, "total_time : " +  map.get(HiHealthKitConstant.BUNDLE_KEY_TOTAL_TIME));
                Log.i(TAG, "total_distance : " + map.get(HiHealthKitConstant.BUNDLE_KEY_TOTAL_DISTANCE));
                Log.i(TAG, "total_calories : " + map.get(HiHealthKitConstant.BUNDLE_KEY_TOTAL_CALORIES));
                Log.i(TAG, "step : " + map.get(HiHealthKitConstant.BUNDLE_KEY_STEP));
                Log.i(TAG, "average_pace : " + map.get(HiHealthKitConstant.BUNDLE_KEY_AVERAGEPACE));
                Log.i(TAG, "average_speed : " + map.get(HiHealthKitConstant.BUNDLE_KEY_AVERAGE_SPEED));
                Log.i(TAG, "average_step_rate : " + map.get(HiHealthKitConstant.BUNDLE_KEY_AVERAGE_STEP_RATE));
                Log.i(TAG, "step_distance : " + map.get(HiHealthKitConstant.BUNDLE_KEY_STEP_DISTANCE));
                Log.i(TAG, "average_heart_rate : " + map.get(HiHealthKitConstant.BUNDLE_KEY_AVERAGE_HEART_RATE));
                Log.i(TAG, "total_altitude : " + map.get(HiHealthKitConstant.BUNDLE_KEY_TOTAL_ALTITUDE));
                Log.i(TAG, "total_descent : " + map.get(HiHealthKitConstant.BUNDLE_KEY_TOTALDESCENT));
                Log.i(TAG, "data source : " + map.get(HiHealthKitConstant.BUNDLE_KEY_DATA_SOURCE));
            }
        }
     }
 });
```