Engenheiro de software
Nesta sessão, faremos uma investigação técnica da evolução dos blocos de tela cheia para os widgets dinâmicos de altura parcial do Wear.
Vamos analisar os benefícios técnicos da API unificada do Jetpack Glance e do novo mecanismo do Remote Compose, detalhando como eles se alinham perfeitamente ao Modern Android Development.
Por fim, vamos compartilhar as orientações mais recentes de UX e mostrar estratégias práticas de migração para ajudar você a criar a próxima geração de interfaces de fácil visualização.
Transcrição
Olá!
Meu nome é Frank e eu lidero as experiências de visualização rápida do Wear OS.
Os relógios são companheiros essenciais para os usuários durante todo o dia. Hoje, vamos mostrar como aproveitar a nova experiência dos widgets do Wear.
Vamos analisar duas maneiras de oferecer os recursos mais importantes diretamente no pulso dos usuários, usando as APIs mais recentes, que foram projetadas para que a criação para o Wear OS seja familiar e intuitiva para desenvolvedores Android, como você.
Para mostrar exatamente como estamos fazendo isso, convido ao palco meu brilhante colega e companheiro de mesa, Yuri.
Olá, meu nome é Yuri Schimke.
Sou engenheiro na equipe do Wear OS e meu foco principal é trabalhar nas APIs Remote Compose.
Hoje, vou apresentar brevemente o Remote Compose e falar sobre o processo de migração de blocos para widgets do Wear.
Agora, vou passar a palavra de volta para o Frank.
Ótimo.
Obrigado, Yuri.
Estamos trazendo oficialmente o pulso para o ecossistema unificado de widgets do Android, transformando nossos blocos de tela cheia em widgets do Wear.
Como essas experiências agora são criadas usando o Jetpack Glance, você pode ver que é a mesma infraestrutura usada para widgets de dispositivos móveis.
Você vai perceber que criar para o Wear vai ficar cada vez mais familiar no futuro.
Para potencializar essas experiências, estamos evoluindo a pilha de interface, que é a base de tudo.
Os widgets do Wear usam o Remote Compose, um novo framework do Android para interfaces remotas.
Isso significa que você pode usar uma API declarativa alinhada ao Jetpack Compose para criar experiências fora do app, como widgets.
O Remote Compose traz gráficos modernos e layouts responsivos para widgets, garantindo desempenho e eficiência de bateria.
E, ao separar a interface do processo do aplicativo, os widgets são serializados com segurança e renderizados com facilidade em uma ampla variedade de dispositivos Android.
Isso significa que você pode levar essas experiências ricas para praticamente todas as plataformas remotas compatíveis com o mínimo de esforço.
Para facilitar ao máximo a transição dos blocos para os widgets, estamos lançando dois tamanhos de contêiner padrão: pequeno e grande.
Eles foram projetados para se alinhar aos formatos de widget baseados em grade de 2x1 e 2x2 que você já conhece ao criar widgets em dispositivos móveis.
E, a partir do Wear 7, em alguns fabricantes de hardware, os widgets do Wear vão ficar em uma bandeja de rolagem vertical ao lado dos widgets de outros apps.
Por exemplo, é assim que os widgets do Wear aparecem em um Samsung Galaxy Watch.
A arquitetura da plataforma foi criada para oferecer suporte a bandejas de rolagem vertical, já que os fabricantes de hardware vão adotar esse padrão no futuro.
E você não precisa se preocupar em deixar nenhum usuário, ou usuários atuais, para trás.
As APIs Jetpack Glance Wear foram projetadas para oferecer compatibilidade com versões anteriores até o Wear 4.
Em dispositivos mais antigos que oferecem suporte a uma bandeja horizontal de widgets ou blocos, o sistema vai cuidar disso para você.
Basta declarar no código que você quer oferecer suporte ao modo de tela cheia, e o sistema vai pegar o widget grande e mostrá-lo como um bloco tradicional em tela cheia nesses dispositivos.
Agora, Yuri vai mostrar o código para que você entenda exatamente como migrar seus blocos para os widgets do Wear.
Obrigado, Frank.
Se você estiver criando um novo widget do Wear do zero, é simples.
Basta definir um GlanceWearWidgetService e criar sua interface usando o Remote Compose.
No entanto, se o app já tiver um bloco em produção, será necessário migrar os serviços, a interface e os dados.
Para que essa transição seja o mais tranquila possível, oferecemos caminhos para garantir uma experiência sem interrupções para os usuários atuais.
Para o serviço, é possível escolher entre duas estratégias: serviço duplo ou único.
Com o serviço duplo, que recomendamos, você mantém o TileService atual para dispositivos Wear OS mais antigos e adiciona um novo GlanceWearWidgetService para o Wear OS 7+.
Por outro lado, com o serviço único, você cria um novo GlanceWearWidgetService que processa os dois protocolos, sendo executado em um modo de compatibilidade para traduzir o widget em um bloco em dispositivos mais antigos.
De qualquer forma, você vai continuar alcançando seus usuários.
Isso só afeta o código que você vai manter daqui para frente.
E acabamos de publicar uma documentação abrangente no site para desenvolvedores Android detalhando exatamente como e quando usar esses dois padrões.
Confira para saber qual estratégia é melhor para seu app.
Agora vamos falar sobre a camada de interface.
Ao migrar, seu modelo mental muda.
Antes, os blocos usavam o ProtoLayout, uma biblioteca criada exclusivamente para blocos do Wear.
Já os widgets do Wear são baseados no Remote Compose.
O Remote Compose compartilha exatamente a mesma base que a interface do Jetpack Compose, usando o mesmo compilador e ambiente de execução principais.
Ele foi projetado para ser familiar, correspondendo de perto às bibliotecas de interface do Compose, como Foundation e Material 3.
Mas, em vez de uma caixa, coluna ou texto, basta trocar por RemoteBox, RemoteColumn ou RemoteText.
Nesse caso, você vai se acostumar a escrever muito "remote".
Além disso, para acelerar seu fluxo de trabalho, incluímos um conjunto completo de componentes do Remote Compose prontos para uso, projetados especificamente para widgets.
Isso significa que seus designs atuais do Wear Material 3, incluindo o que você tem no Figma, podem ser facilmente adaptados para widgets, o que é familiar para engenheiros e designers.
Para entender como esses frameworks estão alinhados, vamos analisar um exemplo concreto.
Vamos criar nosso próprio componente de app com base no botão de alternância do Material 3.
À esquerda, temos o componente padrão do Wear Compose que você escreveria no app, e à direita está a versão do Remote Compose.
// Wear Compose
@Composable
fun HistoryEraserButton(
onClick: () -> Unit,
modifier: Modifier = Modifier,
checked: Boolean = true,
onCheckedChange: (Boolean) -> Unit = {},
)// Wear RemoteCompose
@RemoteComposable
@Composable
fun RemoteHistoryEraserButton(
onClick: Action,
modifier: RemoteModifier = RemoteModifier,
checked: RemoteBoolean = true.rb,
onCheckedChange: Action = Action.Empty,
)Observe como os padrões são semelhantes.
No entanto, ao escrever o código do Remote Compose, você vai usar a anotação RemoteComposable.
E, quando você usa parâmetros conhecidos, como modificador e booleano, eles têm tipos equivalentes do Remote Compose, como RemoteModifier e RemoteBoolean.
E você usa ações em vez de lambdas.
Em vez de definir o código a ser executado dentro do lambda, você precisa declarar uma ação, como iniciar uma intent pendente, chamar um serviço ou atualizar um valor.
Vamos para o corpo do componente.
Até o tema está alinhado.
É possível aplicar o esquema de cores do Material 3 ao componente do Remote Compose exatamente como você faria no Compose padrão, o que permite que o widget corresponda exatamente à marca do seu app.
// Wear Compose
val colors = MaterialTheme.colorScheme
SplitSwitchButton(
checked = checked,
onCheckedChange = onCheckedChange,
toggleContentDescription = "Arm Eraser",
onContainerClick = onClick,
modifier = modifier.fillMaxWidth(),
colors =
SwitchButtonDefaults.splitSwitchButtonColors(
checkedContainerColor = colors.errorContainer, …
),
label = { Text("History Eraser") },
secondaryLabel = { Text("Don't touch it!") },
)// Wear RemoteCompose
val colors = RemoteMaterialTheme.colorScheme
RemoteSplitSwitchButton(
checked = checked,
onCheckedChange = onCheckedChange,
toggleContentDescription = "Arm Eraser".rs,
onContainerClick = onClick,
modifier = modifier.fillMaxWidth(),
colors =
RemoteSplitSwitchButtonDefaults.splitSwitchButtonColors(
checkedContainerColor = colors.errorContainer, …
),
label = { RemoteText("History Eraser".rs) },
secondaryLabel = { RemoteText("Don't touch it!".rs) },
)Por fim, vamos ver o resultado. A interface resultante é exatamente o que você espera: um botão ativar/desativar limpo, responsivo e animado.
É difícil perceber a diferença.
Para ajudar você a projetar, iterar e verificar os layouts de widgets sem precisar sempre executar em um dispositivo ou emulador, adicionamos suporte à visualização do Compose para widgets do Wear.
Como os widgets dependem de restrições de contêiner específicas do OEM, como tamanho, formato, padding e raio de canto, criamos com base na anotação de prévia padrão, mas com um provedor de parâmetros extra para injetar essas configurações.
@Preview(name = "Squircle Preview", device = "spec:width=1000dp,height=1000dp,dpi=320")
@Composable
fun WeatherWidgetSquirclePreview(
@PreviewParameter(SquircleAllWidgetPreviewParams::class) params: WearWidgetParams
) = WearWidgetPreview(MockWeatherWidget(65, WeatherCondition.CLOUDY), params)
@Preview(name = "Round Preview", device = "spec:width=1000dp,height=1000dp,dpi=320")
@Composable
fun WeatherWidgetRoundPreview(
@PreviewParameter(RoundAllWidgetPreviewParams::class) params: WearWidgetParams
) = WearWidgetPreview(MockWeatherWidget(65, WeatherCondition.CLOUDY), params)A biblioteca oferece implementações pré-configuradas de provedores PreviewParameter que correspondem a dispositivos Wear OS reais.
Essas anotações de visualização permitem verificar facilmente o layout em vários formatos de hardware diferentes.
Além de testar seus layouts, você pode usar essas prévias para gerar as imagens estáticas necessárias para o seletor de widgets no dispositivo.
@Preview(name = "Widget Picker Preview", device = "spec:width=1000dp,height=1000dp,dpi=320")
@Composable
fun WeatherWidgetRectangularPreview(
@PreviewParameter(RectangularAllWidgetPreviewParams::class) params: WearWidgetParams
) = WearWidgetPreview(MockWeatherWidget(65, WeatherCondition.CLOUDY), params)A biblioteca fornece um parâmetro para a forma de visualização retangular que emula os contêineres retangulares completos sem cortes.
E se você quiser ver como isso funciona na prática, atualizamos nosso app de exemplo de mídia do Horologist para incluir um novo widget que mostra essa API.
Mostramos como adicionar as versões pequena e grande do widget de mídia.
Confira no GitHub uma comparação lado a lado do código legado do ProtoLayout e da nova implementação do Remote Compose.
Temos certeza de que você vai gostar.
Por fim, não seria uma palestra sem mencionar a IA.
Para acelerar sua migração, publicamos uma nova habilidade de migração de blocos para widgets no GitHub.
Você pode usá-la com o agente de sua escolha ou adicioná-la diretamente ao Android Studio usando a CLI do Android.
android skills add wear-widgets
android skills add --allAgora, vou passar a palavra para o François, que vai encerrar o assunto.
Nossa equipe agradece.
Obrigado, Yuri.
Como você pode ver, transformar seus blocos em widgets do Wear é um processo muito simples.
Ao migrar para o Jetpack Glance Wear e o Remote Compose, você não está apenas atualizando sua interface, mas também se juntando a um ecossistema de widgets do Android mais unificado.
Agora você pode criar widgets bonitos e compatíveis com versões anteriores usando um framework moderno e declarativo totalmente alinhado ao Jetpack Compose.
Você pode começar a migrar seus blocos hoje mesmo. Acesse o guia de iniciação no site para desenvolvedores Android para conferir todos os detalhes.
Estamos ansiosos para ver todas as experiências expressivas e fáceis de ler que você vai criar para o pulso.
Agradecemos muito pela participação de vocês hoje, nesta manhã, e esperamos que aproveitem o restante da conferência.
Nossa equipe agradece.
// Wear Compose
@Composable
fun HistoryEraserButton(
onClick: () -> Unit,
modifier: Modifier = Modifier,
checked: Boolean = true,
onCheckedChange: (Boolean) -> Unit = {},
)// Wear RemoteCompose
@RemoteComposable
@Composable
fun RemoteHistoryEraserButton(
onClick: Action,
modifier: RemoteModifier = RemoteModifier,
checked: RemoteBoolean = true.rb,
onCheckedChange: Action = Action.Empty,
)// Wear Compose
val colors = MaterialTheme.colorScheme
SplitSwitchButton(
checked = checked,
onCheckedChange = onCheckedChange,
toggleContentDescription = "Arm Eraser",
onContainerClick = onClick,
modifier = modifier.fillMaxWidth(),
colors =
SwitchButtonDefaults.splitSwitchButtonColors(
checkedContainerColor = colors.errorContainer, …
),
label = { Text("History Eraser") },
secondaryLabel = { Text("Don't touch it!") },
)// Wear RemoteCompose
val colors = RemoteMaterialTheme.colorScheme
RemoteSplitSwitchButton(
checked = checked,
onCheckedChange = onCheckedChange,
toggleContentDescription = "Arm Eraser".rs,
onContainerClick = onClick,
modifier = modifier.fillMaxWidth(),
colors =
RemoteSplitSwitchButtonDefaults.splitSwitchButtonColors(
checkedContainerColor = colors.errorContainer, …
),
label = { RemoteText("History Eraser".rs) },
secondaryLabel = { RemoteText("Don't touch it!".rs) },
)@Preview(name = "Squircle Preview", device = "spec:width=1000dp,height=1000dp,dpi=320")
@Composable
fun WeatherWidgetSquirclePreview(
@PreviewParameter(SquircleAllWidgetPreviewParams::class) params: WearWidgetParams
) = WearWidgetPreview(MockWeatherWidget(65, WeatherCondition.CLOUDY), params)
@Preview(name = "Round Preview", device = "spec:width=1000dp,height=1000dp,dpi=320")
@Composable
fun WeatherWidgetRoundPreview(
@PreviewParameter(RoundAllWidgetPreviewParams::class) params: WearWidgetParams
) = WearWidgetPreview(MockWeatherWidget(65, WeatherCondition.CLOUDY), params)@Preview(name = "Widget Picker Preview", device = "spec:width=1000dp,height=1000dp,dpi=320")
@Composable
fun WeatherWidgetRectangularPreview(
@PreviewParameter(RectangularAllWidgetPreviewParams::class) params: WearWidgetParams
) = WearWidgetPreview(MockWeatherWidget(65, WeatherCondition.CLOUDY), params)android skills add wear-widgets
android skills add --all