Apps-Grundlage

Von Tiles zu Wear-Widgets mit Jetpack Glance und Remote Compose

Lautsprecher

  • François Deschênes

    Produktmanager

  • Yuri Schimke

    Softwareentwickler

Info

Über den Vortrag

In dieser Sitzung befassen wir uns eingehend mit der Entwicklung von Vollbild-Tiles zu dynamischen Wear-Widgets mit Teilhöhe.

Wir sehen uns die technischen Vorteile der einheitlichen Jetpack Glance API und der neuen Remote Compose-Engine an und erläutern, wie sie sich nahtlos in Modern Android Development einfügen.

Abschließend stellen wir die neuesten UX-Leitfäden vor und gehen auf praktische Migrationsstrategien ein, mit denen ihr die nächste Generation von übersichtlichen Benutzeroberflächen entwickeln könnt.

Zusammenfassung

  • Wear OS entwickelt Vollbild-Tiles zu Wear-Widgets weiter, die mit Jetpack Glance erstellt und von Remote Compose unterstützt werden.
  • Remote Compose verwendet denselben Compiler und dieselbe Laufzeit wie die Jetpack Compose-UI, während die UI sicher vom App-Prozess entkoppelt wird.
  • Kleine und große Containergrößen sind auf mobile Rasterformate abgestimmt, während die Jetpack Glance Wear APIs die Abwärtskompatibilität bis Wear 4 aufrechterhalten.
  • Für die Migration vorhandener Tiles zu Widgets kann entweder eine empfohlene Dual-Service-Strategie oder ein einzelner Kompatibilitätsdienst verwendet werden.

Fähigkeiten

Ressourcen

Gemini Notebook

Themen

  • wear-os
  • wear-widgets
  • jetpack-glance
  • remote-compose
  • tiles-migration
  • jetpack-compose

Transkript

Einführung

  • Hallo zusammen.

  • Mein Name ist Frank und ich leite das Team für Glanceable Experiences für Wear OS.

  • Smartwatches sind für Nutzer unverzichtbare Begleiter im Alltag. Heute zeigen wir euch, wie ihr die neuen Wear-Widgets optimal nutzen könnt.

  • Wir sehen uns zwei Möglichkeiten an, wie Sie Ihre wichtigsten Funktionen direkt auf das Handgelenk Ihrer Nutzer bringen können. Dazu verwenden wir die neuesten APIs, die so konzipiert sind, dass sich die Entwicklung für Wear OS für Android-Entwickler wie Sie vertraut und intuitiv anfühlt.

  • Mein brillanter Kollege und Desk-Buddy Yuri wird mir heute auf der Bühne helfen, genau zu erklären, wie wir das machen.

  • Hallo, ich bin Yuri Schimke.

  • Ich bin Engineer im Wear OS-Team und arbeite hauptsächlich an den Remote Compose APIs.

  • Ich gebe Ihnen heute eine kurze Einführung in Remote Compose und spreche auch über den Prozess der Migration von Kacheln zu Wear-Widgets.

  • Damit übergebe ich wieder an Frank.

Von Tiles zu Wear-Widgets

  • Super.

  • Vielen Dank, Yuri.

  • Wir bringen die Smartwatch offiziell in das einheitliche Android-Widget-Ökosystem, indem wir unsere Vollbild-Tiles zu Wear-Widgets weiterentwickeln.

  • Und da diese Funktionen jetzt mit Jetpack Glance erstellt werden, können Sie sehen, dass es sich um dieselbe zugrunde liegende Infrastruktur handelt, die auch für mobile Widgets verwendet wird.

  • Sie werden sehen, dass die Entwicklung für Wear OS in Zukunft noch vertrauter wird.

Übersicht über Remote Compose

  • Um diese Funktionen zu ermöglichen, entwickeln wir den zugrunde liegenden UI-Stack weiter.

  • Wear-Widgets basieren auf Remote Compose, einem neuen Android-Framework für Remote-UIs.

  • Das bedeutet, dass Sie endlich eine deklarative API verwenden können, die eng auf Jetpack Compose abgestimmt ist, um Ihre Out-of-App-Inhalte wie Widgets zu erstellen.

  • Remote Compose bietet moderne Grafiken und responsive Layouts für Widgets und sorgt gleichzeitig für Leistung und Akkueffizienz.

  • Und durch die Entkopplung der Benutzeroberfläche vom Anwendungsprozess werden Ihre Widgets sicher serialisiert und können auf einer Vielzahl von Android-Geräten gerendert werden.

  • So können Sie diese vielfältigen Funktionen mit minimalem Aufwand auf so gut wie allen unterstützten Remote-Oberflächen bereitstellen.

Layoutgrößen und Abwärtskompatibilität

  • Damit der Übergang von Kacheln zu Widgets so reibungslos wie möglich verläuft, führen wir zwei Standardcontainergrößen ein: klein und groß.

  • Diese sind so konzipiert, dass sie den 2 × 1- und 2 × 2-Raster-Widget-Formaten ähneln, die Sie bereits von der Erstellung von Widgets auf Mobilgeräten kennen.

  • Ab Wear 7 werden Ihre Wear-Widgets bei einigen Hardwareherstellern in einer vertikal scrollbaren Leiste neben den Widgets anderer Apps angezeigt.

  • So sehen Wear-Widgets beispielsweise auf einer Samsung Galaxy Watch aus.

  • Die Plattformarchitektur ist so konzipiert, dass sie vertikal scrollbare Ablagen unterstützt, da Hardwarehersteller dieses Muster in Zukunft übernehmen werden.

  • Und Sie müssen sich keine Sorgen machen, dass Sie Nutzer oder bestehende Nutzer zurücklassen.

  • Die Jetpack Glance Wear APIs sind so konzipiert, dass sie abwärtskompatibel bis Wear 4 sind.

  • Auf älteren Geräten, die eine horizontale Ablage für Widgets oder Kacheln unterstützen, übernimmt das System die Verwaltung für dich.

  • Sie müssen in Ihrem Code lediglich angeben, dass Sie den Vollbildmodus unterstützen möchten. Das System nimmt dann Ihr großes Widget und zeigt es auf diesen Geräten als herkömmliche Kachel im Vollbildmodus an.

  • Yuri zeigt uns jetzt anhand des Codes, wie ihr eure vorhandenen Tiles zu Wear-Widgets migrieren könnt.

Migrationsstrategien für vorhandene Kacheln

  • Danke, Frank.

  • Wenn Sie ein neues Wear-Widget von Grund auf erstellen, ist das ganz einfach.

  • Sie müssen nur einen GlanceWearWidgetService definieren und Ihre Benutzeroberfläche mit Remote Compose erstellen.

  • Wenn deine App jedoch bereits eine Kachel in der Produktion hat, musst du sowohl deine Dienste, die Benutzeroberfläche als auch die Daten migrieren.

  • Damit die Umstellung so reibungslos wie möglich verläuft, haben wir Möglichkeiten geschaffen, die bestehenden Nutzern eine nahtlose Nutzung ermöglichen.

  • Für den Dienst können Sie zwischen zwei Strategien wählen: Dual-Service oder Single-Service.

  • Bei der von uns empfohlenen dualen Bereitstellung behalten Sie den vorhandenen „TileService“ für ältere Wear OS-Geräte bei und fügen einen neuen „GlanceWearWidgetService“ für Wear OS 7 und höher hinzu.

  • Im Gegensatz dazu erstellen Sie mit dem einzelnen Dienst einen neuen, einzelnen GlanceWearWidgetService, der beide Protokolle verarbeitet und in einem Kompatibilitätsmodus ausgeführt wird, um Ihr Widget auf älteren Geräten in eine Tile zu übersetzen.

  • In beiden Fällen erreichen Sie Ihre Nutzer weiterhin.

  • Es wirkt sich nur darauf aus, welchen Code Sie in Zukunft verwalten.

  • Auf der Android-Entwicklerwebsite haben wir eine umfassende Dokumentation veröffentlicht, in der genau beschrieben wird, wie und wann diese beiden Muster verwendet werden sollten.

  • Dort erfährst du, welche Strategie für deine App am besten geeignet ist.

Benutzeroberfläche mit Remote Compose erstellen

  • Kommen wir nun zur UI-Ebene.

  • Bei der Migration ändert sich Ihr Gedankenmodell.

  • Bisher wurde für Tiles ProtoLayout verwendet, eine Bibliothek, die ausschließlich für Wear-Tiles entwickelt wurde.

  • Wear-Widgets hingegen basieren auf Remote Compose.

  • Remote Compose basiert auf derselben Grundlage wie die Jetpack Compose-UI und verwendet denselben Core-Compiler und dieselbe Laufzeit.

  • Die Benutzeroberfläche ist so gestaltet, dass sie vertraut wirkt und eng an Compose-UI-Bibliotheken wie Foundation und Material 3 angelehnt ist.

  • Anstelle von Box, Column oder Text verwenden Sie einfach RemoteBox, RemoteColumn oder RemoteText.

  • In diesem Fall werden Sie sich daran gewöhnen, oft „remote“ zu schreiben.

  • Außerdem haben wir eine Reihe von sofort einsatzbereiten Remote Compose-Komponenten speziell für Widgets hinzugefügt, um Ihren Workflow zu beschleunigen.

  • Das bedeutet, dass Ihre vorhandenen Wear Material 3-Designs, einschließlich derer in Figma, ganz einfach an Widgets angepasst werden können. Das ist sowohl für Entwickler als auch für Designer vertraut.

  • Sehen wir uns ein konkretes Beispiel an, um zu verdeutlichen, wie gut diese Frameworks aufeinander abgestimmt sind.

  • Wir erstellen unsere eigene App-Komponente auf Basis des Material 3-Schalters.

  • Links sehen Sie die Standard-Wear Compose-Komponente, die Sie in Ihrer App schreiben würden, und rechts die Remote Compose-Version.

    // Wear Compose
    @Composable
    fun HistoryEraserButton(
      onClick: () -> Unit,
      modifier: Modifier = Modifier,
      checked: Boolean = true,
      onCheckedChange: (Boolean) -> Unit = {},
    )
    // Wear RemoteCompose
    @RemoteComposable
    @Composable
    fun RemoteHistoryEraserButton(
      onClick: Action,
      modifier: RemoteModifier = RemoteModifier,
      checked: RemoteBoolean = true.rb,
      onCheckedChange: Action = Action.Empty,
    )
  • Wie Sie sehen, sind die Muster sehr ähnlich.

  • Wenn Sie Remote Compose-Code schreiben, verwenden Sie jedoch die Annotation RemoteComposable.

  • Und wenn Sie vertraute Parameter wie „modifier“ und „boolean“ verwenden, gibt es entsprechende Remote Compose-Typen wie RemoteModifier und RemoteBoolean.

  • Und Sie verwenden Aktionen anstelle von Lambdas.

  • Anstatt den auszuführenden Code innerhalb des Lambda-Ausdrucks zu definieren, müssen Sie eine Aktion deklarieren, z. B. das Starten eines ausstehenden Intents, das Aufrufen eines Dienstes oder das Aktualisieren eines Werts.

  • Sehen wir uns den Textkörper der Komponente an.

  • Sogar das Design ist aufeinander abgestimmt.

  • Sie können Ihr Material 3-Farbschema auf die Remote Compose-Komponente genauso anwenden wie in Standard-Compose. So passt Ihr Widget genau zum Branding Ihrer App.

    // Wear Compose
      val colors = MaterialTheme.colorScheme
      SplitSwitchButton(
        checked = checked,
        onCheckedChange = onCheckedChange,
        toggleContentDescription = "Arm Eraser",
        onContainerClick = onClick,
        modifier = modifier.fillMaxWidth(),
        colors =
          SwitchButtonDefaults.splitSwitchButtonColors(
            checkedContainerColor = colors.errorContainer, …
          ),
        label = { Text("History Eraser") },
        secondaryLabel = { Text("Don't touch it!") },
      )
    // Wear RemoteCompose
      val colors = RemoteMaterialTheme.colorScheme
      RemoteSplitSwitchButton(
        checked = checked,
        onCheckedChange = onCheckedChange,
        toggleContentDescription = "Arm Eraser".rs,
        onContainerClick = onClick,
        modifier = modifier.fillMaxWidth(),
        colors =
          RemoteSplitSwitchButtonDefaults.splitSwitchButtonColors(
            checkedContainerColor = colors.errorContainer, …
          ),
        label = { RemoteText("History Eraser".rs) },
        secondaryLabel = { RemoteText("Don't touch it!".rs) },
      )
  • Sehen wir uns nun das Ergebnis an. Die resultierende Benutzeroberfläche ist genau das, was Sie erwarten: ein sauberer, responsiver, animierter Schalter.

Compose-Vorschauunterstützung für Widgets

  • Es ist schwer, den Unterschied zu erkennen.

  • Damit Sie Ihre Widget-Layouts entwerfen, iterieren und prüfen können, ohne sie immer auf einem Gerät oder Emulator ausführen zu müssen, haben wir die Compose-Vorschau für Wear-Widgets hinzugefügt.

  • Da Widgets von OEM-spezifischen Container-Einschränkungen wie Größe, Form, Innenabstand und Eckenradius abhängen, bauen wir auf der Standard-Vorschauannotation auf, verwenden aber einen zusätzlichen Parameteranbieter, um diese Konfigurationen einzufügen.

    @Preview(name = "Squircle Preview", device = "spec:width=1000dp,height=1000dp,dpi=320")
    @Composable
    fun WeatherWidgetSquirclePreview(
        @PreviewParameter(SquircleAllWidgetPreviewParams::class) params: WearWidgetParams
    ) = WearWidgetPreview(MockWeatherWidget(65, WeatherCondition.CLOUDY), params)
    
    @Preview(name = "Round Preview", device = "spec:width=1000dp,height=1000dp,dpi=320")
    @Composable
    fun WeatherWidgetRoundPreview(
        @PreviewParameter(RoundAllWidgetPreviewParams::class) params: WearWidgetParams
    ) = WearWidgetPreview(MockWeatherWidget(65, WeatherCondition.CLOUDY), params)
  • Die Bibliothek bietet vorkonfigurierte PreviewParameter-Anbieterimplementierungen, die echten Wear OS-Geräten entsprechen.

  • Mit diesen Vorschau-Annotationen können Sie Ihr Layout ganz einfach auf verschiedenen Hardwareformen prüfen.

  • Sie können diese Vorschauen nicht nur zum Testen Ihrer Layouts verwenden, sondern auch, um die statischen Bilder zu generieren, die für die Widget-Auswahl auf dem Gerät erforderlich sind.

    @Preview(name = "Widget Picker Preview", device = "spec:width=1000dp,height=1000dp,dpi=320")
    @Composable
    fun WeatherWidgetRectangularPreview(
        @PreviewParameter(RectangularAllWidgetPreviewParams::class) params: WearWidgetParams
    ) = WearWidgetPreview(MockWeatherWidget(65, WeatherCondition.CLOUDY), params)
  • Die Bibliothek bietet einen Parameter für eine rechteckige Vorschauform, die die vollständigen, nicht zugeschnittenen rechteckigen Container emuliert.

Beispielimplementierung und Migrationstools

  • Wenn ihr sehen möchtet, wie genau das in der Praxis funktioniert, könnt ihr euch unsere aktualisierte Horologist-Beispiel-App für Medien ansehen. Sie enthält ein neues Widget, das diese API demonstriert.

  • Wir zeigen, wie du sowohl die kleine als auch die große Version des Medien-Widgets hinzufügst.

  • Auf GitHub kannst du dir einen direkten Vergleich des alten ProtoLayout-Codes und der neuen Remote Compose-Implementierung ansehen.

  • Wir sind uns sicher, dass es dir gefallen wird.

  • Und schließlich wäre dies kein Vortrag, wenn wir nicht auch KI erwähnen würden.

  • Um die Migration zu beschleunigen, haben wir auf GitHub eine neue Anleitung zur Migration von Kacheln zu Widgets veröffentlicht.

  • Sie können sie mit dem KI-Agenten Ihrer Wahl verwenden oder über die Android CLI direkt in Android Studio einbinden.

    android skills add wear-widgets
    android skills add --all
  • Jetzt übergebe ich wieder an François, der die Präsentation abschließen wird.

Zusammenfassung

  • Vielen Dank.

  • Vielen Dank, Yuri.

  • Wie Sie sehen, ist die Weiterentwicklung Ihrer Tiles zu Wear-Widgets ein sehr unkomplizierter Prozess.

  • Zusammenfassend lässt sich sagen, dass Sie durch die Migration zu Jetpack Glance Wear und Remote Compose nicht nur Ihre Benutzeroberfläche aktualisieren, sondern auch Teil eines einheitlicheren Android-Widget-Ökosystems werden.

  • Mit einem modernen, deklarativen Framework, das vollständig auf Jetpack Compose abgestimmt ist, kannst du jetzt ansprechende, abwärtskompatible Widgets erstellen.

  • Du kannst noch heute mit der Migration deiner Tiles beginnen. Weitere Informationen findest du im Startleitfaden auf der Android-Entwickler-Website.

  • Wir freuen uns schon auf die ausdrucksstarken, übersichtlichen Erlebnisse, die du für das Handgelenk entwickeln wirst.

  • Vielen Dank, dass Sie heute Morgen dabei waren. Wir wünschen Ihnen noch eine schöne Konferenz.

  • Vielen Dank.

Code

Code-Snippets

Kotlin-Snippet bei 06:40 in „Building UI with Remote Compose“
// Wear Compose
@Composable
fun HistoryEraserButton(
  onClick: () -> Unit,
  modifier: Modifier = Modifier,
  checked: Boolean = true,
  onCheckedChange: (Boolean) -> Unit = {},
)
Kotlin-Snippet bei 06:40 in „Building UI with Remote Compose“
// Wear RemoteCompose
@RemoteComposable
@Composable
fun RemoteHistoryEraserButton(
  onClick: Action,
  modifier: RemoteModifier = RemoteModifier,
  checked: RemoteBoolean = true.rb,
  onCheckedChange: Action = Action.Empty,
)
Kotlin-Snippet bei 07:39 in „Building UI with Remote Compose“
// Wear Compose
  val colors = MaterialTheme.colorScheme
  SplitSwitchButton(
    checked = checked,
    onCheckedChange = onCheckedChange,
    toggleContentDescription = "Arm Eraser",
    onContainerClick = onClick,
    modifier = modifier.fillMaxWidth(),
    colors =
      SwitchButtonDefaults.splitSwitchButtonColors(
        checkedContainerColor = colors.errorContainer, …
      ),
    label = { Text("History Eraser") },
    secondaryLabel = { Text("Don't touch it!") },
  )
Kotlin-Snippet bei 07:39 in „Building UI with Remote Compose“
// Wear RemoteCompose
  val colors = RemoteMaterialTheme.colorScheme
  RemoteSplitSwitchButton(
    checked = checked,
    onCheckedChange = onCheckedChange,
    toggleContentDescription = "Arm Eraser".rs,
    onContainerClick = onClick,
    modifier = modifier.fillMaxWidth(),
    colors =
      RemoteSplitSwitchButtonDefaults.splitSwitchButtonColors(
        checkedContainerColor = colors.errorContainer, …
      ),
    label = { RemoteText("History Eraser".rs) },
    secondaryLabel = { RemoteText("Don't touch it!".rs) },
  )
Kotlin-Snippet bei 08:20 in „Compose preview support for widgets“
@Preview(name = "Squircle Preview", device = "spec:width=1000dp,height=1000dp,dpi=320")
@Composable
fun WeatherWidgetSquirclePreview(
    @PreviewParameter(SquircleAllWidgetPreviewParams::class) params: WearWidgetParams
) = WearWidgetPreview(MockWeatherWidget(65, WeatherCondition.CLOUDY), params)

@Preview(name = "Round Preview", device = "spec:width=1000dp,height=1000dp,dpi=320")
@Composable
fun WeatherWidgetRoundPreview(
    @PreviewParameter(RoundAllWidgetPreviewParams::class) params: WearWidgetParams
) = WearWidgetPreview(MockWeatherWidget(65, WeatherCondition.CLOUDY), params)
Kotlin-Snippet bei 08:58 in „Compose preview support for widgets“
@Preview(name = "Widget Picker Preview", device = "spec:width=1000dp,height=1000dp,dpi=320")
@Composable
fun WeatherWidgetRectangularPreview(
    @PreviewParameter(RectangularAllWidgetPreviewParams::class) params: WearWidgetParams
) = WearWidgetPreview(MockWeatherWidget(65, WeatherCondition.CLOUDY), params)
Shell-Snippet bei 09:58 in „Beispielimplementierung und Migrationstools“
android skills add wear-widgets
android skills add --all