ব্যায়াম রুট যোগ করুন

এই নির্দেশিকাটি হেলথ কানেক্ট সংস্করণ 1.1.0-alpha12 এর সাথে সামঞ্জস্যপূর্ণ।

এক্সারসাইজ রুট ব্যবহারকারীদের সংশ্লিষ্ট ব্যায়ামের জন্য একটি জিপিএস রুট ট্র্যাক করতে এবং তাদের ওয়ার্কআউটের ম্যাপ অন্যান্য অ্যাপের সাথে শেয়ার করতে দেয়।

হেলথ কানেক্ট-এর প্রাপ্যতা যাচাই করুন

হেলথ কানেক্ট ব্যবহার করার চেষ্টা করার আগে, আপনার অ্যাপের যাচাই করে নেওয়া উচিত যে ব্যবহারকারীর ডিভাইসে হেলথ কানেক্ট উপলব্ধ আছে কিনা। সব ডিভাইসে হেলথ কানেক্ট আগে থেকে ইনস্টল করা নাও থাকতে পারে বা এটি নিষ্ক্রিয় করা থাকতে পারে। আপনি HealthConnectClient.getSdkStatus() মেথড ব্যবহার করে এর উপলব্ধতা পরীক্ষা করতে পারেন।

হেলথ কানেক্ট-এর প্রাপ্যতা কীভাবে পরীক্ষা করবেন

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() থেকে প্রাপ্ত স্ট্যাটাসের উপর নির্ভর করে, প্রয়োজনে আপনি ব্যবহারকারীকে গুগল প্লে স্টোর থেকে হেলথ কানেক্ট ইনস্টল বা আপডেট করার জন্য নির্দেশনা দিতে পারেন।

এই নির্দেশিকাটিতে ব্যবহারকারীর কাছ থেকে অনুমতি চাওয়ার পদ্ধতি সম্পর্কে তথ্য দেওয়া হয়েছে এবং একটি অনুশীলন সেশনের অংশ হিসেবে অ্যাপগুলো কীভাবে রাউট ডেটা লেখার অনুমতি পায়, তার রূপরেখাও বর্ণনা করা হয়েছে।

এক্সারসাইজ রুটগুলির রিড এবং রাইট কার্যকারিতার মধ্যে অন্তর্ভুক্ত রয়েছে:

  1. অ্যাপগুলো এক্সারসাইজ রুটগুলোর জন্য একটি নতুন রাইট পারমিশন তৈরি করে।
  2. একটি রুটকে ফিল্ড হিসেবে ব্যবহার করে একটি এক্সারসাইজ সেশন লেখার মাধ্যমে ইনসারশন সম্পন্ন হয়।
  3. পঠন:
    1. সেশন মালিকের জন্য, সেশন রিড ব্যবহার করে ডেটা অ্যাক্সেস করা হয়।
    2. একটি থার্ড-পার্টি অ্যাপ থেকে, একটি ডায়ালগের মাধ্যমে যা ব্যবহারকারীকে কোনো একটি রুট একবার পড়ার অনুমতি দেয়।

যদি ব্যবহারকারীর লেখার অনুমতি না থাকে এবং রাউটটি সেট করা না থাকে, তাহলে রাউটটি আপডেট হয় না।

যদি আপনার অ্যাপে রাউট লেখার অনুমতি থাকে এবং কোনো রাউট ছাড়া শুধু একটি সেশন অবজেক্ট দিয়ে সেশন আপডেট করার চেষ্টা করা হয়, তাহলে বিদ্যমান রাউটটি মুছে যায়।

বৈশিষ্ট্যের প্রাপ্যতা

কোনো ব্যবহারকারীর ডিভাইস হেলথ কানেক্ট-এ পরিকল্পিত ব্যায়াম সমর্থন করে কিনা তা নির্ধারণ করতে, ক্লায়েন্টে FEATURE_PLANNED_EXERCISE এর উপলব্ধতা পরীক্ষা করুন:

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

  // Feature is available
} else {
  // Feature isn't available
}
আরও জানতে ‘ফিচারের প্রাপ্যতা যাচাই করুন’ দেখুন।

প্রয়োজনীয় অনুমতি

ব্যায়ামের রুটে প্রবেশ নিম্নলিখিত অনুমতি দ্বারা সুরক্ষিত:

  • android.permission.health.READ_EXERCISE_ROUTES
  • android.permission.health.WRITE_EXERCISE_ROUTE
দ্রষ্টব্য: এই অনুমতি প্রকারের ক্ষেত্রে, READ_EXERCISE_ROUTES বহুবচন এবং 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_ROUTES" />
...
</application>

আপনাকে ব্যায়ামের অনুমতিও ঘোষণা করতে হবে, কারণ প্রতিটি রুট একটি ব্যায়াম সেশনের সাথে যুক্ত থাকে (একটি সেশন = একটি ওয়ার্কআউট)।

অনুমতি অনুরোধ করতে, আপনার অ্যাপটিকে প্রথমবার হেলথ কানেক্ট-এর সাথে সংযুক্ত করার সময় 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 ব্যবহার করুন। এটি হেলথ কানেক্ট পারমিশন স্ক্রিনটি প্রদর্শন করবে।

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

যেহেতু ব্যবহারকারীরা যেকোনো সময় অনুমতি দিতে বা প্রত্যাহার করতে পারেন, তাই আপনার অ্যাপকে প্রতিবার ব্যবহারের আগে অনুমতি যাচাই করতে হবে এবং অনুমতি হারিয়ে গেলে তার মোকাবিলা করতে হবে।

একটি ব্যায়াম সেশনের রেকর্ডে অন্তর্ভুক্ত তথ্য

প্রতিটি ব্যায়াম সেশনের রেকর্ডে নিম্নলিখিত তথ্য থাকে:

  • ব্যায়ামের ধরন , যেমন, সাইকেল চালানো।
  • ব্যায়ামের পথ , যাতে অক্ষাংশ, দ্রাঘিমাংশ এবং উচ্চতার মতো তথ্য থাকে।

সমর্থিত সমষ্টি

ExerciseSessionRecord জন্য নিম্নলিখিত সমষ্টিগত মানগুলি উপলব্ধ:

উদাহরণ ব্যবহার

নিম্নলিখিত কোড স্নিপেটগুলো দেখায় কিভাবে একটি এক্সারসাইজ রুট পড়া এবং লেখা যায়।

ব্যায়ামের পথ পড়ুন

আপনার অ্যাপটি ব্যাকগ্রাউন্ডে চলার সময় অন্য অ্যাপ দ্বারা তৈরি ব্যায়ামের রুটের ডেটা পড়তে পারে না।

যখন আপনার অ্যাপ ব্যাকগ্রাউন্ডে চলে এবং অন্য কোনো অ্যাপ দ্বারা তৈরি করা একটি এক্সারসাইজ রুট পড়ার চেষ্টা করে, তখন হেলথ কানেক্ট একটি ExerciseRouteResult.ConsentRequired রেসপন্স রিটার্ন করে, এমনকি যদি আপনার অ্যাপে এক্সারসাইজ রুট ডেটাতে অ্যাক্সেসের জন্য 'Always allow' অপশনটি চালু করা থাকে।

এই কারণে, আমরা দৃঢ়ভাবে সুপারিশ করছি যে, ব্যবহারকারী যখন আপনার অ্যাপের UI-এর সাথে সক্রিয়ভাবে যুক্ত থাকেন এবং ইচ্ছাকৃতভাবে অ্যাপটি ব্যবহার করেন, তখনই যেন আপনি রাউটের জন্য অনুরোধ করেন।

ব্যাকগ্রাউন্ড রিড সম্পর্কে আরও জানতে, ব্যাকগ্রাউন্ড রিড উদাহরণ দেখুন।

নিম্নলিখিত কোড স্নিপেটটি দেখায় কিভাবে হেলথ কানেক্ট-এ একটি সেশন পড়তে হয় এবং সেই সেশন থেকে একটি রুটের জন্য অনুরোধ করতে হয়:

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

একটি ব্যায়ামের পথ লিখুন

নিম্নলিখিত কোডটি দেখায় কিভাবে একটি এক্সারসাইজ রুট সহ একটি সেশন রেকর্ড করতে হয়:

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

ব্যায়াম সেশন

ব্যায়ামের মধ্যে দৌড়ানো থেকে শুরু করে ব্যাডমিন্টন পর্যন্ত যেকোনো কিছুই অন্তর্ভুক্ত থাকতে পারে।

ব্যায়াম সেশন লিখুন

সেশন অন্তর্ভুক্ত করে একটি ইনসারশন রিকোয়েস্ট তৈরি করার পদ্ধতিটি হলো:

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

একটি ব্যায়ামের সেশন পড়ুন

একটি অনুশীলন সেশন কীভাবে পড়তে হয় তার একটি উদাহরণ এখানে দেওয়া হলো:

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,
  metadata = Metadata.manualEntry()
)

একটি অনুশীলন সেশন মুছে ফেলুন

একটি ব্যায়াম সেশন মুছে ফেলার দুটি উপায় আছে:

  1. সময়সীমা অনুযায়ী।
  2. ইউআইডি দ্বারা।

সময়সীমা অনুযায়ী সাবটাইপ ডেটা ডিলিট করার পদ্ধতি নিচে দেওয়া হলো:

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