Mostrar atualizações periódicas em blocos

Crie blocos com conteúdo que muda com o passar do tempo.

Trabalhar com linhas do tempo

Uma linha do tempo consiste em uma ou mais instâncias de TimelineEntry, cada uma com um layout mostrado durante um intervalo de tempo específico. Todos os blocos precisam de uma linha do tempo.

Diagrama da linha do tempo em blocos

Blocos de entrada única

Geralmente, um bloco pode ser descrito com uma única TimelineEntry. O layout é fixo, e apenas as informações contidas nele mudam. Por exemplo, um bloco que mostra o progresso de condicionamento físico do dia sempre tem o mesmo layout de progresso, embora você possa o ajustar para mostrar valores diferentes. Nesses casos, você não sabe com antecedência quando o conteúdo pode mudar.

Confira este exemplo de um bloco com uma única TimelineEntry:

Kotlin

override fun onTileRequest(
    requestParams: 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(...)
        ).build()
    return Futures.immediateFuture(tile)
}

Java

@Override
protected ListenableFuture<Tile> onTileRequest(
       @NonNull TileRequest requestParams
) {
   Tile tile = new 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(...)
       ).build();
   return Futures.immediateFuture(tile);
}

Entradas da linha do tempo com prazos

Uma TimelineEntry pode definir um período de validade, permitindo que um bloco mude o layout em um momento conhecido sem precisar que o app envie um novo bloco.

O exemplo canônico é um bloco de agenda com uma lista de eventos futuros. Cada evento futuro contém um período de validade para indicar quando será mostrado.

A API Tiles permite a sobreposição de períodos de validade, em que a tela com o menor período restante é mostrada. Apenas um evento aparece por vez.

Os desenvolvedores podem fornecer uma entrada substituta padrão. Por exemplo, o bloco de agenda pode ter um bloco com um período de validade infinito, que vai ser usado se nenhuma outra entrada da linha do tempo for válida, conforme mostrado no exemplo de código abaixo:

Kotlin

public override fun onTileRequest(
    requestParams: 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(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(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.
                TimeInterval.Builder()
                    .setEndMillis(meeting.dateTimeMillis).build()
            ).build()
        )
    }

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

Java

@Override
protected ListenableFuture<Tile> onTileRequest(
       @NonNull RequestBuilders.TileRequest requestParams
) {
   Timeline.Builder timeline = new Timeline.Builder();
   // Add fallback "no meetings" entry
   // Use the version of TimelineEntry that's in androidx.wear.protolayout.
   timeline.addTimelineEntry(new TimelineEntry.Builder().setLayout(getNoMeetingsLayout()).build());
   // Retrieve a list of scheduled meetings
   List<Meeting> meetings = MeetingsRepo.getMeetings();
   // Add a timeline entry for each meeting
   for(Meeting meeting : meetings) {
        timeline.addTimelineEntry(new 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.
                new TimeInterval.builder()
                    .setEndMillis(meeting.getDateTimeMillis()).build()
            ).build()
        );
    }

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

Atualizar um bloco

As informações mostradas em um bloco podem expirar depois de um tempo. Por exemplo, um bloco de clima que mostra a mesma temperatura ao longo do dia não é exato.

Para gerenciar dados prestes a expirar, defina um intervalo de atualização no momento da criação do bloco, que especifica por quanto tempo ele é válido. No exemplo do bloco de clima, você pode atualizar o conteúdo a cada hora, conforme mostrado no exemplo de código abaixo:

Kotlin

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

Java

@Override
protected ListenableFuture<Tile> onTileRequest(
       @NonNull TileRequest requestParams
) {
    return Futures.immediateFuture(new Tile.Builder()
        .setResourcesVersion(RESOURCES_VERSION)
        .setFreshnessIntervalMillis(60 * 60 * 1000) // 60 minutes
        .setTimeline(Timeline.fromLayoutElement(
            getWeatherLayout())
        ).build());
}

Quando você define um intervalo de atualização, o sistema chama onTileRequest() logo após o fim do intervalo. Se você não definir um intervalo de atualização, o sistema não vai chamar onTileRequest().

Um bloco também pode expirar devido a um evento externo. Por exemplo, um usuário pode remover uma reunião da agenda dele. Se o bloco não for atualizado, ele ainda vai mostrar essa reunião excluída. Nesse caso, solicite uma atualização de qualquer lugar no código do aplicativo, conforme mostrado no exemplo de código abaixo:

Kotlin

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

Java

public void eventDeletedCallback() {
   TileService.getUpdater(context)
           .requestUpdate(MyTileService.class);
}

Escolher um fluxo de trabalho de atualização

Siga estas práticas recomendadas para determinar como configurar as atualizações de blocos:

  • Use uma linha do tempo se a atualização for previsível, por exemplo, no próximo evento da agenda do usuário.
  • Ao buscar dados da plataforma, use a vinculação de dados para que o sistema atualize os dados automaticamente.
  • Se a atualização puder ser calculada no dispositivo em um curto período, como atualizar a posição de uma imagem em um bloco do nascer do sol, use onTileRequest().

    Isso é particularmente útil quando você precisa gerar todas as imagens com antecedência. Se você precisar gerar uma nova imagem no futuro, chame setFreshnessIntervalMillis().

  • Se estiver fazendo trabalhos em segundo plano mais intensos repetidamente, como a pesquisa de dados meteorológicos, use WorkManager e envie atualizações ao bloco.

  • Se a atualização for em resposta a um evento externo, como acender as luzes, receber um e-mail ou atualizar uma nota, envie uma mensagem do Firebase Cloud Messaging (FCM) para ativar o app novamente e enviar as atualizações ao bloco.

  • Se o processo de sincronização de dados de bloco for caro, faça o seguinte:

    1. Programe uma sincronização de dados.
    2. Inicie um timer de 1 a 2 segundos.
    3. Se você receber uma atualização de uma fonte de dados remota antes que o tempo acabe, mostre o valor atualizado da sincronização de dados. Caso contrário, mostre um valor local armazenado em cache.