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>?
)