Coder Social home page Coder Social logo

dariosalvi78 / cordova-plugin-health Goto Github PK

View Code? Open in Web Editor NEW

This project forked from incentfit/healthkit

176.0 18.0 127.0 1.11 MB

Cordova plugin for the HealthKit and Google Fit frameworks

License: MIT License

Objective-C 34.74% JavaScript 14.49% Java 50.77%
cordova-plugin healthkit fitness health mhealth google-fit

cordova-plugin-health's Introduction

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

  • Make sure your app id has the 'HealthKit' entitlement when this plugin is installed (see iOS dev center).
  • Also, make sure your app and App Store description comply with the Apple review guidelines.
  • There are two keys to be added to the info.plist file: NSHealthShareUsageDescription and NSHealthUpdateUsageDescription. These are assigned with a default string by the plugin, but you may want to contextualise them for your app.

Android requirements

  • HealthConnect is made standard on (Google versions of) Android from version 14 (API level 34). On older versions of Android, the user has to install the Health Connect app from the Play Store.
  • Health Connect SDK supports Android 8 (API level 26) or higher, while the Health Connect app is only compatible with Android 9 (API level 28) or higher see this.
  • Health Connect SDK requires targeting Android API level 34. The current cordova-android package (12.0.1) targets version 33 and uses a version of Gradle that is incompatible with API level 34, so you need to fix the versions of gradle (to 8.4), gradle plugin (to 8.1.1), target SDK version (to 34) and minimum SDK version (to 26). Add the following to the config.xml of your cordova app project:
<platform name="android">
  ...
  <preference name="GradleVersion" value="8.4" />
  <preference name="AndroidGradlePluginVersion" value="8.1.1" />
  <preference name="android-minSdkVersion" value="26" />
  <preference name="android-targetSdkVersion" value="34" />
  ...
<platform>

Additionally, there are issues with some kotlin depenendencies which are fixed automatically by the plugin in src/android/build-extras.gradle. All these hacks will hopefully be removed with future versions of the cordova-android platform.

  • Download a recent version of gradle (8.4 or later).
  • If you use Android Studio, download at least version Hedgehog.
  • Be aware that Health Connect requires the user to have screen lock enabled with a PIN, pattern, or password.
  • When publishing the app, you need to comply to these requests from Google.
  • This plugin uses AndroidX. You may need to activate AndroidX in the Android platform and make sure all other plugins you use are AndroidX compatible.

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>
  1. 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>
  1. 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" />
  1. modify the main build.gradle file and update:
classpath 'com.android.tools.build:gradle:8.1.1'
  1. 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
workout_route bpm HKWorkoutRouteType NA
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:

  • startDate: a date indicating when the data point starts
  • endDate: a date indicating when the data point ends
  • unit: the unit of measurement, as a string
  • value: the actual value
  • sourceBundleId: the identifier of the app that produced the data
  • sourceName: (only on iOS) the name of the app that produced the data (as it appears to the user)
  • sourceDevice: (only on Android) the device where the data came from manufacturer and model
  • entryMethod: (only on Android) method of insertion, can be "actively_recorded", "automatically_recorded", "manual_entry" or "unknown"
  • id: the unique identifier of that measurement

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: 'capillary_blood' }
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)
  • successCallback: if available a true is passed as argument, false otherwise
  • errorCallback: called if something went wrong, err contains a textual description of the problem

getHealthConnectFromStore() Android only

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)
  • successCallback: if available a true is passed as argument, false otherwise
  • errorCallback: called if something went wrong, err contains a textual description of the problem

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)
  • successCallback: screen has been launched
  • errorCallback: called if something went wrong

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)
  • datatypes: an object containing data types you want to be granted access to. Example:
{
  read : ['steps'],            // Read permission
  write : ['steps', 'weight']  // Write permission
}
  • successCallback: called if permission process completed, called independently of if the user has granted permissions or not, the argument may indicate if the permissions have been granted (but it is not guaranteed that all have been granted)
  • errorCallback: called if something went wrong, the argument contains a textual description of the problem

Android quirks

  • Currently, permissions can be requested no more than 3 times in an app! This is regardless of if the permissions were granted, denied or the dialog was canceled. This is how it is implemented in HealthConnect, not an issue with the plugin. See discussion here. A workaround in your app can be to check if permissions have been granted upon request and, in the negative case, launch the HealthConnect app instead and ask users to grant permissions manually, which is a horrible user experience.

iOS quirks

  • HealthKit does never reveal if the user has actually granted permission. This is by design.
  • Once the user has rejected permissions, this function will not prompt the user again, but will call the callback immediately. See this for further explanation.

isAuthorized()

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

cordova.plugins.health.isAuthorized(datatypes, successCallback, errorCallback)
  • datatypes: an object containing data types you want to be granted access to. Example:
{
  read : ['steps'],            // Read permission
  write : ['steps', 'weight']  // Write permission
}
  • successCallback: if the argument is true, the app is authorized
  • errorCallback: called if something went wrong, the argument contains a textual description of the problem

iOS quirks

  • This method will only check authorization status for writeable data. Read-only data will always be considered as not authorized. This is an intended behaviour of HealthKit.

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)
  • startDate: start date from which to get data
  • endDate: end data to which to get the data
  • dataType: the data type to be queried (see above)
  • limit: optional, sets a maximum number of returned values, default is 1000
  • ascending: optional, datapoints are ordered in an descending fashion (from newer to older) se this to true to revert this behaviour
  • filterOutUserInput: optional, if true, filters out user-entered activities (iOS only)
  • includeCalories: optional, used only for dataType "activity". When querying, for each activity, also the active calories (in kcal) will be added. Warning the app requires access to calories.active to be granted
  • includeDistance: optional, used only for dataType "activity". When querying, for each activity, also the distance, run or cycled, (in m) will be added. Warning the app requires access to distance to be granted
  • includeRoute: iOS only, optional, use only for dataType "activity". When querying, for each activity, also the route will be added. Warning the app requires access to workout_route to be granted.
    • if true, the activity contains a field route, the route is an array of { lat: number, lng: number, alt: number, timestamp: Date } objects.
    • If the activity contains multiple routes (e.g. the workout was paused), these are concatenated in the same array. Therefore do not try to calculate the distance from the route, but use the distance field instead.
  • includeHeartRate: iOS only, optional, use only for dataType "activity". When querying, for each activity, also the heart rate will be added. Warning the app requires access to heart_rate to be granted
    • if true, the activity contains a field heartRate with an array of { bpm: number, timestamp: Date } objects.
  • successCallback: called if all OK, argument contains the result of the query in the form of an array of: { startDate: Date, endDate: Date, value: xxx, unit: 'xxx', sourceName: 'aaaa', sourceBundleId: 'bbbb' }
  • errorCallback: called if something went wrong, argument contains a textual description of the problem

iOS quirks

  • HealthKit does not calculate active and basal calories - these must be input from an app
  • HealthKit does not detect activities automatically - these must be input from an app
  • When querying for activities, only events whose startDate and endDate are both in the query range will be returned.
  • When duration (in seconds) is returned, this may be different than the endTime - startTime and actually more accurate.

Android quirks

  • Health Connect can read data for up to 30 days prior to the time permission was first granted. If the app is reinstalled, the permission history is lost and you can only query from 30 days before installation. See note here.
  • Not all datatypes support start and end timestamps, some, such as weight, only have one timestamp. The plugin will just set both start and end to the same value in those cases.
  • Active and basal calories can be automatically calculated by Health Connect.
  • calories.basal is returned as an average per day (kcal/day), and is usually stored quite sparsely (it rarely change, but chnages in weight and height trigger a ricalculation).
  • Calories and distance for activities are actually queried indipendently, using the timestamps for each returned activity. This may considerably slow down the query if the returned activities are many. Use with care.
  • sleep in HealthConnect is stored in sessions composed of stages. If you want to retrieve sessions instead of single stages, add the following flag to the query object: sleepSession: true. The returned value will be an array of objects like: [ { startDate: Date, endDate: Date, stage: 'sleep.light' }, ... ]
  • heart_rate is in reality stored as an array of values within a given window of time, however, each value is returned separately here to make the API compatible with iOS.

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)
  • startDate: start date from which to get data
  • endDate: end data to which to get the data
  • dataType: the data type to be queried (see below for supported data types)
  • bucket: if specified, aggregation is grouped an array of "buckets" (windows of time), supported values are: 'hour', 'day', 'week', 'month', 'year'
  • filterOutUserInput: optional, filters out user-entered activities if set to true (only works on iOS)
  • successCallback: called if all OK, argument contains the result of the query, see below for returned data types. If no buckets is specified, the result is an object. If a bucketing strategy is specified, the result is an array.
  • errorCallback: called if something went wrong, argument contains a textual description of the problem

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

  • Health Connect does not currently support grouping by activity type, therefore only the total time for all activities can be returned.
  • The start and end dates returned are the date of the first and the last available samples. If no samples are found, start and end may not be set.
  • When bucketing, buckets will include the whole hour / day / month / week / year where start and end times fall into. For example, if your start time is 2016-10-21 10:53:34, the first daily bucket will start at 2016-10-21 00:00:00.
  • Weeks start on Monday.
  • Aggreagated height only exists on Android.

Android quirks

  • Currently, it is not possible to group by activity type in aggregated queries, only the total time for all activities can be returned. See discussion here.
  • When storing heart_rate, you can also provide the value as an array of [ {bpm: 81, timestamp: Date }, ... ]. This is how the heart rate is actually stored internally and is probably and more efficient.

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)
  • startDate: start date from which the new data starts
  • endDate: end date to which he new data ends
  • dataType: the data type
  • value: the value, depending on the actual data type
  • successCallback: called if all OK, in Android, argument returns the ID of the data point that has been inserted
  • errorCallback: called if something went wrong, argument contains a textual description of the problem

iOS quirks

  • In iOS you cannot store the total calories, you need to specify either basal or active. If you use total calories, the active ones will be stored.
  • In iOS distance is assumed to be of type WalkingRunning, if you want to explicitly set it to Cycling you need to add the field cycling: true.
  • When storing an activity, you can also specify calories (active, in kcal) and/or distance (in meters). For example: dataType: 'activity', value: 'walking', calories: 20, distance: 520. Distance is set as DistanceWalkingRunning unless an additional cycling: true is added to the object. Be aware that you need permission to write calories and distance first, or the call will fail.
  • In iOS you cannot store the total calories, you need to specify either basal or active. If you use total calories, the active ones will be stored.
  • In iOS distance is assumed to be of type WalkingRunning, if you want to explicitly set it to Cycling you need to add the field cycling: true.

Android quirks

  • This operation correponds to an insert, not an update. If you want to update the data point you need to delete it first.
  • Not all datatypes support start and end timestamps, some, such as weight, only have one timestamp. The plugin will use the start timestamp to set the actual one.
  • In Android you can only store basal rate, that is a power. This is estimated from the kcals provided as an argument, divided by the time between the start and end time. When you query the individual sample, you get the kcal/day back, not the kcal, unless you do an aggregated query.
  • sleep in HealthConnect is stored in sessions composed of stages. By default, this function will store each stage as an indipendent session, but if you want to aggregate the stages into a single session, use the flag: sleepSession: true and use an array of objects like [ { startDate: Date, endDate: Date, stage: 'sleep.light' }, ... ] as value.

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)
  • startDate: start date from which to delete data
  • endDate: end date to which to delete the data
  • id: id of the point to be deleted (Android only for now)
  • dataType: the data type to be deleted
  • successCallback: called if all OK
  • errorCallback: called if something went wrong, argument contains a textual description of the problem

iOS quirks

  • You cannot delete the total calories, you need to specify either basal or active. If you use total calories, the active ones will be delete.
  • Distance is assumed to be of type WalkingRunning, if you want to explicitly set it to Cycling you need to add the field cycling: true.
  • Deleting sleep is not supported at the moment.

Android quirks

  • Health Connect doesn't allow you to delete data points that were generated by other apps, only those generated by your app.

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!

cordova-plugin-health's People

Contributors

aggarwalankush avatar albertinad avatar benlaknet avatar chandratejoma avatar dariosalvi78 avatar eddyverbruggen avatar emmavray avatar fniwes avatar hdavidzhu avatar jamtholee avatar julianlaval avatar kingsfleet avatar lamuertepeluda avatar lineke avatar lnelson-videra avatar marioshtika avatar mrjustreborn avatar mschristo avatar nexus-uw avatar noktilux avatar northmccormick avatar paulmillr avatar petemcwilliams avatar pushparajsamant avatar rafaellop avatar randnetdd avatar schigh avatar seanbdoherty avatar sgilroy avatar wongpeiyi avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

cordova-plugin-health's Issues

Duration data in returned object incorrect on Android?

Dear @dariosalvi78 ,

First of all: thanks for you awesome plugin. It really is very awesome :)

However I am wondering if the duration information in the returned object for Android is correct?
An example of an object returned if I query for 'Activity' with navigator.health.queryAggregated

{"startDate":"2017-04-22T22:00:00.000Z","endDate":"2017-04-23T21:59:59.000Z","value":{
"still":{"duration":86399000,"calories":1068.8572998046875},
"in_vehicle":{"duration":81471533,"calories":341.9926452636719},
"walking":{"duration":44405366,"calories":762.522216796875},
"running":{"duration":5969930,"calories":1568.8883056640625},
"unknown":{"duration":1936558,"calories":0}
},"unit":"activitySummary"}

If you count everything together, tot total is a bit more than 61 hours, while the timespan is 24 hours.
The "running" information is correct, but for example the "walking" information is wrong.

Thanks for having a look!
Frank

datatype Activity: Calories not included in returned object (Android)

Hi,

First of all: thanks for you awesome plugin.

Currently I notice (with version 0.8.3 of the plugin) that the returned object for Activity Datatype, does not include calories.
Not 100% sure, but I think it should be returned in this case?

If I do this query:

navigator.health.queryAggregated({
startDate: new Date(year, month, day, 0, 0, 0).getTime() - 4 * 24 * 60 * 60 * 1000,
endDate: new Date(year, month, day, 23, 59, 59).getTime() - 4 * 24 * 60 * 60 * 1000,
dataType: 'activity',
}, function (msg) {
var str4 = JSON.stringify(msg);
console.log('day4 object is' + str4);
}, function (e) {
console.log(e);
});

The returned object is (which I log in the console as a string for test purpose):

{
"startDate":"2017-02-06T23:00:00.000Z",
"endDate":"2017-02-07T22:59:59.000Z",
"value":{"still":{"duration":70888730},
"in_vehicle":{"duration":6331334},
"walking":{"duration":5074437},
"swimming.pool":{"duration":1800000},
"running":{"duration":194074}},
"unit":"activitySummary"
}

If I look up this data in Google Fit, the calories for all these activities are available.

Kind regards, and thanks in advance
Frank

estimated steps

"queryAggregated" doesn't return estimated steps but "all" steps, even not significant ones. The result is that Google Fit app show me 2300 steps and "queryAggregated" returns me 6900 steps.

As explains here: https://developers.google.com/fit/faq#values-no-match
It should be interesting for "queryAggregated" to return estimated steps.
I tried and it works perfectly. I edited HealthPlugin.java and replaced line 622 by:

DataSource ESTIMATED_STEP_DELTAS = new DataSource.Builder()
.setDataType(DataType.TYPE_STEP_COUNT_DELTA)
.setType(DataSource.TYPE_DERIVED)
.setStreamName("estimated_steps")
.setAppPackageName("com.google.android.gms")
.build();
builder.aggregate(ESTIMATED_STEP_DELTAS, DataType.AGGREGATE_STEP_COUNT_DELTA);

ios10 - Missing parameters in info.plist

Not quite sure where exactly it belongs into, but as of iOS 10 following two parameters have to be present in the info.plist

https://developer.apple.com/library/content/documentation/General/Reference/InfoPlistKeyReference/Articles/CocoaKeys.html#//apple_ref/doc/uid/TP40009251-SW48

NSHealthShareUsageDescription="A very good reason why I want to read your data.";
NSHealthUpdateUsageDescription="A very good reason why I want to write data.";

80leaves

p.s. also opened up the same issue in HealthKit project
EddyVerbruggen#77

add HKMetadataKeyBloodGlucoseMealTime with Blood Glucose meal in iOS

include the HKMetadataKeyBloodGlucoseMealTime meta key with the blood glucose.

This needs:

  • the blood_glucose query to return an object with glucose and mealtime
  • meal time should be coded into these: "fasting", "breakfast", "dinner", "lunch", "snack", "unknown", "before_breakfast", "before_dinner", "before_lunch", "before_snack", "after_breakfast", "after_dinner", "after_lunch", "after_snack"
  • the key must be added to HealthKit.m
  • should work for both read and write

Android exec() call threading issue

Hi,

in an App where we sync several fitness data to server I encountered an App-freeze on calls. I thought that was a problem with proper dereferencing in the app and found it was caused by the plugins query and queryAggregated methods. (see logcat below)

W/PluginManager(32105): THREAD WARNING: exec() call to health.queryAggregated blocked the main thread for 643ms. Plugin should use CordovaInterface.getThreadPool().

W/PluginManager(32105): THREAD WARNING: exec() call to health.query blocked the main thread for 354ms. Plugin should use CordovaInterface.getThreadPool().

If I got some spare time I am going to try to fix it by myself, but do not want to leave it unmentioned.

Thank you very much for providing this repo,
keep going and have a nice weekend.

Update documentation for limit and ascending

Healthkit.m

(void)querySampleType:(CDVInvokedUrlCommand *)command {
   NSDictionary *args = command.arguments[0];
   NSDate *startDate = [NSDate dateWithTimeIntervalSince1970:[args[HKPluginKeyStartDate] longValue]];
   NSDate *endDate = [NSDate dateWithTimeIntervalSince1970:[args[HKPluginKeyEndDate] longValue]];
   NSString *sampleTypeString = args[HKPluginKeySampleType];
   NSString *unitString = args[HKPluginKeyUnit];
   NSUInteger limit = ((args[@"limit"] != nil) ? [args[@"limit"] unsignedIntegerValue] : 100);
   BOOL ascending = (args[@"ascending"] != nil && [args[@"ascending"] boolValue]);

is accepting limit as an int and ascending as a boolean value.

Would be great to update the documentation and examples. Otherwise the default is set to 100 and reverse order.

Incorrect activity durations from Google Fit

Weird bug (feature?) with Google Fit - returned activities have the wrong start time if the query startDate param is between said activities' start and end time. Steps to reproduce:

  • Create an activity starting at 9am and ending at 10am via Google Fit.
  • Query activity with startDate = 9.30am and endDate = any time after 10am
  • startDate for the returned activity will be 9.30am rather than 9am.

This differs with HealthKit behaviour, which is to not return this event if the query startDate is between an activity's start and end time.

First of all, can you reproduce? Second, is this expected behaviour?

Is it possible to pass in NSHealthShareUsageDescription and NSHealthUpdateUsageDescription strings using Phonegap Build?

Is it possible to pass in NSHealthShareUsageDescription and NSHealthUpdateUsageDescription strings using Phonegap Build?

For example the cordova-plugin-media-capture uses variables to add strings to the info.plist:

<plugin name="cordova-plugin-media-capture" source="npm" version="1.4.1">
        <variable name="CAMERA_USAGE_DESCRIPTION" value="App would like to access the camera." />
        <variable name="MICROPHONE_USAGE_DESCRIPTION" value="App would like to access the microphone." />
        <variable name="PHOTOLIBRARY_USAGE_DESCRIPTION" value="App would like to access the library." />
    </plugin>

Ionic2 Support

Hey there!

Is there any update on supporting Ionic2 and creating an IonicNative wrapper?

Potential device support error when including cordova-plugin-health to an existing iOS app

Per https://developer.apple.com/library/content/qa/qa1623/_index.html:

iTunes Connect does not allow uploading an updated version of an app when the update runs on fewer devices than the version of the app currently in the App Store. This is by design.

As it stands, the project's plugin.xml includes healthkit as a required capability (see UIRequiredDeviceCapabilities info here). This is problematic for a number of reasons:

  • Adding cordova-plugin-health to an app that has previously been added to the App Store is impossible, as this new key removes support for a number of devices that were previously supported (e.g. iPads), triggering the error mentioned above
  • Unless I'm mistaken, new apps that include this key will inherently be unavailable for devices that don't support Apple Health

Having removed this key from my fork, I can confirm that the plugin still works correctly and updates can once again be uploaded to the App Store. Although I haven't yet been able to test the app on a device that doesn't support Apple Health, I would assume that the isAvailable method respond correctly to this scenario.

Bearing all of this in mind, maybe it would be a good idea to remove this requirement altogether, or at least make it optional?

Real iOS device issue

Hello, I realize that this comes from the original Health Plugin, so I've opened the issue there, but this plugin depends on it, so it's better to have it here as well I think:

Building/Deploying for the actual iOS device causes a linker error:
Undefined symbols for architecture armv7:
"_HKCharacteristicTypeIdentifierFitzpatrickSkinType", referenced from:
-[HealthKit readFitzpatrickSkinType:] in HealthKit-18B10050B69093A6.o
ld: symbol(s) not found for architecture armv7
clang: error: linker command failed with exit code 1 (use -v to see invocation)

Android error on empty nutrient query

Attempting to query a specific nutrient from Google Fit, e.g. nutrition.cholesterol, for which no data exists yields the following error:

Attempt to invoke virtual method 'float java.lang.Float.floatValue()' on a null object reference

Diving deeper, it seems this issue only occurs when other nutrients aren't empty. For example, in my setup I concurrently poll nutrition and all common nutrients. If all are empty, no problem:

nutrition.calories []
nutrition.carbs.total []
nutrition.dietary_fiber []
nutrition.sugar []
nutrition.protein []
nutrition.vitamin_a []
nutrition.vitamin_c []
nutrition.calcium []
nutrition.iron []
nutrition.water []
nutrition.fat.saturated []
nutrition []
nutrition.potassium []
nutrition.cholesterol []
nutrition.fat.monounsaturated []
nutrition.fat.polyunsaturated []
nutrition.fat.total []
nutrition.sodium []

But if values are returned for some, the error is thrown for others:

nutrition.fat.saturated [{"startDate":"2017-06-03T11:00:00.000Z","endDate":"2017-06-03T11:00:00.005Z","sourceName":"lifesum-nutrition","sourceBundleId":"com.sillens.shapeupclub","value":21.850000381469727,"unit":"g"}]
nutrition.calories [{"startDate":"2017-06-03T11:00:00.000Z","endDate":"2017-06-03T11:00:00.005Z","sourceName":"lifesum-nutrition","sourceBundleId":"com.sillens.shapeupclub","value":1100,"unit":"kcal"}]
nutrition.fat.polyunsaturated error: "Attempt to invoke virtual method 'float java.lang.Float.floatValue()' on a null object reference"
nutrition.fat.monounsaturated [{"startDate":"2017-06-03T11:00:00.000Z","endDate":"2017-06-03T11:00:00.005Z","sourceName":"lifesum-nutrition","sourceBundleId":"com.sillens.shapeupclub","value":31.149999618530273,"unit":"g"}]
nutrition.sodium [{"startDate":"2017-06-03T11:00:00.000Z","endDate":"2017-06-03T11:00:00.005Z","sourceName":"lifesum-nutrition","sourceBundleId":"com.sillens.shapeupclub","value":1.8665000200271606,"unit":"mg"}]
nutrition.cholesterol error: "Attempt to invoke virtual method 'float java.lang.Float.floatValue()' on a null object reference"
nutrition.carbs.total [{"startDate":"2017-06-03T11:00:00.000Z","endDate":"2017-06-03T11:00:00.005Z","sourceName":"lifesum-nutrition","sourceBundleId":"com.sillens.shapeupclub","value":114,"unit":"g"}]
nutrition.potassium [{"startDate":"2017-06-03T11:00:00.000Z","endDate":"2017-06-03T11:00:00.005Z","sourceName":"lifesum-nutrition","sourceBundleId":"com.sillens.shapeupclub","value":0.7770000100135803,"unit":"mg"}]
nutrition.sugar [{"startDate":"2017-06-03T11:00:00.000Z","endDate":"2017-06-03T11:00:00.005Z","sourceName":"lifesum-nutrition","sourceBundleId":"com.sillens.shapeupclub","value":15,"unit":"g"}]
nutrition [{"startDate":"2017-06-03T11:00:00.000Z","endDate":"2017-06-03T11:00:00.005Z","sourceName":"lifesum-nutrition","sourceBundleId":"com.sillens.shapeupclub","value":{"item":"Pizza margherita","meal_type":"lunch","nutrients":{"nutrition.calories":1100,"nutrition.fat.total":53,"nutrition.fat.saturated":21.850000381469727,"nutrition.fat.monounsaturated":31.149999618530273,"nutrition.sodium":1.8665000200271606,"nutrition.potassium":0.7770000100135803,"nutrition.carbs.total":114,"nutrition.dietary_fiber":8.5,"nutrition.sugar":15,"nutrition.protein":41.5}},"unit":"nutrition"}]
nutrition.dietary_fiber [{"startDate":"2017-06-03T11:00:00.000Z","endDate":"2017-06-03T11:00:00.005Z","sourceName":"lifesum-nutrition","sourceBundleId":"com.sillens.shapeupclub","value":8.5,"unit":"g"}]
nutrition.protein [{"startDate":"2017-06-03T11:00:00.000Z","endDate":"2017-06-03T11:00:00.005Z","sourceName":"lifesum-nutrition","sourceBundleId":"com.sillens.shapeupclub","value":41.5,"unit":"g"}]
nutrition.fat.total [{"startDate":"2017-06-03T11:00:00.000Z","endDate":"2017-06-03T11:00:00.005Z","sourceName":"lifesum-nutrition","sourceBundleId":"com.sillens.shapeupclub","value":53,"unit":"g"}]
nutrition.calcium error: "Attempt to invoke virtual method 'float java.lang.Float.floatValue()' on a null object reference"
nutrition.vitamin_c error: "Attempt to invoke virtual method 'float java.lang.Float.floatValue()' on a null object reference"
nutrition.vitamin_a error: "Attempt to invoke virtual method 'float java.lang.Float.floatValue()' on a null object reference"
nutrition.water []
nutrition.iron error: "Attempt to invoke virtual method 'float java.lang.Float.floatValue()' on a null object reference"

Any ideas?

How to revoke Authorization?

Hello,

Once I do

navigator.health.requestAuthorization(['steps'], successCallback, errorCallback)

How to revoke or detach this?

"GoogleApiClient is not connected yet." Android race condition

Querying Google Fit shortly after requesting authorisation sometimes yields the following:

nutrition error: "Cannot connect to Google Fit"
activity error: "Cannot connect to Google Fit"
GoogleApiClient is not connected yet.
nutrition.fat.total error: "Cannot connect to Google Fit"
nutrition.calories error: "Cannot connect to Google Fit"
nutrition.fat.saturated error: "Cannot connect to Google Fit"
nutrition.fat.polyunsaturated error: "Cannot connect to Google Fit"
nutrition.fat.monounsaturated error: "Cannot connect to Google Fit"
nutrition.sodium error: "Cannot connect to Google Fit"
nutrition.cholesterol error: "Cannot connect to Google Fit"
nutrition.carbs.total error: "Cannot connect to Google Fit"
nutrition.potassium error: "Cannot connect to Google Fit"
nutrition.dietary_fiber error: "Cannot connect to Google Fit"
nutrition.sugar error: "Cannot connect to Google Fit"
nutrition.vitamin_a error: "Cannot connect to Google Fit"
nutrition.calcium error: "Cannot connect to Google Fit"
nutrition.vitamin_c error: "Cannot connect to Google Fit"
nutrition.protein error: "Cannot connect to Google Fit"
nutrition.iron error: "Cannot connect to Google Fit"

This seems to only occur on first install / reinstall / hard reload. I suspect the root cause may be a race condition where Google Fit isn't yet ready but the authentication callback has successfully returned.

Any ideas on how this can avoided?

Details:
Galaxy S7 emulated in Genymotion
Android 7.1.0
API 25
Latest Play services (10.2.6)

Paging on query

I am having an issue getting all the data back from the query method on iOS. It only returns me the first handful of results for a day rather than the whole payload. Is there a way to "page" the data to retreive the rest of it?

build.phonegap.com fail after update

There is a new error from build.phonegap.com:

Error - The following 3rd-party plugin is causing the build to fail and may need to be updated to a newer version: cordova-plugin-health

Attach all error log
error-log.txt

cannot access AbstractSafeParcelable

After I updated Andoid SDK the fallowing error showed up

Error : C:...\android\src\org\apache\cordova\health\HealthPlugin.java:70: error: cannot access AbstractSafeParcelable
Error : public static Map<String, DataType> activitydatatypes = new HashMap<String, DataType>();
Error : class file for com.google.android.gms.common.internal.safeparcel.AbstractSafeParcelable not found

OAuth2 consent error

I am getting this error: Application needs OAuth consent from the user.
These are the permissions I have passed to requestAuthorization():
permissionsAndroid = [
'TYPE_HEIGHT',
'TYPE_WEIGHT',
'TYPE_STEP_COUNT_DELTA'
];

Support queries for nutrition (dietary/food) data types in Google Fit

We are currently using the Telerik HealthKit plugin to get nutrition data types from HealthKit. The cordova-plugin-health plugin does not (yet) support this.

To get nutrition data from Google Fit, query for DataType.TYPE_NUTRITION. For each data point, nutrients can be retrieved via datapoint.getValue(Field.FIELD_NUTRIENTS) and then specifiec nutrients can be retrieved via nutrients.getKeyValue(Field.NUTRIENT_TOTAL_CARBS).

Documentation on Field.FIELD_NUTRIENTS:
https://developers.google.com/android/reference/com/google/android/gms/fitness/data/Field.html#FIELD_NUTRIENTS

List of nutrients (constants prefixed with NUTRIENT_):
https://developers.google.com/android/reference/com/google/android/gms/fitness/data/Field#constant-summary

Tasks:

  • include all nutrient types in nutritiondatatypes
  • support retrieving samples for each nutrient type in query() with appropriate unit value for each type
  • support aggregated queries for each nutrient type in queryAggregated()

Eventually, support should also be added for mapping and querying the equivalent data types in HealthKit (see this list for reference). However, I don't personally need HealthKit support at the moment, so I will start with just the Google Fit side.

Doesn't do anything on Android

I'm using this plugin with success on iOS, but Android is still not working for me.
I followed the documentation for setting up the Google Project with the proper oAuth keys.

I get no errors or what so ever when I try to authorize or retrieve data, even in adb logcat I don't see any errors with the tag cordova-plugin-health. When I use the method promptInstallFit and I don't have Google Fit installed it does redirect to the Play Store.

I do see this in adb logcat, but I don't know if it has anything to do with this plugin:

01-23 13:26:37.265 17072 17174 W CordovaPlugin: Attempted to send a second callback for ID: health1911832199
01-23 13:26:37.265 17072 17174 W CordovaPlugin: Result was: false

My system information:

Cordova CLI: 6.4.0
Ionic CLI Version: 2.1.14
Ionic App Lib Version: 2.1.7
ios-deploy version: 1.9.0
ios-sim version: 5.0.8
OS: OS X El Capitan
Node Version: v4.6.0
Xcode version: Xcode 8.2.1 Build version 8C1002

Not getting sourceName in query response

Hi,
I am using Google Fit App to log the data and created my own application, in which I am using query method.
It returns only data without source name

[
{"startDate":"2017-06-06T01:50:35.663Z","endDate":"2017-06-6T01:51:35.663Z","value":7,"unit":"count"},
{"startDate":"2017-06-06T01:51:59.000Z","endDate":"2017-06-06T01:52:19.932Z","value":9,"unit":"count"},
{"startDate":"2017-06-06T01:52:19.932Z","endDate":"2017-06-6T01:52:59.000Z","value":18,"unit":"count"},
{"startDate":"2017-06-06T01:52:59.000Z","endDate":"2017-06-06T01:53:01.880Z","value":5,"unit":"count"},
{"startDate":"2017-06-06T01:53:29.384Z","endDate":"2017-06-06T01:54:29.384Z","value":7,"unit":"count"},
{"startDate":"2017-06-06T01:54:36.892Z","endDate":"2017-06-6T01:55:42.892Z","value":33,"unit":"count"},
{"startDate":"2017-06-06T01:58:03.893Z","endDate":"2017-06-6T01:59:03.893Z","value":16,"unit":"count"},
{"startDate":"2017-06-06T02:00:58.889Z","endDate":"2017-06-6T02:01:58.889Z","value":12,"unit":"count"},
{"startDate":"2017-06-06T02:01:58.889Z","endDate":"2017-06-06T02:02:01.631Z","value":2,"unit":"count"},
{"startDate":"2017-06-06T02:22:03.212Z","endDate":"2017-06-6T02:22:28.901Z","value":37,"unit":"count"},
{"startDate":"2017-06-06T02:22:36.901Z","endDate":"2017-06-06T02:23:36.901Z","value":8,"unit":"count"},
{"startDate":"2017-06-06T02:23:36.901Z","endDate":"2017-06-6T02:24:16.891Z","value":38,"unit":"count"},
{"startDate":"2017-06-06T02:24:16.891Z","endDate":"2017-06-06T02:24:23.445Z","value":7,"unit":"count"},
{"startDate":"2017-06-06T02:24:23.445Z","endDate":"2017-06-6T02:25:32.237Z","value":85,"unit":"count"},
{"startDate":"2017-06-06T02:25:32.237Z","endDate":"2017-06-06T02:25:33.535Z","value":2,"unit":"count"}
]

Let me know how to get sourceName. Also is it possible to get sourceName in queryAggregated() method?

Getting empty array in result.

I want to fetch step history by query 3 days result i.e

navigator.health.queryAggregated({ startDate: new Date(new Date().getTime() - 3 * 24 * 60 * 60 * 1000), // three days ago endDate: new Date(), // now dataType: 'steps', bucket: 'day' }, function (succ) { $scope.result = succ; }, function (err) { $scope.error = err; });

this returned empty array in iOS 10

add healthkit entitlements in plugin.xml

add these two to plugin.xml as done in Telerik's plugin:

    <config-file target="*/Entitlements-Debug.plist" parent="com.apple.developer.healthkit">
      <true/>
    </config-file>

    <config-file target="*/Entitlements-Release.plist" parent="com.apple.developer.healthkit">
      <true/>
    </config-file>

create isAuthorized() function

the function must accept an array of data types and must return if the app is authorized to access those types.

Telerik's plugin already has it implemented, it only needs data types to be translated.

In Google Fit, the only way is by asking for authorization without trying to auto-resolve the issue.

Do not open Google Play Store for Google Fit if not available

Currently if Google Fit is not installed, calling health.isAvailable() will open the Google Play Store to allow the user to install Google Fit. This is problematic in my application because we want to determine if Google Fit is available and then from there decide what to show the user with regards to options for enabling Google Fit integration. Checking for availability should be a separate method from helping the user install Google Fit. Alternatively, the same method isAvailable() could be used, but an optional parameter could be used to determine whether to open the Google Play Store if the package is not installed.

[iOS] unknown data type distanace

When I was calling requestAuthorization to authorize "distance" on iOS, it returns with this error message.

unknown data type distanace

It is working on Android though.
I've tried "calories" and "activity", and they are working on iOS.

GoogleApiClient is not connected yet.

It sometimes raise the message as title said, but not always. It seems that the onConnected() method will still be called in spite of current state is "isConnecting"
logcat log:
W/GmsClientEvents(20908): unregisterConnectionCallbacks(): listener org.apache.cordova.health.HealthPlugin$4@1a2e9b7 not found
I/cordova-plugin-health(20908): Google Fit connected
I/cordova-plugin-health(20908): enter_function_authReqSuccess
I/cordova-plugin-health(20908): after_request
I/cordova-plugin-health(23675): isConnected=false
I/cordova-plugin-health(23675): isConnecting= true
I/cordova-plugin-health(20908): authReqSuccess_error3= GoogleApiClient is not connected yet.

Details:
[email protected]
Nexus 7
Android 5.1.1
API 25
compile "com.google.android.gms:play-services-fitness:9.4.0"
compile "com.google.android.gms:play-services-gcm:9.0.2+"

src code:
`private void authReqSuccess(){
//Create custom data types
new Thread(new Runnable(){

        @Override
        public void run() {
            try{
                Log.i(TAG, "enter_function_authReqSuccess");
                String packageName = cordova.getActivity().getApplicationContext().getPackageName();
                DataTypeCreateRequest request = new DataTypeCreateRequest.Builder()
                        .setName(packageName+".gender")
                        .addField("gender",Field.FORMAT_STRING)
                        .build();
                Log.i(TAG, "after_request");
                Log.i(TAG, "isConnected="+mClient.isConnected());
                Log.i(TAG, "isConnecting= "+mClient.isConnecting());
                PendingResult<DataTypeResult> pendingResult =  Fitness.ConfigApi.createCustomDataType(mClient, request);
                ...
                authReqCallbackCtx.success();
            } catch (Exception ex){
                Log.i(TAG, "authReqSuccess_error3= "+ex.getMessage());
                authReqCallbackCtx.error(ex.getMessage());
            }
        }
    }).start();
}`

Is there a way to check if it has been authorized?

I want to implement the setting page which can show if Google fit is connected. As I observed from other apps on iOS, it seems it is not possible on iOS right?
But for Google Fit, since requestAuthorization prompts the user if it has not been authorized, is there a way to know that without prompting the user?

Android queryAggregated() function date problem

I have a trouble with date param in queryAggregated() function.
So that code works perfectly in iOS:

navigator.health.queryAggregated({
    startDate: new Date(new Date().getTime() - 3 * 24 * 60 * 60 * 1000), // three days ago
    endDate: new Date(), // now
    dataType: 'steps'
}, function(msg){
    console.log(msg)
}, function(e){
    console.log(e)
})

returns in iOS:

endDate: Fri Aug 05 2016 13:25:40 GMT+0500 (AZST)
startDate: Tue Aug 02 2016 13:25:40 GMT+0500 (AZST)

return in Android:

endDate: Thu Aug 04 2016 19:25:11 GMT+0500 (AZST)
startDate: Thu Aug 04 2016 19:24:11 GMT+0500 (AZST)

So you see there are not correct result in Android.
Please help with advice, thanks.

P.S.

That code even don't works on Android, but in iOS works good:

var year = new Date().getFullYear();
var month = new Date().getMonth();
var date = new Date().getDate();

var morning = new Date(year, month, date, 0, 0, 0);
var night = new Date(year, month, date, 23, 59, 59);

navigator.health.queryAggregated({
    startDate: morning, 
    endDate:night, 
    dataType: 'steps'
}, function(msg){
    console.log(msg)
}, function(e){
    console.log(e)
})

iOS return:

endDate: Fri Aug 05 2016 23:59:59 GMT+0500 (AZST)
startDate: Fri Aug 05 2016 00:00:00 GMT+0500 (AZST)
unit: "count"
value: 840

Android return:

endDate: Invalid Date
startDate: Invalid Date

for the same code

Plugin dont exist in Android

Plugin dont exist in Android plugins.healthkit
In iPhone everything is good.
Samsung S4
Android 5.0.1
Google Fit & Play are installed

Calories empty in iOS

Hi,

I have a problem that the calories stay empty on iOS. I can access my steps

[{"startDate":"2017-03-09T23:00:00.000Z","endDate":"2017-03-10T23:00:00.000Z","value":3501,"unit":"count"},{"startDate":"2017-03-10T23:00:00.000Z","endDate":"2017-03-11T23:00:00.000Z","value":2719,"unit":"count"},{"startDate":"2017-03-11T23:00:00.000Z","endDate":"2017-03-12T23:00:00.000Z","value":290,"unit":"count"},{"startDate":"2017-03-12T23:00:00.000Z","endDate":"2017-03-13T23:00:00.000Z","value":1031,"unit":"count"},{"startDate":"2017-03-13T23:00:00.000Z","endDate":"2017-03-14T23:00:00.000Z","value":620,"unit":"count"},{"startDate":"2017-03-14T23:00:00.000Z","endDate":"2017-03-15T23:00:00.000Z","value":1092,"unit":"count"},{"startDate":"2017-03-15T23:00:00.000Z","endDate":"2017-03-16T23:00:00.000Z","value":736,"unit":"count"},{"startDate":"2017-03-16T23:00:00.000Z","endDate":"2017-03-17T23:00:00.000Z","value":640.9999999999999,"unit":"count"},{"startDate":"2017-03-17T23:00:00.000Z","endDate":"2017-03-18T23:00:00.000Z","value":2118,"unit":"count"},{"startDate":"2017-03-18T23:00:00.000Z","endDate":"2017-03-19T23:00:00.000Z","value":43,"unit":"count"},{"startDate":"2017-03-19T23:00:00.000Z","endDate":"2017-03-20T23:00:00.000Z","value":0,"unit":"count"}]

But when I look at calories it remains 0.
[{"startDate":"2017-03-09T23:00:00.000Z","endDate":"2017-03-10T23:00:00.000Z","value":0,"unit":"kcal"},{"startDate":"2017-03-10T23:00:00.000Z","endDate":"2017-03-11T23:00:00.000Z","value":0,"unit":"kcal"},{"startDate":"2017-03-11T23:00:00.000Z","endDate":"2017-03-12T23:00:00.000Z","value":0,"unit":"kcal"},{"startDate":"2017-03-12T23:00:00.000Z","endDate":"2017-03-13T23:00:00.000Z","value":0,"unit":"kcal"},{"startDate":"2017-03-13T23:00:00.000Z","endDate":"2017-03-14T23:00:00.000Z","value":0,"unit":"kcal"},{"startDate":"2017-03-14T23:00:00.000Z","endDate":"2017-03-15T23:00:00.000Z","value":0,"unit":"kcal"},{"startDate":"2017-03-15T23:00:00.000Z","endDate":"2017-03-16T23:00:00.000Z","value":0,"unit":"kcal"},{"startDate":"2017-03-16T23:00:00.000Z","endDate":"2017-03-17T23:00:00.000Z","value":0,"unit":"kcal"},{"startDate":"2017-03-17T23:00:00.000Z","endDate":"2017-03-18T23:00:00.000Z","value":0,"unit":"kcal"},{"startDate":"2017-03-18T23:00:00.000Z","endDate":"2017-03-19T23:00:00.000Z","value":0,"unit":"kcal"},{"startDate":"2017-03-19T23:00:00.000Z","endDate":"2017-03-20T23:00:00.000Z","value":0,"unit":"kcal"}]

Is this a misconfiguration?

Kind regards,
Kab

Using obfuscated API Field.zzn() in Android

The Android source is using an undocumented and obfuscated, but publicly accessible method, called Field.zzn(...). The problem with this, that because it is obfuscated, it can change anytime.
In my source version it is already called Field.zzl(...), so the plugin does not work out of the box for Android.

User cancelled the dialog

I am getting this error (User cancelled the dialog) when I try the simple code as the example in the Ionic Framework.

this.health.isAvailable()
.then((available:boolean) => {
  console.log(available);
  this.health.requestAuthorization([
    'distance', 'nutrition',  //read and write permissions
    {
      read: ['steps'],       //read only permission
      write: ['height', 'weight']  //write only permission
    }
  ])
  .then(res => console.log(res))
  .catch(e => console.log(e));
})
.catch(e => console.log(e));

Support new Google Fit health data types (blood glucose, etc)

It looks like Google Fit has added support for blood pressure, blood glucose, and some other health data types. It would be great to have support for reading and writing these data types via cordova-plugin-health, and updated documentation to go with it.

References:
https://developers.google.com/android/reference/com/google/android/gms/fitness/data/HealthDataTypes.html#TYPE_BLOOD_GLUCOSE
https://developers.google.com/fit/scenarios/write-blood-glucose-data
https://developers.google.com/fit/scenarios/write-bp-data

I have not seen an explicit announcement for the change, but the above pages say "Last updated February 15, 2017".

These new metrics fall into the category of "restricted data types".

Note: Because health data is potentially sensitive, Google Fit restricts write access for the data types in HealthDataTypes to only certain developers.

how to just ask for 'read' permissions?

I'm using 'requestAuthorization' to read health data as mentioned in docs. But by default, it asks for read and write permissions. How can I just ask for 'read' permission?

cannot find symbol method hasPermission

Got following error on running the application:
`Error:(165, 29) error: cannot find symbol method hasPermission(String)
Error:(172, 24) error: cannot find symbol method requestPermissions(HealthPlugin,int,String[])
Error:Execution failed for task ':compileDebugJava'.

Compilation failed; see the compiler error output for details.
Information:BUILD FAILED
`

public void requestDynamicPermissions() {
        if (dynPerms.isEmpty()) {
            authReqCallbackCtx.success();
        } else {
            LinkedList<String> perms = new LinkedList<String>();
            for (String p : dynPerms) {
                if (!cordova.hasPermission(p)) {
                    perms.add(p);
                }
            }
            if (perms.isEmpty()) {
                authReqCallbackCtx.success();
            } else {
                cordova.requestPermissions(this, REQUEST_DYN_PERMS, perms.toArray(new String[perms.size()]));
            }
        }
    }

differentiate between basal and expended calories

HK laready has a clear differentiation:
HKQuantityTypeIdentifierBasalEnergyBurned;
HKQuantityTypeIdentifierActiveEnergyBurned;

in Android there are:
TYPE_BASAL_METABOLIC_RATE
TYPE_CALORIES_EXPENDED
but TYPE_CALORIES_EXPENDED includes also the basal expenditure AFAIK

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.