From 783bfba64357bc31a1272b4b1c6f9a38a3b9923a Mon Sep 17 00:00:00 2001 From: Aamir Farooq Date: Thu, 19 Sep 2024 21:56:44 +0200 Subject: [PATCH] Add checks for Health Connect availability on Android when configuring and reading/writing data --- packages/health/lib/src/health_plugin.dart | 31 ++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/packages/health/lib/src/health_plugin.dart b/packages/health/lib/src/health_plugin.dart index 3da6c82b0..3ebb38737 100644 --- a/packages/health/lib/src/health_plugin.dart +++ b/packages/health/lib/src/health_plugin.dart @@ -27,6 +27,8 @@ class Health { String? _deviceId; final _deviceInfo = DeviceInfoPlugin(); + HealthConnectSdkStatus _healthConnectSdkStatus = + HealthConnectSdkStatus.sdkUnavailable; Health._() { _registerFromJsonFunctions(); @@ -48,11 +50,27 @@ class Health { /// Configure the health plugin. Must be called before using the plugin. Future configure() async { + if (Platform.isAndroid) { + await _checkIfHealthConnectAvailable(); + } + _deviceId = Platform.isAndroid ? (await _deviceInfo.androidInfo).id : (await _deviceInfo.iosInfo).identifierForVendor; } + Future _checkIfHealthConnectAvailable() async { + if (!Platform.isAndroid) return; + + _healthConnectSdkStatus = await getHealthConnectSdkStatus() ?? + HealthConnectSdkStatus.sdkUnavailable; + + if (_healthConnectSdkStatus == HealthConnectSdkStatus.sdkUnavailable || _healthConnectSdkStatus == HealthConnectSdkStatus.sdkUnavailableProviderUpdateRequired) { + throw UnsupportedError( + 'Health Connect is not available on this device, prompt the user to install it using installHealthConnect.'); + } + } + /// Check if a given data type is available on the platform bool isDataTypeAvailable(HealthDataType dataType) => Platform.isAndroid ? dataTypeKeysAndroid.contains(dataType) @@ -86,6 +104,7 @@ class Health { List types, { List? permissions, }) async { + await _checkIfHealthConnectAvailable(); if (permissions != null && permissions.length != types.length) { throw ArgumentError( "The lists of types and permissions must be of same length."); @@ -111,6 +130,7 @@ class Health { /// NOTE: The app must be completely killed and restarted for the changes to take effect. /// Not implemented on iOS as there is no way to programmatically remove access. Future revokePermissions() async { + await _checkIfHealthConnectAvailable(); try { if (Platform.isIOS) { throw UnsupportedError( @@ -183,6 +203,7 @@ class Health { List types, { List? permissions, }) async { + await _checkIfHealthConnectAvailable(); if (permissions != null && permissions.length != types.length) { throw ArgumentError( 'The length of [types] must be same as that of [permissions].'); @@ -311,6 +332,7 @@ class Health { DateTime? endTime, RecordingMethod recordingMethod = RecordingMethod.automatic, }) async { + await _checkIfHealthConnectAvailable(); if (Platform.isIOS && [RecordingMethod.active, RecordingMethod.unknown] .contains(recordingMethod)) { @@ -386,6 +408,7 @@ class Health { required DateTime startTime, DateTime? endTime, }) async { + await _checkIfHealthConnectAvailable(); endTime ??= startTime; if (startTime.isAfter(endTime)) { throw ArgumentError("startTime must be equal or earlier than endTime"); @@ -421,6 +444,7 @@ class Health { DateTime? endTime, RecordingMethod recordingMethod = RecordingMethod.automatic, }) async { + await _checkIfHealthConnectAvailable(); if (Platform.isIOS && [RecordingMethod.active, RecordingMethod.unknown] .contains(recordingMethod)) { @@ -461,6 +485,7 @@ class Health { DateTime? endTime, RecordingMethod recordingMethod = RecordingMethod.automatic, }) async { + await _checkIfHealthConnectAvailable(); if (Platform.isIOS && [RecordingMethod.active, RecordingMethod.unknown] .contains(recordingMethod)) { @@ -594,6 +619,7 @@ class Health { double? zinc, RecordingMethod recordingMethod = RecordingMethod.automatic, }) async { + await _checkIfHealthConnectAvailable(); if (Platform.isIOS && [RecordingMethod.active, RecordingMethod.unknown] .contains(recordingMethod)) { @@ -674,6 +700,7 @@ class Health { required bool isStartOfCycle, RecordingMethod recordingMethod = RecordingMethod.automatic, }) async { + await _checkIfHealthConnectAvailable(); if (Platform.isIOS && [RecordingMethod.active, RecordingMethod.unknown] .contains(recordingMethod)) { @@ -804,6 +831,7 @@ class Health { required DateTime endTime, List recordingMethodsToFilter = const [], }) async { + await _checkIfHealthConnectAvailable(); List dataPoints = []; for (var type in types) { @@ -829,6 +857,7 @@ class Health { required List types, required int interval, List recordingMethodsToFilter = const []}) async { + await _checkIfHealthConnectAvailable(); List dataPoints = []; for (var type in types) { @@ -848,6 +877,7 @@ class Health { int activitySegmentDuration = 1, bool includeManualEntry = true, }) async { + await _checkIfHealthConnectAvailable(); List dataPoints = []; final result = await _prepareAggregateQuery( @@ -1095,6 +1125,7 @@ class Health { String? title, RecordingMethod recordingMethod = RecordingMethod.automatic, }) async { + await _checkIfHealthConnectAvailable(); if (Platform.isIOS && [RecordingMethod.active, RecordingMethod.unknown] .contains(recordingMethod)) {