Cómo mostrar actualizaciones periódicas en tarjetas

Crea tarjetas con contenido que cambie con el paso del tiempo.

Cómo trabajar con cronogramas

Un cronograma consta de una o más instancias de TimelineEntry, cada una de las cuales contiene un diseño que se muestra durante un intervalo específico. Todas las tarjetas necesitan un cronograma.

Diagrama del cronograma de tarjetas

Tarjetas de una sola entrada

Con frecuencia, una tarjeta se puede describir con un solo elemento TimelineEntry. El diseño es fijo y solo cambia la información dentro del diseño. Por ejemplo, una tarjeta que muestra tu progreso diario de entrenamiento siempre tendrá el mismo diseño de progreso, aunque puedes ajustar ese diseño para que muestre valores diferentes. En estos casos, no sabes de antemano el momento en que puede cambiar el contenido.

Consulta el siguiente ejemplo de una tarjeta con un solo elemento 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 del cronograma con un plazo

De manera opcional, un objeto TimelineEntry puede definir un período de validez, lo que permite que una tarjeta cambie su diseño en un momento conocido sin necesidad de que la app envíe una nueva tarjeta.

El ejemplo canónico es una tarjeta de agenda cuyo cronograma contiene una lista de eventos futuros. Cada evento futuro contiene un período de validez para indicar cuándo mostrarlo.

La API de Tiles permite superponer períodos de validez, en los que se muestra la pantalla con el período más corto restante. Solo se muestra un evento a la vez.

Los desarrolladores pueden proporcionar una entrada de resguardo predeterminada. Por ejemplo, la tarjeta de agenda podría tener una tarjeta con un período de validez infinito, que se utiliza si ninguna otra entrada de cronograma es válida, como se indica en la siguiente muestra de código:

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

Cómo actualizar una tarjeta

La información que se muestra en una tarjeta puede vencerse después de un tiempo. Por ejemplo, una tarjeta del clima que muestra la misma temperatura durante el día no es precisa.

Para procesar datos que vencen, establece un intervalo de actualización cuando creas una tarjeta que especifique el tiempo de validez de la tarjeta. En el ejemplo de la tarjeta del clima, puedes actualizar el contenido cada hora, como se indica en la siguiente muestra de código:

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

Cuando configuras un intervalo de actualización, el sistema llama a onTileRequest() poco después de que finaliza el intervalo. Si no estableces un intervalo de actualización, el sistema no llamará a onTileRequest().

Una tarjeta también puede vencer por un evento externo. Por ejemplo, un usuario podría quitar una reunión de su calendario y, si no se actualizara la tarjeta, aún mostraría esa reunión borrada. En este caso, solicita una actualización desde cualquier lugar del código de la aplicación, como se indica en la siguiente muestra de código:

Kotlin

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

Java

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

Cómo elegir un flujo de trabajo de actualización

Usa estas prácticas recomendadas para determinar cómo configurar las actualizaciones de tus tarjetas:

  • Si la actualización es predecible (por ejemplo, si es para el próximo evento del calendario del usuario), usa un cronograma.
  • Cuando recuperes los datos de la plataforma, usa la vinculación de datos para que el sistema actualice los datos automáticamente.
  • Si la actualización se puede calcular en el dispositivo en una pequeña cantidad de tiempo (como actualizar la posición de una imagen en una tarjeta del amanecer), usa onTileRequest().

    Esto es muy útil cuando necesitas generar todas las imágenes con anticipación. Si necesitas generar una imagen nueva en el futuro, llama a setFreshnessIntervalMillis().

  • Si realizas tareas más intensas en segundo plano con regularidad, como sondear datos del clima, usa WorkManager y envía actualizaciones a tu tarjeta.

  • Si la actualización es en respuesta a un evento externo (como luces que se encienden, recibir un correo electrónico o actualizar una nota), envía un mensaje de Firebase Cloud Messaging (FCM) para volver a activar tu app y, luego, envía las actualizaciones a la tarjeta.

  • Si el proceso de sincronización de datos de la tarjeta puede ser costoso, haz lo siguiente:

    1. Programa una sincronización de datos.
    2. Inicia un cronómetro entre 1 y 2 segundos.
    3. Si recibes una actualización de una fuente de datos remota antes de que se acabe el tiempo, muestra el valor actualizado de la sincronización de datos. De lo contrario, muestra un valor local almacenado en caché.