Questa guida è compatibile con la versione 1.1.0-alpha12 di Connessione Salute.
I percorsi di allenamento consentono agli utenti di monitorare un percorso GPS per le attività di allenamento associate e condividere le mappe dei loro allenamenti con altre app.
Questa guida fornisce informazioni su come richiedere le autorizzazioni all'utente e descrive anche come le app ricevono l'autorizzazione a scrivere dati di itinerario nell'ambito di una sessione di allenamento.
La funzionalità di lettura e scrittura per i percorsi di allenamento include:
- Le app creano una nuova autorizzazione di 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, i dati vengono accessibili 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 la route non è impostata, la route non viene aggiornata.
Se la tua app dispone di un'autorizzazione di scrittura delle corse e tenta di aggiornare una sessione passando un oggetto sessione senza una corsa, la corsa esistente viene eliminata.
Controllare 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
a installare o aggiornare Connessione Salute dal Google Play Store, se necessario.
Disponibilità della funzionalità
Per determinare se il dispositivo di un utente supporta l'allenamento pianificato su Health Connect, 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 dell'allenamento è protetto dalle seguenti autorizzazioni:
android.permission.health.READ_EXERCISE_ROUTES
android.permission.health.WRITE_EXERCISE_ROUTE
READ_EXERCISE_ROUTES
è al plurale, mentre
WRITE_EXERCISE_ROUTE
è al singolare.
Per aggiungere la funzionalità di percorso dell'allenamento alla tua app, inizia richiedendo le autorizzazioni di scrittura per il tipo di dati ExerciseSession
.
Ecco l'autorizzazione che devi dichiarare per poter scrivere il percorso dell'allenamento:
<application>
<uses-permission
android:name="android.permission.health.WRITE_EXERCISE_ROUTE" />
...
</application>
Per leggere il percorso dell'allenamento, devi richiedere le seguenti autorizzazioni:
<application>
<uses-permission
android:name="android.permission.health.READ_EXERCISE_ROUTES" />
...
</application>
Devi anche dichiarare un'autorizzazione per l'esercizio, 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:
- Leggere i dati relativi a salute e attività fisica, inclusi i dati sui percorsi:
HealthPermission.getReadPermission(ExerciseSessionRecord::class)
- Scrivere dati relativi a salute e attività fisica, inclusi i dati del percorso:
HealthPermission.getWritePermission(ExerciseSessionRecord::class)
- Scrittura dei dati del percorso per l'esercizio fisico:
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 insieme di autorizzazioni per i tipi di dati richiesti. Assicurati che le autorizzazioni nell'insieme siano dichiarate prima nel file 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.
- L'itinerario dell'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 per l'esercizio fisico
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 itinerari in seguito a un'interazione deliberata dell'utente con la tua app, quando l'utente è attivamente coinvolto con l'interfaccia utente 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.acc.wang/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.
}
}
Scrivere un percorso per l'esercizio fisico
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.
Scrittura delle 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"
),
// ... other records
)
)
}
Lettura di 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
}
}
Scrivere 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
)
Eliminare 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. In questo modo viene eliminata 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()
)
}