dariosalvi78 / cordova-plugin-health

Cordova plugin for the HealthKit and Google Fit frameworks
MIT License
174 stars 126 forks source link
cordova-plugin fitness google-fit health healthkit mhealth

Cordova Health Plugin

cordova-plugin-health MIT license

A plugin that abstracts fitness and health repositories like Apple HealthKit or Google Health Connect.

This work is based on cordova healthkit plugin. This plugin is kept up to date and requires a recent version of cordova (12 and on) as well as recent iOS and Android SDKs. For bugs and improvements use the issues tracker. For general question or small issue, please use the gitter channel.

Warning about the new version (3+)

This is a complete rewrite of the Android version of the plugin to support the new HealthConnect API. Google Fit APIs are deprecated and will be made obsolete in 2024.

Google Fit is no longer supported by this plugin. If, for any masochistic reason, you want to use Google Fit, you need to use an older version of this pluign (2.1.1).

Please be remindful that THIS IS STILL A WORK-IN-PROGRESS. While all functionalities listed here are implemented and working, there are several data types that were supported in older versions and are not supported in this version YET. If you need support for a given data type, please check if it is already implemented in iOS, then either add support in Java and send a pull request, or add an issue and I will prioritize it.

Installation

In Cordova:

cordova plugin add cordova-plugin-health --variable HEALTH_READ_PERMISSION='App needs read access' --variable HEALTH_WRITE_PERMISSION='App needs write access'

HEALTH_READ_PERMISSION and HEALTH_WRITE_PERMISSION are shown when the app tries to grant access to data in HealthKit.

iOS requirements

Android requirements

Permissions in AndroidManifest.xml

Health Connect requires that each data type accessed is listed as permission in the AndroidManifest.xml file. This plugin will NOT ADD PERMISSIONS for the data types that you need, the list is too long to add them all and having all permissions listed may be problematic when submitting to the Play Store. You need to modify the AndroidManifest.xml file and add the needed permissions manually. See this to understand which permissions you need, depending on the data types that you actually want to access. The best way to add permissions is to include them in the config.xml file of your cordova project so that the build process is reproducible and the permissions are not lost when removing and re-adding the Android platform. Example:

<platform name="android">
    ...
    <config-file target="AndroidManifest.xml" parent="/*" xmlns:android="http://schemas.android.com/apk/res/android">
        <uses-permission android:name="android.permission.health.READ_STEPS" />
        <uses-permission android:name="android.permission.health.WRITE_STEPS" />
        <uses-permission android:name="android.permission.health.READ_TOTAL_CALORIES_BURNED" />
        ...
    </config-file>
    ...
</platform>

Mandatory Privacy Policy on Android

A Privacy Policy must be present on Android in order for the app to be approved for distribution. The plugin includes a simple webview, with no JS activated, to show the Privacy Policy when requested. The Privacy Policy must be formatted as an HTML page (no JS) and placed as a file with name: privacypolicy.html under the www folder of the project (in other words, the webview loads the following URL: file:///android_asset/www/privacypolicy.html). It is possible to change that URL by changing the value inside the file platforms/android/app/src/main/res/values/CordovaPluginHealthStrings.xml. WARNING: I suspect that this file may get reset sometimes to its original value. There should be a way to modify it from config.xml using edit-config but I was not able to make it work. Help is welcome.

Manual setup in Capacitor (Ionic)

Capacitor does not automatically include all changes to AndroidManifest.xml or gradle files from plugin.xml. This is a short guide to do it manually. Based on plugin v3.1.0 and @capacitor/android v5.5.1, future versions may be different.

  1. install the plugin from npm (npm install cordova-plugin-health), DO NOT USE awesome-codova-plugin, it hasn't been updated yet. Build the app as you would normally do (npm run build), sync it with the android code (npx cap sync) and get the Android project in Android Studio (npx cap open android) or another editor.

  2. add the Privacy Policy activity to AndroidManifest.xml, inside <application></application>:

      <!-- For supported versions through Android 13, create an activity to show the rationale
       of Health Connect permissions once users click the privacy policy link. -->
      <activity
        android:name="org.apache.cordova.health.PermissionsRationaleActivity"
        android:exported="true">
        <intent-filter>
          <action android:name="androidx.health.ACTION_SHOW_PERMISSIONS_RATIONALE" />
        </intent-filter>
      </activity>
    
      <!-- For versions starting Android 14, create an activity alias to show the rationale
       of Health Connect permissions once users click the privacy policy link. -->
      <activity-alias
        android:name="ViewPermissionUsageActivity"
        android:exported="true"
        android:targetActivity="org.apache.cordova.health.PermissionsRationaleActivity"
        android:permission="android.permission.START_VIEW_PERMISSION_USAGE">
        <intent-filter>
          <action android:name="android.intent.action.VIEW_PERMISSION_USAGE" />
          <category android:name="android.intent.category.HEALTH_PERMISSIONS" />
        </intent-filter>
      </activity-alias>
  3. add the possibility to query for the presence of Health Connect to AndroidManifest.xml, inside the root tag:

      <!-- Check if Health Connect is installed -->
      <queries>
        <package android:name="com.google.android.apps.healthdata" />
      </queries>
  4. add permissions to AndroidManifest.xml , inside the root tag. This depends on the actual data types you want to access. See this for a list.

      <uses-permission android:name="android.permission.health.READ_STEPS" />
      <uses-permission android:name="android.permission.health.WRITE_STEPS" />
  5. modify the main build.gradle file and update:

    classpath 'com.android.tools.build:gradle:8.1.1'
  6. modify the variables.gradle file, particularly:

    minSdkVersion = 26
    targetSdkVersion = 34
    compileSdkVersion = 34

Supported data types

These are currently supported in both Android and iOS. Please notice that older versions of this plugin included more data types, but with Google Fit, not Health Connect. Support for previously supported data types has not been removed on iOS, it's simply not listed here. The plan is to complete the porting of all previously supported data types from Google Fit to Health Connect, just be patient, or give us a hand.

Data type Unit HealthKit equivalent Health Connect equivalent
gender HKCharacteristicTypeIdentifierBiologicalSex NA
date_of_birth HKCharacteristicTypeIdentifierDateOfBirth NA
weight kg HKQuantityTypeIdentifierBodyMass Weight
height m HKQuantityTypeIdentifierHeight HeightRecord
bmi count HKQuantityTypeIdentifierBodyMassIndex NA
fat_percentage % HKQuantityTypeIdentifierBodyFatPercentage BodyFatRecord
steps count HKQuantityTypeIdentifierStepCount StepsRecord
distance m HKQuantityTypeIdentifierDistanceWalkingRunning + HKQuantityTypeIdentifierDistanceCycling DistanceRecord
activity activityType HKWorkoutTypeIdentifier ExerciseSessionRecord
appleExerciseTime min HKQuantityTypeIdentifierAppleExerciseTime NA
sleep sleep HKCategoryTypeIdentifierSleepAnalysis SleepSessionRecord
calories.active kcal HKQuantityTypeIdentifierActiveEnergyBurned ActiveCaloriesBurnedRecord
calories.basal kcal HKQuantityTypeIdentifierBasalEnergyBurned BasalMetabolicRateRecord * time window
calories kcal HKQuantityTypeIdentifierActiveEnergyBurned + HKQuantityTypeIdentifierBasalEnergyBurned TotalCaloriesBurnedRecord
heart_rate bpm HKQuantityTypeIdentifierHeartRate HeartRateRecord
blood_glucose mmol/L HKQuantityTypeIdentifierBloodGlucose BloodGlucoseRecord
mindfulness sec HKCategoryTypeIdentifierMindfulSession NA
UVexposure count HKQuantityTypeIdentifierUVExposure NA

Note: units of measurement are fixed!

Returned objects contain a set of fixed fields:

Example values:

Data type Value
gender "male"
Notes: only available on iOS
date_of_birth { day: 3, month: 12, year: 1978 }
Notes: currently only available on iOS
weight 83.3
height 1.72
bmi 25
Notes: only available on iOS
fat_percentage 0.312
steps 34
distance 101.2
activity "walking"
Notes: recognized activities and their mappings in Health Connect / HealthKit can be found here. Additional calories (in kcal) and distance (in m) can be added if the query has the includeCalories and/or includeDistance flags set. Warning If you want to fetch calories and/or distance, permission to access those quantities should be requested.
appleExerciseTime 24
Notes: only available on iOS
sleep 'sleep.light'
Notes: recognized sleep stages and their mappings in HealthConnect / HealthKit can be found here
in Android it is also possible to retrieve an entire session, in which case the value is an array of sleep stages [ { startDate: Date, endDate: Date, stage: 'sleep.light' }, ...]
calories.X 245.3
heart_rate 66
blood_glucose { glucose: 5.5, meal: 'breakfast', sleep: 'fully_awake', source: 'capillaryblood' }
Notes: to convert to mg/dL, multiply by 18.01559 (The molar mass of glucose is 180.1559). meal can be: 'before
' / 'after' / 'fasting' (Android only) + 'meal' (iOS only) / 'breakfast' / 'dinner' / 'lunch' / 'snack' / 'unknown'. sleep can be (iOS only): 'fully_awake', 'before_sleep', 'on_waking', 'during_sleep'. source can be: 'capillary_blood' ,'interstitial_fluid', 'plasma', 'serum', 'tears', whole_blood', 'unknown'
mindfulness 1800
Notes: only available on iOS
UVexposure 12
Notes: only available on iOS

Methods

isAvailable()

Tells if either HealthKit of Health Connect are available.

cordova.plugins.health.isAvailable(successCallback, errorCallback)

getHealthConnectFromStore()

Android only. Launches the PlayStore to retrieve HealthConnect. From Android 14 and up this is not needed, becuase HealthConnect becomes part of the operating system.

cordova.plugins.health.getHealthConnectFromStore(successCallback, errorCallback)

launchPrivacyPolicy() Android only

Launches the Privacy Policy screen needed by Health Connect. Use it for testing how it appears.

cordova.plugins.health.launchPrivacyPolicy(successCallback, errorCallback)

openHealthSettings()

Opens the health app (HealthConnect on Android, Health app on iOS) so that the user can review permissions and settings.

cordova.plugins.health.openHealthSettings(successCallback, errorCallback)

requestAuthorization()

Requests read and/or write access to a set of data types. It is recommendable to always explain why the app needs access to the data before asking the user to authorize it.

If the user has already granted permissions to the app, this function will not prompt the user again, but will call the callback immediately.

cordova.plugins.requestAuthorization(datatypes, successCallback, errorCallback)

Android quirks

iOS quirks

isAuthorized()

Check if the app has authorization to read/write a set of datatypes.

cordova.plugins.health.isAuthorized(datatypes, successCallback, errorCallback)

iOS quirks

query()

Gets all the data points of a certain data type within a certain time window.

Warning: if the time span is big, it can generate long arrays!

cordova.plugins.health.query({
  startDate: new Date(new Date().getTime() - 3 * 24 * 60 * 60 * 1000), // three days ago
  endDate: new Date(), // now
  dataType: 'steps',
  limit: 1000,
  ascending: true,
}, successCallback, errorCallback)

iOS quirks

Android quirks

queryAggregated()

Gets aggregated data in a certain time window. Usually the sum is returned for the given quantity.

cordova.plugins.health.queryAggregated({
  startDate: new Date(new Date().getTime() - 3 * 24 * 60 * 60 * 1000), // three days ago
  endDate: new Date(), // now
  dataType: 'steps',
  bucket: 'day'
}, successCallback, errorCallback)

Not all data types are supported for aggregated queries. The following table shows what types are supported and examples of the returned object:

Data type Example of returned object
height { startDate: Date, endDate: Date, value: { average: 1.8, min:1.7, max: 1.8 }, unit: 'count' }
Note: Android only
weight { startDate: Date, endDate: Date, value: { average: 73, min:72.5, max: 74 }, unit: 'kg' }
Note: Android only
steps { startDate: Date, endDate: Date, value: 5780, unit: 'count' }
distance { startDate: Date, endDate: Date, value: 12500.0, unit: 'm' }
calories { startDate: Date, endDate: Date, value: 2892.4, unit: 'kcal' }
calories.active { startDate: Date, endDate: Date, value: 25698.4, unit: 'kcal' }
calories.basal { startDate: Date, endDate: Date, value: 3547.3, unit: 'kcal' }
activity Android: { startDate: Date, endDate: Date, value: 567000, unit: 'ms' }
iOS: { startDate: Date, endDate: Date, value: { still: { duration: 520000 }, walking: { duration: 223000 }}, unit: 'activitySummary' }
Note: durations are expressed in milliseconds
sleep { startDate: Date, endDate: Date, value: 493, unit: 's' }
Notes: Android iOS
appleExerciseTime { startDate: Date, endDate: Date, value: 500, unit: 'min' }
Notes: iOS only
heart_rate { startDate: Date, endDate: Date, value: { average: 72, min: 68, max: 82 }, unit: 'bpm' }

Quirks

Android quirks

store()

Stores a data point.

cordova.plugins.health.store({
    startDate:  new Date(new Date().getTime() - 3 * 60 * 1000), // three minutes ago
    endDate: new Date(),
    dataType: 'steps',
    value: 180,
}, successCallback, errorCallback)

iOS quirks

Android quirks

delete()

Deletes data points. You can either delete a single data point (using its id, Android only), or a set of datapoints within a time range.

cordova.plugins.health.delete({
    startDate:  new Date(new Date().getTime() - 3 * 60 * 1000), // three minutes ago
    endDate: new Date(),
    dataType: 'steps'
}, successCallback, errorCallback)

or (Android only):

cordova.plugins.health.delete({
    id: '812n12123nd23edj3234'
    dataType: 'steps'
}, successCallback, errorCallback)

iOS quirks

Android quirks

External resources

Contributions

Any help is more than welcome! Raise issues, send Pull requests. I usually reply quickly. I know that this plugin is used in production in some commercial apps (includine mine), so I take it seriously.

I don't know Objective C and I am not interested in learning it now, so I would particularly appreciate someone who could give me a hand with the iOS part. Also, I would love to know from you if the plugin is currently used in any app actually available online. Paid consultancy is also possible if you need to speed up some part of the plugin, or other stuff. Just send me an email to my_username at gmail.com.

Thanks!