HealthDataStore is a proxy that provides a way to access the data stored in the Samsung Health app.
An app can access the data through the operations provided by HealthDataStore, such as reading, aggregating, inserting, updating and deleting data.
Use the HealthDataService.getStore method to retrieve an instance from HealthDataStore.
The below examples show how apps can retrieve appropriate permissions and read data in two styles of programming. All requests are processed immediately after moving to a dedicated thread pool, so the caller can invoke the method on any thread. However, of the various manners of waiting for the result of AsyncCompletableFuture
or AsyncSingleFuture
, do not use the sync manner on the main thread.
Synchronous request
The below sample code uses suspend functions, showing how to retrieve a step count for a defined time period.
Be careful not to use the synchronous request on the main thread.
@Throws(InterruptedException::class)
suspend fun showSteps(date: LocalDate, context: Context) {
val store = HealthDataService.getStore(context)
val requiredPermissions: MutableSet<Permission> = HashSet(
listOf(Permission.of(DataTypes.STEPS, AccessType.READ))
)
// This checks if the app has the user's permission for the target data types.
if (areAllPermissionsObtained(store, requiredPermissions)) {
val stepsResponse = getAggregateResult(store, date)
stepsResponse.dataList.forEach {
val steps = it.value
// This processes the value.
}
}
}
@Throws(InterruptedException::class)
private suspend fun areAllPermissionsObtained(
store: HealthDataStore,
permissions: Set<Permission>
): Boolean {
try {
// An API call to check the granted permissions.
val initialResult: Set<Permission> = store.getGrantedPermissions(permissions)
if (!initialResult.containsAll(permissions)) {
val requiredPermissions =
permissions.toMutableSet().apply { removeAll(initialResult) }
// An API call for a permission request.
val obtainedResult = store.requestPermissions(requiredPermissions, this.context)
if (obtainedResult.containsAll(requiredPermissions)) {
return true
}
} else {
return true
}
} catch (error: HealthDataException) {
if (error is ResolvablePlatformException && error.hasResolution) {
Log.i(TAG, error.toString())
error.resolve(this.context)
// An exception to indicate that the Samsung Health app is not ready to serve the specified operation.
} else if (error is AuthorizationException) {
Log.i(TAG, error.toString())
// An exception to indicate that the app is not authorized to perform the specified operation.
} else if (error is InvalidRequestException) {
Log.i(TAG, error.toString())
// An exception to indicate that the app requests the specified operation with invalid conditions.
} else if (error is PlatformInternalException) {
Log.i(TAG, error.toString())
// An exception to indicate that the Samsung Health app experienced an internal error
// that cannot be resolved by the app.
}
}
return false
}
private suspend fun getAggregateResult(
store: HealthDataStore,
date: LocalDate
): DataResponse<AggregatedData<Long>> {
val stepsRequest: AggregateRequest<Long> =
DataType.StepsType.TOTAL.requestBuilder.setLocalTimeFilterWithGroup(
LocalTimeFilter.of(date.atStartOfDay(), date.plusDays(1).atStartOfDay()),
LocalTimeGroup.of(LocalTimeGroupUnit.MINUTELY, 30)
).build()
// An API call for an aggregate request.
return store.aggregateData(stepsRequest)
}
Asynchronous request
This manner returns `AsyncCompletableFuture` or `AsyncSingleFuture`.
The actual requests are processed immediately after moving to a dedicated thread pool,
so the method can be invoked on any thread.
To get a query result, processing `Futures` using sync manner on the main thread is not recommended.
@Throws(InterruptedException::class)
fun showSteps(date: LocalDate, context: Activity) {
Log.i(TAG, "showSteps Async")
this.context = context
val store = HealthDataService.getStore(context)
val requiredPermissions: MutableSet<Permission> = HashSet(
listOf(Permission.of(DataTypes.STEPS, AccessType.READ))
)
// This checks the user's permissions for the target data types.
runOnAllPermissionsObtained(store, requiredPermissions) {
val stepsResponse = getAggregateResult(store, date)
stepsResponse.dataList.forEach {
val steps = it.value
// This processes the value.
Log.i(TAG, "step count: $steps")
}
}
}
private fun runOnAllPermissionsObtained(
store: HealthDataStore,
permissions: Set<Permission>,
runnable: Runnable
) {
// An asynchronous API call to check if the correct permissions are granted.
store.getGrantedPermissionsAsync(permissions)
.setCallback(
Looper.getMainLooper(),
Consumer { initialResult: Set<Permission> ->
if (!initialResult.containsAll(permissions)) {
val requiredPermissions =
permissions.toMutableSet().apply { removeAll(initialResult) }
// An asynchronous API call for a permission request.
store.requestPermissionsAsync(permissions, context)
.setCallback(Looper.getMainLooper(),
Consumer { obtainedResult ->
if (!obtainedResult.containsAll(requiredPermissions)) {
return@Consumer
}
runnable.run()
}
) { error ->
if (error is ResolvablePlatformException && error.hasResolution) {
error.resolve(context)
}
}
} else {
runnable.run();
}
}
) { error ->
if (error is ResolvablePlatformException && error.hasResolution) {
error.resolve(context)
// An exception to indicate that the Samsung Health app is not ready to serve the specified operation.
} else if (error is AuthorizationException) {
// An exception to indicate that the app is not authorized to perform given operation.
} else if (error is InvalidRequestException) {
// An exception to indicate that the app requests the given operation with invalid conditions.
} else if (error is PlatformInternalException) {
// An exception to indicate that the Samsung Health app experienced an internal error
// that cannot be resolved by the app.
}
}
}
private fun getAggregateResult(
store: HealthDataStore,
date: LocalDate
): DataResponse<AggregatedData<Long>> {
val stepsRequest =
DataType.StepsType.TOTAL.requestBuilder.setLocalTimeFilterWithGroup(
LocalTimeFilter.of(date.atStartOfDay(), date.plusDays(1).atStartOfDay()),
LocalTimeGroup.of(LocalTimeGroupUnit.MINUTELY, 30)
).build()
// An asynchronous API call for an aggregate request.
return store.aggregateDataAsync(stepsRequest).get()
}
Since
1.0.0