Spike SDK Usage

Step 1 - Create Spike connection

To set up the Spike SDK create SpikeConnectionV3 instance with your Spike application id, application user id and signature unique to each of your apllication users (more on generating signatures here):

// Replace with your API credentials provided exclusively to you
private const val APP_ID = 1000

val spikeConnection = SpikeConnectionAPIv3.createConnection(
    applicationId = APP_ID,
    signature = "xxxxx",
    endUserId = "user-id",
    context = context
)

Step 2 - Ask user for permissions

If you want to read data from Android Health Connect, you have to ensure user gives your app permissions. First, you have to check if Health Connect is available on users phone:

val hcAvailability = spikeConnection.checkHealthConnectAvailability()

where

public enum class HealthConnectAvailabilityStatus(public val value: String) {
    /**
     * The Health Connect SDK is unavailable on this device at the time. This can be due to the
     * device running a lower than required Android Version. Apps should hide any integration
     * points to Health Connect in this case.
     */
    NOT_INSTALLED("NOT_INSTALLED"),

    /**
     * The Health Connect SDK APIs are currently unavailable, the provider is either not installed
     * or needs to be updated. Apps may choose to redirect to package installers to find a suitable
     * APK.
     */
    UPDATE_REQUIRED("UPDATE_REQUIRED"),

    /**
     * The Health Connect SDK APIs are available.
     */
    INSTALLED("INSTALLED"),
}

If update is required, you can use Spike helper to open Play Store for user to install Health Connect:

spikeConnection.openHealthConnectInstallation()

If Health Connect is installed, you can get permissions that are needed and a list of already provided permissions:

// HC integration has to be enabled in SpikeSDK connection before 
// using further methods for reading data or managing permissions:
spikeConnection.enableHealthConnectIntegration()

val permissionManager = spikeConnection.getHealthConnectPermissionManager()
val requiredPermissions = permissionManager.getPermissionsForStatistics(
    statisticsTypes = setOf(StatisticsType.STEPS, StatisticsType.DISTANCE_TOTAL)
)

val grantedPermissions = permissionManager.getGrantedPermissions()

If you have missing permissions you can ask Android to present user with a modal asking user for permission to read the data. Example for Compose:

val permissionLauncher = rememberLauncherForActivityResult(
    permissionManager.getRequestPermissionResultContract(),
    onResult = {
        
    }
)
  • Please note that users might only grant partial permissions. In such cases, it’s up to you to decide whether your app can function effectively with limited access. The SpikeSDK itself will still operate even without full permissions; however, it may result in no data being returned in certain scenarios. Conversely, if your app has been granted additional permissions beyond the minimum required for specific data types, we may enhance certain entries by incorporating data sourced from other types (e.g., identifying manually entered data).

You can now use StatisticsFilter(providers = listOf(Provider.HEALTH_CONNECT)) to specifically retrieve data from Health Connect. Alternatively, you can omit the providers parameter entirely and allow Spike to choose the most suitable data source based on your request.

Step 3 - Get data

Info: The maximum permitted date range is 90 days

There are two types of data you can retrieve from Spike:

  • Records consist of the raw data points collected from user devices or applications.
  • Statistics, on the other hand, are calculated values derived from records.

Statistics

Get daily statistics for steps and total distance from health Connect:

val dailyStatistics = spikeConnection.getStatistics(
    types = setOf(StatisticsType.STEPS, StatisticsType.DISTANCE_TOTAL),
    from = LocalDate.now().minusWeeks(1).atStartOfDay(ZoneId.systemDefault()).toInstant(),
    to = Instant.now(),
    interval = StatisticsInterval.DAY,
    filter = StatisticsFilter(providers = listOf(Provider.HEALTH_CONNECT))
)

where:

enum class StatisticsType(public val value: String) {
    STEPS("steps"),
    DISTANCE_TOTAL("distance_total"),
    DISTANCE_WALKING("distance_walking"),
    DISTANCE_CYCLING("distance_cycling"),
    DISTANCE_RUNNING("distance_running"),
    CALORIES_BURNED_TOTAL("calories_burned_total"),
    CALORIES_BURNED_BASAL("calories_burned_basal"),
    CALORIES_BURNED_ACTIVE("calories_burned_active")
}

// Interval
StatisticsInterval.HOUR
StatisticsInterval.DAY

public data class StatisticsFilter(
    val excludeManual: Boolean = false,
    val providers: List<Provider>? = null
)

// Result:

public data class Statistic(
    val start: Instant,
    val end: Instant,
    val duration: Int,
    val type: StatisticsType,
    val value: Double,
    val unit: com.spikeapi.apiv3.datamodels.Unit,
    val rowCount: Int?,
    val recordIds: List<UUID>?
)

Records

Get all records we have from Garmin provider:

val records = spikeConnection.getRecords(
    types = setOf(MetricType.STEPS_TOTAL, MetricType.CALORIES_BURNED_TOTAL),
    from = LocalDate.now().minusWeeks(1).atStartOfDay(ZoneId.systemDefault()).toInstant(),
    to = Instant.now(),
    filter = StatisticsFilter(providers = listOf(Provider.GARMIN))
)

where

public enum class MetricType(public val value: String) {
    CALORIES_BURNED_ACTIVE("calories_burned_active"),
    CALORIES_BURNED_BASAL("calories_burned_basal"),
    CALORIES_BURNED("calories_burned"),
    CALORIES_INTAKE("calories_intake"),
    STEPS_TOTAL("steps"),
    DISTANCE_TOTAL("distance"),
    DISTANCE_WALKING("distance_walking"),
    DISTANCE_CYCLING("distance_cycling"),
    DISTANCE_RUNNING("distance_running"),
    DISTANCE_WHEELCHAIR("distance_wheelchair"),
    DISTANCE_SWIMMING("distance_swimming"),
}


// Result: 

public data class Record(
    val recordId: UUID,
    val inputMethod: InputMethod?,
    val startAt: Instant,
    val endAt: Instant?,
    val modifiedAt: Instant,
    val duration: Int?,
    val provider: Provider?,
    val providerSource: ProviderSource?,
    val isSourceAggregated: Boolean?,
    val source: RecordSource?,
    val metrics: Map<String, Double>?,
    val activityTags: List<ActivityTag>?,
    val activityType: ActivityType?,
    val sessions: List<ActivityEntry>?,
    val laps: List<ActivityEntry>?,
    val segments: List<ActivityEntry>?,
    val splits: List<ActivityEntry>?,
    val samples: List<ActivitySamples>?,
    val routePoints: List<ActivitySamples>?
)

Reading data

Statistics

Get daily statistics for steps and total distance from Garmin:

val dailyStatistics = spikeConnection.getStatistics(
    types = setOf(StatisticsType.STEPS, StatisticsType.DISTANCE_TOTAL),
    from = LocalDate.now().minusWeeks(1).atStartOfDay(ZoneId.systemDefault()).toInstant(),
    to = Instant.now(),
    interval = StatisticsInterval.DAY,
    filter = StatisticsFilter(providers = listOf(Provider.GARMIN))
)

where:

// Interval
StatisticsInterval.HOUR
StatisticsInterval.DAY

// Filter
public data class StatisticsFilter(
    val excludeManual: Boolean = false,
    val providers: List<Provider>? = null
)

Records

Get all records we have from Garmin provider:

val records = spikeConnection.getRecords(
    types = setOf(MetricType.STEPS_TOTAL, MetricType.CALORIES_BURNED_TOTAL),
    from = LocalDate.now().minusWeeks(1).atStartOfDay(ZoneId.systemDefault()).toInstant(),
    to = Instant.now(),
    filter = StatisticsFilter(providers = listOf(Provider.GARMIN))
)

where

public enum class MetricType(public val value: String) {
    CALORIES_BURNED_ACTIVE("calories_burned_active"),
    CALORIES_BURNED_BASAL("calories_burned_basal"),
    CALORIES_BURNED("calories_burned"),
    CALORIES_INTAKE("calories_intake"),
    STEPS_TOTAL("steps"),
    DISTANCE_TOTAL("distance"),
    DISTANCE_WALKING("distance_walking"),
    DISTANCE_CYCLING("distance_cycling"),
    DISTANCE_RUNNING("distance_running"),
    DISTANCE_WHEELCHAIR("distance_wheelchair"),
    DISTANCE_SWIMMING("distance_swimming"),
}


// Result: 

public data class Record(
    val recordId: UUID,
    val inputMethod: InputMethod?,
    val startAt: Instant,
    val endAt: Instant?,
    val modifiedAt: Instant,
    val duration: Int?,
    val provider: Provider?,
    val providerSource: ProviderSource?,
    val isSourceAggregated: Boolean?,
    val source: RecordSource?,
    val metrics: Map<String, Double>?,
    val activityTags: List<ActivityTag>?,
    val activityType: ActivityType?,
    val sessions: List<ActivityEntry>?,
    val laps: List<ActivityEntry>?,
    val segments: List<ActivityEntry>?,
    val splits: List<ActivityEntry>?,
    val samples: List<ActivitySamples>?,
    val routePoints: List<ActivitySamples>?
)