Search code examples
androidkotlinpermissionshealthconnect

How to architecturally implement the HealthConnect Client inside a helperclass?


Im trying to implement the HealthConnect utilities inside a helperclass that handles the object usage, permission handling and availability of Health Connect on the users phone, however I have problems to find the right architecture that handles all of this in an elegant way. Here is the Code of the helperclass, HealthConnectManager.kt :


import android.app.Activity
import android.content.Context
import android.content.Intent
import android.net.Uri
import android.widget.Toast
import androidx.activity.result.contract.ActivityResultContract
import androidx.core.content.ContextCompat.startActivity
import androidx.health.connect.client.HealthConnectClient
import androidx.health.connect.client.PermissionController
import androidx.health.connect.client.permission.HealthPermission
import androidx.health.connect.client.records.HeartRateRecord
import androidx.health.connect.client.records.StepsRecord

class HealthConnectManager(val context: Context) {
    private val healthConnectClient by lazy { HealthConnectClient.getOrCreate(context) }

    // build a set of permissions for required data types
    val PERMISSIONS =
        setOf(
            HealthPermission.getReadPermission(HeartRateRecord::class),
            HealthPermission.getWritePermission(HeartRateRecord::class),
            HealthPermission.getReadPermission(StepsRecord::class),
            HealthPermission.getWritePermission(StepsRecord::class)
        )

    init {
        checkAvailability()
    }

  private  fun checkAvailability() {
        if (HealthConnectClient.isProviderAvailable(context)) {
            // Health Connect is available and installed.
            healthConnectClient = HealthConnectClient.getOrCreate(context)
        } else {
            Toast.makeText(
                context, "Health Connect is not available", Toast.LENGTH_SHORT
            ).show()
            val uri = Uri.parse("market://details?id=com.google.android.apps.healthdata")
            val gpIntent = Intent(Intent.ACTION_VIEW, uri)
            context.startActivity(gpIntent)
        }
    }

    suspend fun hasAllPermissions(permissions: Set<HealthPermission>): Boolean {
        return permissions == hcClient.permissionController.getGrantedPermissions(
            permissions
        )
    }

    fun requestPermissionsActivityContract(): ActivityResultContract<Set<HealthPermission>, Set<HealthPermission>> {
        return PermissionController.createRequestPermissionResultContract()
    }

}

My plan is to make an HealthConnectManagerObject in the places where I need Healthconnect, for example in the Mainactivity:

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        val hcManager = HealthConnectManager(this)

    }

}

Now I have several Problems. How should I instantiate HealthConnectClient, only if HealthConnect is installed (see the function checkAvailability) In my code, I instantiate it twice, once as a class member and once in said function. Another problem I cant really solve is Permission handling. The functions requestPermissionsActivityContract() and hasAllPermissions dont work in this helper class, partly because the syntax isnt 100% correct yet (its from the documentation) and partly because it needs to be called in an Activity, which I dont have in this Class. Should I give this Class in the Constructor an Activity additionaly to the context?

I tried to call the hasAllPermissions and requestPermissionsActivityContract() on the context which is given to this class, however it needs an Activity. Im not sure how to implement that. The Problem is that there isnt a lot of references on health connect, because its still in beta (The documentation isnt really helpful in this case)

If you have a better suggestion how to implement this I would gladly take it.

Thank you in advance


Solution

  • You can always instantiate your manager-instance regardless if the health connect app is installed on your user's device. The HC SDK functions separately from the inner-workings of the application itself. The instance of the HC SDK that resides within your app primarily communicates with the HC App via IPC. Theoretically, you can have a HealthConnectClient variable declared anywhere, you just need to make sure that HC is available via checkAvailability before calling any of its other method calls. If HC is unavailable, handle those cases gracefully and reflect the state on user's UI.

    As far as architecture goes, make your HealthConnectManager singleton. Take advantage of dependency injection libraries like Hilt&Dagger for Android : https://developer.android.com/training/dependency-injection/hilt-android