Показывать периодические обновления в элементах мозаики

Создавайте плитки с контентом, который меняется со временем.

Как работать с временными шкалами

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

Временная шкала плитки, включающая две записи, каждая из которых содержит макет.
Схема хронологии фрагментов.

Плитки с одним входом

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

Ниже приведен пример фрагмента кода с одним тегом TimelineEntry:

override fun onTileRequest(
    requestParams: RequestBuilders.TileRequest
): ListenableFuture<Tile?> {
    val tile =
        Tile.Builder()
            .setResourcesVersion(RESOURCES_VERSION)
            // We add a single timeline entry when our layout is fixed, and
            // we don't know in advance when its contents might change.
            .setTileTimeline(Timeline.fromLayoutElement(simpleLayout(this)))
            .build()
    return Futures.immediateFuture(tile)
}

Записи хронологии с временными ограничениями

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

Классический пример – это карточка повестки дня, на временной шкале которой перечислены предстоящие события. У каждого предстоящего события есть период действия, в течение которого оно будет показываться.

API тайлов позволяет задавать перекрывающиеся периоды действия, при этом показывается экран с самым коротким оставшимся периодом. Одновременно показывается только одно событие.

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

override fun onTileRequest(
    requestParams: RequestBuilders.TileRequest
): ListenableFuture<Tile?> {
    val timeline = Timeline.Builder()

    // Add fallback "no meetings" entry
    // Use the version of TimelineEntry that's in androidx.wear.protolayout.
    timeline.addTimelineEntry(
        TimelineBuilders.TimelineEntry.Builder().setLayout(getNoMeetingsLayout()).build()
    )

    // Retrieve a list of scheduled meetings
    val meetings = MeetingsRepo.getMeetings()
    // Add a timeline entry for each meeting
    meetings.forEach { meeting ->
        timeline.addTimelineEntry(
            TimelineBuilders.TimelineEntry.Builder()
                .setLayout(getMeetingLayout(meeting))
                .setValidity(
                    // The tile should disappear when the meeting begins
                    // Use the version of TimeInterval that's in
                    // androidx.wear.protolayout.
                    TimelineBuilders.TimeInterval.Builder()
                        .setEndMillis(meeting.dateTimeMillis)
                        .build()
                )
                .build()
        )
    }

    val tile =
        Tile.Builder()
            .setResourcesVersion(RESOURCES_VERSION)
            .setTileTimeline(timeline.build())
            .build()
    return Futures.immediateFuture(tile)
}

Как обновить карточку

Информация на плитке может устареть. Например, если на виджете погоды в течение дня показывается одна и та же температура, это неточно.

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

override fun onTileRequest(
    requestParams: RequestBuilders.TileRequest
): ListenableFuture<Tile?> =
    Futures.immediateFuture(
        Tile.Builder()
            .setResourcesVersion(RESOURCES_VERSION)
            .setFreshnessIntervalMillis(60 * 60 * 1000) // 60 minutes
            .setTileTimeline(Timeline.fromLayoutElement(getWeatherLayout()))
            .build()
    )

Когда вы задаете интервал обновления, система вызывает onTileRequest() вскоре после окончания интервала. Если не задать интервал обновления, система не будет вызывать onTileRequest().

Срок действия карточки также может истечь из-за внешнего события. Например, пользователь может удалить встречу из календаря, но если плитка не обновится, она будет по-прежнему показывать удаленную встречу. В этом случае запросите обновление из любого места в коде приложения, как показано в следующем примере:

fun eventDeletedCallback() {
     TileService.getUpdater(context)
             .requestUpdate(MyTileService::class.java)
}

Как обновить рабочий процесс

Чтобы настроить обновление плиток, следуйте приведенным ниже рекомендациям.

  • Если обновление предсказуемо, например если оно относится к следующему мероприятию в календаре пользователя, используйте временную шкалу.
  • При получении данных платформы используйте привязку данных, чтобы система обновляла их автоматически.
  • Если обновление можно рассчитать на устройстве за короткое время, например при изменении положения изображения на карточке восхода солнца, используйте onTileRequest().

    Это особенно полезно, если вам нужно сгенерировать все изображения заранее. Если вам понадобится сгенерировать новое изображение, вызовите функцию setFreshnessIntervalMillis().

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

  • Если обновление связано с внешним событием, например включением света, получением письма или изменением заметки, отправьте сообщение Firebase Cloud Messaging (FCM), чтобы снова активировать приложение, а затем передать обновления на элемент.

  • Если синхронизация данных с виджета может быть дорогостоящей, сделайте следующее:

    1. Запланируйте синхронизацию данных.
    2. Запустить таймер на 1–2 секунды.
    3. Если вы получите обновление из удаленного источника данных до истечения времени ожидания, покажите обновленное значение из синхронизации данных. В противном случае покажите кешированное локальное значение.