یک ماژول Wear OS را ادغام کنید

تجربه سلامت و تناسب اندام برنامه خود را با گسترش آن در دستگاه های پوشیدنی مجهز به Wear OS افزایش دهید.

یک ماژول Wear OS اضافه کنید

Android Studio یک جادوگر مفید برای اضافه کردن یک ماژول Wear OS به برنامه شما ارائه می دهد. در منوی File > New Module ، Wear OS را انتخاب کنید، همانطور که در تصویر زیر نشان داده شده است:

جادوگر ماژول OS را در Android Studio بپوشید
شکل 1 : یک ماژول Wear OS ایجاد کنید

توجه به این نکته مهم است که حداقل SDK باید API 30 یا بالاتر باشد تا بتوانید از آخرین نسخه خدمات بهداشتی استفاده کنید. خدمات بهداشتی با پیکربندی خودکار حسگرهای سلامت، ردیابی معیارها و ثبت داده‌ها را آسان‌تر می‌کند.

پس از تکمیل ویزارد، پروژه خود را همگام کنید. پیکربندی Run زیر ظاهر می شود:

تصویری که دکمه اجرای برنامه Wear OS را نشان می‌دهد
شکل 2 : دکمه اجرا برای ماژول جدید Wear OS

این به شما امکان می دهد ماژول Wear OS را روی یک دستگاه پوشیدنی اجرا کنید. شما دو گزینه دارید:

اجرای پیکربندی، برنامه را در شبیه ساز یا دستگاه Wear OS مستقر می کند و یک تجربه "سلام جهان" را نشان می دهد. این راه‌اندازی رابط کاربری اولیه است که از Compose for Wear OS برای شروع کار با برنامه‌تان استفاده می‌کند.

خدمات بهداشتی و Hilt را اضافه کنید

کتابخانه های زیر را در ماژول Wear OS خود ادغام کنید:

  • خدمات بهداشتی : دسترسی به حسگرها و داده های ساعت را بسیار راحت و کارآمدتر می کند.
  • Hilt : امکان تزریق و مدیریت موثر وابستگی را فراهم می کند.

مدیر خدمات بهداشتی را ایجاد کنید

برای اینکه استفاده از Health Services کمی راحت‌تر باشد، و یک API کوچکتر و روان‌تر را در معرض دید قرار دهید، می‌توانید یک wrapper مانند زیر ایجاد کنید:

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 ، می‌توانید ضربان قلب کاربر ( heartRateMeasureFlow() ) را در HealthServicesManager بخوانید. در رابط کاربری برنامه 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
)

ارسال داده به دستگاه دستی

برای ارسال داده های سلامت و تناسب اندام به یک دستگاه دستی، از کلاس DataClient در Health Services استفاده کنید. قطعه کد زیر نحوه ارسال داده های ضربان قلب را که قبلاً برنامه شما جمع آوری کرده است نشان می دهد:

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 به سازنده مدل view خود تزریق کنید. این شی HeartRateMonitor داده‌های ضربان قلب را مشاهده می‌کند و در صورت نیاز به‌روزرسانی‌های UI را منتشر می‌کند.