Egzersiz rotası ekleme

Bu kılavuz, Health Connect 1.1.0-alpha12 veya sonraki sürümlerle uyumludur.

Egzersiz rotaları, kullanıcıların ilgili egzersiz etkinlikleri için bir GPS rotasını takip etmesine ve antrenmanlarının haritalarını diğer uygulamalarla paylaşmasına olanak tanır.

Health Connect'in kullanılabilirlik durumunu kontrol etme

Uygulamanız, Health Connect'i kullanmaya çalışmadan önce Health Connect'in kullanıcının cihazında kullanılabilir olduğunu doğrulamalıdır. Health Connect, tüm cihazlara önceden yüklenmemiş veya devre dışı bırakılmış olabilir. HealthConnectClient.getSdkStatus() yöntemini kullanarak kullanılabilirlik durumunu kontrol edebilirsiniz.

Health Connect'in kullanılabilirliğini kontrol etme

fun checkHealthConnectAvailability(context: Context) {
    val providerPackageName = "com.google.android.apps.healthdata"
    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
}

getSdkStatus() tarafından döndürülen duruma bağlı olarak, kullanıcıyı gerekirse Google Play Store'dan Health Connect'i yüklemeye veya güncellemeye yönlendirebilirsiniz.

Bu kılavuzda, kullanıcıdan nasıl izin isteyeceğinizle ilgili bilgiler verilmekte ve uygulamaların, egzersiz oturumu kapsamında rota verilerini yazma iznini nasıl alacağı açıklanmaktadır.

Egzersiz rotaları için okuma ve yazma işlevi şunları içerir:

  1. Uygulamalar, egzersiz rotaları için yeni bir yazma izni oluşturur.
  2. Ekleme işlemi, alanı rota olan bir egzersiz oturumu yazılarak gerçekleştirilir.
  3. Okuma:
    1. Oturum sahibi için verilere oturum okuma kullanılarak erişilir.
    2. Üçüncü taraf uygulamasından, kullanıcının bir rotayı tek seferlik okumasına izin veren bir iletişim kutusu aracılığıyla.

Kullanıcının yazma izni yoksa ve rota ayarlanmamışsa rota güncellenmez.

Uygulamanızın rota yazma izni varsa ve rota içermeyen bir oturum nesnesi ileterek oturumu güncellemeye çalışırsa mevcut rota silinir.

Özelliğin kullanılabilirliği

Bir kullanıcının cihazının Health Connect'te planlı egzersizi destekleyip desteklemediğini belirlemek için istemcide FEATURE_PLANNED_EXERCISE özelliğinin kullanılabilirliğini kontrol edin:

if (healthConnectClient
     .features
     .getFeatureStatus(
       HealthConnectFeatures.FEATURE_PLANNED_EXERCISE
     ) == HealthConnectFeatures.FEATURE_STATUS_AVAILABLE) {

  // Feature is available
} else {
  // Feature isn't available
}
Daha fazla bilgi edinmek için Özellik kullanılabilirliğini kontrol etme bölümünü inceleyin.

Gerekli izinler

Egzersiz rotasına erişim aşağıdaki izinlerle korunur:

  • android.permission.health.READ_EXERCISE_ROUTES
  • android.permission.health.WRITE_EXERCISE_ROUTE
Not: Bu izin türü için READ_EXERCISE_ROUTES çoğul, WRITE_EXERCISE_ROUTE ise tekildir.

Uygulamanıza egzersiz rotası özelliği eklemek için ExerciseSession veri türüyle ilgili izinleri isteyerek başlayın.

Egzersiz rotası yazabilmek için beyan etmeniz gereken izin şudur:

<application>
  <uses-permission
android:name="android.permission.health.WRITE_EXERCISE_ROUTE" />
...
</application>

Egzersiz rotasını okumak için aşağıdaki izinleri istemeniz gerekir:

<application>
  <uses-permission
android:name="android.permission.health.READ_EXERCISE_ROUTES" />
...
</application>

Her rota bir egzersiz oturumuyla (bir oturum = bir antrenman) ilişkilendirildiğinden egzersiz izni de beyan etmeniz gerekir.

İzin istemek için uygulamanızı Health Connect'e ilk kez bağladığınızda PermissionController.createRequestPermissionResultContract() yöntemini kullanın. İsteyebileceğiniz izinlerden bazıları şunlardır:

  • Rota verileri de dahil olmak üzere sağlık ve fitness verilerini okuma: HealthPermission.getReadPermission(ExerciseSessionRecord::class)
  • Rota verileri de dahil olmak üzere sağlık ve fitness verilerini yazma: HealthPermission.getWritePermission(ExerciseSessionRecord::class)
  • Egzersiz rotası verilerini yazma: HealthPermission.PERMISSION_WRITE_EXERCISE_ROUTE

Kullanıcıdan izin isteme

Uygulamanız, istemci örneği oluşturduktan sonra kullanıcıdan izin istemelidir. Kullanıcıların diledikleri zaman izin vermelerine veya izinleri reddetmelerine olanak tanınmalıdır. Bunun için gerekli veri türlerine yönelik bir izin grubu oluşturun. Öncelikle, kümedeki izinlerin Android manifest dosyanızda beyan edildiğinden emin olun.

val permissions =
    setOf(
        HealthPermission.getReadPermission(ExerciseSessionRecord::class),
        HealthPermission.getWritePermission(ExerciseSessionRecord::class)
    )
Uygulamanızın gerekli izinlere sahip olup olmadığını görmek için getGrantedPermissions yöntemini kullanın. Aksi takdirde bu izinleri istemek için createRequestPermissionResultContract yöntemini kullanın. Health Connect izinleri ekranı açılır.
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.") }
    }
}
Kullanıcılar istedikleri zaman izin verebildikleri veya izinleri iptal edebildikleri için uygulamanızın, izinleri kullanmadan önce her seferinde kontrol etmesi ve iznin kaybedildiği senaryoları yönetmesi gerekir.

Egzersiz seansı kaydında yer alan bilgiler

Her egzersiz seansı kaydı aşağıdaki bilgileri içerir:

  • Egzersiz türü (ör. bisiklet sürme).
  • Enlem, boylam ve rakım gibi bilgileri içeren egzersiz rotası.

Desteklenen toplamalar

ExerciseSessionRecord için aşağıdaki toplu değerler kullanılabilir:

Örnek kullanım

Aşağıdaki kod snippet'lerinde, egzersiz rotasının nasıl okunacağı ve yazılacağı gösterilmektedir.

Egzersiz rotasını okuma

Uygulamanız arka planda çalışırken diğer uygulamalar tarafından oluşturulan egzersiz rotası verilerini okuyamaz.

Uygulamanız arka planda çalışırken başka bir uygulama tarafından oluşturulan egzersiz rotasını okumaya çalıştığında, uygulamanızın egzersiz rotası verilerine Her zaman izin verilir erişimi olsa bile Health Connect, ExerciseRouteResult.ConsentRequired yanıtı döndürür.

Bu nedenle, kullanıcının uygulamanızın kullanıcı arayüzüyle aktif olarak etkileşimde bulunduğu durumlarda, uygulamanızla bilinçli kullanıcı etkileşimi üzerine rota istemenizi önemle tavsiye ederiz.

Arka planda okuma hakkında daha fazla bilgi edinmek için Arka planda okuma örneği başlıklı makaleyi inceleyin.

Aşağıdaki kod snippet'inde, Health Connect'te bir oturumun nasıl okunacağı ve bu oturumdan nasıl rota isteneceği gösterilmektedir:

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

Egzersiz rotası yazma

Aşağıdaki kodda, egzersiz rotası içeren bir oturumun nasıl kaydedileceği gösterilmektedir:

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.activelyRecorded(
            device = Device(type = Device.TYPE_PHONE)
        )
    )

    // 4. Insert into Health Connect
    client.insertRecords(listOf(exerciseSessionRecord))
}

Egzersiz seansları

Egzersiz seansları, koşudan badmintona kadar her şeyi içerebilir.

Egzersiz seanslarını yazma

Oturum içeren bir ekleme isteği şu şekilde oluşturulur:

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

Egzersiz seansını okuma

Egzersiz seansını okumayla ilgili bir örnek:

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
    }
}

Alt tür verilerini yazma

Oturumlar, oturumu ek bilgilerle zenginleştiren isteğe bağlı alt tür verilerinden de oluşabilir.

Örneğin, egzersiz seansları ExerciseSegment, ExerciseLap ve ExerciseRoute sınıflarını içerebilir:

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

Egzersiz oturumunu silme

Egzersiz oturumunu silmenin iki yolu vardır:

  1. Zaman aralığına göre silme
  2. UID'ye göre.

Alt tür verilerini zaman aralığına göre silmek için aşağıdaki adımları uygulayın:

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

Alt tür verilerini UID'ye göre de silebilirsiniz. Bu işlem yalnızca egzersiz oturumunu siler, ilişkili verileri silmez:

suspend fun deleteExerciseSessionByUid(
    healthConnectClient: HealthConnectClient,
    exerciseRecord: ExerciseSessionRecord,
) {
    healthConnectClient.deleteRecords(
        ExerciseSessionRecord::class,
        recordIdsList = listOf(exerciseRecord.metadata.id),
        clientRecordIdsList = emptyList()
    )
}