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]
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 expressivos do Material 3, mostra imagens e processa interações para abrir o app.
MessagingTileService
MessagingTileService
estende a classe SuspendingTileService
que conferimos anteriormente.
7. Adicionar componentes da interface
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 do 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 que, embora o layout geral esteja correto, as imagens estão ausentes:
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. 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 visualização:
No entanto, se o bloco for implantado em um dispositivo, as imagens vão estar ausentes. 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 uma ação fictícia fornecida pelo clickable()
sem argumentos para o ícone e cada um dos botões. 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 novamente. 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 novamente. 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.