This guide covers adding the Sonar Android SDK to your Kotlin app. The SDK supports two data sources through a single interface: Health Connect (Google's unified health API, available on all Android devices) and Samsung Health (Samsung devices only).

Requirements

Android API 28+ (minSdk 28)
Kotlin 1.8+
Sonar account With an API key (sign up here)
Health Connect Health Connect app installed on the device
Samsung Health Approval via Sonar support (Samsung devices only)

Two Data Sources, One SDK

Both sources use the same SDK, the same initialization, and the same data methods. The differences are in setup and available metrics.

Health Connect Samsung Health
Availability All Android devices with Health Connect app Samsung devices with Samsung Health app
Approval Google Play Health Connect API review Contact Sonar support
Metric coverage ~30 metrics (broadest Android coverage) ~25 metrics
Unique data VO2 max, mindfulness, respiratory rate Skin temperature

Install the SDK

Add the dependency to your module-level build.gradle.kts:

kotlin
dependencies {
    implementation("co.sonar:sonar-android:1.+")
}

Sync your project with Gradle files.

Configure Permissions

Health Connect

1. Declare Permissions

Add the data types you need to your AndroidManifest.xml:

xml
<uses-permission android:name="android.permission.health.READ_STEPS" />
<uses-permission android:name="android.permission.health.READ_HEART_RATE" />
<uses-permission android:name="android.permission.health.READ_SLEEP" />
<uses-permission android:name="android.permission.health.READ_EXERCISE" />
<uses-permission android:name="android.permission.health.READ_DISTANCE" />
<uses-permission android:name="android.permission.health.READ_TOTAL_CALORIES_BURNED" />
<uses-permission android:name="android.permission.health.READ_ACTIVE_CALORIES_BURNED" />
<uses-permission android:name="android.permission.health.READ_WEIGHT" />
<uses-permission android:name="android.permission.health.READ_HEIGHT" />

2. Add Privacy Policy Activity

Health Connect requires your app to declare a privacy policy activity:

xml
<activity
    android:name=".PrivacyPolicyActivity"
    android:exported="true">
    <intent-filter>
        <action android:name="androidx.health.ACTION_SHOW_PERMISSIONS_RATIONALE" />
    </intent-filter>
</activity>

3. Apply for Production Access

Samsung Health

Samsung Health requires a separate approval process:

  1. Contact Sonar support to submit a Samsung Health access request
  2. Once approved, Samsung Health data is available through the same SDK interface
  3. No additional manifest changes are needed

Integration Lifecycle

The SDK follows the same lifecycle as the iOS SDK: initialize, connect, sync, disconnect.

Initialize(token)Connect(HC / Samsung)BackgroundSyncDisconnect Initialize(token)Connect(HC / Samsung)BackgroundSyncDisconnect

Step 1 — Initialize the SDK

Create the SDK instance in your Activity.onCreate() or Application.onCreate(). Initialization is asynchronous — wait for the callback before using the manager.

kotlin
import co.sonar.sdk.Sonar
import co.sonar.sdk.SonarManager

class MainActivity : AppCompatActivity() {
    private var sonar: SonarManager? = null

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        val token = fetchTokenFromBackend() // Your backend generates this

        Sonar.initialize(
            token = token,
            context = this
        ) { manager, error ->
            error?.let {
                Log.e("Sonar", "Init failed: ${it.message}")
                return@initialize
            }
            sonar = manager
        }
    }
}

Step 2 — Connect a User

Once initialized, connect the user to a health data source. This triggers the system permission dialog.

kotlin
// Health Connect
sonar?.connect(
    source = Source.HEALTH_CONNECT,
    permissions = setOf(),      // Empty = request all available types
    backgroundSync = true,
    context = this
) { success, error ->
    if (success) {
        Log.d("Sonar", "Connected to Health Connect")
    } else {
        Log.e("Sonar", "Failed: ${error?.message}")
    }
}
kotlin
// Samsung Health
sonar?.connect(
    source = Source.SAMSUNG_HEALTH,
    permissions = setOf(),
    backgroundSync = true,
    context = this
) { success, error ->
    if (success) {
        Log.d("Sonar", "Connected to Samsung Health")
    } else {
        Log.e("Sonar", "Failed: ${error?.message}")
    }
}

Step 3 — Validate Connection

Check whether a connection is active before taking actions that depend on it:

kotlin
// Check Health Connect
val hcUserId = sonar?.getUserId(source = Source.HEALTH_CONNECT)

// Check Samsung Health
val shUserId = sonar?.getUserId(source = Source.SAMSUNG_HEALTH)

if (hcUserId != null) {
    Log.d("Sonar", "Health Connect connected: $hcUserId")
}
if (shUserId != null) {
    Log.d("Sonar", "Samsung Health connected: $shUserId")
}

Disconnect

To stop syncing from the client side:

kotlin
// Disconnect Health Connect
sonar?.disconnect(source = Source.HEALTH_CONNECT) { success ->
    Log.d("Sonar", "Disconnected: $success")
}

// Disconnect Samsung Health
sonar?.disconnect(source = Source.SAMSUNG_HEALTH) { success ->
    Log.d("Sonar", "Disconnected: $success")
}

This stops scheduled syncs and removes local connection state. Historical data remains available in the Sonar platform through the REST API.

Full Example

A minimal MainActivity.kt showing the complete Health Connect flow:

kotlin
class MainActivity : AppCompatActivity() {
    private var sonar: SonarManager? = null

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        initialize()
    }

    override fun onResume() {
        super.onResume()
        initialize() // Re-init on every foreground
    }

    private fun initialize() {
        val token = fetchTokenFromBackend()

        Sonar.initialize(token = token, context = this) { manager, error ->
            error?.let {
                Log.e("Sonar", "Init failed: ${it.message}")
                return@initialize
            }
            sonar = manager

            // Check for existing connection
            val userId = manager?.getUserId(source = Source.HEALTH_CONNECT)
            if (userId != null) {
                Log.d("Sonar", "Already connected: $userId")
            } else {
                connect()
            }
        }
    }

    private fun connect() {
        sonar?.connect(
            source = Source.HEALTH_CONNECT,
            permissions = setOf(),
            backgroundSync = true,
            context = this
        ) { success, error ->
            if (success) {
                Log.d("Sonar", "Connected to Health Connect")
            } else {
                Log.e("Sonar", "Connection failed: ${error?.message}")
            }
        }
    }

    private fun fetchTokenFromBackend(): String {
        // Call your backend to generate a mobile token
        // See the Authentication Flow section in the SDK overview
        return "token_from_your_backend"
    }
}

Troubleshooting

Issue Cause Solution
Permission dialog doesn't appear Health Connect app not installed Prompt user to install Health Connect from the Play Store
getUserId() returns null SDK not initialized or connection expired Re-initialize with a fresh token and call connect()
Samsung Health not available Approval pending or app not installed Verify Samsung Health app is installed and Sonar support has approved access
Data appears incomplete User denied some permissions Guide user to Settings > Health Connect to check permissions
Build fails with dependency error minSdk too low Verify minSdk is 28+ in your build.gradle.kts
Google Play rejection Missing Health Connect API approval Apply through the Google Play Console — start early, reviews take weeks

Go Deeper

  • Mobile SDK Overview — Architecture, auth flow, and choosing the right SDK
  • Data Catalog — Full metric reference with Health Connect and Samsung Health availability
  • Data Delivery — Get notified when synced data is ready
  • Authentication — Mobile token lifecycle and API key management