Skip to main content

Usage: Availability and permissions

Check permissions and device compatibility with Health Connect.

RookPermissionsManager

Create an instance of RookPermissionsManager providing a context:

val rookPermissionsManager = RookPermissionsManager(context)

Recommendations

We recommend to use the RookPermissionsManager as a singleton with a ServiceLocator or with Dependency Injection.

<!--AndroidManifest.xml-->
<application
android:name=".RookApplication">
</application>
class RookApplication : Application() {
lateinit var serviceLocator: ServiceLocator

override fun onCreate() {
super.onCreate()

serviceLocator = ServiceLocator(applicationContext)
}
}

class ServiceLocator(context: Context) {
val rookPermissionsManager: RookPermissionsManager by lazy {
RookPermissionsManager(context)
}
}

Check availability

Before proceeding further, ensure the user's device is compatible with Health Connect and check if the APK is installed. Call checkHealthConnectAvailability:

StatusDescriptionWhat to do
INSTALLEDAPK is installedProceed to check permissions
NOT_INSTALLEDAPK is not installedPrompt the user to install Health Connect.
NOT_SUPPORTEDThis device does not support Health ConnectTake the user out of the Health Connect section
val message = when (rookPermissionsManager.checkHealthConnectAvailability()) {
AvailabilityStatus.INSTALLED -> "Health Connect is installed! You can skip the next step"
AvailabilityStatus.NOT_INSTALLED -> "Health Connect is not installed. Please download from the Play Store"
else -> "This device is not compatible with health connect. Please close the app"
}
info

There is an alternative version of checkHealthConnectAvailability which accepts a Context available in the companion object of RookPermissionsManager.

Health Connect permissions

These are permissions used to extract data, each Health Connect data type is subject to a different permission, below you can see the list of permissions ROOK needs:

  • READ_SLEEP
  • READ_STEPS
  • READ_DISTANCE
  • READ_FLOORS_CLIMBED
  • READ_ELEVATION_GAINED
  • READ_OXYGEN_SATURATION
  • READ_VO2_MAX
  • READ_TOTAL_CALORIES_BURNED
  • READ_ACTIVE_CALORIES_BURNED
  • READ_HEART_RATE
  • READ_RESTING_HEART_RATE
  • READ_HEART_RATE_VARIABILITY
  • READ_EXERCISE
  • READ_SPEED
  • READ_WEIGHT
  • READ_HEIGHT
  • READ_BLOOD_GLUCOSE
  • READ_BLOOD_PRESSURE
  • READ_HYDRATION
  • READ_BODY_TEMPERATURE
  • READ_RESPIRATORY_RATE
  • READ_NUTRITION
  • READ_MENSTRUATION
  • READ_POWER

Check permissions

To check permissions call checkHealthConnectPermissions:

val hasAllHealthConnectPermissions = rookPermissionsManager.checkHealthConnectPermissions().fold(
{ hasAllPermissions ->
hasAllPermissions
},
{ throwable ->
false
}
)

The previous function will check for all permissions, to check if at least one permission is granted, call checkHealthConnectPermissionsPartially:

val hasSomeHealthConnectPermissions = rookPermissionsManager.checkHealthConnectPermissionsPartially().fold(
{ hasSomePermissions ->
hasSomePermissions
},
{ throwable ->
false
}
)
info

There are alternative versions of checkHealthConnectPermissions and checkHealthConnectPermissionsPartially which accept a Context available in the companion object of RookPermissionsManager.

Request permissions

To request permissions call requestHealthConnectPermissions:

rookPermissionsManager.requestHealthConnectPermissions().fold(
{
when (it) {
RequestPermissionsStatus.ALREADY_GRANTED -> {
// Permissions already granted, update your UI
}

RequestPermissionsStatus.REQUEST_SENT -> {
// Wait for broadcast result
}
}
},
{
// Handle error
}
)

This function will return a RequestPermissionsStatus with 2 possible values:

  • ALREADY_GRANTED: The permissions are already granted thus no request was sent.
  • REQUEST_SENT: The permissions request was sent, and you can get notified if the permissions were granted or denied using a BroadcastReceiver.

The BroadcastReceiver can be registered using the RookPermissionsManager.ACTION_HEALTH_CONNECT_PERMISSIONS action and will contain the following extras:

note

EXTRA_HEALTH_CONNECT_BACKGROUND_PERMISSION_GRANTED is only available in 2.0.0-alpha01+ version.

  • RookPermissionsManager.EXTRA_HEALTH_CONNECT_PERMISSIONS_GRANTED: Boolean describing if all Health Connect permissions were granted.
  • RookPermissionsManager.EXTRA_HEALTH_CONNECT_PERMISSIONS_PARTIALLY_GRANTED: Boolean describing if some (at least one) Health Connect permissions were granted. Note that if EXTRA_HEALTH_CONNECT_PERMISSIONS_GRANTED is true, this will also be true.
  • RookPermissionsManager.EXTRA_HEALTH_CONNECT_BACKGROUND_PERMISSION_GRANTED: Boolean describing if background read permission was granted. Note that if this device does not support background read, this will be false.
// 1.- Create broadcast receiver
private val healthConnectBroadcastReceiver = object : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
val allPermissionsGranted = intent?.getBooleanExtra(
/* name = */ RookPermissionsManager.EXTRA_HEALTH_CONNECT_PERMISSIONS_GRANTED,
/* defaultValue = */ false
) ?: false

val permissionsPartiallyGranted = intent?.getBooleanExtra(
/* name = */ RookPermissionsManager.EXTRA_HEALTH_CONNECT_PERMISSIONS_PARTIALLY_GRANTED,
/* defaultValue = */ false
) ?: false

// This will make your app more flexible by allowing it to work even if some permissions are not granted:
val permissionsGranted = allPermissionsGranted || permissionsPartiallyGranted

val backgroundPermissionGranted = intent?.getBooleanExtra(
/* name = */ RookPermissionsManager.EXTRA_HEALTH_CONNECT_BACKGROUND_PERMISSION_GRANTED,
/* defaultValue = */ false
) ?: false

// Updated your UI
}
}

// 2.- Register broadcast receiver (onCreate)
ContextCompat.registerReceiver(
context,
healthConnectBroadcastReceiver,
IntentFilter(RookPermissionsManager.ACTION_HEALTH_CONNECT_PERMISSIONS),
ContextCompat.RECEIVER_EXPORTED,
)

// 3.- Request permissions
rookPermissionsManager.requestHealthConnectPermissions().fold(
{
when (it) {
RequestPermissionsStatus.ALREADY_GRANTED -> {
// Permissions already granted, update your UI
}

RequestPermissionsStatus.REQUEST_SENT -> {
// Wait for broadcast result
}
}
},
{
// Handle error
}
)

// 4.- Unregister broadcast receiver (onDestroy)
context.unregisterReceiver(healthConnectBroadcastReceiver)
info

There is an alternative version of requestHealthConnectPermissions which accepts a Context available in the companion object of RookPermissionsManager.

Health Connect permissions denied

If the user clicks cancel or navigates away from the permissions screen, Health Connect will consider it as a denial of permissions. If the user denies the permissions twice, your app will be blocked by Health Connect and your only option will be to open the Health Connect app and ask your users to grant permissions manually.

When your app is blocked, any permissions request will be ignored.

To solve this problem, we recommend including an Open Health Connect button in your permissions UI. This button will use rookPermissionsManager.openHealthConnectSettings() to open the Health Connect application.

rookPermissionsManager.openHealthConnectSettings().fold(
{
// Health Connect was opened
},
{
// Error opening Health Connect
}
)
info

There is an alternative version of openHealthConnectSettings which accepts a Context available in the companion object of RookPermissionsManager.

Background read permissions

note

Background read permissions are only available in 2.0.0-alpha01+ version.

Health Connect now supports full background data reads, but it has a few requirements, first is that the user MUST grant a new permission (READ_HEALTH_DATA_IN_BACKGROUND) and the second is that the user's device MUST have a Health Connect application version that supports background reads.

You can use the function checkBackgroundReadStatus available on RookPermissionsManager (Instance and Companion) to check both scenarios:

rookPermissionsManager.checkBackgroundReadStatus().fold(
{
when (it) {
BackgroundReadStatus.UNAVAILABLE -> {
// Background read is not available on this device. Try asking the user to update their Health Connect application.
}
BackgroundReadStatus.PERMISSION_NOT_GRANTED -> {
// Background read permission is not granted. Try requesting background read permission.
}
BackgroundReadStatus.PERMISSION_GRANTED -> {
// Background read permission is granted.
}
}
},
{
// Handle error
}
)

To request background read permission call requestHealthConnectPermissions, this function, in addition of requesting for "normal" data types permissions will also ask for background reads permissions (if the device supports it, otherwise this permission won't be included in the request). You will receive an update of the permission acceptation status in the EXTRA_HEALTH_CONNECT_BACKGROUND_PERMISSION_GRANTED extra included in the ACTION_HEALTH_CONNECT_PERMISSIONS action, see the complete example here.

Customizing permissions

If you want to reduce the Health Connect permissions used by this SDK you can do it following the manifest merge documentation and the SDK will change the behavior of request/check permissions functions based on the declared permissions:

For example if you remove the READ_MENSTRUATION permission...


<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">

<uses-permission
android:name="android.permission.health.READ_MENSTRUATION"
tools:node="remove"/>
</manifest>

the functions checkHealthConnectPermissions and requestHealthConnectPermissions will check/request all permissions excluding READ_MENSTRUATION

info

Note that this process only works for removing Health Connect permissions, adding a permission that is not included in the default list will do nothing.

Health Connect permissions are declared with the syntax: android.permission.health.READ.., to see the current Health Connect permissions go to you AndroidManifest and select the merged manifest tab.

Android permissions

Android permissions are the normal permission every non-health app may need, in this case we use the following permissions to track steps and/or automatically sync health data:

  • POST_NOTIFICATIONS
  • ACTIVITY_RECOGNITION
  • FOREGROUND_SERVICE
  • FOREGROUND_SERVICE_HEALTH

To check permissions call checkAndroidPermissions:

val hasAndroidPermissions = rookPermissionsManager.checkAndroidPermissions()

To request permissions call requestAndroidPermissions:

val requestPermissionsStatus = rookPermissionsManager.requestAndroidPermissions()

when (requestPermissionsStatus) {
RequestPermissionsStatus.ALREADY_GRANTED -> {
// Permissions already granted, update your UI
}

RequestPermissionsStatus.REQUEST_SENT -> {
// Wait for broadcast result
}
}

This function will return a RequestPermissionsStatus with 2 possible values:

  • ALREADY_GRANTED: The permissions are already granted thus no request was sent.
  • REQUEST_SENT: The permissions request was sent, and you can get notified if the permissions were granted or denied using a BroadcastReceiver.

The BroadcastReceiver can be registered using the RookPermissionsManager.ACTION_ANDROID_PERMISSIONS action and will contain the following extras:

  • RookPermissionsManager.EXTRA_ANDROID_PERMISSIONS_GRANTED: Boolean describing if all Android permissions were granted.
  • RookPermissionsManager.EXTRA_ANDROID_PERMISSIONS_DIALOG_DISPLAYED: Boolean describing if the permissions dialog was displayed. You can also use RookPermissionsManager.shouldRequestAndroidPermissions(activity) to know if the permissions dialog WILL BE displayed before calling requestAndroidPermissions and manage this scenario with more anticipation.
// 1.- Create broadcast receiver                                                        
private val androidBroadcastReceiver = object : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
val permissionsGranted = intent?.getBooleanExtra(
/* name = */ RookPermissionsManager.EXTRA_ANDROID_PERMISSIONS_GRANTED,
/* defaultValue = */ false
) ?: false

// Updated your UI
}
}

// 2.- Register broadcast receiver (onCreate)
ContextCompat.registerReceiver(
context,
androidBroadcastReceiver,
IntentFilter(RookPermissionsManager.ACTION_ANDROID_PERMISSIONS),
ContextCompat.RECEIVER_EXPORTED,
)

// 3.- Request permissions
val requestPermissionsStatus = rookPermissionsManager.requestAndroidPermissions()

when (requestPermissionsStatus) {
RequestPermissionsStatus.ALREADY_GRANTED -> {
// Permissions already granted, update your UI
}

RequestPermissionsStatus.REQUEST_SENT -> {
// Wait for broadcast result
}
}

// 4.- Unregister broadcast receiver (onDestroy)
context.unregisterReceiver(androidBroadcastReceiver)
info

There are alternative versions of checkAndroidPermissions and requestAndroidPermissions which accept a Context available in the companion object of RookPermissionsManager.

Android permissions denied

If a user denies a permission Android will not show a dialog the next time you ask for permissions, use RookPermissionsManager.shouldRequestAndroidPermissions(activity) to check if the user has previously denied the permissions and based on the result request permissions or navigate the user to your app's settings.

val shouldRequest = RookPermissionsManager.shouldRequestAndroidPermissions(activity)

if (shouldRequest) {
// Request permissions
} else {
// Open your application's settings
// Show a toast indicating your users that they must enable permissions manually
}

Health Connect permissions request launcher

info

This is an alternative to the requestHealthConnectPermissions and BroadcastReceiver approach, we highly recommend using that before using the permissions request launcher.

Call registerPermissionsRequestLauncher, providing an activity or fragment.

The following block of code must be called before your activity or fragment reaches the resume state, preferably as part of the onCreate or onCreateView function.

RookPermissionsManager.registerPermissionsRequestLauncher(activity / fragment)

Then call launchPermissionsRequest.

RookPermissionsManager.launchPermissionsRequest()

Finally, unregister your activity/fragment when you don't need to request permissions anymore. Preferably as part of the onDestroy function.

fun onDestroy() {
RookPermissionsManager.unregisterPermissionsRequestLauncher()
super.onDestroy()
}