Ingeniero de software
En esta sesión, analizaremos en profundidad los aspectos técnicos de la evolución de las tarjetas de pantalla completa a los widgets de Wear dinámicos de altura parcial.
Exploraremos los beneficios técnicos de la API unificada de Jetpack Glance y el nuevo motor de Remote Compose, y detallaremos cómo se alinean sin problemas con Modern Android Development.
Por último, compartiremos la orientación más reciente sobre UX y analizaremos estrategias de migración prácticas para ayudarte a compilar la próxima generación de IU de un vistazo.
Transcripción
Hola.
Me llamo Frank y dirijo nuestras experiencias de un vistazo para Wear OS.
Los relojes son compañeros esenciales para los usuarios durante todo el día. Hoy, te explicaremos cómo aprovechar la nueva experiencia de widgets de Wear.
Veremos dos formas de entregar las funciones más importantes directamente a la muñeca de los usuarios, y lo haremos con las APIs más recientes, que están diseñadas para que la compilación para Wear OS sea familiar e intuitiva para los desarrolladores de Android, como tú.
Hoy me acompaña en el escenario mi brillante colega y compañero de escritorio Yuri para analizar exactamente cómo lo estamos haciendo.
Hola, soy Yuri Schimke.
Soy ingeniero del equipo de Wear OS y mi enfoque principal es trabajar en las APIs de Remote Compose.
Hoy te daré una breve introducción a Remote Compose y también hablaré sobre el proceso para migrar de tarjetas a widgets de Wear.
Dicho esto, le cedo la palabra a Frank.
Excelente.
Gracias, Yuri.
Oficialmente, llevaremos las muñecas al ecosistema unificado de widgets de Android, ya que convertiremos nuestras tarjetas de pantalla completa en widgets de Wear.
Y, como estas experiencias ahora se compilan con Jetpack Glance, puedes ver que es la misma infraestructura subyacente que se usa para los widgets para dispositivos móviles.
Verás que, en el futuro, te resultará aún más familiar compilar para Wear.
Para potenciar estas experiencias, estamos evolucionando la pila de IU, la pila de IU subyacente.
Los widgets de Wear se basan en Remote Compose, un nuevo framework de Android para la IU remota.
Esto significa que, finalmente, puedes usar una API declarativa estrechamente alineada con Jetpack Compose para compilar tus experiencias fuera de la app, como los widgets.
Remote Compose ofrece gráficos modernos y diseños responsivos a los widgets, a la vez que garantiza el rendimiento y la eficiencia de la batería.
Además, al desacoplar la IU del proceso de la aplicación, los widgets se serializan de forma segura y se renderizan con facilidad en una amplia variedad de dispositivos Android.
Esto significa que puedes llevar estas experiencias enriquecidas a casi todas las plataformas remotas compatibles con un esfuerzo mínimo.
Para que la transición de las tarjetas a los widgets sea lo más fluida posible, presentamos dos tamaños de contenedores estándar: pequeño y grande.
Están diseñados para alinearse estrechamente con los formatos de widgets basados en cuadrículas de dos por uno y dos por dos a los que ya te acostumbraste cuando creabas widgets en dispositivos móviles.
A partir de Wear 7, en algunos fabricantes de hardware, tus widgets de Wear ahora se ubicarán en una bandeja de desplazamiento vertical junto con los widgets de otras apps.
Por ejemplo, así es como se ven los widgets de Wear en un Samsung Galaxy Watch.
La arquitectura de la plataforma está diseñada para admitir bandejas de desplazamiento vertical a medida que los fabricantes de hardware adopten este patrón en el futuro.
Y no tienes que preocuparte por dejar atrás a ningún usuario, ni a los existentes.
Las APIs de Jetpack Glance Wear están diseñadas para admitir la retrocompatibilidad hasta Wear 4.
En los dispositivos más antiguos que admiten una bandeja horizontal de widgets o de tarjetas, el sistema se encargará de eso por ti.
Solo debes indicar en tu código que quieres admitir el modo de pantalla completa, y el sistema tomará tu widget grande y lo mostrará como una tarjeta tradicional de pantalla completa en estos dispositivos.
Ahora, Yuri nos guiará por el código para mostrarte exactamente cómo migrar tus tarjetas existentes a widgets de Wear.
Gracias, Frank.
Si estás creando un widget de Wear nuevo desde cero, es sencillo.
Solo debes definir un GlanceWearWidgetService y compilar tu IU con Remote Compose.
Sin embargo, si tu app ya tiene una tarjeta en producción, deberás migrar tus servicios, la IU y los datos.
Para que esta transición sea lo más fluida posible, proporcionamos rutas que garantizan una experiencia sin problemas para los usuarios existentes.
Para el servicio, puedes elegir entre dos estrategias: servicio doble o servicio único.
Con el servicio doble, que recomendamos, conservarás tu TileService existente para los dispositivos Wear OS más antiguos y agregarás un nuevo GlanceWearWidgetService para Wear OS 7 y versiones posteriores.
En cambio, con el servicio único, creas un nuevo GlanceWearWidgetService único que controla ambos protocolos y se ejecuta en un modo de compatibilidad para traducir tu widget en una tarjeta en dispositivos más antiguos.
De cualquier manera, seguirás llegando a tus usuarios.
Solo afecta el código que mantendrás en el futuro.
Además, acabamos de publicar documentación completa en el sitio para desarrolladores de Android en la que se detalla exactamente cómo y cuándo usar ambos patrones.
Consúltalo para obtener más información sobre qué estrategia es la mejor para tu app.
Ahora, hablemos de la capa de IU.
Cuando migras, tu modelo mental cambia.
Anteriormente, las tarjetas usaban ProtoLayout, una biblioteca compilada exclusivamente para tarjetas de Wear.
Por otro lado, los widgets de Wear se basan en Remote Compose.
Remote Compose comparte exactamente la misma base que la IU de Jetpack Compose, con el mismo compilador y entorno de ejecución principales.
Además, está diseñada para que resulte familiar, ya que se asemeja mucho a las bibliotecas de IU de Compose, como Foundation y Material 3.
Pero, en lugar de un cuadro, una columna o un texto, simplemente los cambiarás por RemoteBox, RemoteColumn o RemoteText.
En este caso, te familiarizarás con escribir mucho "remote".
Además, para acelerar tu flujo de trabajo, incluimos un conjunto completo de componentes de Remote Compose listos para usar diseñados específicamente para widgets.
Esto significa que tus diseños existentes de Wear Material 3, incluido lo que tienes en Figma, se pueden adaptar fácilmente a los widgets, lo que hace que esto sea familiar tanto para los ingenieros como para los diseñadores.
Para ver qué tan alineados están estos marcos, veamos un ejemplo concreto.
Crearemos nuestro propio componente de app sobre el botón de interruptor de Material 3.
A la izquierda, tenemos el componente estándar de Wear Compose que escribirías en tu app y, a la derecha, la versión de 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,
)Observa lo similares que son los patrones.
Sin embargo, cuando escribas código de Remote Compose, usarás la anotación RemoteComposable.
Y cuando usas parámetros conocidos, como modificadores y booleanos, estos tienen tipos equivalentes de Remote Compose, como RemoteModifier y RemoteBoolean.
Y usas acciones en lugar de lambdas.
En lugar de definir la ejecución del código que se ejecutará dentro de la lambda, deberás declarar una acción, como iniciar un intent pendiente, llamar a un servicio o actualizar un valor.
Pasemos al cuerpo de nuestro componente.
Notarás que incluso el tema está alineado.
Puedes aplicar tu esquema de colores de Material 3 al componente Remote Compose exactamente como lo harías en Compose estándar, lo que permite que tu widget coincida exactamente con el desarrollo de la marca de tu 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 último, veamos el resultado. La IU resultante es exactamente lo que esperarías: un botón de activación animado, responsivo y limpio.
Es difícil notar la diferencia.
Para ayudarte a diseñar, iterar y verificar los diseños de tus widgets sin tener que ejecutarlos siempre en un dispositivo o emulador, agregamos compatibilidad con la vista previa de Compose para los widgets de Wear.
Como los widgets dependen de las restricciones de contenedores específicas del OEM, como el tamaño, la forma, el padding y el radio de las esquinas, compilamos sobre la anotación de vista previa estándar, pero con un proveedor de parámetros adicional para insertar estas configuraciones.
@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)La biblioteca proporciona implementaciones de proveedores de PreviewParameter preconfiguradas que coinciden con dispositivos Wear OS reales.
Estas anotaciones de vista previa te permiten verificar fácilmente tu diseño en diferentes formas de hardware.
Además de probar tus diseños, puedes usar estas vistas previas para generar las imágenes estáticas que se requieren para el selector de widgets integrado en el 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)La biblioteca proporciona un parámetro para la forma de vista previa rectangular que emula los contenedores rectangulares completos sin recortar.
Y si quieres ver cómo funciona exactamente en la práctica, actualizamos nuestra app de ejemplo de contenido multimedia de Horologist para incluir un widget nuevo que muestra esta API.
Mostramos cómo agregar las versiones pequeña y grande del widget de contenido multimedia.
Consulta el repositorio en GitHub para ver una comparación en paralelo del código heredado de ProtoLayout y la nueva implementación de Remote Compose.
Estamos seguros de que te gustará.
Por último, esta no sería una charla completa sin mencionar la IA.
Para acelerar la migración, publicamos una nueva habilidad de migración de tarjetas a widgets en GitHub.
Puedes usarla con el agente que elijas o agregarla directamente a Android Studio con la CLI de Android.
android skills add wear-widgets
android skills add --allAhora, François concluirá la presentación.
Gracias.
Gracias, Yuri.
Como puedes ver, convertir tus tarjetas en widgets de Wear es un proceso muy sencillo.
En resumen, cuando migras a Jetpack Glance Wear y Remote Compose, no solo actualizas tu IU, sino que te unes a un ecosistema de widgets de Android más unificado.
Ahora puedes compilar widgets atractivos y retrocompatibles con un framework moderno y declarativo que está completamente alineado con Jetpack Compose.
Puedes comenzar a migrar tus tarjetas hoy mismo. Para ello, consulta la guía de introducción en el sitio de Android Developers y obtén todos los detalles.
Esperamos ver todas las experiencias expresivas y fáciles de consultar que crees para la muñeca.
Muchas gracias por acompañarnos hoy, esta mañana, y esperamos que disfruten el resto de la conferencia.
Gracias.
// 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