Questa guida è compatibile con la versione 1.1.0-alpha12 di Connessione Salute.
I percorsi di allenamento consentono agli utenti di tracciare un percorso GPS per le attività fisiche associate e di condividere le mappe dei propri allenamenti con altre app.
Controlla la disponibilità di Connessione Salute
Prima di tentare di utilizzare Connessione Salute, la tua app deve verificare che Connessione Salute sia disponibile
sul dispositivo dell'utente. Connessione Salute potrebbe non essere preinstallata su tutti i dispositivi o potrebbe essere disattivata.
Puoi verificare la disponibilità utilizzando il metodo HealthConnectClient.getSdkStatus().
Come verificare la disponibilità di Connessione Salute
fun checkHealthConnectAvailability(context: Context) { val providerPackageName = "com.google.android.apps.healthdata" // Or get from HealthConnectClient.DEFAULT_PROVIDER_PACKAGE_NAME val availabilityStatus = HealthConnectClient.getSdkStatus(context, providerPackageName) if (availabilityStatus == HealthConnectClient.SDK_UNAVAILABLE) { // Health Connect is not available. Guide the user to install/enable it. // For example, show a dialog. return // early return as there is no viable integration } if (availabilityStatus == HealthConnectClient.SDK_UNAVAILABLE_PROVIDER_UPDATE_REQUIRED) { // Health Connect is available but requires an update. // Optionally redirect to package installer to find a provider, for example: val uriString = "market://details?id=$providerPackageName&url=healthconnect%3A%2F%2Fonboarding" context.startActivity( Intent(Intent.ACTION_VIEW).apply { setPackage("com.android.vending") data = Uri.parse(uriString) putExtra("overlay", true) putExtra("callerId", context.packageName) } ) return } // Health Connect is available, obtain a HealthConnectClient instance val healthConnectClient = HealthConnectClient.getOrCreate(context) // Issue operations with healthConnectClient }
A seconda dello stato restituito da getSdkStatus(), puoi guidare l'utente
all'installazione o all'aggiornamento di Connessione Salute dal Google Play Store, se necessario.
Questa guida fornisce informazioni su come richiedere le autorizzazioni all'utente e descrive inoltre come le app ricevono l'autorizzazione a scrivere i dati del percorso come parte di una sessione di allenamento.
La funzionalità di lettura e scrittura per i percorsi di allenamento prevede quanto segue:
- Le app creano una nuova autorizzazione in scrittura per i percorsi di allenamento.
- L'inserimento avviene scrivendo una sessione di allenamento con un percorso come campo.
- Lettura:
- Per il proprietario della sessione, l'accesso ai dati avviene tramite una lettura della sessione.
- Da un'app di terze parti, tramite una finestra di dialogo che consente all'utente di concedere una lettura una tantum di un percorso.
Se l'utente non dispone delle autorizzazioni di scrittura e il percorso non è impostato, il percorso non viene aggiornato.
Se la tua app dispone di un'autorizzazione in scrittura del percorso e tenta di aggiornare una sessione passando un oggetto sessione senza un percorso, il percorso esistente viene eliminato.
Controlla la disponibilità di Connessione Salute
Prima di tentare di utilizzare Connessione Salute, la tua app deve verificare che Connessione Salute sia disponibile
sul dispositivo dell'utente. Connessione Salute potrebbe non essere preinstallata su tutti i dispositivi o potrebbe essere disattivata.
Puoi verificare la disponibilità utilizzando il metodo HealthConnectClient.getSdkStatus().
Come verificare la disponibilità di Connessione Salute
fun checkHealthConnectAvailability(context: Context) { val providerPackageName = "com.google.android.apps.healthdata" // Or get from HealthConnectClient.DEFAULT_PROVIDER_PACKAGE_NAME val availabilityStatus = HealthConnectClient.getSdkStatus(context, providerPackageName) if (availabilityStatus == HealthConnectClient.SDK_UNAVAILABLE) { // Health Connect is not available. Guide the user to install/enable it. // For example, show a dialog. return // early return as there is no viable integration } if (availabilityStatus == HealthConnectClient.SDK_UNAVAILABLE_PROVIDER_UPDATE_REQUIRED) { // Health Connect is available but requires an update. // Optionally redirect to package installer to find a provider, for example: val uriString = "market://details?id=$providerPackageName&url=healthconnect%3A%2F%2Fonboarding" context.startActivity( Intent(Intent.ACTION_VIEW).apply { setPackage("com.android.vending") data = Uri.parse(uriString) putExtra("overlay", true) putExtra("callerId", context.packageName) } ) return } // Health Connect is available, obtain a HealthConnectClient instance val healthConnectClient = HealthConnectClient.getOrCreate(context) // Issue operations with healthConnectClient }
A seconda dello stato restituito da getSdkStatus(), puoi guidare l'utente
all'installazione o all'aggiornamento di Connessione Salute dal Google Play Store, se necessario.
Disponibilità della funzionalità
Per determinare se il dispositivo di un utente supporta l'allenamento pianificato su Connessione Salute, controlla la disponibilità diFEATURE_PLANNED_EXERCISE sul client:
if (healthConnectClient
.features
.getFeatureStatus(
HealthConnectFeatures.FEATURE_PLANNED_EXERCISE
) == HealthConnectFeatures.FEATURE_STATUS_AVAILABLE) {
// Feature is available
} else {
// Feature isn't available
}
Autorizzazioni richieste
L'accesso al percorso di allenamento è protetto dalle seguenti autorizzazioni:
android.permission.health.READ_EXERCISE_ROUTESandroid.permission.health.WRITE_EXERCISE_ROUTE
READ_EXERCISE_ROUTES è plurale, mentre
WRITE_EXERCISE_ROUTE è singolare.
Per aggiungere la funzionalità di percorso di allenamento alla tua app, inizia richiedendo
le autorizzazioni per il tipo di dati ExerciseSession.
Ecco l'autorizzazione che devi dichiarare per poter scrivere il percorso di allenamento:
<application>
<uses-permission
android:name="android.permission.health.WRITE_EXERCISE_ROUTE" />
...
</application>
Per leggere il percorso di allenamento, devi richiedere le seguenti autorizzazioni:
<application>
<uses-permission
android:name="android.permission.health.READ_EXERCISE_ROUTES" />
...
</application>
Devi anche dichiarare un'autorizzazione di allenamento, poiché ogni percorso è associato a una sessione di allenamento (una sessione = un allenamento).
Per richiedere le autorizzazioni, utilizza il
metodo PermissionController.createRequestPermissionResultContract() quando
colleghi per la prima volta la tua app a Connessione Salute. Alcune autorizzazioni che potresti
voler richiedere sono:
- Lettura di dati relativi a salute e attività fisica, inclusi i dati del percorso:
HealthPermission.getReadPermission(ExerciseSessionRecord::class) - Scrittura di dati relativi a salute e attività fisica, inclusi i dati del percorso:
HealthPermission.getWritePermission(ExerciseSessionRecord::class) - Scrittura di dati sul percorso di allenamento:
HealthPermission.PERMISSION_WRITE_EXERCISE_ROUTE
Richiedi le autorizzazioni all'utente
Dopo aver creato un'istanza client, la tua app deve richiedere le autorizzazioni all'utente. Gli utenti devono poter concedere o negare le autorizzazioni in qualsiasi momento.
A questo scopo, crea un set di autorizzazioni per i tipi di dati richiesti. Assicurati che le autorizzazioni nel set siano dichiarate prima nel manifest di Android.
// Create a set of permissions for required data types
val PERMISSIONS =
setOf(
HealthPermission.getReadPermission(ExerciseSessionRecord::class),
HealthPermission.getWritePermission(ExerciseSessionRecord::class)
)
Utilizza getGrantedPermissions per verificare se alla tua app sono già state concesse le autorizzazioni richieste. In caso contrario, utilizza
createRequestPermissionResultContract per richiedere
queste autorizzazioni. Viene visualizzata la schermata delle autorizzazioni di Connessione Salute.
// Create the permissions launcher
val requestPermissionActivityContract = PermissionController.createRequestPermissionResultContract()
val requestPermissions = registerForActivityResult(requestPermissionActivityContract) { granted ->
if (granted.containsAll(PERMISSIONS)) {
// Permissions successfully granted
} else {
// Lack of required permissions
}
}
suspend fun checkPermissionsAndRun(healthConnectClient: HealthConnectClient) {
val granted = healthConnectClient.permissionController.getGrantedPermissions()
if (granted.containsAll(PERMISSIONS)) {
// Permissions already granted; proceed with inserting or reading data
} else {
requestPermissions.launch(PERMISSIONS)
}
}
Poiché gli utenti possono concedere o revocare le autorizzazioni in qualsiasi momento, la tua app deve controllare periodicamente le autorizzazioni concesse e gestire gli scenari in cui l'autorizzazione viene revocata.
Informazioni incluse in un record di sessione di allenamento
Ogni record di sessione di allenamento contiene le seguenti informazioni:
- Il tipo di allenamento, ad esempio ciclismo.
- Il percorso di allenamento, che contiene informazioni come latitudine, longitudine e altitudine.
Aggregazioni supportate
Per ExerciseSessionRecord sono disponibili i seguenti valori aggregati:
Esempio di utilizzo
I seguenti snippet di codice mostrano come leggere e scrivere un percorso di allenamento.
Lettura del percorso di allenamento
Quando viene eseguita in background, l'app non può leggere i dati del percorso di allenamento creati da altre app.
Quando la tua app viene eseguita in background e tenta di leggere un percorso di allenamento creato
da un'altra app, Connessione Salute restituisce una risposta ExerciseRouteResult.ConsentRequired, anche se la tua app ha accesso Consenti sempre ai dati
del percorso di allenamento.
Per questo motivo, ti consigliamo vivamente di richiedere i percorsi a seguito di un'interazione intenzionale dell'utente con la tua app, quando l'utente interagisce attivamente con l'interfaccia della tua app.
Per saperne di più sulle letture in background, consulta Esempio di lettura in background.
Il seguente snippet di codice mostra come leggere una sessione in Connessione Salute e richiedere un percorso da quella sessione:
suspend fun readExerciseSessionAndRoute() {
val endTime = Instant.now()
val startTime = endTime.minus(Duration.ofHours(1))
val grantedPermissions =
healthConnectClient.permissionController.getGrantedPermissions()
if (!grantedPermissions.contains(
HealthPermission.getReadPermission(ExerciseSessionRecord::class))) {
// The user doesn't allow the app to read exercise session data.
return
}
val readResponse =
healthConnectClient.readRecords(
ReadRecordsRequest(
ExerciseSessionRecord::class,
TimeRangeFilter.between(startTime, endTime)
)
)
val exerciseRecord = readResponse.records.first()
val recordId = exerciseRecord.metadata.id
// See https://developer.android.com/training/basics/intents/result#launch
// for appropriately handling ActivityResultContract.
val requestExerciseRouteLauncher = fragment.registerForActivityResul
(ExerciseRouteRequestContract()) { exerciseRoute: ExerciseRoute? ->
if (exerciseRoute != null) {
displayExerciseRoute(exerciseRoute)
} else {
// Consent was denied
}
}
val exerciseSessionRecord =
healthConnectClient.readRecord(ExerciseSessionRecord::class, recordId).record
when (val exerciseRouteResult = exerciseSessionRecord.exerciseRouteResult) {
is ExerciseRouteResult.Data ->
displayExerciseRoute(exerciseRouteResult.exerciseRoute)
is ExerciseRouteResult.ConsentRequired ->
requestExerciseRouteLauncher.launch(recordId)
is ExerciseRouteResult.NoData -> Unit // No exercise route to show
else -> Unit
}
}
fun displayExerciseRoute(route: ExerciseRoute?) {
val locations = route.route.orEmpty()
for (location in locations) {
// Handle location.
}
}
Scrivi un percorso di allenamento
Il seguente codice mostra come registrare una sessione che include un percorso di allenamento:
suspend fun InsertExerciseRoute(healthConnectClient: HealthConnectClient) {
val grantedPermissions =
healthConnectClient.permissionController.getGrantedPermissions()
if (!grantedPermissions.contains(
getWritePermission(ExerciseSessionRecord::class))) {
// The user doesn't allow the app to write exercise session data.
return
}
val sessionStartTime = Instant.now()
val sessionDuration = Duration.ofMinutes(20)
val sessionEndTime = sessionStartTime.plus(sessionDuration)
val exerciseRoute =
if (grantedPermissions.contains(PERMISSION_WRITE_EXERCISE_ROUTE)) ExerciseRoute(
listOf(
ExerciseRoute.Location(
// Location times must be on or after the session start time
time = sessionStartTime,
latitude = 6.5483,
longitude = 0.5488,
horizontalAccuracy = Length.meters(2.0),
verticalAccuracy = Length.meters(2.0),
altitude = Length.meters(9.0),
), ExerciseRoute.Location(
// Location times must be before the session end time
time = sessionEndTime.minusSeconds(1),
latitude = 6.4578,
longitude = 0.6577,
horizontalAccuracy = Length.meters(2.0),
verticalAccuracy = Length.meters(2.0),
altitude = Length.meters(9.2),
)
)
)
else
// The user doesn't allow the app to write exercise route data.
null
val exerciseSessionRecord = ExerciseSessionRecord(
startTime = sessionStartTime,
startZoneOffset = ZoneOffset.UTC,
endTime = sessionEndTime,
endZoneOffset = ZoneOffset.UTC,
exerciseType = ExerciseSessionRecord.EXERCISE_TYPE_BIKING,
title = "Morning Bike Ride",
exerciseRoute = exerciseRoute,
metadata = Metadata.manualEntry(
device = Device(type = Device.TYPE_PHONE)
),
)
val response = healthConnectClient.insertRecords(listOf(exerciseSessionRecord))
}
Sessioni di allenamento
Le sessioni di allenamento possono includere qualsiasi attività, dalla corsa al badminton.
Scrivi le sessioni di allenamento
Ecco come creare una richiesta di inserimento che includa una sessione:
suspend fun writeExerciseSession(healthConnectClient: HealthConnectClient) {
healthConnectClient.insertRecords(
listOf(
ExerciseSessionRecord(
startTime = START_TIME,
startZoneOffset = START_ZONE_OFFSET,
endTime = END_TIME,
endZoneOffset = END_ZONE_OFFSET,
exerciseType = ExerciseSessionRecord.ExerciseType.RUNNING,
title = "My Run",
metadata = Metadata.manualEntry()
),
// ... other records
)
)
}
Leggi una sessione di allenamento
Ecco un esempio di come leggere una sessione di allenamento:
suspend fun readExerciseSessions(
healthConnectClient: HealthConnectClient,
startTime: Instant,
endTime: Instant
) {
val response =
healthConnectClient.readRecords(
ReadRecordsRequest(
ExerciseSessionRecord::class,
timeRangeFilter = TimeRangeFilter.between(startTime, endTime)
)
)
for (exerciseRecord in response.records) {
// Process each exercise record
// Optionally pull in with other data sources of the same time range.
val distanceRecord =
healthConnectClient
.readRecords(
ReadRecordsRequest(
DistanceRecord::class,
timeRangeFilter =
TimeRangeFilter.between(
exerciseRecord.startTime,
exerciseRecord.endTime
)
)
)
.records
}
}
Scrivi i dati del sottotipo
Le sessioni possono anche essere composte da dati di sottotipo facoltativi, che arricchiscono la sessione con informazioni aggiuntive.
Ad esempio, le sessioni di allenamento possono includere le classi ExerciseSegment, ExerciseLap
e ExerciseRoute:
val segments = listOf(
ExerciseSegment(
startTime = Instant.parse("2022-01-02T10:10:10Z"),
endTime = Instant.parse("2022-01-02T10:10:13Z"),
segmentType = ActivitySegmentType.BENCH_PRESS,
repetitions = 373
)
)
val laps = listOf(
ExerciseLap(
startTime = Instant.parse("2022-01-02T10:10:10Z"),
endTime = Instant.parse("2022-01-02T10:10:13Z"),
length = 0.meters
)
)
ExerciseSessionRecord(
exerciseType = ExerciseSessionRecord.EXERCISE_TYPE_CALISTHENICS,
startTime = Instant.parse("2022-01-02T10:10:10Z"),
endTime = Instant.parse("2022-01-02T10:10:13Z"),
startZoneOffset = ZoneOffset.UTC,
endZoneOffset = ZoneOffset.UTC,
segments = segments,
laps = laps,
route = route,
metadata = Metadata.manualEntry()
)
Elimina una sessione di allenamento
Esistono due modi per eliminare una sessione di allenamento:
- Per intervallo di tempo.
- Per UID.
Ecco come eliminare i dati dei sottotipi in base all'intervallo di tempo:
suspend fun deleteExerciseSessionByTimeRange(
healthConnectClient: HealthConnectClient,
exerciseRecord: ExerciseSessionRecord,
) {
val timeRangeFilter = TimeRangeFilter.between(exerciseRecord.startTime, exerciseRecord.endTime)
healthConnectClient.deleteRecords(ExerciseSessionRecord::class, timeRangeFilter)
// delete the associated distance record
healthConnectClient.deleteRecords(DistanceRecord::class, timeRangeFilter)
}
Puoi anche eliminare i dati dei sottotipi in base all'UID. Questo consente di eliminare solo la sessione di allenamento, non i dati associati:
suspend fun deleteExerciseSessionByUid(
healthConnectClient: HealthConnectClient,
exerciseRecord: ExerciseSessionRecord,
) {
healthConnectClient.deleteRecords(
ExerciseSessionRecord::class,
recordIdsList = listOf(exerciseRecord.metadata.id),
clientRecordIdsList = emptyList()
)
}