Wear OS modülünü entegre etme

Uygulamanızı Wear OS destekli giyilebilir cihazlara genişleterek sağlık ve fitness deneyimini iyileştirin.

Wear OS modülü ekleyin

Android Studio, uygulamanıza Wear OS modülü eklemek için kullanışlı bir sihirbaz sağlar. Dosya > Yeni Modül menüsünde Wear OS'i aşağıdaki resimde gösterildiği gibi seçin:

Android Studio'da Wear OS modül sihirbazı
Şekil 1: Wear OS modülü oluşturma

Sağlık Hizmetleri'nin en son sürümünü kullanabilmeniz için Minimum SDK'nın API 30 veya daha yeni olması gerektiğini unutmayın. Health Services, sağlık sensörlerini otomatik olarak yapılandırarak metrikleri takip etmeyi ve verileri kaydetmeyi kolaylaştırır.

Sihirbazı tamamladıktan sonra projenizi senkronize edin. Aşağıdaki Çalıştır yapılandırması görünür:

Wear OS uygulamasını çalıştırma düğmesini gösteren resim
Şekil 2: Yeni Wear OS modülü için çalıştırma düğmesi

Bu şekilde Wear OS modülünü giyilebilir bir cihazda çalıştırabilirsiniz. İki seçeneğiniz vardır:

Yapılandırma çalıştırıldığında uygulama Wear OS emülatörüne veya cihazına dağıtılır ve "merhaba dünya" deneyimi gösterilir. Bu, uygulamanızı kullanmaya başlamak üzere Wear OS için Oluşturma'nın kullanıldığı temel kullanıcı arayüzü kurulumudur.

Sağlık Hizmetleri ve Hilt Ekleyin

Aşağıdaki kitaplıkları Wear OS modülünüze entegre edin:

  • Sağlık Hizmetleri: Kol saatindeki sensörlere ve verilere erişimi çok kolay hale getirir ve gücü daha verimli hale getirir.
  • Hilt: Etkili bir bağımlılık yerleştirme ve yönetimi sağlar.

Sağlık Hizmetleri Yöneticisi'ni oluşturma

Sağlık Hizmetleri'nin kullanımını kolaylaştırmak ve daha küçük ve daha yumuşak bir API kullanmak için aşağıdaki gibi bir sarmalayıcı oluşturabilirsiniz:

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

Yönetmek için Hilt modülünü oluşturduktan sonra aşağıdaki snippet'i kullanarak:

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

HealthServicesManager öğesini başka bir Hilt bağımlılığı olarak yerleştirebilirsiniz.

Yeni HealthServicesManager, kalp monitörü dinleyicisini kaydeden ve alınan verileri yayınlayan bir heartRateMeasureFlow() yöntemi sunar.

Giyilebilir cihazlarda veri güncellemelerini etkinleştir

Fitness ile ilgili veri güncellemeleri için BODY_SENSORS izni gereklidir. Henüz yapmadıysanız uygulamanızın manifest dosyasında BODY_SENSORS iznini beyan edin. Ardından, bu snippet'te gösterildiği gibi izni isteyin:

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

[...]

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

Uygulamanızı fiziksel bir cihazda test ederseniz veriler güncellenmeye başlayacaktır.

Wear OS 4'ten itibaren emülatörler de test verilerini otomatik olarak gösterir. Önceki sürümlerde, sensörden gelen veri akışını simüle edebilirsiniz. Bir terminal penceresinde şu ADB komutunu çalıştırın:

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

Farklı nabız değerleri görmek için farklı egzersizleri simüle etmeyi deneyin. Bu komut, yürümeyi simüle eder:

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

Bu komut, çalıştırmayı simüle eder:

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

Verilerin simülasyonunu durdurmak için şu komutu çalıştırın:

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

Nabız verilerini okuma

BODY_SENSORS izni verildikten sonra kullanıcının nabzını (heartRateMeasureFlow()) HealthServicesManager üzerinden okuyabilirsiniz. Wear OS uygulamasının kullanıcı arayüzünde, giyilebilir cihazdaki sensör tarafından ölçülen mevcut nabız değeri görüntülenir.

Aşağıdaki snippet'te gösterildiği gibi ViewModel cihazınızda nabız akış nesnesini kullanarak veri toplamaya başlayın:

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

Uygulamanızın kullanıcı arayüzünde canlı verileri görüntülemek için aşağıdakine benzer bir şekilde oluşturulabilir bir nesne kullanın:

val heartRate by viewModel.hr

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

Avuç içi cihaza veri gönderme

Sağlık ve fitness verilerini avuç içi cihazlara göndermek için Sağlık Hizmetleri'ndeki DataClient sınıfını kullanın. Aşağıdaki kod snippet'i, uygulamanızın daha önce topladığı nabız verilerini nasıl göndereceğinizi gösterir:

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

Verileri telefona al

Verileri telefonda almak için bir WearableListenerService oluşturun:

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

Bu adımı tamamladıktan sonra dikkat çekici birkaç ayrıntıya dikkat edin:

  • @AndroidEntryPoint ek açıklaması, bu sınıfta Hilt'i kullanmamızı sağlar.
  • @Inject lateinit var heartRateMonitor: HeartRateMonitor, gerçekten de bu sınıfa bir bağımlılık ekler
  • Sınıf onDataChanged() öğesini uygular ve ayrıştırıp kullanabileceğiniz bir etkinlik koleksiyonu alır.

Aşağıdaki HeartRateMonitor mantığı, alınan nabız değerlerini uygulamanızın kod tabanının başka bir bölümüne göndermenize olanak tanır:

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

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

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

Veri veri yolu, etkinlikleri onDataChanged() yönteminden alır ve SharedFlow kullanan veri gözlemcilerinin kullanımına sunar.

Son kısım, AndroidManifest.xml telefon uygulamasındaki Service beyanıdır:

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

Avuç içi cihazda gerçek zamanlı verileri göster

Uygulamanızın avuç içi cihazlarda çalışan bir bölümünde, görünüm modelinizin oluşturucusuna HeartRateMonitor ekleyin. Bu HeartRateMonitor nesnesi, nabız verilerini gözlemler ve gerektiğinde kullanıcı arayüzü güncellemeleri yapar.