Wear OS मॉड्यूल को इंटिग्रेट करना

Wear OS की मदद से, अपने ऐप्लिकेशन को पहनने योग्य डिवाइसों पर उपलब्ध कराएं. इससे, सेहत और फ़िटनेस से जुड़े ऐप्लिकेशन को इस्तेमाल करने का अनुभव बेहतर होगा.

Wear OS मॉड्यूल जोड़ना

Android Studio, आपके ऐप्लिकेशन में Wear OS मॉड्यूल जोड़ने के लिए एक आसान विज़र्ड उपलब्ध कराता है. File > New Module मेन्यू में जाकर, Wear OS को चुनें. यह विकल्प, यहां दी गई इमेज में दिखाया गया है:

Android Studio में Wear OS मॉड्यूल विज़र्ड
पहली इमेज: Wear OS मॉड्यूल बनाना

यह ध्यान रखना ज़रूरी है कि Health Services के नए वर्शन का इस्तेमाल करने के लिए, कम से कम एसडीके एपीआई 30 या इससे ज़्यादा होना चाहिए. Health Services, स्वास्थ्य से जुड़े सेंसर को अपने-आप कॉन्फ़िगर करके, मेट्रिक को ट्रैक करने और डेटा को रिकॉर्ड करने की प्रोसेस को आसान बनाती है.

विज़र्ड पूरा करने के बाद, अपने प्रोजेक्ट को सिंक करें. आपको यह Run कॉन्फ़िगरेशन दिखेगा:

Wear OS ऐप्लिकेशन में 'चलाएं' बटन दिखाने वाली इमेज
दूसरी इमेज: Wear OS के नए मॉड्यूल के लिए 'चलाएं' बटन

इससे, किसी पहने जाने वाले डिवाइस पर Wear OS मॉड्यूल चलाया जा सकता है. आपके पास दो विकल्प हैं:

कॉन्फ़िगरेशन चलाने पर, ऐप्लिकेशन को Wear OS एम्युलेटर या डिवाइस पर डिप्लॉय किया जाता है. साथ ही, "hello world" का अनुभव दिखता है. यह Compose for Wear OS का इस्तेमाल करके, बुनियादी यूज़र इंटरफ़ेस (यूआई) सेटअप करने का तरीका है. इससे आपको अपने ऐप्लिकेशन को शुरू करने में मदद मिलेगी.

Health Services और Hilt को जोड़ना

Wear OS मॉड्यूल में इन लाइब्रेरी को इंटिग्रेट करें:

  • Health Services: इससे वॉच पर सेंसर और डेटा को ऐक्सेस करना बहुत आसान हो जाता है. साथ ही, यह बैटरी की खपत भी कम करता है.
  • Hilt: इससे डिपेंडेंसी इंजेक्शन और मैनेजमेंट को बेहतर तरीके से मैनेज किया जा सकता है.

मेडिकल ऐंड हैल्थ सर्विसेज़ मैनेजर बनाना

Health Services का इस्तेमाल थोड़ा और आसान बनाने के लिए, इस तरह का रैपर बनाया जा सकता है. इससे, छोटा और बेहतर एपीआई उपलब्ध कराया जा सकता है:

private const val TAG = "WATCHMAIN"

class HealthServicesManager(context: Context) {
    private val measureClient = HealthServices.getClient(context).measureClient

    suspend fun hasHeartRateCapability() = runCatching {
        val capabilities = measureClient.getCapabilities()
        (DataType.HEART_RATE_BPM in capabilities.supportedDataTypesMeasure)
    }.getOrDefault(false)

    /**
     * Returns a cold flow. When activated, the flow will register a callback for heart rate data
     * and start to emit messages. When the consuming coroutine is canceled, the measure callback
     * is unregistered.
     *
     * [callbackFlow] creates a  bridge between a callback-based API and Kotlin flows.
     */
    @ExperimentalCoroutinesApi
    fun heartRateMeasureFlow(): Flow<MeasureMessage> = callbackFlow {
        val callback = object : MeasureCallback {
            override fun onAvailabilityChanged(dataType: DeltaDataType<*, *>, availability: Availability) {
                // Only send back DataTypeAvailability (not LocationAvailability)
                if (availability is DataTypeAvailability) {
                    trySendBlocking(MeasureMessage.MeasureAvailability(availability))
                }
            }

            override fun onDataReceived(data: DataPointContainer) {
                val heartRateBpm = data.getData(DataType.HEART_RATE_BPM)
                Log.d(TAG, "💓 Received heart rate: ${heartRateBpm.first().value}")
                trySendBlocking(MeasureMessage.MeasureData(heartRateBpm))
            }
        }

        Log.d(TAG, "⌛ Registering for data...")
        measureClient.registerMeasureCallback(DataType.HEART_RATE_BPM, callback)

        awaitClose {
            Log.d(TAG, "👋 Unregistering for data")
            runBlocking {
                measureClient.unregisterMeasureCallback(DataType.HEART_RATE_BPM, callback)
            }
        }
    }
}

sealed class MeasureMessage {
    class MeasureAvailability(val availability: DataTypeAvailability) : MeasureMessage()
    class MeasureData(val data: List<SampleDataPoint<Double>>) : MeasureMessage()
}

इसे मैनेज करने के लिए, यहां दिए गए स्निपेट का इस्तेमाल करके Hilt मॉड्यूल बनाएं:

@Module
@InstallIn(SingletonComponent::class)
internal object DataModule {
    @Provides
    @Singleton
    fun provideHealthServices(@ApplicationContext context: Context): HealthServicesManager = HealthServicesManager(context)
}

HealthServicesManager को किसी भी अन्य Hilt डिपेंडेंसी के तौर पर इंजेक्ट किया जा सकता है.

नया HealthServicesManager, heartRateMeasureFlow() तरीका उपलब्ध कराता है. यह तरीका, हार्ट मॉनिटर के लिए लिसनर को रजिस्टर करता है और मिले हुए डेटा को दिखाता है.

वियरेबल डिवाइसों पर डेटा अपडेट होने की सुविधा चालू करना

फ़िटनेस से जुड़े डेटा को अपडेट करने के लिए, BODY_SENSORS अनुमति की ज़रूरत होती है. अगर आपने अब तक ऐसा नहीं किया है, तो अपने ऐप्लिकेशन की मेनिफ़ेस्ट फ़ाइल में BODY_SENSORS अनुमति का एलान करें. इसके बाद, यहां दिए गए स्निपेट में दिखाए गए तरीके से अनुमति का अनुरोध करें:

val permissionState = rememberPermissionState(
    permission = Manifest.permission.BODY_SENSORS,
    onPermissionResult = { granted -> /* do something */ }
)

[...]

if (permissionState.status.isGranted) {
    // do something
} else {
    permissionState.launchPermissionRequest()
}

अगर आपने अपने ऐप्लिकेशन को किसी फ़िज़िकल डिवाइस पर टेस्ट किया है, तो डेटा अपडेट होना शुरू हो जाएगा.

Wear OS 4 से, एम्युलेटर में भी टेस्ट डेटा अपने-आप दिखता है. पिछले वर्शन में, सेंसर से मिलने वाली डेटा स्ट्रीम को सिम्युलेट किया जा सकता है. टर्मिनल विंडो में, यह ADB कमांड चलाएं:

adb shell am broadcast \
-a "whs.USE_SYNTHETIC_PROVIDERS" \
com.google.android.wearable.healthservices

दिल की धड़कन की अलग-अलग वैल्यू देखने के लिए, अलग-अलग कसरत करके देखें. इस कमांड से चलने का ऐनिमेशन बनाया जाता है:

adb shell am broadcast \
-a "whs.synthetic.user.START_WALKING" \
com.google.android.wearable.healthservices

यह कमांड, इन कार्रवाइयों को चलाने का सिम्युलेशन करती है:

adb shell am broadcast \
-a "whs.synthetic.user.START_RUNNING" \
com.google.android.wearable.healthservices

डेटा का सिम्युलेशन बंद करने के लिए, यह निर्देश चलाएं:

adb shell am broadcast -a \
"whs.USE_SENSOR_PROVIDERS" \
com.google.android.wearable.healthservices

धड़कन की दर का डेटा पढ़ने की अनुमति दें

BODY_SENSORS अनुमति मिलने के बाद, HealthServicesManager में उपयोगकर्ता की हार्ट रेट (heartRateMeasureFlow()) पढ़ी जा सकती है. Wear OS ऐप्लिकेशन के यूज़र इंटरफ़ेस (यूआई) में, धड़कन की मौजूदा दर दिखती है. इसे पहनने लायक डिवाइस पर मौजूद सेंसर से मापा जाता है.

ViewModel में, दिल की धड़कन की दर के फ़्लो ऑब्जेक्ट का इस्तेमाल करके डेटा इकट्ठा करना शुरू करें. इसका तरीका यहां दिया गया है:

val hr: MutableState<Double> = mutableStateOf(0.0)

[...]

healthServicesManager
    .heartRateMeasureFlow()
    .takeWhile { enabled.value }
    .collect { measureMessage ->
        when (measureMessage) {
            is MeasureData -> {
                val latestHeartRateValue = measureMessage.data.last().value
                hr.value = latestHeartRateValue
            }

            is MeasureAvailability -> availability.value =
                    measureMessage.availability
        }
    }

अपने ऐप्लिकेशन के यूज़र इंटरफ़ेस (यूआई) में लाइव डेटा दिखाने के लिए, यहां दिए गए कंपोज़ेबल ऑब्जेक्ट का इस्तेमाल करें:

val heartRate by viewModel.hr

Text(
  text = "Heart Rate: $heartRate",
  style = MaterialTheme.typography.display1
)

हैंडहेल्ड डिवाइस पर डेटा भेजना

सेहत और फ़िटनेस का डेटा किसी हैंडहेल्ड डिवाइस पर भेजने के लिए, Health Services में मौजूद DataClient क्लास का इस्तेमाल करें. नीचे दिए गए कोड स्निपेट में, हार्ट रेट का वह डेटा भेजने का तरीका बताया गया है जिसे आपके ऐप्लिकेशन ने पहले इकट्ठा किया था:

class HealthServicesManager(context: Context) {
    private val dataClient by lazy { Wearable.getDataClient(context) }

[...]

    suspend fun sendToHandheldDevice(heartRate: Int) {
        try {
            val result = dataClient
                .putDataItem(PutDataMapRequest
                    .create("/heartrate")
                    .apply { dataMap.putInt("heartrate", heartRate) }
                    .asPutDataRequest()
                    .setUrgent())
                .await()

            Log.d(TAG, "DataItem saved: $result")
        } catch (cancellationException: CancellationException) {
            throw cancellationException
        } catch (exception: Exception) {
            Log.d(TAG, "Saving DataItem failed: $exception")
        }
    }
}

फ़ोन पर डेटा पाना

फ़ोन पर डेटा पाने के लिए, WearableListenerService बनाएं:

@AndroidEntryPoint
class DataLayerListenerService : WearableListenerService() {

    @Inject
    lateinit var heartRateMonitor: HeartRateMonitor

    override fun onDataChanged(dataEvents: DataEventBuffer) {

        dataEvents.forEach { event ->
            when (event.type) {
                DataEvent.TYPE_CHANGED -> {
                    event.dataItem.run {
                        if (uri.path?.compareTo("/heartrate") == 0) {
                            val heartRate = DataMapItem.fromDataItem(this)
                                    .dataMap.getInt(HR_KEY)
                            Log.d("DataLayerListenerService",
                                    "New heart rate value received: $heartRate")
                            heartRateMonitor.send(heartRate)
                        }
                    }
                }

                DataEvent.TYPE_DELETED -> {
                    // DataItem deleted
                }
            }
        }
    }
}

इस चरण को पूरा करने के बाद, कुछ दिलचस्प जानकारी देखें:

  • @AndroidEntryPoint एनोटेशन की मदद से, इस क्लास में Hilt का इस्तेमाल किया जा सकता है
  • @Inject lateinit var heartRateMonitor: HeartRateMonitor इस क्लास में डिपेंडेंसी इंजेक्ट करेगा
  • यह क्लास onDataChanged() को लागू करती है और इवेंट का एक ऐसा कलेक्शन पाती है जिसे पार्स करके इस्तेमाल किया जा सकता है

यहां दिए गए HeartRateMonitor लॉजिक का इस्तेमाल करके, आपको मिली हार्ट रेट की वैल्यू को ऐप्लिकेशन के कोडबेस के किसी दूसरे हिस्से में भेजा जा सकता है:

class HeartRateMonitor {
    private val datapoints = MutableSharedFlow<Int>(extraBufferCapacity = 10)

    fun receive(): SharedFlow<Int> = datapoints.asSharedFlow()

    fun send(hr: Int) {
        datapoints.tryEmit(hr)
    }
}

डेटा बस को onDataChanged() तरीके से इवेंट मिलते हैं. इसके बाद, वह SharedFlow का इस्तेमाल करके, डेटा ऑब्ज़र्वर के लिए उन्हें उपलब्ध कराता है.

आखिरी चरण में, फ़ोन ऐप्लिकेशन में Service का एलान करना होता है AndroidManifest.xml:

<service
    android:name=".DataLayerListenerService"
    android:exported="true">
    <intent-filter>
        <!-- listeners receive events that match the action and data filters -->
        <action android:name="com.google.android.gms.wearable.DATA_CHANGED" />
        <data
            android:host="*"
            android:pathPrefix="/heartrate"
            android:scheme="wear" />
    </intent-filter>
</service>

हैंडहेल्ड डिवाइस पर रीयल-टाइम डेटा दिखाना

अपने ऐप्लिकेशन के उस हिस्से में HeartRateMonitor को इंजेक्ट करें जो हैंडहेल्ड डिवाइस पर चलता है. इसके लिए, अपने व्यू मॉडल के कंस्ट्रक्टर का इस्तेमाल करें. यह HeartRateMonitor ऑब्जेक्ट, दिल की धड़कन की दर के डेटा को मॉनिटर करता है और ज़रूरत के हिसाब से यूज़र इंटरफ़ेस (यूआई) को अपडेट करता है.