Wear OS modülünü entegre etme

Uygulamanızı Wear OS destekli giyilebilir cihazlara taşıyarak sağlık ve fitness deneyimini geliştirin.

Wear OS modülü ekleme

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

Android Studio'daki 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 yüksek olması gerektiğini unutmayın. Sağlık Hizmetleri, sağlık sensörlerini otomatik olarak yapılandırarak metrikleri izlemeyi ve verileri kaydetmeyi kolaylaştırır.

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

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

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

Yapılandırmayı çalıştırmak, uygulamayı Wear OS emülatörüne veya cihazına dağıtır ve "hello world" deneyimini gösterir. Bu, uygulamanızı kullanmaya başlamak için Wear OS'te Compose'u kullanarak temel kullanıcı arayüzü kurulumudur.

Add Health Services ve Hilt'i ekleme

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 ve daha enerji tasarruflu hale getirir.
  • Hilt: Etkili bağımlılık ekleme ve yönetimi sağlar.

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

Health Services'i kullanmayı biraz daha kolaylaştırmak ve daha küçük ve sorunsuz bir API sunmak 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()
}

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

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

HealthServicesManager öğesini diğer Hilt bağımlılıkları gibi ekleyebilirsiniz.

Yeni HealthServicesManager, kalp atışı monitörü için bir dinleyici kaydeden ve alınan verileri yayan bir heartRateMeasureFlow() yöntemi sağlar.

Giyilebilir cihazlarda veri güncellemelerini etkinleştirme

Fitness ile ilgili veri güncellemeleri için BODY_SENSORS izni gerekir. 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şlar.

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ğerlerini 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, aşağıdakilerin çalıştırılmasını simüle eder:

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

Veri 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 verildiğinde, HealthServicesManager içinde kullanıcının kalp atış hızını (heartRateMeasureFlow()) okuyabilirsiniz. Wear OS uygulamasının kullanıcı arayüzünde, giyilebilir cihazdaki sensör tarafından ölçülen mevcut kalp atış hızı değeri gösterilir.

ViewModel bölümünde, aşağıdaki snippet'te gösterildiği gibi kalp atış hı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östermek için aşağıdakine benzer bir composable nesne kullanın:

val heartRate by viewModel.hr

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

Elde taşınan bir cihaza veri gönderme

Sağlık ve fitness verilerini bir elde taşınabilir cihaza göndermek için Sağlık Hizmetleri'ndeki DataClient sınıfını kullanın. Aşağıdaki kod snippet'inde, uygulamanızın daha önce topladığı kalp atış hızı verilerinin nasıl gönderileceği gösterilmektedir:

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

Telefonda verileri almak için 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 birkaç ilginç ayrıntı fark edeceksiniz:

  • @AndroidEntryPoint ek açıklaması, bu sınıfta Hilt'i kullanmamıza olanak tanır.
  • @Inject lateinit var heartRateMonitor: HeartRateMonitor gerçekten de bu sınıfa bir bağımlılık yerleştirecek.
  • 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 kalp atış hı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)
    }
}

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

Son adım, telefon uygulamasında Service beyan etmektir 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>

Elde taşınan bir cihazda anlık verileri gösterme

Uygulamanızın elde taşınabilir bir cihazda çalışan bölümünde, HeartRateMonitor öğesini görünüm modelinizin oluşturucusuna yerleştirin. Bu HeartRateMonitor nesne, kalp atış hızı verilerini izler ve gerektiğinde kullanıcı arayüzü güncellemeleri yayınlar.