Wear OS-Modul einbinden

Gesundheits- und Fitnessfunktionen deiner App durch die Erweiterung auf Wearables verbessern Geräte mit Wear OS.

Wear OS-Modul hinzufügen

Android Studio bietet einen praktischen Assistenten, mit dem du deiner App ein Wear OS-Modul hinzufügen kannst. In das Menü Datei > Neues Modul die Option Wear OS aus, wie im Folgenden gezeigt. Bild:

<ph type="x-smartling-placeholder">
</ph> Assistent für Wear OS-Module in Android Studio <ph type="x-smartling-placeholder">
</ph> Abbildung 1: Wear OS-Modul erstellen

Wichtig: Die Mindest-SDK-Version muss API 30 oder höher sein. damit Sie die neueste Version von Health Services verwenden können. Gesundheitsdienste vereinfacht das Tracking von Messwerten und das Aufzeichnen von Daten durch Konfigurieren des Zustands automatisch Sensoren an.

Synchronisieren Sie nach Abschluss des Assistenten Ihr Projekt. Die folgende Run Konfiguration angezeigt:

<ph type="x-smartling-placeholder">
</ph> Ein Bild, auf dem die Schaltfläche „Ausführen“ in der Wear OS App zu sehen ist <ph type="x-smartling-placeholder">
</ph> Abbildung 2: Schaltfläche „Ausführen“ für das neue Wear OS-Modul

So kannst du das Wear OS-Modul auf einem Wearable-Gerät ausführen. Es gibt zwei Möglichkeiten:

Wenn Sie die Konfiguration ausführen, wird die App im Wear OS-Emulator bereitgestellt. und zeigt eine „Hello World“- Nutzererfahrung. Dies ist die grundlegende Einrichtung der Benutzeroberfläche mit Schreibe eine Nachricht für Wear OS, um deine App zu nutzen.

Gesundheitsdienstleistungen und Fitbit hinzufügen

Binde die folgenden Bibliotheken in dein Wear OS-Modul ein:

  • Gesundheitsdienste:ermöglicht den Zugriff auf Sensoren und Daten auf der Smartwatch sehr praktisch und energieeffizienter.
  • Hilt:Ermöglicht eine effektive Abhängigkeitsinjektion und -verwaltung.

Health Services Manager erstellen

Um die Nutzung von Gesundheitsdiensten zu vereinfachen und eine kleinere und reibungsloseren API arbeiten, können Sie einen Wrapper wie den folgenden erstellen:

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

Nachdem Sie das Hilt-Modul zu dessen Verwaltung mit dem folgenden Snippet erstellt haben:

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

Sie können die HealthServicesManager wie jede andere Hilt-Abhängigkeit injizieren.

Die neue HealthServicesManager bietet eine heartRateMeasureFlow()-Methode, die registriert einen Listener für den Herzmonitor und gibt die empfangenen Daten aus.

Datenupdates auf Wearable-Geräten aktivieren

Für die Aktualisierung von Fitnessdaten ist die Berechtigung BODY_SENSORS erforderlich. Wenn Sie noch nicht getan haben, deklarieren Sie die Berechtigung BODY_SENSORS in Ihrem in der Manifestdatei der App. Fordern Sie dann die Berechtigung an, wie in diesem Snippet gezeigt:

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

[...]

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

Wenn Sie Ihre App auf einem physischen Gerät testen, sollten die Daten aktualisiert werden.

Ab Wear OS 4 zeigen Emulatoren auch automatisch Testdaten an. Auf vorheriger können Sie den Datenstream des Sensors simulieren. In einem Terminal führen Sie diesen ADB-Befehl aus:

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

Probiere verschiedene Übungen aus, um verschiedene Herzfrequenzwerte zu sehen. Dieser Befehl simuliert den Gehen:

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

Dieser Befehl simuliert die Ausführung:

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

Führen Sie den folgenden Befehl aus, um die Simulation der Daten zu beenden:

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

Herzfrequenzdaten lesen

Mit der Berechtigung BODY_SENSORS kannst du die Herzfrequenz des Benutzers lesen (heartRateMeasureFlow()) in HealthServicesManager. In der App Auf der Benutzeroberfläche wird der aktuelle Herzfrequenzwert angezeigt, der vom Sensor am Wearable.

Fang in deinem ViewModel an, Daten mithilfe des Herzfrequenz-Flussobjekts zu erfassen. Dies wird im folgenden Snippet gezeigt:

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

Verwenden Sie ein zusammensetzbares Objekt ähnlich dem folgenden, um die Live-Daten in der Benutzeroberfläche Ihrer App:

val heartRate by viewModel.hr

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

Daten an ein Handheld-Gerät senden

Wenn du Gesundheits- und Fitnessdaten an ein Mobilgerät senden möchtest, verwende die DataClient im Bereich Gesundheitsdienstleistungen. Im folgenden Code-Snippet sehen Sie, wie ein Herz zuvor von deiner App erhobene Daten bewerten:

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

Daten auf dem Smartphone empfangen

Um die Daten auf dem Smartphone zu empfangen, erstelle einen 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
                }
            }
        }
    }
}

Beachten Sie nach Abschluss dieses Schritts einige interessante Details:

  • Mit der Annotation @AndroidEntryPoint können wir Hilt in dieser Klasse verwenden.
  • Das @Inject lateinit var heartRateMonitor: HeartRateMonitor wird tatsächlich Abhängigkeit in diese Klasse einfügen
  • Die Klasse implementiert onDataChanged() und empfängt eine Sammlung von Ereignissen, die können Sie parsen und

Mit der folgenden HeartRateMonitor-Logik kannst du die empfangene Herzfrequenz in einen anderen Teil der Codebasis Ihrer App einfügen:

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

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

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

Ein Datenbus empfängt die Ereignisse von der Methode onDataChanged() und stellt sie auf für Datenbeobachter mit SharedFlow zur Verfügung.

Das letzte Bit ist die Deklaration von Service in der Telefon-App. 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>

Echtzeitdaten auf einem Handheld-Gerät anzeigen

In den Teil deiner App, der auf einem Handheld-Gerät ausgeführt wird, fügst du den HeartRateMonitor in den Konstruktor Ihres Ansichtsmodells. Dieses HeartRateMonitor -Objekt die Herzfrequenzdaten und gibt bei Bedarf Aktualisierungen der Benutzeroberfläche aus.