1. Introdução

Os Blocos do Wear OS oferecem fácil acesso às informações e ações de que os usuários precisam para realizar tarefas. Com um simples gesto de deslizar no mostrador do relógio, o usuário pode ver a previsão mais recente ou iniciar um timer.
Um bloco é executado como parte da IU do sistema em vez de ser executado no próprio contêiner do aplicativo. Usamos um Serviço para descrever o layout e o conteúdo do bloco. A IU do sistema vai renderizar o bloco quando necessário.
O que você vai fazer

Você vai criar um bloco para um app de mensagens que mostra conversas recentes. Nele, o usuário pode realizar três tarefas comuns:
- Abrir uma conversa
- Escrever uma nova mensagem
O que você vai aprender
Neste codelab, você vai aprender a criar seu próprio bloco do Wear OS, incluindo como:
- Criar um TileService.
- Testar um bloco em um dispositivo.
- Visualizar a IU de um bloco no Android Studio.
- Desenvolver a IU de um bloco.
- Adicionar imagens
- Processar interações
Pré-requisitos
- Noções básicas do Kotlin
2. Etapas da configuração
Nesta etapa, você configurará seu ambiente e fará o download de um projeto inicial.
O que é preciso
- Atualização de recursos do Android Studio Koala | 2024.1.2 Canary 1 ou mais recente
- Dispositivo ou emulador do Wear OS
Caso não saiba usar o Wear OS, leia este guia rápido (em inglês) antes de começar. Ele inclui instruções para a configuração do emulador do Wear OS e descreve como navegar pelo sistema.
Fazer o download do código
Se você tiver o git instalado, execute o comando abaixo para clonar o código deste repositório (link em inglês).
git clone https://github.com/android/codelab-wear-tiles.git cd codelab-wear-tiles
Caso você não tenha o git, clique no botão abaixo para fazer o download de todo o código para este codelab:
Abrir o projeto no Android Studio
Na janela "Welcome to Android Studio", selecione   Open an Existing Project ou File > Open e selecione a pasta [Download Location]
 Open an Existing Project ou File > Open e selecione a pasta [Download Location]
3. Criar um bloco básico
O ponto de entrada de um bloco é o serviço de bloco. Nesta etapa, você vai registrar um serviço de bloco e definir um layout para o bloco.
HelloWorldTileService
Uma classe que implementa o TileService precisa especificar dois métodos:
- onTileResourcesRequest(requestParams: ResourcesRequest): ListenableFuture<Resources>
- onTileRequest(requestParams: TileRequest): ListenableFuture<Tile>
O primeiro método retorna um objeto Resources que mapeia IDs de string para os recursos de imagem que vamos usar no bloco.
A segunda retorna uma descrição de um bloco, incluindo o layout dele. É aqui que definimos o layout de um bloco e como os dados são vinculados a ele.
Abra HelloWorldTileService.kt no módulo start. Todas as mudanças vão ser feitas neste módulo. Há também um módulo finished se você quiser dar uma olhada no resultado deste codelab.
O HelloWorldTileService estende o SuspendingTileService, um wrapper com suporte para corrotinas do Kotlin da biblioteca Horologist Tiles (link em inglês). O Horologist é um grupo de bibliotecas do Google que visa fornecer aos desenvolvedores do Wear OS recursos que normalmente são exigidos por eles, mas que ainda não estão disponíveis no Jetpack.
O SuspendingTileService fornece duas funções de suspensão, que são equivalentes de corrotina das funções do TileService:
- suspend resourcesRequest(requestParams: ResourcesRequest): Resources
- suspend tileRequest(requestParams: TileRequest): Tile
Para saber mais sobre corrotinas, consulte a documentação sobre Corrotinas do Kotlin no Android.
O HelloWorldTileService ainda não foi concluído. Precisamos registrar o serviço no nosso manifesto e fornecer uma implementação para o tileLayout.
Registrar o serviço do bloco
Depois que o serviço do bloco é registrado no manifesto, ele aparece na lista de blocos disponíveis para o usuário adicionar.
Adicione o <service> ao elemento <application>:
start/src/main/AndroidManifest.xml
<service
    android:name="com.example.wear.tiles.hello.HelloWorldTileService"
    android:icon="@drawable/ic_waving_hand_24"
    android:label="@string/hello_tile_label"
    android:description="@string/hello_tile_description"
    android:exported="true"
    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_hello" />
</service>
O ícone e o rótulo são usados, como um marcador, quando o bloco é carregado pela primeira vez ou se ocorre um erro ao carregar o bloco. Os metadados no fim definem uma imagem de visualização que é mostrada no carrossel quando o usuário está adicionando um bloco.
Definir um layout para o bloco
O HelloWorldTileService tem uma função com o nome tileLayout com uma TODO() como o corpo. Vamos substituir isso por uma implementação em que definimos o layout do bloco:
start/src/main/java/com/example/wear/tiles/hello/HelloWorldTileService.kt
fun tileLayout(
    context: Context,
    deviceConfiguration: DeviceParametersBuilders.DeviceParameters,
    message: String,
) =
    materialScope(
        context = context,
        deviceConfiguration = deviceConfiguration,
        allowDynamicTheme = false,
    ) {
        primaryLayout(mainSlot = { text(message.layoutString) })
    }
Você criou seu primeiro bloco do Wear OS. Vamos instalar esse bloco e ver a aparência dele.
4. Testar o bloco em um dispositivo
Com o módulo inicial selecionado no menu suspenso para a configuração de execução, você pode instalar o app (o módulo start) no dispositivo ou emulador e instalar manualmente o bloco, como um usuário faria.
No entanto, o Android Studio tem um atalho para fazer isso: toque no ícone Run service (▷) no gutter e selecione "Run ‘HelloWorldTileService'" para instalar e iniciar o bloco em um dispositivo conectado.

Selecione "Run ‘HelloWorldTileService'" para criar e executar o bloco em um dispositivo conectado. Ele vai ficar parecido com a captura de tela abaixo.

O ícone de mão acenando que aparece na parte de cima da tela é fornecido pelo sistema. Para mudá-lo, edite a propriedade android:icon do elemento <service> do bloco no manifesto.
Para sua conveniência, esse processo também vai criar uma configuração de execução "HelloWorldTileService" para uso futuro.

5. Adicionar funções de visualização
Podemos conferir a visualização da interface do bloco no Android Studio. Isso encurta o ciclo de feedback ao desenvolver a interface, aumentando a velocidade de desenvolvimento.
Adicione uma visualização de bloco para o HelloWorldTileService no final do arquivo HelloWorldTileService.kt.
start/src/main/java/com/example/wear/tiles/hello/HelloWorldTileService.kt
@Preview(device = WearDevices.SMALL_ROUND, name = "Small Round")
@Preview(device = WearDevices.LARGE_ROUND, name = "Large Round")
internal fun helloLayoutPreview(context: Context): TilePreviewData {
    return TilePreviewData {
        TilePreviewHelper.singleTimelineEntryTileBuilder(
            helloLayout(context, it.deviceConfiguration, "Hello, preview tile!")
        )
            .build()
    }
}
Use o modo de edição "Split" para conferir o bloco:

Observe que a anotação @Composable não é fornecida. Embora os blocos usem a mesma interface de visualização das Funções combináveis, os blocos não usam o Compose e não são combináveis.
6. Criar um bloco de mensagens

O bloco de mensagens que estamos prestes a criar é mais parecido com um bloco do mundo real. Ao contrário do exemplo HelloWorld, este demonstra os componentes do Material 3 Expressive, mostra imagens e processa interações para abrir o app.
Para esse bloco, vamos trabalhar com um novo serviço (MessagingTileService) que estende o SuspendingTileService que vimos antes e fornece os layouts e recursos ao sistema.
7. Adicionar componentes da UI
A biblioteca ProtoLayout oferece componentes e layouts pré-criados, permitindo que você crie blocos que usam o design expressivo do Material 3 mais recente para Wear OS.
Adicione a dependência do Tiles Material ao arquivo build.gradle:
start/build.gradle
implementation "androidx.wear.protolayout:protolayout-material3:$protoLayoutVersion"
Adicione o código de layout à função tileLayout() como o corpo da função materialScope(). Isso cria um layout de duas linhas (com dois botões cada) e um botão de borda.
Encontre a linha "TODO() // Add primaryLayout()" e substitua pelo código abaixo.
start/src/main/java/com/example/wear/tiles/messaging/tile/Layout.kt
primaryLayout(
    mainSlot = {
        // This layout code assumes "contacts" contains at least 4 elements, for sample code
        // that can handle an arbitrary number of contacts, and also shows different numbers
        // of contacts based on the physical screen size, see
        // <https://github.com/android/wear-os-samples/tree/main/WearTilesKotlin>.
        Column.Builder()
            .apply {
                setWidth(expand())
                setHeight(expand())
                addContent(
                    buttonGroup {
                        buttonGroupItem { contactButton(contacts[0]) }
                        buttonGroupItem { contactButton(contacts[1]) }
                    }
                )
                addContent(DEFAULT_SPACER_BETWEEN_BUTTON_GROUPS)
                addContent(
                    buttonGroup {
                        buttonGroupItem { contactButton(contacts[2]) }
                        buttonGroupItem { contactButton(contacts[3]) }
                    }
                )
            }
            .build()
    },
    bottomSlot = {
        textEdgeButton(
            onClick = clickable(), // TODO: Launch new conversation activity
            labelContent = { text("New".layoutString) },
        )
    },
)
A função contactButton() no mesmo arquivo cria os botões de contato individuais. Se o contato tiver uma imagem associada, ela vai aparecer no botão. Caso contrário, as iniciais do contato serão usadas.
Você pode notar aqui que, embora o layout geral esteja correto, as imagens estão faltando:

Você verá a mesma coisa se implantar o bloco em um dispositivo:

Na próxima etapa, vamos corrigir as imagens ausentes.
8. Adicionar imagens
De modo geral, os blocos consistem em dois itens: um layout (que faz referência a recursos por IDs de string) e os próprios recursos (que podem ser imagens).
No momento, nosso código está fornecendo o layout, mas não os recursos em si. Para corrigir a visualização, precisamos fornecer os recursos da imagem. Para fazer isso, encontre "TODO: Add onTileResourceRequest" e adicione o código abaixo como outro argumento nomeado para TilePreviewData():
start/src/main/java/com/example/wear/tiles/messaging/tile/Layout.kt
// Additional named argument to TilePreviewData
onTileResourceRequest = { resourcesRequest ->
    Resources.Builder()
        .setVersion(resourcesRequest.version)
        .apply {
            contacts.forEach {
                if (it.avatarSource is AvatarSource.Resource) {
                    addIdToImageMapping(
                        it.imageResourceId(),
                        it.avatarSource.resourceId
                    )
                }
            }
        }
        .build()
}
As imagens vão aparecer na prévia:

No entanto, se o bloco for implantado em um dispositivo, as imagens estarão faltando. Para corrigir isso, substitua a função resourcesRequest() no Service.kt pelo seguinte:
start/src/main/java/com/example/wear/tiles/messaging/tile/Service.kt
override suspend fun resourcesRequest(
    requestParams: ResourcesRequest
): Resources {
    // resourceIds is a list of the ids we need to provide images for. If we're passed an empty
    // list, set resourceIds to all resources.
    val resourceIds =
        requestParams.resourceIds.ifEmpty {
            contacts.map { it.imageResourceId() }
        }
    // resourceMap maps (tile) resource ids to (Android) resource ids.
    val resourceMap =
        contacts
            .mapNotNull {
                when (it.avatarSource) {
                    is AvatarSource.Resource ->
                        it.imageResourceId() to
                            it.avatarSource.resourceId
                    else -> null
                }
            }
            .toMap()
            .filterKeys {
                it in resourceIds
            } // filter to only the resources we need
    // Add images in the resourceMap to the Resources object, and return the result.
    return Resources.Builder()
        .setVersion(requestParams.version)
        .apply {
            resourceMap.forEach { (id, imageResource) ->
                addIdToImageMapping(id, imageResource)
            }
        }
        .build()
}
Agora as imagens também são mostradas quando o bloco é implantado em um dispositivo:

Na próxima etapa, vamos processar os cliques em cada um dos elementos.
9. Processar interações
Uma das coisas mais úteis que podemos fazer com um bloco é fornecer atalhos para as jornadas ideais do usuário. Isso é diferente do Acesso rápido aos apps, que apenas abre o app. Aqui, temos espaço para fornecer atalhos contextuais a uma tela específica do app.
Até agora, usamos para o ícone e cada um dos botões uma ação fictícia fornecida pelo clickable() sem argumentos. Isso é bom para visualizações, que não são interativas, mas vamos conferir como adicionar ações aos elementos.
LaunchAction
LaunchAction pode ser usada para iniciar uma atividade. Vamos modificar o Layout para que o toque no botão "New" inicie a jornada de nova conversa para o usuário.
Encontre a linha "TODO: Launch new conversation activity" e substitua clickable() por:
start/src/main/java/com/example/wear/tiles/messaging/tile/Layout.kt
clickable(
    id = "new_button",
    action =
        launchAction(
            ComponentName(
                "com.example.wear.tiles",
                "com.example.wear.tiles.messaging.MainActivity",
            ),
            mapOf(
                MainActivity.EXTRA_JOURNEY to
                    ActionBuilders.stringExtra(
                        MainActivity.EXTRA_JOURNEY_NEW
                    )
            ),
        ),
)
Implante o bloco de novo. Agora, em vez de não fazer nada, tocar em "New" vai iniciar MainActivity e a jornada de nova conversa do usuário:

Da mesma forma, modifique o Layout para que tocar em um botão de contato inicie uma conversa com um usuário específico.
Encontre a linha "Launch open conversation activity" e substitua o clickable() por:
start/src/main/java/com/example/wear/tiles/messaging/tile/Layout.kt
clickable(
    id = contact.id.toString(),
    action =
        launchAction(
            ComponentName(
                "com.example.wear.tiles",
                "com.example.wear.tiles.messaging.MainActivity",
            ),
            mapOf(
                MainActivity.EXTRA_JOURNEY to
                    ActionBuilders.stringExtra(
                        MainActivity
                            .EXTRA_JOURNEY_CONVERSATION
                    ),
                MainActivity.EXTRA_CONVERSATION_CONTACT to
                    ActionBuilders.stringExtra(
                        contact.name
                    ),
            ),
        ),
)
Implante o bloco de novo. Agora, em vez de não fazer nada, tocar em um contato vai iniciar uma conversa com ele:

10. Parabéns
Parabéns! Você aprendeu a criar um bloco para Wear OS.
Qual é a próxima etapa?
Para mais informações, confira as Implementações de blocos dourados no GitHub (em inglês), o Guia de blocos do Wear OS e as diretrizes de design.
