Основы работы с приложениями

Как превратить карточки в виджеты Wear OS с помощью Jetpack Glance и Remote Compose

Колонки

  • Франсуа Дешен (François Deschênes)

    Менеджер по продукту

  • Юрий Шимке (Yuri Schimke)

    Разработчик ПО

О приложении

О выступлении

На этом занятии мы подробно рассмотрим технические аспекты перехода от полноэкранных карточек к динамическим виджетам Wear OS частичной высоты.

Мы рассмотрим технические преимущества унифицированного Jetpack Glance API и нового движка Remote Compose, а также подробно расскажем, как они сочетаются с Modern Android Development.

В конце мы поделимся последними рекомендациями по UX и рассмотрим практические стратегии перехода, которые помогут вам создать интерфейс следующего поколения.

Сводка

  • Полноэкранные карточки Wear OS заменяются виджетами Wear, созданными с помощью Jetpack Glance и Remote Compose.
  • Remote Compose использует тот же компилятор и среду выполнения, что и Jetpack Compose UI, но при этом безопасно отделяет интерфейс от процесса приложения.
  • Маленькие и большие контейнеры соответствуют форматам сетки для мобильных устройств, а Jetpack Glance Wear API поддерживает обратную совместимость вплоть до Wear 4.
  • Перенести существующие карточки в виджеты можно с помощью рекомендуемой стратегии двойного сервиса или единого сервиса совместимости.

Умение

Ресурсы

Gemini Notebook

Темы

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

Расшифровка

Введение

  • Всем привет!

  • Меня зовут Фрэнк, и я руковожу разработкой виджетов для Wear OS.

  • Часы – это незаменимый спутник пользователей в течение всего дня. Сегодня мы расскажем, как использовать новые виджеты Wear OS.

  • Мы рассмотрим два способа доставки наиболее важных функций прямо на запястье ваших пользователей, и мы сделаем это с помощью новейших API, API, которые разработаны для того, чтобы сделать создание для Wear OS знакомым и интуитивно понятным для разработчиков Android, таких как вы.

  • Сегодня на сцене ко мне присоединится мой замечательный коллега и сосед по рабочему столу Юрий. Он расскажет, как именно мы это делаем.

  • Здравствуйте! Меня зовут Юрий Шимке.

  • Я инженер в команде Wear OS и в основном работаю с Remote Compose API.

  • Сегодня я кратко расскажу о Remote Compose, а также о процессе перехода от карточек к виджетам Wear.

  • А теперь я передаю слово Фрэнку.

Переход от карточек к виджетам Wear OS

  • Отлично!

  • Спасибо, Юрий.

  • Мы официально добавляем наручные устройства в единую экосистему виджетов Android, превращая полноэкранные карточки в виджеты Wear OS.

  • А поскольку эти виджеты теперь создаются с помощью Jetpack Glance, вы можете видеть, что используется та же базовая инфраструктура, что и для мобильных виджетов.

  • В будущем вы увидите, что разработка для Wear OS станет для вас ещё более привычной.

Общие сведения об удаленном создании писем

  • Чтобы обеспечить работу этих функций, мы развиваем стек пользовательского интерфейса – базовый стек пользовательского интерфейса.

  • Виджеты Wear OS работают на базе Remote Compose – нового фреймворка Android для удаленного интерфейса.

  • Это означает, что вы наконец-то можете использовать декларативный API, тесно связанный с Jetpack Compose, для создания элементов за пределами приложения, например виджетов.

  • Remote Compose позволяет использовать в виджетах современную графику и адаптивные макеты, обеспечивая при этом высокую производительность и эффективное энергопотребление.

  • А благодаря тому, что интерфейс отделен от процесса приложения, виджеты безопасно сериализуются и легко отрисовываются на самых разных устройствах Android.

  • Это означает, что вы можете с минимальными усилиями перенести этот богатый опыт практически на все поддерживаемые удаленные поверхности.

Размеры макетов и обратная совместимость

  • Чтобы переход от карточек к виджетам был максимально плавным, мы вводим два стандартных размера контейнеров: маленький и большой.

  • Они похожи на сетки 2 x 1 и 2 x 2, которые вы уже используете для мобильных виджетов.

  • Начиная с Wear OS 7 на устройствах некоторых производителей виджеты Wear OS будут находиться в вертикально прокручиваемой панели вместе с виджетами других приложений.

  • Вот как виджеты Wear OS выглядят на Samsung Galaxy Watch.

  • Архитектура платформы поддерживает вертикальную прокрутку, поскольку производители устройств могут начать использовать ее в будущем.

  • И вам не нужно беспокоиться о том, что вы оставите каких-либо пользователей или существующих пользователей позади.

  • API Jetpack Glance Wear поддерживают обратную совместимость вплоть до Wear 4.

  • На более старых устройствах, которые поддерживают горизонтальную панель виджетов или карточек, система будет обрабатывать это за вас.

  • Вам нужно лишь указать в коде, что вы хотите поддерживать полноэкранный режим, и система будет показывать ваш большой виджет как полноэкранную карточку на таких устройствах.

  • Теперь Юрий покажет нам код и расскажет, как перенести существующие карточки в виджеты Wear.

Стратегии переноса существующих карточек

  • Спасибо, Франк.

  • Если вы создаете виджет для Wear OS с нуля, это просто.

  • Вы просто определяете GlanceWearWidgetService и создаете пользовательский интерфейс с помощью Remote Compose.

  • Если же в вашем приложении уже есть карточка, вам нужно будет перенести сервисы, интерфейс и данные.

  • Чтобы переход был максимально плавным, мы предусмотрели несколько вариантов для существующих пользователей.

  • Вы можете выбрать одну из двух стратегий: двойное или одиночное обслуживание.

  • Мы рекомендуем использовать двойной сервис: оставить TileService для старых устройств Wear OS и добавить GlanceWearWidgetService для Wear OS 7 и более поздних версий.

  • В отличие от этого, при использовании одного сервиса вы создаете новый, единый GlanceWearWidgetService, который обрабатывает оба протокола, работая в режиме совместимости для преобразования вашего виджета в плитку на старых устройствах.

  • В любом случае вы сможете обращаться к пользователям.

  • Это влияет только на то, какой код вы будете поддерживать в дальнейшем.

  • На сайте для разработчиков Android мы опубликовали подробную документацию о том, как и когда использовать эти шаблоны.

  • Ознакомьтесь с ним, чтобы узнать, какая стратегия лучше всего подходит для вашего приложения.

Как создать интерфейс с помощью Remote Compose

  • Теперь поговорим о слое пользовательского интерфейса.

  • При миграции ваша ментальная модель меняется.

  • Раньше для карточек использовалась библиотека ProtoLayout, созданная специально для карточек Wear OS.

  • Виджеты Wear OS, напротив, создаются на основе Remote Compose.

  • Remote Compose использует ту же основу, что и Jetpack Compose UI, включая компилятор и среду выполнения.

  • Он разработан так, чтобы быть похожим на библиотеки Compose UI, такие как foundation и Material 3.

  • Но вместо поля, столбца или текста вы просто заменяете их на RemoteBox, RemoteColumn или RemoteText.

  • В этом случае вы часто будете писать слово "remote".

  • Чтобы ускорить рабочий процесс, мы включили полный набор готовых к использованию компонентов Remote Compose, разработанных специально для виджетов.

  • Это значит, что существующие дизайны Wear Material 3, в том числе созданные в Figma, можно легко адаптировать для виджетов. Это удобно и для инженеров, и для дизайнеров.

  • Чтобы увидеть, насколько согласованы эти фреймворки, давайте рассмотрим конкретный пример.

  • Мы создадим собственный компонент приложения на основе кнопки-переключателя Material 3.

  • Слева показан стандартный компонент Wear Compose, который вы можете написать в своем приложении, а справа – версия Remote Compose.

    // 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,
    )
  • Обратите внимание, насколько похожи эти паттерны.

  • Однако при написании кода Remote Compose вы будете использовать аннотацию RemoteComposable.

  • А там, где вы используете знакомые параметры, такие как модификатор и логическое значение, у них есть эквивалентные типы Remote Compose, такие как RemoteModifier и RemoteBoolean.

  • И вы используете действия вместо лямбд.

  • Вместо того чтобы определять код, который будет выполняться внутри лямбда-выражения, вам нужно будет объявить действие, например запуск отложенного интента, вызов сервиса или обновление значения.

  • Перейдем к телу компонента.

  • Вы заметите, что даже тема выровнена.

  • Вы можете применить цветовую схему Material 3 к компоненту Remote Compose точно так же, как и в стандартном 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!") },
      )
    // 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) },
      )
  • И наконец, давайте посмотрим на результат. В результате получается именно то, что вы ожидаете: чистая, отзывчивая, анимированная кнопка-переключатель.

Поддержка предварительного просмотра виджетов в Compose

  • Разницу сложно заметить.

  • Чтобы вы могли проектировать, дорабатывать и проверять макеты виджетов, не запуская их на устройстве или в эмуляторе, мы добавили поддержку предварительного просмотра Compose для виджетов Wear.

  • Поскольку виджеты зависят от ограничений контейнера, специфичных для OEM, таких как размер, форма, отступы, радиус угла, мы строим на основе стандартной аннотации предварительного просмотра, но с дополнительным поставщиком параметров для внедрения этих конфигураций.

    @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)
  • Библиотека содержит предварительно настроенные реализации поставщика PreviewParameter, которые соответствуют реальным устройствам Wear OS.

  • Эти аннотации предварительного просмотра позволяют легко проверить макет на различных аппаратных формах.

  • Помимо тестирования макетов, вы можете использовать эти предварительные просмотры для создания статических изображений, необходимых для выбора виджетов на устройстве.

    @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)
  • В библиотеке есть параметр для прямоугольной формы предварительного просмотра, который имитирует полные необрезанные прямоугольные контейнеры.

Примеры реализации и инструменты переноса

  • Чтобы посмотреть, как это работает на практике, мы обновили пример приложения Horologist, добавив в него новый виджет, демонстрирующий возможности этого API.

  • Мы покажем, как добавить маленький и большой виджеты.

  • На GitHub вы можете сравнить устаревший код ProtoLayout и новую реализацию Remote Compose.

  • Уверены, вам понравится.

  • И наконец, в наше время ни один разговор не обходится без упоминания ИИ.

  • Чтобы ускорить переход, мы опубликовали на GitHub новый навык переноса карточек в виджеты.

  • Вы можете использовать его с выбранным агентом или добавить непосредственно в Android Studio с помощью Android CLI.

    android skills add wear-widgets
    android skills add --all
  • А теперь я передаю слово Франсуа, который подведет итоги.

Краткое содержание и заключение

  • Спасибо!

  • Спасибо, Юрий.

  • Как видите, превратить карточки в виджеты Wear OS очень просто.

  • Подведем итоги. Переходя на Jetpack Glance Wear и Remote Compose, вы не просто обновляете интерфейс, а присоединяетесь к более унифицированной экосистеме виджетов Android.

  • Теперь вы можете создавать красивые виджеты с обратной совместимостью, используя современный декларативный фреймворк, полностью совместимый с Jetpack Compose.

  • Вы можете начать перенос карточек уже сегодня. Для этого ознакомьтесь с руководством по началу работы на сайте Android для разработчиков.

  • Мы с нетерпением ждем ваших новых выразительных и удобных приложений для носимых устройств.

  • Большое спасибо, что присоединились к нам сегодня утром. Надеемся, что вам понравится остальная часть конференции.

  • Спасибо!

Код

Фрагменты кода

Фрагмент кода на Kotlin в видео "Создание пользовательского интерфейса с помощью Remote Compose"
// Wear Compose
@Composable
fun HistoryEraserButton(
  onClick: () -> Unit,
  modifier: Modifier = Modifier,
  checked: Boolean = true,
  onCheckedChange: (Boolean) -> Unit = {},
)
Фрагмент кода на Kotlin в видео "Создание интерфейса с помощью Remote Compose"
// Wear RemoteCompose
@RemoteComposable
@Composable
fun RemoteHistoryEraserButton(
  onClick: Action,
  modifier: RemoteModifier = RemoteModifier,
  checked: RemoteBoolean = true.rb,
  onCheckedChange: Action = Action.Empty,
)
Фрагмент кода на Kotlin в видео "Создание интерфейса с помощью 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 в видео "Создание интерфейса с помощью 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 на 08:20 в видео "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 на 08:58 в видео "Поддержка предварительного просмотра Compose для виджетов"
@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 на 09:58 в разделе "Пример реализации и инструменты переноса"
android skills add wear-widgets
android skills add --all