Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion android/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ buildscript {
}

dependencies {
classpath "com.android.tools.build:gradle:7.2.1"
classpath "com.android.tools.build:gradle:8.1.2"
// noinspection DifferentKotlinGradleVersion
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
Expand Down
2 changes: 1 addition & 1 deletion android/gradle.properties
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
HealthConnect_kotlinVersion=1.8.0
HealthConnect_minSdkVersion=26
HealthConnect_targetSdkVersion=34
HealthConnect_compileSdkVersion=34
HealthConnect_compileSdkVersion=35
HealthConnect_ndkversion=21.4.7075529
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,11 @@ class PermissionUtils {
return@mapNotNull HealthPermission.PERMISSION_READ_HEALTH_DATA_HISTORY
}

val recordClass = reactRecordTypeToClassMap[recordType]
?: throw InvalidRecordType()
if (accessType == "read" && recordType == "BackgroundAccessPermission") {
return@mapNotNull HealthPermission.PERMISSION_READ_HEALTH_DATA_IN_BACKGROUND
}

val recordClass = reactRecordTypeToClassMap[recordType] ?: throw InvalidRecordType()

when (accessType) {
"write" -> HealthPermission.getWritePermission(recordClass)
Expand All @@ -40,6 +43,7 @@ class PermissionUtils {

fun mapPermissionResult(grantedPermissions: Set<String>): WritableNativeArray {
return WritableNativeArray().apply {
// Handle regular permissions
for ((recordType, recordClass) in reactRecordTypeToClassMap) {
val readPermissionForRecord = HealthPermission.getReadPermission(recordClass)
val writePermissionForRecord = HealthPermission.getWritePermission(recordClass)
Expand All @@ -52,8 +56,17 @@ class PermissionUtils {
pushMap(ReactPermission(AccessType.WRITE, recordType).toReadableMap())
}
}

// Handle special permissions
if (grantedPermissions.contains(HealthPermission.PERMISSION_WRITE_EXERCISE_ROUTE)) {
pushMap(ReactPermission(AccessType.WRITE, "ExerciseRoute").toReadableMap())
}

if (grantedPermissions.contains(HealthPermission.PERMISSION_READ_HEALTH_DATA_IN_BACKGROUND)
) {
pushMap(ReactPermission(AccessType.READ, "BackgroundAccessPermission").toReadableMap())
}
}
}

}
}
34 changes: 33 additions & 1 deletion docs/docs/api/methods/03-requestPermission.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,11 @@ const requestPermissions = () => {
};
```

If your app needs to write exercise routes, can include it as a special permission:
## Special Permissions

### Exercise Route Permission

If your app needs to write exercise routes, you can include it as a special permission:

```ts
import { requestPermission } from 'react-native-health-connect';
Expand All @@ -56,4 +60,32 @@ const requestPermissions = () => {
console.log('Granted permissions ', { permissions });
});
};
```

### Background Access Permission

If your app needs to read health data in the background, you can request the background access permission:

```ts
import { requestPermission } from 'react-native-health-connect';

const requestBackgroundAccess = () => {
requestPermission([
{
accessType: 'read',
recordType: 'BackgroundAccessPermission',
},
// Other permissions you need...
{
accessType: 'read',
recordType: 'Steps',
},
{
accessType: 'read',
recordType: 'HeartRate',
}
]).then((permissions) => {
console.log('Granted permissions ', { permissions });
});
};
```
66 changes: 66 additions & 0 deletions docs/docs/api/methods/17-backgroundAccessPermission.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
---
title: Background Access Permission
---

# Background Access Permission

Health Connect provides a special permission that allows your app to read health data in the background. This is useful for apps that need to monitor health data continuously, such as fitness tracking apps.

## Setup

### 1. Add the permission to your AndroidManifest.xml

First, you need to declare the background access permission in your app's `AndroidManifest.xml` file:

```xml
<uses-permission android:name="android.permission.health.READ_HEALTH_DATA_IN_BACKGROUND"/>
```

### 2. Request the permission in your app

Then, include the background access permission in your permission request:

```ts
import { requestPermission } from 'react-native-health-connect';

const requestPermissions = () => {
requestPermission([
{
accessType: 'read',
recordType: 'BackgroundAccessPermission',
},
// Other permissions...
]).then((permissions) => {
console.log('Granted permissions ', { permissions });
});
};
```

## Android Implementation

Under the hood, this permission maps to `HealthPermission.PERMISSION_READ_HEALTH_DATA_IN_BACKGROUND` in the Android Health Connect API. This permission allows your app to read health data even when it's not in the foreground.

## Checking for Background Access

You can check if your app has been granted background access permission using the `getGrantedPermissions` method:

```ts
import { getGrantedPermissions } from 'react-native-health-connect';

const checkBackgroundAccess = async () => {
const permissions = await getGrantedPermissions();
const hasBackgroundAccess = permissions.some(
(permission) =>
permission.accessType === 'read' &&
permission.recordType === 'BackgroundAccessPermission'
);

console.log('Has background access:', hasBackgroundAccess);
};
```

## Important Notes

- Background access is a powerful permission that should be used responsibly
- Make sure to explain to users why your app needs background access to their health data
- This permission is only available on Android devices with Health Connect support
7 changes: 7 additions & 0 deletions docs/docs/api/overview.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,10 @@ title: Overview
| aggregateRecord | Reads aggregated results according to requested read criteria, for e.g, data origin filter and within a time range. |
| deleteRecordsByUuids | Deletes one or more records by their identifiers. Deletion of multiple records is executed in a single transaction - if one fails, none is deleted. |
| deleteRecordsByTimeRange | Deletes any record of the given record type in the given time range (automatically filtered to a record belonging to the calling application). Deletion of multiple records is executed in a transaction - if one fails, none is deleted. |
| requestExerciseRoute | Requests permission to access exercise route data for a specific exercise session. |

## Special Permissions

| **Permission Type** | **Description** |
| ------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| BackgroundAccessPermission | Allows your app to read health data in the background, even when your app is not in the foreground. |
45 changes: 45 additions & 0 deletions docs/docs/permissions.md
Original file line number Diff line number Diff line change
Expand Up @@ -159,3 +159,48 @@ You will need to use [EAS Build](https://docs.expo.dev/eas/) and [Config plugins
| WriteExerciseRoute | android.permission.health.WRITE_EXERCISE_ROUTE | N/A |

You can read more about data types and permissions [here](https://developer.android.com/guide/health-and-fitness/health-connect/data-and-data-types/data-types).

## Special Permissions

In addition to the standard record type permissions, Health Connect provides special permissions for specific functionality:

### Background Access Permission

This permission allows your app to read health data in the background, even when your app is not in the foreground.

First, add the background access permission to your `AndroidManifest.xml`:

```xml
<uses-permission android:name="android.permission.health.READ_HEALTH_DATA_IN_BACKGROUND"/>
```

Then, request the permission in your app:

```ts
// Request background access permission
requestPermission([
{
accessType: 'read',
recordType: 'BackgroundAccessPermission',
},
// Other permissions...
]);
```

Under the hood, this maps to `HealthPermission.PERMISSION_READ_HEALTH_DATA_IN_BACKGROUND` in the Android Health Connect API.

See the [Background Access Permission](./api/methods/17-backgroundAccessPermission.md) documentation for more details.

### Exercise Route Permission

This special permission is required to write exercise routes:

```ts
requestPermission([
{
accessType: 'write',
recordType: 'ExerciseRoute',
},
// Other permissions...
]);
```
1 change: 1 addition & 0 deletions example/android/app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
<uses-permission android:name="android.permission.health.READ_EXERCISE_ROUTES"/>
<uses-permission android:name="android.permission.health.WRITE_EXERCISE_ROUTE"/>
<uses-permission android:name="android.permission.health.READ_HEALTH_DATA_HISTORY"/>
<uses-permission android:name="android.permission.health.READ_HEALTH_DATA_IN_BACKGROUND"/>

<application
android:name=".MainApplication"
Expand Down
86 changes: 47 additions & 39 deletions example/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import * as React from 'react';
import {
Button,
NativeSyntheticEvent,
ScrollView,
StyleSheet,
TextInput,
TextInputChangeEventData,
Expand Down Expand Up @@ -241,6 +242,10 @@ export default function App() {

const requestSamplePermissions = () => {
requestPermission([
{
accessType: 'read',
recordType: 'BackgroundAccessPermission',
},
{
accessType: 'read',
recordType: 'Steps',
Expand Down Expand Up @@ -339,45 +344,47 @@ export default function App() {
};

return (
<View style={styles.container}>
<Button title="Initialize" onPress={initializeHealthConnect} />
<Button
title="Open Health Connect settings"
onPress={openHealthConnectSettings}
/>
<Button
title="Open Health Connect data management"
onPress={() => openHealthConnectDataManagement()}
/>
<Button title="Check availability" onPress={checkAvailability} />
<Button
title="Request sample permissions"
onPress={requestSamplePermissions}
/>
<Button title="Get granted permissions" onPress={grantedPermissions} />
<Button title="Revoke all permissions" onPress={revokeAllPermissions} />
<Button title="Insert sample data" onPress={insertSampleData} />
<Button title="Read sample data" onPress={readSampleData} />
<Button title="Read specific data" onPress={readSampleDataSingle} />
<Button title="Aggregate sample data" onPress={aggregateSampleData} />
<Button
title="Aggregate sample group data by duration"
onPress={aggregateSampleGroupByDuration}
/>
<Button
title="Aggregate sample group data by period"
onPress={aggregateSampleGroupByPeriod}
/>
<Button title="Insert random exercise" onPress={insertRandomExercise} />
<TextInput
id="record-id"
placeholder="Record ID"
value={recordId}
onChange={updateRecordId}
/>
<Button title="Read exercise" onPress={readExercise} />
<Button title="Request exercise route" onPress={readExerciseRoute} />
</View>
<ScrollView>
<View style={styles.container}>
<Button title="Initialize" onPress={initializeHealthConnect} />
<Button
title="Open Health Connect settings"
onPress={openHealthConnectSettings}
/>
<Button
title="Open Health Connect data management"
onPress={() => openHealthConnectDataManagement()}
/>
<Button title="Check availability" onPress={checkAvailability} />
<Button
title="Request sample permissions"
onPress={requestSamplePermissions}
/>
<Button title="Get granted permissions" onPress={grantedPermissions} />
<Button title="Revoke all permissions" onPress={revokeAllPermissions} />
<Button title="Insert sample data" onPress={insertSampleData} />
<Button title="Read sample data" onPress={readSampleData} />
<Button title="Read specific data" onPress={readSampleDataSingle} />
<Button title="Aggregate sample data" onPress={aggregateSampleData} />
<Button
title="Aggregate sample group data by duration"
onPress={aggregateSampleGroupByDuration}
/>
<Button
title="Aggregate sample group data by period"
onPress={aggregateSampleGroupByPeriod}
/>
<Button title="Insert random exercise" onPress={insertRandomExercise} />
<TextInput
id="record-id"
placeholder="Record ID"
value={recordId}
onChange={updateRecordId}
/>
<Button title="Read exercise" onPress={readExercise} />
<Button title="Request exercise route" onPress={readExerciseRoute} />
</View>
</ScrollView>
);
}

Expand All @@ -387,6 +394,7 @@ const styles = StyleSheet.create({
alignItems: 'center',
justifyContent: 'center',
rowGap: 16,
padding: 16,
},
box: {
width: 60,
Expand Down
Loading