Wear OS modülünü entegre etme

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

Wear OS modülü ekle

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

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

Minimum SDK'nın API 30 veya sonraki bir sürüm olması gerektiğini unutmayın izin modunu güncelleyerek Sağlık Hizmetleri'nin son sürümünü kullanabilirsiniz. Sağlık Hizmetleri durum ayarlarını yapılandırarak metrikleri takip etmeyi ve veri kaydetmeyi kolaylaştırır çalışmasını sağlar.

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

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

Bu işlem, Wear OS modülünü giyilebilir bir cihazda çalıştırmanızı sağlar. İki seçeneğiniz vardır:

Yapılandırma çalıştırıldığında, uygulama Wear OS emülatörüne veya "merhaba dünya" ifadesini gösteren bir ekran sahip olacaksınız. Bu, kullanıcı arayüzünün Uygulamanızı kullanmaya başlamak için Wear OS için ileti oluşturun.

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şilmesini sağlar daha kullanışlı ve daha verimli.
  • Hilt: Etkili bir bağımlılık yerleştirme ve yönetim imkanı sağlar.

Health Services Manager oluşturma

Sağlık Hizmetleri'ni kullanmayı daha kolay hale getirmek ve ve daha akıcı bir API ile 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 diğer herhangi bir Hilt bağımlılığı olarak ekleyebilirsiniz.

Yeni HealthServicesManager, aşağıdaki özelliklere sahip bir heartRateMeasureFlow() yöntemi sağlar: nabız takibi için bir dinleyici kaydeder ve alınan verileri yayınlar.

Giyilebilir cihazlarda veri güncellemelerini etkinleştirin

Fitness ile ilgili veri güncellemeleri için BODY_SENSORS izni gerekiyor. Şu durumda: Henüz yapmadıysanız şurada BODY_SENSORS iznini beyan edin: uygulamanızın manifest dosyasına ait dosyayı indirin. 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, test verilerini de otomatik olarak gösterir. Önceki veri akışını sensörden simüle edebilirsiniz. Terminalde 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ğıdaki komutu çalıştırmayı 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 izniyle kullanıcının nabzını okuyabilirsiniz HealthServicesManager içinde (heartRateMeasureFlow()). Wear OS uygulamasında kullanıcı arayüzünde sensör tarafından ölçülen geçerli nabız değeri gösterilir giyilebilir cihaz.

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

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

Canlı verileri şurada görüntülemek için aşağıdakine benzer bir composable nesne kullanın: Uygulamanızın kullanıcı arayüzü:

val heartRate by viewModel.hr

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

Avuç içi cihaza veri gönderin

Sağlık ve fitness verilerini avuç içi bir cihaza göndermek için DataClient uygulamasını kullanın en iyi uygulamaları paylaşacağım. Aşağıdaki kod snippet'inde kalp gönderme yönteminin nasıl gönderileceği gösterilmektedir uygulamanızın daha önce topladığı verileri değerlendirin:

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

Telefonunuzdaki verileri almak için 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
                }
            }
        }
    }
}

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

  • @AndroidEntryPoint ek açıklaması, bu sınıfta Hilt'ı kullanmamıza olanak tanır.
  • @Inject lateinit var heartRateMonitor: HeartRateMonitor gerçekten de bu sınıfa bağımlılık ekle
  • Sınıf onDataChanged() uygular ve şu etkinliklere sahip bir koleksiyon alır: URL'leri doğru şekilde

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

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, etkinlikleri onDataChanged() yönteminden alır ve bunları gerçekleştirir veri gözlemcilerinin kullanımına sunulan bir SharedFlow sağlar.

Son olarak, telefon uygulamasında Service beyanının yer alması 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>

Mobil cihazda gerçek zamanlı verileri gösterin

Uygulamanızın bir avuç içi cihazda çalışan bölümüne Görünüm modelinizin oluşturucusuna HeartRateMonitor ekleyin. Bu HeartRateMonitor nesne, nabız verilerini gözlemler ve gerektiğinde kullanıcı arayüzü güncellemelerini yayar.