diff --git a/packages/health/android/src/main/kotlin/cachet/plugins/health/HealthPlugin.kt b/packages/health/android/src/main/kotlin/cachet/plugins/health/HealthPlugin.kt index a9c23af2c..5fb0a5a79 100644 --- a/packages/health/android/src/main/kotlin/cachet/plugins/health/HealthPlugin.kt +++ b/packages/health/android/src/main/kotlin/cachet/plugins/health/HealthPlugin.kt @@ -15,6 +15,7 @@ import androidx.health.connect.client.HealthConnectFeatures import androidx.health.connect.client.PermissionController import androidx.health.connect.client.permission.HealthPermission import androidx.health.connect.client.permission.HealthPermission.Companion.PERMISSION_READ_HEALTH_DATA_HISTORY +import androidx.health.connect.client.permission.HealthPermission.Companion.PERMISSION_READ_HEALTH_DATA_IN_BACKGROUND import androidx.health.connect.client.records.* import androidx.health.connect.client.records.MealType.MEAL_TYPE_BREAKFAST import androidx.health.connect.client.records.MealType.MEAL_TYPE_DINNER @@ -153,6 +154,9 @@ class HealthPlugin(private var channel: MethodChannel? = null) : "isHealthDataHistoryAvailable" -> isHealthDataHistoryAvailable(call, result) "isHealthDataHistoryAuthorized" -> isHealthDataHistoryAuthorized(call, result) "requestHealthDataHistoryAuthorization" -> requestHealthDataHistoryAuthorization(call, result) + "isHealthDataInBackgroundAvailable" -> isHealthDataInBackgroundAvailable(call, result) + "isHealthDataInBackgroundAuthorized" -> isHealthDataInBackgroundAuthorized(call, result) + "requestHealthDataInBackgroundAuthorization" -> requestHealthDataInBackgroundAuthorization(call, result) "hasPermissions" -> hasPermissions(call, result) "requestAuthorization" -> requestAuthorization(call, result) "revokePermissions" -> revokePermissions(call, result) @@ -568,6 +572,55 @@ class HealthPlugin(private var channel: MethodChannel? = null) : healthConnectRequestPermissionsLauncher!!.launch(setOf(PERMISSION_READ_HEALTH_DATA_HISTORY)) } + /** + * Checks if the health data in background feature is available on this device + */ + @OptIn(ExperimentalFeatureAvailabilityApi::class) + private fun isHealthDataInBackgroundAvailable(call: MethodCall, result: Result) { + scope.launch { + result.success( + healthConnectClient + .features + .getFeatureStatus(HealthConnectFeatures.FEATURE_READ_HEALTH_DATA_IN_BACKGROUND) == + HealthConnectFeatures.FEATURE_STATUS_AVAILABLE) + } + } + + /** + * Checks if PERMISSION_READ_HEALTH_DATA_IN_BACKGROUND has been granted + */ + private fun isHealthDataInBackgroundAuthorized(call: MethodCall, result: Result) { + scope.launch { + result.success( + healthConnectClient + .permissionController + .getGrantedPermissions() + .containsAll(listOf(PERMISSION_READ_HEALTH_DATA_IN_BACKGROUND)), + ) + } + } + + /** + * Requests authorization for PERMISSION_READ_HEALTH_DATA_IN_BACKGROUND + */ + private fun requestHealthDataInBackgroundAuthorization(call: MethodCall, result: Result) { + if (context == null) { + result.success(false) + return + } + + if (healthConnectRequestPermissionsLauncher == null) { + result.success(false) + Log.i("FLUTTER_HEALTH", "Permission launcher not found") + return + } + + // Store the result to be called in [onHealthConnectPermissionCallback] + mResult = result + isReplySubmitted = false + healthConnectRequestPermissionsLauncher!!.launch(setOf(PERMISSION_READ_HEALTH_DATA_IN_BACKGROUND)) + } + private fun hasPermissions(call: MethodCall, result: Result) { val args = call.arguments as HashMap<*, *> val types = (args["types"] as? ArrayList<*>)?.filterIsInstance()!! diff --git a/packages/health/example/android/app/src/main/AndroidManifest.xml b/packages/health/example/android/app/src/main/AndroidManifest.xml index 1b559506e..70e81aa1e 100644 --- a/packages/health/example/android/app/src/main/AndroidManifest.xml +++ b/packages/health/example/android/app/src/main/AndroidManifest.xml @@ -57,6 +57,9 @@ + + + { // request access to read historic data await health.requestHealthDataHistoryAuthorization(); + // request access in background + await health.requestHealthDataInBackgroundAuthorization(); + } catch (error) { debugPrint("Exception in authorize: $error"); } diff --git a/packages/health/lib/src/health_plugin.dart b/packages/health/lib/src/health_plugin.dart index 0c7bae82b..536392458 100644 --- a/packages/health/lib/src/health_plugin.dart +++ b/packages/health/lib/src/health_plugin.dart @@ -264,6 +264,70 @@ class Health { } } + /// Checks if the Health Data in Background feature is available. + /// + /// See this for more info: https://developer.android.com/reference/androidx/health/connect/client/permission/HealthPermission#PERMISSION_READ_HEALTH_DATA_IN_BACKGROUND() + /// + /// + /// Android only. Returns false on iOS or if an error occurs. + Future isHealthDataInBackgroundAvailable() async { + if (Platform.isIOS) return false; + + try { + final status = + await _channel.invokeMethod('isHealthDataInBackgroundAvailable'); + return status ?? false; + } catch (e) { + debugPrint( + '$runtimeType - Exception in isHealthDataInBackgroundAvailable(): $e'); + return false; + } + } + + /// Checks the current status of the Health Data in Background permission. + /// Make sure to check [isHealthConnectAvailable] before calling this method. + /// + /// See this for more info: https://developer.android.com/reference/androidx/health/connect/client/permission/HealthPermission#PERMISSION_READ_HEALTH_DATA_IN_BACKGROUND() + /// + /// + /// Android only. Returns true on iOS or false if an error occurs. + Future isHealthDataInBackgroundAuthorized() async { + if (Platform.isIOS) return true; + + try { + final status = + await _channel.invokeMethod('isHealthDataInBackgroundAuthorized'); + return status ?? false; + } catch (e) { + debugPrint( + '$runtimeType - Exception in isHealthDataInBackgroundAuthorized(): $e'); + return false; + } + } + + /// Requests the Health Data in Background permission. + /// + /// Returns true if successful, false otherwise. + /// + /// See this for more info: https://developer.android.com/reference/androidx/health/connect/client/permission/HealthPermission#PERMISSION_READ_HEALTH_DATA_IN_BACKGROUND() + /// + /// + /// Android only. Returns true on iOS or false if an error occurs. + Future requestHealthDataInBackgroundAuthorization() async { + if (Platform.isIOS) return true; + + await _checkIfHealthConnectAvailableOnAndroid(); + try { + final bool? isAuthorized = + await _channel.invokeMethod('requestHealthDataInBackgroundAuthorization'); + return isAuthorized ?? false; + } catch (e) { + debugPrint( + '$runtimeType - Exception in requestHealthDataInBackgroundAuthorization(): $e'); + return false; + } + } + /// Requests permissions to access health data [types]. /// /// Returns true if successful, false otherwise.