Skip to main content

Overview

The Synheart Wear Kotlin SDK provides native Android support for streaming biometric data from wearables via Health Connect, vendor cloud APIs (WHOOP, Garmin, Fitbit, Oura), and direct BLE heart-rate sensors.

Installation

Gradle (Kotlin DSL)

Published to Maven Central as ai.synheart:synheart-wear:
dependencies {
    implementation("ai.synheart:synheart-wear:0.4.0")
}

Requirements

  • Android SDK API 21+ (target SDK 34+)
  • Kotlin 2.3+
  • Android Gradle Plugin 9.0+ (Gradle 9.2+)
  • Health Connect installed on device (built-in on Android 14+, otherwise via Play Store)

Quick Start

Initialize SDK

import ai.synheart.wear.SynheartWear
import ai.synheart.wear.config.SynheartWearConfig
import ai.synheart.wear.models.DeviceAdapter

val synheartWear = SynheartWear(
    context = this,
    config = SynheartWearConfig(
        enabledAdapters = setOf(DeviceAdapter.HEALTH_CONNECT)
    )
)

Request Permissions & Initialize

lifecycleScope.launch {
    val permissions = setOf(
        PermissionType.HEART_RATE,
        PermissionType.STEPS,
        PermissionType.CALORIES
    )

    val result = synheartWear.requestPermissions(permissions)

    if (result[PermissionType.HEART_RATE] == true) {
        synheartWear.initialize()
        val metrics = synheartWear.readMetrics()
        Log.d(TAG, "HR: ${metrics.getMetric(MetricType.HR)} bpm")
    }
}

Real-Time Streaming

lifecycleScope.launch {
    synheartWear.streamHR(intervalMs = 5000L).collect { metrics ->
        val hr = metrics.getMetric(MetricType.HR)
        Log.d(TAG, "Current HR: $hr bpm")
    }
}

Health Connect

Reads from any wearable companion app that syncs to Jetpack Health Connect — Fitbit, Garmin Connect, Samsung Health, Google Fit, Polar Flow, etc. (Apple Watch is not supported on Android — it only syncs to Apple Health on iOS.)

AndroidManifest.xml

The SDK ships its own AndroidManifest.xml declaring all 17 Health Connect read permissions plus the <queries> block for the Health Connect app, so the consumer app inherits them automatically. If your app needs a privacy-policy intent (required by the Play Store policy for Health Connect apps), add this in your own manifest:
<activity-alias
    android:name="ViewPermissionUsageActivity"
    android:exported="true"
    android:targetActivity=".MainActivity"
    android:permission="android.permission.START_VIEW_PERMISSION_USAGE">
    <intent-filter>
        <action android:name="androidx.health.ACTION_SHOW_PERMISSIONS_RATIONALE" />
    </intent-filter>
</activity-alias>

Supported record types

HealthConnectAdapter reads: HeartRateRecord, HeartRateVariabilityRmssdRecord, RestingHeartRateRecord, StepsRecord, TotalCaloriesBurnedRecord, ActiveCaloriesBurnedRecord, DistanceRecord, ExerciseSessionRecord, SleepSessionRecord, OxygenSaturationRecord, RespiratoryRateRecord, BodyTemperatureRecord, WeightRecord, HeightRecord, BodyFatRecord.

Caveats

  • On devices below Android 14 without the Play Store Health Connect app, the adapter returns an empty snapshot rather than throwing. Check HealthConnectAdapter.getAvailabilityStatus() to detect this case.
  • Per-record retention is platform-controlled (typically ~30 days for HR, longer for sleep). For older data use a cloud OAuth provider.

Cloud providers (WHOOP / Garmin / Fitbit / Oura)

Each cloud vendor is exposed via a typed accessor on SynheartWear plus the generic getProvider() lookup:
val whoop:  WhoopProvider?  = synheartWear.whoop
val garmin: GarminProvider? = synheartWear.garmin
val fitbit: FitbitProvider? = synheartWear.fitbit
val oura:   OuraProvider?   = synheartWear.oura
OAuth is mediated by the Synheart Wear API; the device never sees vendor access tokens. Call provider.connect() to get an authorization URL, open it in a browser, then call provider.connectWithCode(code, state, redirectUri) from your deep-link handler. Full code sample in the SDK README’s Cloud Wearables section.

BLE Heart Rate Monitor

Connect directly to any standard Bluetooth LE heart rate monitor for real-time HR streaming.

Setup

Add to AndroidManifest.xml:
<uses-permission android:name="android.permission.BLUETOOTH_SCAN" android:usesPermissionFlags="neverForLocation" />
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />

Usage

import ai.synheart.wear.SynheartWear
import ai.synheart.wear.config.SynheartWearConfig
import ai.synheart.wear.models.DeviceAdapter

val synheartWear = SynheartWear(
    context = this,
    config = SynheartWearConfig(
        enabledAdapters = setOf(DeviceAdapter.BLE_HRM)
    )
)

val bleHrm = synheartWear.bleHrm ?: return

// Scan for nearby HR monitors
val devices = bleHrm.scan(timeoutMs = 10000, namePrefix = "WHOOP")

// Connect to a device
bleHrm.connect(deviceId = devices.first().deviceId, sessionId = "my-session")

// Collect heart rate samples
bleHrm.heartRateFlow.collect { sample ->
    Log.d(TAG, "BPM: ${sample.bpm}")
    sample.rrIntervalsMs?.let { rr ->
        Log.d(TAG, "RR: $rr")
    }
}

// Disconnect
bleHrm.disconnect()

Supported Devices

Works with any BLE device implementing the standard Heart Rate Profile (0x180D):
  • WHOOP (Broadcast HR mode)
  • Polar H10, OH1
  • Wahoo TICKR
  • Garmin HRM-Pro / HRM-Dual

Garmin Health SDK (Native RTS)

The GarminHealth facade provides native Garmin device integration for scanning, pairing, and real-time streaming using generic SDK-owned types.
Important: The Garmin Health SDK Real-Time Streaming (RTS) capability requires a separate license from Garmin. This facade is available on demand for licensed integrations. The underlying Garmin Health SDK code is proprietary to Garmin and is not distributed as open source.
import ai.synheart.wear.adapters.GarminHealth

val garmin = GarminHealth(licenseKey = "your-garmin-sdk-key")
garmin.initialize()

// Scan for devices
garmin.scannedDevicesFlow.collect { devices ->
    println("Found ${devices.size} devices")
}

// Pair and read metrics
val paired = garmin.pairDevice(scannedDevice)
val metrics = garmin.readMetrics()
For cloud-based Garmin data (OAuth + webhooks), use GarminProvider instead.

Resources

For comprehensive documentation, see the full README on GitHub.