Skip to content

Commit 973ceea

Browse files
author
Mads Christensen
committed
[Health] 5.0.0 support for Health Connect
1 parent bdd5e5a commit 973ceea

8 files changed

Lines changed: 517 additions & 280 deletions

File tree

packages/health/CHANGELOG.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,10 @@
1+
## 5.0.0
2+
3+
- Added initial support for the new Health Connect API, as Google Fit is being deprecated.
4+
- Does not yet support `revokePermissions`, `getTotalStepsInInterval`.
5+
- Changed Intl package version dependancy to `^0.17.0` to work with flutter stable version.
6+
- Updated the example app to handle more buttons.
7+
18
## 4.6.0
29

310
- Added method for revoking permissions. On Android it uses `disableFit()` to remove access to Google Fit - `revokePermissions`. Documented lack of methods for iOS.

packages/health/README.md

Lines changed: 213 additions & 185 deletions
Large diffs are not rendered by default.

packages/health/android/src/main/kotlin/cachet/plugins/health/HealthPlugin.kt

Lines changed: 245 additions & 22 deletions
Large diffs are not rendered by default.

packages/health/example/android/app/build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,5 +69,5 @@ dependencies {
6969
androidTestImplementation 'androidx.test:runner:1.1.1'
7070
androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1'
7171
// The new health connect api
72-
implementation("androidx.health.connect:connect-client:1.0.0-alpha11")
72+
// implementation("androidx.health.connect:connect-client:1.0.0-alpha11")
7373
}

packages/health/example/lib/main.dart

Lines changed: 33 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import 'dart:async';
2-
import 'dart:math';
32

43
import 'package:flutter/material.dart';
54
import 'package:health/health.dart';
@@ -32,9 +31,13 @@ class _HealthAppState extends State<HealthApp> {
3231
AppState _state = AppState.DATA_NOT_FETCHED;
3332
int _nofSteps = 0;
3433

35-
// define the types to get
36-
final types = dataTypesAndroid;
37-
// final types = [
34+
// Define the types to get.
35+
// NOTE: These are only the ones supported on Androids new API Health Connect.
36+
// Both Android's Google Fit and iOS' HealthKit have more types that we support in the enum list [HealthDataType]
37+
// Add more - like AUDIOGRAM, HEADACHE_SEVERE etc. to try them.
38+
static final types = dataTypesAndroid;
39+
// Or selected types
40+
// static final types = [
3841
// HealthDataType.WEIGHT,
3942
// HealthDataType.STEPS,
4043
// HealthDataType.HEIGHT,
@@ -43,25 +46,17 @@ class _HealthAppState extends State<HealthApp> {
4346
// HealthDataType.BLOOD_PRESSURE_DIASTOLIC,
4447
// HealthDataType.BLOOD_PRESSURE_SYSTOLIC,
4548
// // Uncomment these lines on iOS - only available on iOS
46-
// HealthDataType.AUDIOGRAM
49+
// // HealthDataType.AUDIOGRAM
4750
// ];
4851

4952
// with coresponsing permissions
50-
final permissions =
51-
dataTypesAndroid.map((e) => HealthDataAccess.READ_WRITE).toList();
52-
// final permissions = [
53-
// HealthDataAccess.READ_WRITE,
54-
// HealthDataAccess.READ_WRITE,
55-
// HealthDataAccess.READ_WRITE,
56-
// HealthDataAccess.READ_WRITE,
57-
// HealthDataAccess.READ_WRITE,
58-
// HealthDataAccess.READ_WRITE,
59-
// HealthDataAccess.READ_WRITE,
60-
// HealthDataAccess.READ_WRITE,
61-
// ];
53+
// READ only
54+
// final permissions = types.map((e) => HealthDataAccess.READ).toList();
55+
// Or READ and WRITE
56+
final permissions = types.map((e) => HealthDataAccess.READ_WRITE).toList();
6257

6358
// create a HealthFactory for use in the app
64-
HealthFactory health = HealthFactory();
59+
HealthFactory health = HealthFactory(useHealthConnectIfAvailable: true);
6560

6661
Future authorize() async {
6762
// If we are trying to read Step Count, Workout, Sleep or other data that requires
@@ -74,7 +69,7 @@ class _HealthAppState extends State<HealthApp> {
7469

7570
// Check if we have permission
7671
bool? hasPermissions =
77-
await HealthFactory.hasPermissions(types, permissions: permissions);
72+
await health.hasPermissions(types, permissions: permissions);
7873

7974
// hasPermissions = false because the hasPermission cannot disclose if WRITE access exists.
8075
// Hence, we have to request with WRITE as well.
@@ -130,14 +125,19 @@ class _HealthAppState extends State<HealthApp> {
130125
final now = DateTime.now();
131126
final earlier = now.subtract(Duration(minutes: 20));
132127

133-
bool success = await health.writeHealthData(
128+
// Add data for supported types
129+
// NOTE: These are only the ones supported on Androids new API Health Connect.
130+
// Both Android's Google Fit and iOS' HealthKit have more types that we support in the enum list [HealthDataType]
131+
// Add more - like AUDIOGRAM, HEADACHE_SEVERE etc. to try them.
132+
bool success = true;
133+
success &= await health.writeHealthData(
134134
10, HealthDataType.BODY_FAT_PERCENTAGE, earlier, now);
135135
success &= await health.writeHealthData(
136136
1.925, HealthDataType.HEIGHT, earlier, now);
137137
success &=
138138
await health.writeHealthData(90, HealthDataType.WEIGHT, earlier, now);
139-
success &=
140-
await health.writeHealthData(90, HealthDataType.STEPS, earlier, now);
139+
success &= await health.writeHealthData(
140+
90, HealthDataType.HEART_RATE, earlier, now);
141141
success &=
142142
await health.writeHealthData(90, HealthDataType.STEPS, earlier, now);
143143
success &= await health.writeHealthData(
@@ -154,38 +154,13 @@ class _HealthAppState extends State<HealthApp> {
154154
1100, HealthDataType.DISTANCE_DELTA, earlier, now);
155155
success &=
156156
await health.writeHealthData(1.8, HealthDataType.WATER, earlier, now);
157-
158-
// // Store a count of steps taken
159-
// _nofSteps = Random().nextInt(10);
160-
// bool success = await health.writeHealthData(
161-
// _nofSteps.toDouble(), HealthDataType.STEPS, earlier, now);
162-
163-
// // Store a height
164-
// success &=
165-
// await health.writeHealthData(1.93, HealthDataType.HEIGHT, earlier, now);
166-
167-
// // Store a Blood Glucose measurement
168-
// _mgdl = Random().nextInt(10) * 1.0;
169-
// success &= await health.writeHealthData(
170-
// _mgdl, HealthDataType.BLOOD_GLUCOSE, now, now);
171-
172-
// success &= await health.writeBloodPressure(120, 90, now, now);
173-
174-
// // Store a workout eg. running
175-
// success &= await health.writeWorkoutData(
176-
// HealthWorkoutActivityType.RUNNING,
177-
// earlier,
178-
// now,
179-
// // The following are optional parameters
180-
// // and the UNITS are functional on iOS ONLY!
181-
// totalEnergyBurned: 230,
182-
// totalEnergyBurnedUnit: HealthDataUnit.KILOCALORIE,
183-
// totalDistance: 1234,
184-
// totalDistanceUnit: HealthDataUnit.FOOT,
185-
// );
186-
187-
// success &= await health.writeHealthData(
188-
// 3, HealthDataType.SLEEP_ASLEEP, now.subtract(Duration(hours: 3)), now);
157+
success &= await health.writeWorkoutData(
158+
HealthWorkoutActivityType.AMERICAN_FOOTBALL,
159+
now.subtract(Duration(minutes: 15)),
160+
now,
161+
totalDistance: 2430,
162+
totalEnergyBurned: 400);
163+
success &= await health.writeBloodPressure(90, 80, earlier, now);
189164

190165
// Store an Audiogram
191166
// Uncomment these on iOS - only available on iOS
@@ -215,16 +190,10 @@ class _HealthAppState extends State<HealthApp> {
215190
final now = DateTime.now();
216191
final earlier = now.subtract(Duration(hours: 24));
217192

218-
bool success = false;
219-
220-
success = await health.delete(HealthDataType.STEPS, earlier, now);
221-
success &= await health.delete(HealthDataType.HEIGHT, earlier, now);
222-
success &= await health.delete(HealthDataType.BLOOD_GLUCOSE, earlier, now);
223-
success &= await health.delete(HealthDataType.WORKOUT, earlier, now);
224-
success &= await health.delete(
225-
HealthDataType.BLOOD_PRESSURE_SYSTOLIC,
226-
earlier,
227-
now); // on Android this deletes both systolic and diastolic measurements.
193+
bool success = true;
194+
for (HealthDataType type in types) {
195+
success &= await health.delete(type, earlier, now);
196+
}
228197

229198
setState(() {
230199
_state = success ? AppState.DATA_DELETED : AppState.DATA_NOT_DELETED;

packages/health/example/lib/util.dart

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -56,15 +56,15 @@ const List<HealthDataType> dataTypesAndroid = [
5656
HealthDataType.BODY_FAT_PERCENTAGE,
5757
HealthDataType.HEIGHT,
5858
HealthDataType.WEIGHT,
59-
HealthDataType.BODY_MASS_INDEX,
59+
// HealthDataType.BODY_MASS_INDEX,
6060
HealthDataType.BODY_TEMPERATURE,
6161
HealthDataType.HEART_RATE,
6262
HealthDataType.STEPS,
6363
// HealthDataType.MOVE_MINUTES, // TODO: Find alternative for Health Connect
6464
HealthDataType.DISTANCE_DELTA,
65-
HealthDataType.SLEEP_AWAKE,
66-
HealthDataType.SLEEP_ASLEEP,
67-
HealthDataType.SLEEP_IN_BED,
65+
// HealthDataType.SLEEP_AWAKE,
66+
// HealthDataType.SLEEP_ASLEEP,
67+
// HealthDataType.SLEEP_IN_BED,
6868
HealthDataType.WATER,
6969
HealthDataType.WORKOUT,
7070
];

packages/health/lib/src/health_factory.dart

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,20 @@ class HealthFactory {
1414
static const MethodChannel _channel = MethodChannel('flutter_health');
1515
String? _deviceId;
1616
final _deviceInfo = DeviceInfoPlugin();
17+
late bool _useHealthConnectIfAvailable;
1718

1819
static PlatformType _platformType =
1920
Platform.isAndroid ? PlatformType.ANDROID : PlatformType.IOS;
2021

22+
// The plugin was created to use Health Connect (if true) or Google Fit (if false).
23+
bool get useHealthConnectIfAvailable => _useHealthConnectIfAvailable;
24+
25+
HealthFactory({bool useHealthConnectIfAvailable = false}) {
26+
_useHealthConnectIfAvailable = useHealthConnectIfAvailable;
27+
if (_useHealthConnectIfAvailable)
28+
_channel.invokeMethod('useHealthConnectIfAvailable');
29+
}
30+
2131
/// Check if a given data type is available on the platform
2232
bool isDataTypeAvailable(HealthDataType dataType) =>
2333
_platformType == PlatformType.ANDROID
@@ -47,7 +57,7 @@ class HealthFactory {
4757
/// with a READ or READ_WRITE access.
4858
///
4959
/// On Android, this function returns true or false, depending on whether the specified access right has been granted.
50-
static Future<bool?> hasPermissions(List<HealthDataType> types,
60+
Future<bool?> hasPermissions(List<HealthDataType> types,
5161
{List<HealthDataAccess>? permissions}) async {
5262
if (permissions != null && permissions.length != types.length)
5363
throw ArgumentError(

packages/health/pubspec.yaml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
name: health
2-
description: Wrapper for the iOS HealthKit and Android GoogleFit services.
3-
version: 4.6.0
2+
description: Wrapper for HealthKit on iOS and Google Fit and Health Connect on Android.
3+
version: 5.0.0
44
homepage: https://github.com/cph-cachet/flutter-plugins/tree/master/packages/health
55

66
environment:
@@ -10,7 +10,7 @@ environment:
1010
dependencies:
1111
flutter:
1212
sdk: flutter
13-
intl: ^0.18.0
13+
intl: ^0.17.0
1414
device_info_plus: ^8.0.0
1515

1616
dev_dependencies:

0 commit comments

Comments
 (0)