Cómo crear tu primera tarjeta en Wear OS

se deslizan tarjetas desde el reloj hasta el clima y el temporizador.

Mira la animación anterior (demostración de tarjetas). Nota: La animación de los GIF solo se reproduce una vez. Si te la perdiste, vuelve a cargar la página.

Las tarjetas de Wear OS brindan un acceso fácil a la información y a las acciones que los usuarios necesitan para realizar tareas. Con solo deslizar el dedo sobre la cara de reloj, un usuario puede consultar el pronóstico actualizado o iniciar un temporizador.

Desarrollar tarjetas es un poco diferente a escribir una app.

Una tarjeta se ejecuta como parte de la IU del sistema en lugar de ejecutarse en el propio contenedor de la aplicación. Por lo tanto, una tarjeta no tiene acceso a algunos conceptos de programación de Android que quizás conozcas, como actividades y diseños XML.

En cambio, usamos un objeto Service para describir el diseño y el contenido de la tarjeta. Luego, la IU del sistema renderizará la tarjeta cuando sea necesario.

En este codelab, aprenderás a escribir tu propia tarjeta de Wear OS desde cero.

Qué aprenderás

  • Cómo crear una tarjeta
  • Cómo probar una tarjeta en un dispositivo
  • Cómo realizar el diseño de una tarjeta
  • Cómo agregar una imagen
  • Cómo agregar interacción (cuando presionas)

Qué compilarás

Compilarás una tarjeta personalizado que muestre la cantidad de pasos que se necesitan para alcanzar un objetivo diario. Incluye un diseño complejo que te ayudará a conocer diferentes elementos de diseño y contenedores.

A continuación, se muestra cómo lucirá la tarjeta cuando hayas terminado el codelab:

c6e1959693cded21.png

Requisitos previos

En este paso, configurarás tu entorno y descargarás un proyecto inicial.

Lo que necesitarás

  • La versión estable más reciente de Android Studio
  • Un dispositivo o emulador de Wear OS (¿Es la primera vez? Aquí te indicamos cómo configurarlo)

Cómo descargar el código

Si ya instalaste git, solo ejecuta el siguiente comando para clonar el código de este repositorio. Para comprobarlo, escribe git --version en la terminal o línea de comandos y verifica que se ejecute de forma correcta.

git clone https://github.com/googlecodelabs/wear-tiles.git
cd wear-tiles

Si no tienes Git, puedes hacer clic en el siguiente botón a fin de descargar todo el código de este codelab:

Descargar ZIP

En cualquier momento, puedes ejecutar cualquiera de los módulos en Android Studio si realizas cambios en la configuración de ejecución de la barra de herramientas.

8a2e49d6d6d2609d.png

Cómo abrir el proyecto en Android Studio

  1. En la ventana Welcome to Android Studio, selecciona 1f5145c42df4129a.png Open an Existing Project.
  2. Selecciona la carpeta [Download Location].
  3. Cuando Android Studio haya importado el proyecto, prueba si puedes ejecutar los módulos start y finished en un emulador de Wear OS o un dispositivo físico.
  4. El módulo start debería verse como en la siguiente captura de pantalla. En este módulo, realizarás todo el trabajo.

c72e8870facd8458.png

Cómo explorar el código de inicio

  • build.gradle contiene la configuración básica de una app. Incluye las dependencias que se necesitan para crear una tarjeta.
  • main > AndroidManifest.xml incluye las partes que se necesitan para marcar este archivo como una aplicación de Wear OS. Verificaremos este archivo durante el codelab.
  • main > GoalsRepository.kt contiene una clase falsa de repositorio que recupera, de forma asíncrona, una cantidad de pasos al azar que el usuario configuró hoy.
  • main > GoalsTileService.kt contiene código estándar para crear una tarjeta. En este archivo, realizarás la mayor parte del trabajo.
  • debug > AndroidManifest.xml contiene el elemento Activity para el objeto TilePreviewActivity, de modo que podamos obtener una vista previa de la tarjeta.
  • debug > TilesPreviewActivity.kt contiene el elemento Activity que usaremos para obtener una vista previa de la tarjeta.

Para comenzar, abre GoalsTileService en el módulo start. Como puedes observar, esta clase extiende TileProviderService.

TileProviderService es parte de la biblioteca de tarjetas, que brinda los métodos que usaremos para escribir la tarjeta:

  • onTileRequest(): Crea una tarjeta cuando el sistema solicita uno.
  • onResourcesRequest(): Brinda las imágenes que se necesitan para la tarjeta que se muestra en onTileRequest().

Usaremos corrutinas para trabajar con la naturaleza asíncrona de estos métodos. Para obtener más información sobre las corrutinas, consulta la documentación sobre las corrutinas de Android.

Para comenzar, crea una tarjeta simple de "Hello, World".

Antes de comenzar, ten en cuenta que definimos un grupo de constantes al comienzo del archivo que usaremos en todo el codelab. Puedes consultarlas si buscas el comentario "TODO: Review Constants". Definen elementos diferentes, por ejemplo, la versión del recurso para valores en dp (padding, tamaño, etc.), valores en sp (texto), identificadores y más.

Comencemos a programar.

En GoalsTileService.kt, busca el comentario "TODO: Build a Tile" y reemplaza la implementación completa de

onTileRequest() por el siguiente código.

Paso 1

// TODO: Build a Tile.
override fun onTileRequest(requestParams: TileRequest) = serviceScope.future {

    Tile.builder()
        // If there are any graphics/images defined in the Tile's layout, the system will
        // retrieve them using onResourcesRequest() and match them with this version number.
        .setResourcesVersion(RESOURCES_VERSION)

        // Creates a timeline to hold one or more tile entries for a specific time periods.
        .setTimeline(
            Timeline.builder().addTimelineEntry(
                TimelineEntry.builder().setLayout(
                    Layout.builder().setRoot(
                        Text.builder().setText("Hello, world!")
                    )
                )
            )
        ).build()
}

El método onTileRequest() espera que se muestre un elementoFuture, y usamos serviceScope.future { ... } para convertir la corrutina en un objeto ListenableFuture de Java.

Por fuera de eso, en realidad solo necesitas crear un elemento Tile dentro de la corrutina, así que comencemos con ese paso.

Dentro de onTileRequest(), usamos un patrón de compilador para crear una tarjeta "Hello, World!". Consulta los comentarios en el bloque de código para comprender qué está sucediendo.

Primero, configuramos una versión del recurso. La versión del recurso que se muestra en la carga útil de este método debe coincidir con la versión del recurso en la carga útil que muestra desde el elemento onResourcesRequest(), sin importar si, en realidad, se usa algún recurso.

Es una manera de lograr que esos gráficos coincidan con la versión correcta cuando el sistema llama a onResourcesRequest() para obtenerlos.

Sin embargo, para esta tarjeta simple de "Hello, World", no tenemos gráficos.

A continuación, crearemos un elemento Timeline.

Un elemento Timeline consiste de una o más instancias TimelineEntry. Cada una describe un diseño para un intervalo de tiempo específico. Podrías crear varios valores TimelineEntry para que ocurran en el futuro, y el sistema los renderizará en esos momentos del futuro.

fbb666b722376749.png

Puedes obtener más información sobre cómo trabajar con elementos Timeline en la guía de Timeline.

En este ejemplo, únicamente declaramos una instancia TimelineEntry porque solo queremos una tarjeta para todos los tiempos. Luego, configuramos el diseño con setLayout.

La raíz puede construirse con uno o más diseños complejos, pero, para este paso, crearemos un elemento de diseño Text simple que muestre "Hello, World!".

Eso es todo.

Ahora que creamos una tarjeta muy simple, generemos la vista previa en un elemento Activity.

Para obtener una vista previa de esta tarjeta dentro de un elemento Activity, abre el objeto TilePreviewActivity en la carpeta de origen debug de tu app.

Busca el comentario "TODO: Review creation of Tile for Preview" y agrega el siguiente código debajo de este.

Paso 2

// TODO: Review creation of Tile for Preview.
tileManager = TileManager(
    context = this,
    component = ComponentName(this, GoalsTileService::class.java),
    parentView = rootLayout
)
tileManager.create()

La mayor parte de este paso no requiere explicación.

Creas un elemento TileManager, que podemos usar con el fin de obtener una vista previa de la tarjeta, establecer el contexto, el componente (la clase de servicio de tarjeta en la que trabajamos), y la vista superior en la que insertamos la tarjeta.

Luego, creamos la tarjeta con create().

También notarás que realizamos una limpieza en onDestroy() con tileManager.close().

Ahora, ejecuta tu app en el emulador o en el dispositivo de Wear OS (asegúrate de elegir el módulo start). Deberías ver el mensaje "Hello, World!" en el centro de la pantalla:

b9976e1073554422.png

Ahora que contamos con un diseño básico en funcionamiento, amplíalo y crea una tarjeta más compleja. Nuestro objetivo final será el siguiente diseño:

c6e1959693cded21.png

Cómo reemplazar el diseño raíz por un elemento Box

Reemplaza el método onTileRequest completo, que incluye la etiqueta de texto "Hello, World!", por el siguiente código.

Paso 3

// TODO: Build a Tile.
override fun onTileRequest(requestParams: TileRequest) = serviceScope.future {

    // Retrieves progress value to populate the Tile.
    val goalProgress = GoalsRepository.getGoalProgress()
    // Retrieves font styles for any text in the Tile.
    val fontStyles = FontStyles.withDeviceParameters(requestParams.deviceParameters)

    // Creates Tile.
    Tile.builder()
        // If there are any graphics/images defined in the Tile's layout, the system will
        // retrieve them using onResourcesRequest() and match them with this version number.
        .setResourcesVersion(RESOURCES_VERSION)
        // Creates a timeline to hold one or more tile entries for a specific time periods.
        .setTimeline(
            Timeline.builder().addTimelineEntry(
                TimelineEntry.builder().setLayout(
                    Layout.builder().setRoot(
                        // Creates the root [Box] [LayoutElement]
                        layout(goalProgress, fontStyles)
                    )
                )
            )
        ).build()
}

Consulta los comentarios en el bloque de código para comprender qué está sucediendo. Todavía no definimos layout(), así que no te preocupes por el error.

En el primer fragmento de código en el método, comenzamos recuperando el progreso del objetivo real de un repositorio (falso).

También recuperamos el elemento deviceParameters que se pasa al objeto onTileRequest() que usaremos más adelante para compilar el estilo de fuente que etiqueta el texto.

El siguiente fragmento de código vuelve a crear la tarjeta, de la misma manera que se mencionó anteriormente. Es posible que notes que casi todo el código es igual.

De hecho, solo cambiamos la línea en la que configuramos la raíz en layout(goalProgress, fontStyles).

Como ya se mencionó, deberías ver una línea roja debajo del nombre del método porque todavía no existe.

Es un patrón común dividir tu diseño en varias partes lógicas y definirlas en sus propios métodos para evitar un código sumamente anidado. A continuación, definiremos el método layout.

Busca el comentario "TODO: Create root Box layout and content" y agrega el siguiente código.

Paso 4

    // TODO: Create root Box layout and content.
    // Creates a simple [Box] container that lays out its children one over the other. In our
    // case, an [Arc] that shows progress on top of a [Column] that includes the current steps
    // [Text], the total steps [Text], a [Spacer], and a running icon [Image].
    private fun layout(goalProgress: GoalProgress, fontStyles: FontStyles) =
        Box.builder()
            // Sets width and height to expand and take up entire Tile space.
            .setWidth(expand())
            .setHeight(expand())

            // Adds an [Arc] via local function.
            .addContent(progressArc(goalProgress.percentage))

            // TODO: Add Column containing the rest of the data.
            // TODO: START REPLACE THIS LATER
            .addContent(
                Text.builder()
                    .setText("REPLACE ME!")
                    .setFontStyle(fontStyles.display3())
            )
            // TODO: END REPLACE THIS LATER

            .build()

Consulta los comentarios en el bloque de código para comprender qué está sucediendo. Todavía no definimos progressArc(), así que no te preocupes por ningún error en esa llamada al método ni en setRoot().

Nuestro método layout incluye un contenedor Box simple, que organiza sus elementos secundarios uno sobre el otro. Dentro del contenedor Box, agregaremos un poco de contenido. En este caso, llama a un método que todavía no existe.

Una vez más, seguiremos el patrón de división de métodos para diseños complejos, de modo que sean más fáciles de leer.

Es posible que observes algunos comentarios TODO adicionales cerca del final del código (arriba de la llamada build()). Más adelante en el codelab, regresaremos a esos comentarios.

Por ahora, corrijamos ese error y definamos nuestro método local que finalmente declarará el objeto Arc que buscamos.

Cómo crear un elemento ArcLine

Primero, creamos un arco alrededor del borde de la pantalla que se llene a medida que el usuario aumente su conteo de pasos.

La API de tarjetas ofrece varias opciones de contenedor Arc. Usaremos el elemento ArcLine que renderiza una línea curva alrededor del objeto Arc.

Ahora, definamos esa función para eliminar ese error molesto.

Busca el comentario "TODO: Create a function that constructs an Arc representation of the current step progress" y agrega el siguiente código.

Paso 5

    // TODO: Create a function that constructs an Arc representation of the current step progress.
    // Creates an [Arc] representing current progress towards steps goal.
    private fun progressArc(percentage: Float) = Arc.builder()
        .addContent(
            ArcLine.builder()
                // Uses degrees() helper to build an [AngularDimension] which represents progress.
                .setLength(degrees(percentage * ARC_TOTAL_DEGREES))
                .setColor(argb(ContextCompat.getColor(this, R.color.primary)))
                .setThickness(PROGRESS_BAR_THICKNESS)
        )
        // Element will start at 12 o'clock or 0 degree position in the circle.
        .setAnchorAngle(degrees(0.0f))
        // Aligns the contents of this container relative to anchor angle above.
        // ARC_ANCHOR_START - Anchors at the start of the elements. This will cause elements
        // added to an arc to begin at the given anchor_angle, and sweep around to the right.
        .setAnchorType(ARC_ANCHOR_START)
        .build()

Consulta los comentarios en el bloque de código para comprender qué está sucediendo. (Nota: Es posible que se generen errores para degrees(), argb() y ContextCompat. Si es así, solo importa las versiones de las tarjetas haciendo clic en ellas).

En este fragmento de código, se muestra un objeto Arc.Builder con un elemento ArcLine que representa el progreso de nuestro objetivo de pasos.

Configuramos el color, el grosor y la longitud como el porcentaje de nuestro objetivo terminado (convertido en grados).

Luego, especificamos en qué lugar deseamos que comience el arco con el tipo de ancla. Se ofrecen muchas opciones diferentes para el ancla. No dudes en probarlas todas después del codelab.

Ya terminamos con esta parte.

Probemos cómo funciona. Ejecuta la app nuevamente. Deberías ver algo similar a lo siguiente:

ad79daf115d3b6a4.png

Cómo agregar un contenedor Column

Ahora que la tarjeta cuenta con un buen indicador de progreso, agreguemos un texto apropiado.

Como agregaremos varios campos de texto (y, luego, una imagen), queremos que los elementos estén en un objeto Column debajo del centro de la pantalla.

Un objeto Column es otro de los contenedores layout para tarjetas, que nos permite organizar los elementos secundarios de forma vertical, uno tras otro.

Busca el comentario "TODO: Add Column containing the rest of the data" y reemplaza el contenido de texto temporal por el siguiente código, que abarcaría todo desde TODO: START REPLACE THIS LATER hasta TODO: END REPLACE THIS LATER.

Recuerda no quitar la llamada build() al final del bloque de código del original.

Paso 6

            // TODO: Add Column containing the rest of the data.
            // Adds a [Column] containing the two [Text] objects, a [Spacer], and a [Image].
            .addContent(
                Column.builder()
                    // Adds a [Text] using local function.
                    .addContent(
                        currentStepsText(goalProgress.current.toString(), fontStyles)
                    )
                    // Adds a [Text] using local function.
                    .addContent(
                        totalStepsText(
                            resources.getString(R.string.goal, goalProgress.goal),
                            fontStyles
                        )
                    )
                    // TODO: Add Spacer and Image representations of our step graphic.
                    // DO LATER
            )

Consulta los comentarios en el bloque de código para comprender qué está sucediendo.

Crearemos una columna que agrega dos elementos de contenido. Ahora, tenemos dos errores.

¿Tienes idea de qué está sucediendo?

Una vez más, seguimos el patrón de división de los métodos para diseños complejos (y podemos ignorar el comentario DO LATER TODO).

Corrijamos esos errores.

Cómo agregar elementos Text

Busca el comentario "TODO: Create functions that construct/stylize Text representations of the step count & goal" y agrega el siguiente código debajo de este.

Paso 7

    // TODO: Create functions that construct/stylize Text representations of the step count & goal.
    // Creates a [Text] with current step count and stylizes it.
    private fun currentStepsText(current: String, fontStyles: FontStyles) = Text.builder()
        .setText(current)
        .setFontStyle(fontStyles.display2())
        .build()

    // Creates a [Text] with total step count goal and stylizes it.
    private fun totalStepsText(goal: String, fontStyles: FontStyles) = Text.builder()
        .setText(goal)
        .setFontStyle(fontStyles.title3())
        .build()

Consulta los comentarios en el bloque de código para comprender qué está sucediendo.

Es bastante simple. Mediante dos funciones diferentes, creamos elementos de diseño Text diferentes.

Como habrás supuesto, un elemento de diseño Text renderiza una string de texto (opcionalmente con una unión).

Configuramos el tamaño de la fuente a partir de las constantes que se definieron anteriormente en esta clase y, por último, configuramos los estilos de fuente a partir de los estilos que recuperamos en onTileRequest().

Ahora, tenemos texto. Veamos cómo luce la tarjeta ahora. Ejecútalo. Deberías ver algo similar a lo siguiente:

9eaca483c7e51f38.png

Cómo agregar el nombre de la imagen a onTileRequest()

Para la parte final de la IU, agregaremos una imagen.

Busca el comentario "TODO: Add Spacer and Image representations of our step graphic" y reemplaza DO LATER por el siguiente código (asegúrate de no borrar el carácter ")" al final).

Además, recuerda no quitar la llamada build() al final del bloque de código del original.

Paso 8

                    // TODO: Add Spacer and Image representations of our step graphic.
                    // Adds a [Spacer].
                    .addContent(Spacer.builder().setHeight(VERTICAL_SPACING_HEIGHT))
                    // Adds an [Image] using local function.
                    .addContent(startRunButton())

Consulta los comentarios en el bloque de código para comprender qué está sucediendo.

Primero, agregamos un elemento de diseño Spacer para brindar padding entre los elementos, en este caso, la imagen que sigue.

Luego, agregamos el contenido para un elemento de diseño Image que renderiza una imagen, pero como supusiste por el error, lo definimos en una función local diferente.

Busca el comentario "TODO: Create a function that constructs/stylizes a clickable Image of a running icon" y agrega el siguiente código.

Paso 9

    // TODO: Create a function that constructs/stylizes a clickable Image of a running icon.
    // Creates a running icon [Image] that's also a button to refresh the tile.
    private fun startRunButton() =
        Image.builder()
            .setWidth(BUTTON_SIZE)
            .setHeight(BUTTON_SIZE)
            .setResourceId(ID_IMAGE_START_RUN)
            .setModifiers(
                Modifiers.builder()
                    .setPadding(
                        Padding.builder()
                            .setStart(BUTTON_PADDING)
                            .setEnd(BUTTON_PADDING)
                            .setTop(BUTTON_PADDING)
                            .setBottom(BUTTON_PADDING)
                    )
                    .setBackground(
                        Background.builder()
                            .setCorner(Corner.builder().setRadius(BUTTON_RADIUS))
                            .setColor(argb(ContextCompat.getColor(this, R.color.primaryDark)))
                    )
                    // TODO: Add click (START)
                    // DO LATER
                    // TODO: Add click (END)
            )
            .build()

Consulta el bloque de código para comprender qué está sucediendo.

Como siempre, ignora el comentario DO LATER TODO por ahora.

La mayor parte del código no requiere explicación. Configuramos varias dimensiones y estilos con constantes que definimos en la parte superior de la clase. Para obtener más información sobre los modificadores, haz clic aquí.

El aspecto más importante que se debe tener en cuenta es la llamada .setResourceId(ID_IMAGE_START_RUN).

Configuramos el nombre de la imagen en una constante que definimos en la parte superior de la clase.

Como paso final, debemos asignar ese nombre de la constante a una imagen real en la aplicación.

Cómo agregar la asignación de la imagen a onResourcesRequest()

Las tarjetas no tienen acceso a ningún recurso de tu app, lo que significa que no puedes pasar el ID de una imagen de Android a un elemento de diseño Image y esperar que se resuelva. En cambio, debes anular el método onResourcesRequest() y brindar los recursos de forma manual.

Existen dos maneras de brindar imágenes dentro del método onResourcesRequest():

Usa setAndroidResourceByResId() a fin de asignar el nombre que usamos anteriormente para la imagen a una imagen real.

Busca el comentario "TODO: Supply resources (graphics) for the Tile" y reemplaza el código completo existente por el siguiente código.

Paso 10

    // TODO: Supply resources (graphics) for the Tile.
    override fun onResourcesRequest(requestParams: ResourcesRequest) = serviceScope.future {
        Resources.builder()
            .setVersion(RESOURCES_VERSION)
            .addIdToImageMapping(
                ID_IMAGE_START_RUN,
                ImageResource.builder()
                    .setAndroidResourceByResid(
                        AndroidImageResourceByResId.builder()
                            .setResourceId(R.drawable.ic_run)
                    )
            )
            .build()
    }

Consulta el bloque de código para comprender qué está sucediendo.

Como recuerdas de uno de los primeros pasos con onTileRequest(), configuramos un número de versión del recurso.

En este paso, configuramos el mismo número de versión del recurso en nuestro compilador de recursos para lograr que la tarjeta coincida con los recursos correctos.

Luego, es necesario asignar los elementos de diseño Image que creamos a la imagen real mediante el método addIdToImageMapping(). Como habrás notado, usamos el mismo nombre de constante que antes, ID_IMAGE_START_RUN, y ahora configuramos el elemento de diseño específico que queremos que se muestre en .setResourceId(R.drawable.ic_run).

Ahora, ejecútalo. Deberías ver la IU terminada.

c6e1959693cded21.png

Como último paso, queremos agregar una acción de clic a nuestra tarjeta. De esta manera, se podría abrir un elemento Activity dentro de tu app de Wear OS o, en el caso de este codelab, solo activar una actualización para la misma tarjeta.

Busca el comentario "TODO: Add click (START)" y reemplaza DO LATER por el siguiente código.

Recuerda no quitar la llamada build() al final del bloque de código del original.

Paso 11

                    // TODO: Add click (START)
                    .setClickable(
                        Clickable.builder()
                            .setId(ID_CLICK_START_RUN)
                            .setOnClick(ActionBuilders.LoadAction.builder())
                    )
                    // TODO: Add click (END)

Consulta el bloque de código para comprender qué está sucediendo.

Si agregas el modificador Clickable a un elemento de diseño, podrás reaccionar cuando un usuario presione ese elemento de diseño. Como reacción a un evento de clic, puedes realizar dos acciones:

  • LaunchAction: Inicia una actividad.
  • LoadAction: Fuerza la actualización de la tarjeta mediante una llamada a onTileRequest().

En este caso, configuramos un modificador Clickable, pero usamos el elemento LoadAction para actualizar la misma tarjeta con la línea ActionBuilders.LoadAction.builder() simple. De esta manera, se activa una llamada a onTileRequest() pero pasa el ID que configuramos, ID_CLICK_START_RUN.

Si quisiéramos, podríamos verificar el último ID de Clickable que se pasó a onTileRequest() y renderizar una tarjeta diferente según ese ID. Se vería similar a lo siguiente:

Paso 12

// Example of getting the clickable Id
override fun onTileRequest(requestParams: TileRequest) = serviceScope.future {

    if (requestParams.state.lastClickableId == ID_CLICK_START_RUN) {
        // Create start run tile...
    } else {
        // Create default tile...
    }
}

En este caso, no completaremos este paso. Solo, actualizamos la tarjeta (y se obtiene un valor nuevo de nuestra base de datos ficticia).

A fin de obtener más información sobre las opciones adicionales para interactuar con una tarjeta, consulta nuestra guía.

Ahora, vuelve a ejecutar la tarjeta. Observarás que, cuando haces clic en el botón, cambia el valor de los pasos.

e15bba88abc0d832.png

Es probable que ya hayas supuesto que definimos el servicio que estuvimos editando en algún lugar del manifiesto.

Busca el comentario "TODO: Review service". Deberías ver el siguiente código.

Este no se trata de un paso, sino de una revisión del código existente.

Paso 12

<!-- TODO: Review service -->
<service
   android:name="com.example.wear.tiles.GoalsTileService"
   android:label="@string/fitness_tile_label"
   android:description="@string/tile_description"
   android:icon="@drawable/ic_run"
   android:permission="com.google.android.wearable.permission.BIND_TILE_PROVIDER">
   <intent-filter>
       <action android:name="androidx.wear.tiles.action.BIND_TILE_PROVIDER" />
   </intent-filter>

   <!-- The tile preview shown when configuring tiles on your phone -->
   <meta-data
       android:name="androidx.wear.tiles.PREVIEW"
       android:resource="@drawable/tile_goals" />
</service>

Consulta el bloque de código para comprender qué está sucediendo.

Es similar a un servicio normal, pero agregamos algunos elementos específicos para la tarjeta:

  1. Un permiso para vincular al proveedor de tarjetas
  2. Un filtro de intents que registra el servicio como un proveedor de tarjetas
  3. Metadatos adicionales que especifican que se mostrará una imagen de vista previa en el teléfono

¡Felicitaciones! Ya aprendiste a compilar una tarjeta para Wear OS.

¿Qué sigue?

Consulta los otros codelabs de Wear OS:

Lecturas adicionales