Egzersiz rotası ekleme

Bu kılavuz, Health Connect'in 1.1.0-alpha12 sürümüyle uyumludur.

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

Bu kılavuzda, kullanıcıdan nasıl izin isteyeceğiniz ve uygulamaların egzersiz oturumu kapsamında rota verilerini yazma iznini nasıl alacağı hakkında bilgi verilmektedir.

Egzersiz rotaları için okuma ve yazma işlevleri şunlardır:

  1. Uygulamalar, egzersiz rotaları için yeni bir yazma izni oluşturur.
  2. Ekleme işlemi, alanı olarak rotayı içeren bir egzersiz oturumu yazılarak yapılır.
  3. Okuma:
    1. Oturum sahibi için verilere oturum okuma işlemi kullanılarak erişilir.
    2. Üçüncü taraf uygulamasından, kullanıcının 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 bir oturumu rota içermeyen bir oturum nesnesi ileterek güncellemeye çalışırsa mevcut rota silinir.

Health Connect'in kullanılabilirliğini kontrol etme

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

Health Connect'in kullanılabilirliğini kontrol etme

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
}

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

Özellik kullanılabilirliği

Kullanıcının cihazının Health Connect'te planlı egzersizi destekleyip desteklemediğini belirlemek için istemcide FEATURE_PLANNED_EXERCISE simgesinin 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 Özelliklerin kullanılabilirliğini kontrol etme başlıklı makaleyi inceleyin.

Gerekli izinler

Antrenman 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ünde, READ_EXERCISE_ROUTES çoğul, WRITE_EXERCISE_ROUTE ise tekildir.

Uygulamanıza egzersiz rotası özelliği eklemek için öncelikle ExerciseSession veri türü için yazma izni isteyerek başlayın.

Egzersiz rotası yazabilmek için bildirmeniz 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>

Ayrıca, her rota bir egzersiz oturumuyla ilişkilendirildiğinden (bir oturum = bir antrenman) egzersiz izni 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 birkaç izin ş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

İstemci örneği oluşturduktan sonra uygulamanızın kullanıcıdan izin istemesi gerekir. Kullanıcıların izinleri istedikleri zaman vermesine veya reddetmesine izin verilmelidir.

Bunu yapmak için gerekli veri türleri için bir dizi izin oluşturun. Gruptaki izinlerin önce Android manifestinizde beyan edildiğinden emin olun.

// Create a set of permissions for required data types
val PERMISSIONS =
    setOf(
  HealthPermission.getReadPermission(ExerciseSessionRecord::class),
  HealthPermission.getWritePermission(ExerciseSessionRecord::class)
)

Uygulamanıza gerekli izinlerin verilip verilmediğini görmek için getGrantedPermissions aracını kullanın. Aksi takdirde, bu izinleri istemek için createRequestPermissionResultContract simgesini kullanın. Bu işlem, Health Connect izinleri ekranını gösterir.

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

Kullanıcılar izinleri istedikleri zaman verebilir veya iptal edebilir. Bu nedenle, uygulamanızın verilen izinleri düzenli olarak kontrol etmesi ve izinlerin kaybedildiği senaryoları ele alması gerekir.

Egzersiz oturumu kaydında yer alan bilgiler

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

  • Egzersiz türü (ör. bisiklet).
  • Enlem, boylam ve yükseklik 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 okuyamıyor.

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

Bu nedenle, kullanıcının uygulamanızla kasıtlı olarak etkileşimde bulunduğu ve uygulamanızın kullanıcı arayüzüyle aktif olarak ilgilendiği sırada rota isteğinde bulunmanızı ö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:

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

Egzersiz rotası yazma

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

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

Egzersiz seansları

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

Egzersiz oturumlarını yazma

Oturum içeren bir kampanya siparişi isteği oluşturmak için şu adımları uygulayın:

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

Egzersiz oturumunu okuma

Aşağıda, egzersiz oturumunun nasıl okunacağına dair bir örnek verilmiştir:

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, oturuma ek bilgiler ekleyerek zenginleştiren isteğe bağlı alt tür verilerinden de oluşabilir.

Örneğin, egzersiz oturumları 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
)

Egzersiz oturumunu silme

Egzersiz oturumunu silmenin iki yolu vardır:

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

Alt tür verilerini zaman aralığına göre nasıl sileceğiniz aşağıda açıklanmıştır:

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