مسیرهای ورزشی را اضافه کنید

این راهنما با Health Connect نسخه 1.1.0-alpha12 سازگار است.

مسیرهای تمرین به کاربران امکان می دهد مسیر GPS را برای فعالیت های ورزشی مرتبط ردیابی کنند و نقشه های تمرینات خود را با سایر برنامه ها به اشتراک بگذارند.

در دسترس بودن ویژگی

برای تعیین اینکه آیا دستگاه کاربر از مسیرهای تمرینی در Health Connect پشتیبانی می‌کند یا خیر، در دسترس بودن FEATURE_PLANNED_EXERCISE را در مشتری بررسی کنید:

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

  // Feature is available
} else {
  // Feature isn't available
}

برای اطلاعات بیشتر به بررسی در دسترس بودن ویژگی مراجعه کنید.

این راهنما اطلاعاتی را در مورد نحوه درخواست مجوز از کاربر ارائه می‌کند و همچنین نحوه دریافت مجوز برای نوشتن داده‌های مسیر را به عنوان بخشی از جلسه تمرین به برنامه‌ها نشان می‌دهد.

قابلیت خواندن و نوشتن برای مسیرهای تمرینی شامل موارد زیر است:

  1. برنامه ها یک مجوز نوشتن جدید برای مسیرهای تمرین ایجاد می کنند.
  2. درج با نوشتن یک جلسه تمرین با یک مسیر به عنوان میدان آن اتفاق می افتد.
  3. خواندن:
    1. برای صاحب جلسه، داده ها با استفاده از خواندن جلسه قابل دسترسی است.
    2. از یک برنامه شخص ثالث، از طریق یک گفتگو که به کاربر اجازه می دهد یک بار خواندن یک مسیر را بدهد.

اگر کاربر مجوز نوشتن نداشته باشد و مسیر تنظیم نشده باشد، مسیر به‌روزرسانی نمی‌شود.

اگر برنامه شما دارای مجوز نوشتن مسیر باشد و سعی کند یک جلسه را با عبور از یک شی جلسه بدون مسیر به روز کند، مسیر موجود حذف می شود.

مجوزهای مورد نیاز

دسترسی به مسیر تمرین با مجوزهای زیر محافظت می شود:

  • android.permission.health.READ_EXERCISE_ROUTE
  • android.permission.health.WRITE_EXERCISE_ROUTE

برای افزودن قابلیت مسیر تمرین به برنامه خود، با درخواست مجوز نوشتن برای نوع داده ExerciseSession شروع کنید.

در اینجا مجوزی که باید اعلام کنید تا بتوانید مسیر تمرین را بنویسید آمده است:

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

برای خواندن مسیر تمرین، باید مجوزهای زیر را درخواست کنید:

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

شما همچنین باید مجوز ورزش را اعلام کنید، زیرا هر مسیر با یک جلسه تمرین مرتبط است (یک جلسه = یک تمرین).

برای درخواست مجوزها، هنگامی که برای اولین بار برنامه خود را به Health Connect متصل می کنید، از متد PermissionController.createRequestPermissionResultContract() استفاده کنید. چندین مجوز که ممکن است بخواهید درخواست کنید عبارتند از:

  • خواندن داده‌های سلامت، از جمله داده‌های مسیر: HealthPermission.getReadPermission(ExerciseSessionRecord::class)
  • نوشتن داده‌های سلامت، از جمله داده‌های مسیر: HealthPermission.getWritePermission(ExerciseSessionRecord::class)
  • نوشتن اطلاعات مسیر تمرین: HealthPermission.PERMISSION_WRITE_EXERCISE_ROUTE

درخواست مجوز از کاربر

پس از ایجاد یک نمونه مشتری، برنامه شما باید از کاربر مجوز درخواست کند. کاربران باید در هر زمانی اجازه دهند یا رد کنند.

برای انجام این کار، مجموعه ای از مجوزها را برای انواع داده های مورد نیاز ایجاد کنید. مطمئن شوید که ابتدا مجوزهای مجموعه در مانیفست اندروید شما اعلام شده است.

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

از getGrantedPermissions استفاده کنید تا ببینید آیا برنامه شما قبلاً مجوزهای لازم را دارد یا خیر. اگر نه، از createRequestPermissionResultContract برای درخواست آن مجوزها استفاده کنید. این صفحه مجوزهای Health Connect را نشان می دهد.

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

از آنجایی که کاربران می‌توانند در هر زمانی مجوزها را اعطا یا لغو کنند، برنامه شما باید به‌طور دوره‌ای مجوزهای اعطا شده را بررسی کند و سناریوهایی را که در آن مجوز از دست می‌رود رسیدگی کند.

اطلاعات موجود در یک رکورد جلسه تمرین

رکورد هر جلسه تمرین حاوی اطلاعات زیر است:

  • نوع ورزش، به عنوان مثال، دوچرخه سواری.
  • مسیر تمرین که حاوی اطلاعاتی مانند طول جغرافیایی، طول و ارتفاع است.

تجمعات پشتیبانی شده

هیچ تجمع پشتیبانی شده ای برای این نوع داده وجود ندارد.

مثال استفاده

قطعه کد زیر نحوه خواندن و نوشتن یک مسیر تمرین را نشان می دهد.

مسیر تمرین را بخوانید

وقتی برنامه شما در پس‌زمینه اجرا می‌شود، نمی‌تواند داده‌های مسیر تمرین ایجاد شده توسط برنامه‌های دیگر را بخواند.

وقتی برنامه شما در پس‌زمینه اجرا می‌شود و سعی می‌کند مسیر تمرین ایجاد شده توسط برنامه دیگری را بخواند، Health Connect یک پاسخ ExerciseRouteResult.ConsentRequired را برمی‌گرداند، حتی اگر برنامه شما همیشه اجازه دسترسی به داده‌های مسیر تمرین را داشته باشد.

به همین دلیل، اکیداً توصیه می‌کنیم که هنگام تعامل عمدی کاربر با برنامه خود، زمانی که کاربر به طور فعال با رابط کاربری برنامه شما درگیر است، مسیرها را درخواست کنید.

برای کسب اطلاعات بیشتر در مورد خواندن پس‌زمینه، به مثال خواندن پس‌زمینه مراجعه کنید.

قطعه کد زیر نحوه خواندن یک جلسه در Health Connect و درخواست مسیر از آن جلسه را نشان می دهد:

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

یک مسیر تمرین بنویسید

کد زیر نحوه ضبط یک جلسه شامل یک مسیر تمرین را نشان می دهد:

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

جلسات ورزش

جلسات ورزشی می تواند شامل هر چیزی از دویدن گرفته تا بدمینتون باشد.

جلسات تمرین را بنویسید

نحوه ساخت یک درخواست درج شامل یک جلسه به این صورت است:

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

یک جلسه تمرین بخوانید

در اینجا مثالی از نحوه خواندن یک جلسه تمرین آورده شده است:

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

داده های نوع فرعی را بنویسید

جلسات همچنین می توانند از داده های فرعی اختیاری تشکیل شوند که جلسه را با اطلاعات اضافی غنی می کند.

به عنوان مثال، جلسات تمرین می تواند شامل کلاس های ExerciseSegment ، ExerciseLap و 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
)

یک جلسه تمرین را حذف کنید

دو راه برای حذف یک جلسه تمرین وجود دارد:

  1. بر اساس محدوده زمانی
  2. توسط UID.

در اینجا نحوه حذف داده های زیرمجموعه با توجه به محدوده زمانی آمده است:

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

همچنین می توانید داده های زیرنوع را با استفاده از UID حذف کنید. انجام این کار فقط جلسه تمرین را حذف می کند، نه داده های مرتبط:

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