Diese Anleitung ist mit Health Connect-Version 1.1.0-alpha12 kompatibel.
Mit Trainingsrouten können Nutzer eine GPS-Route für zugehörige Trainingsaktivitäten aufzeichnen und Karten ihrer Trainings mit anderen Apps teilen.
Verfügbarkeit von Health Connect prüfen
Bevor Ihre App versucht, Health Connect zu verwenden, sollte sie prüfen, ob Health Connect auf dem Gerät des Nutzers verfügbar ist. Health Connect ist möglicherweise nicht auf allen Geräten vorinstalliert oder wurde deaktiviert.
Sie können die Verfügbarkeit mit der Methode HealthConnectClient.getSdkStatus() prüfen.
So prüfen Sie die Verfügbarkeit von Health Connect
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 }
Je nach dem von getSdkStatus() zurückgegebenen Status können Sie den Nutzer bei Bedarf anweisen, Health Connect im Google Play Store zu installieren oder zu aktualisieren.
In dieser Anleitung erfahren Sie, wie Sie Berechtigungen vom Nutzer anfordern und wie Apps die Berechtigung zum Schreiben von Routendaten im Rahmen einer Trainingseinheit erhalten.
Die Lese- und Schreibfunktionen für Trainingsrouten umfassen:
- Apps erstellen eine neue Schreibberechtigung für Trainingsrouten.
- Das Einfügen erfolgt durch Schreiben einer Trainingseinheit mit einer Route als Feld.
- Lesen:
- Für den Inhaber der Sitzung erfolgt der Zugriff auf Daten über eine Sitzungslesung.
- Über eine Drittanbieter-App über ein Dialogfeld, in dem der Nutzer eine einmalige Lesung einer Route gewähren kann.
Wenn der Nutzer keine Schreibberechtigungen hat und die Route nicht festgelegt ist, wird die Route nicht aktualisiert.
Wenn Ihre App eine Schreibberechtigung für Routen hat und versucht, eine Sitzung zu aktualisieren, indem sie ein Sitzungsobjekt ohne Route übergibt, wird die vorhandene Route gelöscht.
Verfügbarkeit der Funktion
Um herauszufinden, ob das Gerät eines Nutzers geplante Trainings in Health Connect unterstützt, prüfen Sie, obFEATURE_PLANNED_EXERCISE auf dem Client verfügbar ist:
if (healthConnectClient
.features
.getFeatureStatus(
HealthConnectFeatures.FEATURE_PLANNED_EXERCISE
) == HealthConnectFeatures.FEATURE_STATUS_AVAILABLE) {
// Feature is available
} else {
// Feature isn't available
}
Erforderliche Berechtigungen
Der Zugriff auf Trainingsrouten wird durch die folgenden Berechtigungen geschützt:
android.permission.health.READ_EXERCISE_ROUTESandroid.permission.health.WRITE_EXERCISE_ROUTE
READ_EXERCISE_ROUTES im Plural, während WRITE_EXERCISE_ROUTE im Singular steht.
Wenn Sie Ihrer App die Funktion für Trainingsrouten hinzufügen möchten, fordern Sie zuerst Berechtigungen für den Datentyp ExerciseSession an.
Hier ist die Berechtigung, die Sie deklarieren müssen, um Trainingsrouten schreiben zu können:
<application>
<uses-permission
android:name="android.permission.health.WRITE_EXERCISE_ROUTE" />
...
</application>
Zum Lesen von Trainingsrouten müssen Sie die folgenden Berechtigungen anfordern:
<application>
<uses-permission
android:name="android.permission.health.READ_EXERCISE_ROUTES" />
...
</application>
Sie müssen auch eine Trainingsberechtigung deklarieren, da jede Route mit einer Trainingseinheit verknüpft ist (eine Sitzung = ein Training).
Verwenden Sie zum Anfordern von Berechtigungen die Methode PermissionController.createRequestPermissionResultContract(), wenn Sie Ihre App zum ersten Mal mit Health Connect verbinden. Mehrere Berechtigungen, die Sie möglicherweise anfordern möchten:
- Gesundheits- und Fitnessdaten lesen, einschließlich Routendaten:
HealthPermission.getReadPermission(ExerciseSessionRecord::class) - Gesundheits- und Fitnessdaten schreiben, einschließlich Routendaten:
HealthPermission.getWritePermission(ExerciseSessionRecord::class) - Daten zu Trainingsrouten schreiben:
HealthPermission.PERMISSION_WRITE_EXERCISE_ROUTE
Berechtigungen vom Nutzer anfordern
Nachdem Sie eine Clientinstanz erstellt haben, muss Ihre App Berechtigungen vom Nutzer anfordern. Nutzer müssen jederzeit Berechtigungen erteilen oder verweigern können. Erstellen Sie dazu eine Reihe von Berechtigungen für die erforderlichen Datentypen. Achten Sie darauf, dass die Berechtigungen im Set zuerst in Ihrem Android-Manifest deklariert werden.
val permissions = setOf( HealthPermission.getReadPermission(ExerciseSessionRecord::class), HealthPermission.getWritePermission(ExerciseSessionRecord::class) )
getGrantedPermissions
können Sie prüfen, ob Ihrer App bereits die erforderlichen Berechtigungen erteilt wurden. Wenn nicht, fordern Sie diese Berechtigungen mit
createRequestPermissionResultContract
an. Daraufhin wird der Berechtigungsbildschirm von Health Connect angezeigt.
val permissions = setOf( HealthPermission.getReadPermission(StepsRecord::class), HealthPermission.getWritePermission(StepsRecord::class), HealthPermission.getReadPermission(HeartRateRecord::class), HealthPermission.getWritePermission(HeartRateRecord::class) ) val requestPermissionsLauncher = rememberLauncherForActivityResult( contract = PermissionController.createRequestPermissionResultContract() ) { grantedPermissions -> if (grantedPermissions.containsAll(permissions)) { coroutineScope.launch { snackbarHostState.showSnackbar("Permissions granted!") } } else { coroutineScope.launch { snackbarHostState.showSnackbar("Permissions denied.") } } }
Informationen in einem Datensatz für Trainingseinheiten
Jeder Datensatz für Trainingseinheiten enthält die folgenden Informationen:
- Der Typ des Trainings, z. B. Radfahren.
- Die Trainingsroute , die Informationen wie Breitengrad, Längengrad und Höhe enthält.
Unterstützte Aggregationen
Die folgenden aggregierten Werte sind für
ExerciseSessionRecord verfügbar:
Nutzungsbeispiel
Die folgenden Code-Snippets zeigen, wie Sie eine Trainingsroute lesen und schreiben.
Trainingsroute abrufen
Ihre App kann keine Daten zu Trainingsrouten lesen, die von anderen Apps erstellt wurden, wenn sie im Hintergrund ausgeführt wird.
Wenn Ihre App im Hintergrund ausgeführt wird und versucht, eine Trainingsroute zu lesen, die von einer anderen App erstellt wurde, gibt Health Connect die Antwort ExerciseRouteResult.ConsentRequired zurück, auch wenn Ihre App den Zugriff auf Daten zu Trainingsrouten mit der Berechtigung Immer zulassen hat.
Wir empfehlen daher dringend, Routen nur dann anzufordern, wenn der Nutzer bewusst mit Ihrer App interagiert und die Benutzeroberfläche Ihrer App aktiv verwendet.
Weitere Informationen zu Hintergrundlesungen finden Sie im Beispiel für Hintergrundlesungen.
Das folgende Code-Snippet zeigt, wie Sie eine Sitzung in Health Connect lesen und eine Route aus dieser Sitzung anfordern:
private suspend fun readExerciseSessionAndRoute() { val client = healthConnectClient ?: return val endTime = Instant.now() val startTime = endTime.minus(Duration.ofHours(1)) val grantedPermissions = client.permissionController.getGrantedPermissions() // 1. Verify basic Exercise Session permissions if (!grantedPermissions.contains( HealthPermission.getReadPermission(ExerciseSessionRecord::class) ) ) { return } // 2. Read the sessions val readResponse = client.readRecords( ReadRecordsRequest( ExerciseSessionRecord::class, TimeRangeFilter.between(startTime, endTime) ) ) val exerciseRecord = readResponse.records.firstOrNull() ?: return val recordId = exerciseRecord.metadata.id // 3. Read the specific record to check for the route val sessionResponse = client.readRecord(ExerciseSessionRecord::class, recordId) // 4. Handle the Route Result directly from the response when (val routeResult = sessionResponse.record.exerciseRouteResult) { is ExerciseRouteResult.Data -> { displayExerciseRoute(routeResult.exerciseRoute) } is ExerciseRouteResult.ConsentRequired -> { // Since you are in a Service, you cannot launch ActivityResultLauncher. // Send a notification to the user to grant route-specific consent. handleConsentRequired(recordId) } is ExerciseRouteResult.NoData -> Unit else -> Unit } } private fun displayExerciseRoute(route: ExerciseRoute) { val locations = route.route.orEmpty() for (location in locations) { println(location) } }
Trainingsroute schreiben
Der folgende Code zeigt, wie Sie eine Sitzung aufzeichnen, die eine Trainingsroute enthält:
private suspend fun insertExerciseRoute() { val client = healthConnectClient ?: return val grantedPermissions = client.permissionController.getGrantedPermissions() // 1. Verify Session Write Permission val hasWriteSession = grantedPermissions.contains( HealthPermission.getWritePermission(ExerciseSessionRecord::class) ) if (!hasWriteSession) return val sessionStartTime = Instant.now() val sessionDuration = Duration.ofMinutes(20) val sessionEndTime = sessionStartTime.plus(sessionDuration) // 2. Build the route if route-specific write permission is granted val hasWriteRoute = grantedPermissions.contains(HealthPermission.PERMISSION_WRITE_EXERCISE_ROUTE) val exerciseRoute = if (hasWriteRoute) { ExerciseRoute( listOf( ExerciseRoute.Location( 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( 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 { null } // 3. Create the session record 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( device = Device(type = Device.TYPE_PHONE) ) ) // 4. Insert into Health Connect client.insertRecords(listOf(exerciseSessionRecord)) }
Trainingseinheiten
Trainingseinheiten können alles von Laufen bis Badminton umfassen.
Trainingseinheiten schreiben
So erstellen Sie eine Einfügeanfrage, die eine Sitzung enthält:
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
)
)
}
Trainingseinheit lesen
Hier ein Beispiel für das Lesen einer Trainingseinheit:
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
}
}
Untertypdaten schreiben
Sitzungen können auch optionale Untertypdaten enthalten, die die Sitzung mit zusätzlichen Informationen ergänzen.
Trainingseinheiten können beispielsweise die Klassen ExerciseSegment, ExerciseLap und ExerciseRoute enthalten:
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()
)
Trainingseinheit löschen
Es gibt zwei Möglichkeiten, eine Trainingseinheit zu löschen:
- Nach Zeitraum.
- Nach UID.
So löschen Sie Untertypdaten nach Zeitraum:
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) }
Sie können Untertypdaten auch nach UID löschen. Dadurch wird nur die Trainingseinheit gelöscht, nicht die zugehörigen Daten:
suspend fun deleteExerciseSessionByUid( healthConnectClient: HealthConnectClient, exerciseRecord: ExerciseSessionRecord, ) { healthConnectClient.deleteRecords( ExerciseSessionRecord::class, recordIdsList = listOf(exerciseRecord.metadata.id), clientRecordIdsList = emptyList() ) }