Ve más allá del espacio de la pantalla y fija tus apps en el mundo real con Android XR. En esta sesión, aprenderás a crear experiencias de Android XR con reconocimiento geoespacial usando Jetpack Compose Glimmer y la API de Geospatial de ARCore. Comenzaremos por explicar cómo obtener la posición geoespacial de un dispositivo con ARCore. Luego, verás cómo los componentes mínimos de la IU de Glimmer se pueden fijar a ubicaciones del mundo real a través del Sistema de posicionamiento visual (VPS). Combinaremos estos elementos para crear una experiencia espacial sencilla que reconozca la ubicación.
Transcripción
Hola a todos, les damos la bienvenida a su primera sesión sobre Android XR aquí en la Android Developer Summit.
¡Correcto!
Nos alegra mucho ver que tantas personas están aprendiendo sobre Android XR.
Mi nombre es Yasmine Quintana Evjen, y me acompañan Paul Hoover y Dereck Bridie, quienes se unirán a nosotros a medida que avance la conversación.
El tema de esta sesión es Android XR y cómo puedes crear experiencias del mundo real ancladas espacialmente con Jetpack Compose Glimmer y ARCore para Jetpack XR.
Pero primero, hagamos una breve introducción a Android XR.
Android XR es una nueva plataforma que redefine la forma en que los usuarios interactúan con tu app, ya que va más allá de la pantalla y se extiende al espacio que los rodea.
Diseñamos la plataforma para llevar las experiencias digitales directamente al entorno físico.
Ya sea que eso signifique superposiciones visibles que mejoren la presencia en el mundo real o un entorno inmersivo infinito.
Es el mismo ecosistema Android abierto que hemos estado creando juntos durante años, y ahora el lienzo es tan grande como la habitación y tan amplio como tu imaginación lo permita.
Vemos la realidad extendida como un espectro de dispositivos que van desde lentes inteligentes, que permiten experiencias aumentadas y livianas en lentes con audio y pantalla, hasta visores inmersivos y lentes de realidad extendida con cable, que brindan capacidades de enfoque, productividad y entretenimiento inmersivo.
El dispositivo más reciente de la familia de Android XR es el XREAL AURA, que ya está disponible para su compra.
Este dispositivo es liviano y potente, y permite experiencias de RE con la familiaridad de Android.
Nuestra visión para la plataforma es que, si ya desarrollas para Android, estás listo para desarrollar para XR.
Queremos que te resulte sencillo compilar en Android XR con el mismo modelo de app, la misma biblioteca y las mismas herramientas que ya conoces.
Con pocas o ninguna modificación, tu app existente compatible con Android en 2D funcionará en visores de realidad extendida y lentes de realidad extendida con cable de Android XR.
Y, para crear experiencias de RE increíbles que se sientan naturales, proporcionamos un conjunto de bibliotecas creadas especialmente para la plataforma de RE.
Los desarrolladores que están acostumbrados a crear experiencias en otras plataformas envolventes pueden hacerlo con OpenXR y nuestra compatibilidad con motores 3D, como Unity, Godot y Unreal.
Hablemos sobre la compilación con bibliotecas de Android.
Proporcionamos el SDK de Jetpack XR, un conjunto de bibliotecas de Kotlin que te ayuda a extender tus apps a la RE a través de experiencias inmersivas y aumentadas.
Echemos un vistazo a nuestras bibliotecas inmersivas.
Nuestras bibliotecas de Jetpack XR alcanzaron las versiones beta y estable este año, lo que te brinda la confianza que necesitas para comenzar a compilar tus experiencias de XR inmersivas.
La capa de percepción es ARCore para Jetpack XR.
Esta biblioteca proporciona información sobre el mundo real, como dónde se encuentran las superficies planas, como una mesa, y cómo se colocan las manos de los usuarios.
Puede hacer un seguimiento de puntos en un espacio 3D y proporcionar esa información a las apps cuando se les otorgan los permisos adecuados, por supuesto.
Luego, está Jetpack XR SceneCore, que proporciona un gráfico de escena para colocar tu contenido espacial.
Esta biblioteca puede presentar entidades en un espacio 3D y proporciona comportamientos espaciales para la ubicación precisa del gráfico de escena.
Además, Jetpack Compose para RE proporciona nuevos elementos componibles de subespacio creados especialmente para 3D.
Los desarrolladores de Android que ya estén familiarizados con los diseños de Compose, como la compilación con filas y columnas, encontrarán que el diseño de la IU espacial es intuitivo.
Si creas una fila espacial, puedes diseñar de forma declarativa varios paneles espaciales uno al lado del otro sin tener que calcular manualmente su posición o tamaño.
Con los elementos componibles de subespacio, como un panel espacial o un orbitador, puedes extender fácilmente tus componentes 2D a este nuevo espacio 3D.
Y, por supuesto, los elementos componibles de subespacio pueden hacer más que solo mostrar contenido en 2D.
Compose para XR admite videos espaciales y modelos 3D a través de la superficie externa espacial y el modelo glTF espacial.
Ahora, hablemos de las apps aumentadas.
Con la biblioteca de Jetpack Projected, puedes mejorar tu app para dispositivos móviles existente para que use hardware de lentes inteligentes, como sensores y pantallas.
Por último, Jetpack Compose Glimmer, un kit de herramientas de IU para crear experiencias de Android XR optimizadas para lentes.
Jetpack Compose Glimmer consta de estilos y componentes listos para usar que te permiten compilar tus experiencias de RE.
Lanzamos Glimmer el año pasado y seguimos ampliando sus capacidades, incluida una habilidad de agente para ayudarte a crear tus apps de lentes con pantalla con Glimmer.
android skills add display-glasses-with-jetpack-compose-glimmerDesde el lanzamiento, escuchamos tus comentarios.
Nos enteramos de que querías que extendiéramos Jetpack Compose Glimmer más allá de los lentes inteligentes.
Nos complace anunciar que realizamos cambios para mejorar la compatibilidad de tus experiencias ancladas espacialmente con Glimmer, de lo que hablaremos más adelante en esta charla.
Nuestro trabajo para optimizar Glimmer comenzó con la creación de una nueva superficie para mostrar contenido en una variedad de factores de forma de RE, desde una pantalla hasta lentes de RE con cable.
La incorporación de esta nueva plataforma proporciona más flexibilidad para personalizar tu IU.
Y para aumentar la distinción de la marca de tus apps, agregamos colores personalizados a los componentes de Glimmer.
Úsalos para expresar el estilo de tu marca en tus apps de RE.
Nuestra biblioteca de Glimmer de Jetpack Compose proporciona funciones auxiliares que convierten los colores de destino y de la marca en colores seguros para los lentes.
Esto es para garantizar que se optimice la visibilidad en las pantallas ópticas transparentes.
Veamos esto en acción.
Queremos cambiar el color del botón enfocado para esta app de anteojos, para lo que voy a seleccionar uno de mis fabulosos colores favoritos.
val brandColor = Color(0xFFFFAABB)
val focusedColor = ButtonDefaults.focusedColor(brandColor)
Column(…) {
LeadingImageCard(
focusable = false,
…
) {
…
}
ButtonGroup(...) {
Button(
onClick = {},
leadingIcon = { … },
focusedColor = focusedColor,
) { Text("Add to cart") }
/* Rest of the buttons */
}
}Observa que, cuando se agrega el color al código, las funciones auxiliares de Glimmer lo ajustan a un color seguro para los lentes para optimizar la visibilidad.
Todo esto se compila dentro de nuestra biblioteca de Glimmer de Jetpack Compose, lo que garantiza que crees experiencias que se vean geniales en varios entornos.
En Android Studio, puedes usar el emulador para visualizar cómo se verá la IU de tus lentes en varios fondos, como entornos claros, oscuros o con mucha actividad.
Estas actualizaciones son solo el comienzo.
Okay, no.
Mi equipo me dijo que no se iban a reír con ese chiste, pero igual lo incluí.
Pero ahora, le daré la palabra a Paul para que les cuente más sobre Jetpack Compose Glimmer.
¡Muy bien, probemos!
Tengo que presionar el botón Siguiente.
Muy bien, gracias, Yasmine.
Bien.
Como dijo Yasmine, Jetpack Compose Glimmer proporciona los componentes básicos para desarrollar aplicaciones adaptadas a los lentes con pantalla.
Nuestros componentes integran prácticas recomendadas diseñadas para pantallas ópticas transparentes, y las incorporamos directamente en las APIs para que todos los desarrolladores puedan crear experiencias ópticas transparentes hermosas y funcionales.
Cuando diseñes y compiles apps para dispositivos ópticos transparentes como este, debes tener en cuenta algunos aspectos.
En primer lugar, es una pantalla transparente, lo que significa que no hay negro.
Cuando el mundo es visible, el negro es transparente.
Por lo tanto, no puedes confiar en los fondos oscuros para mantener el contraste de la IU, el texto y el contenido.
Aunque el negro es invisible, puedes usarlo para ocultar otro contenido digital.
En el framework de IU de Glimmer, tenemos una API de profundidad que te permite separar fácilmente los elementos de la IU entre sí y proporcionar una sensación de jerarquía.
Aquí, puedes ver cómo la profundidad de las tarjetas de esta pila ayuda a que se mantengan separadas entre sí.
Nuestros científicos de visión proporcionaron relaciones de contraste objetivo para el texto y los íconos que son visibles en la mayoría de las condiciones de iluminación.
Como mostró Yasmine, podemos incorporar esto o lo incorporamos en el framework para que, cuando personalices los colores de la superficie, puedas tener la certeza de que tu interfaz será legible para los usuarios.
Incorporamos estas y otras prácticas recomendadas en nuestra biblioteca de componentes en expansión.
Hace poco, lanzamos muchos componentes nuevos.
Una de nuestras incorporaciones más recientes es el componente de paginador, que incluye transiciones de movimiento y gestos elegantes.
val pagerState =
rememberGlimmerPagerState(pageCount = { 10 })
GlimmerHorizontalPager(pagerState) { page ->
/* Card Content */
}Si conoces el paginador horizontal de Compose, este componente te resultará muy familiar.
También presentamos cuatro nuevas variantes de tarjetas diseñadas para integrarse sin problemas con paginadores, pilas y otros elementos de la IU de Glimmer.
Las tarjetas de imagen iniciales y finales muestran texto e imágenes uno al lado del otro.
Por lo tanto, usa estas variantes de tarjetas cuando tengas la misma cantidad de texto y de información visual.
Separamos la tarjeta de imagen en su propia variante de tarjeta.
Este es el mejor para usar cuando tienes una imagen grande con tal vez una sola línea de texto o leyenda, como este selector de video.
La variante de tarjeta de acción incorpora un solo botón en una tarjeta.
Usa este tipo cuando quieras que los usuarios sepan exactamente qué sucederá cuando activen la tarjeta, como en este selector de destino.
Los grupos de botones te permiten empaquetar muchos botones en un espacio pequeño.
ButtonGroup {
Button(onClick = {}) { Text("Button 1") }
Button(onClick = {}) { Text("Button 2") }
Button(onClick = {}) { Text("Button 3") }
Button(onClick = {}) { Text("Button 4") }
Button(onClick = {}) { Text("Button 5") }
}Con solo este código simple, obtienes estas hermosas transiciones, comportamientos de desplazamiento, scrims que ocultan los bordes y que se muestran solo cuando es necesario, y entradas que se enrutan automáticamente de los lentes al control.
Los grupos de botones también pueden contener nuestros nuevos botones de ícono y de activación.
var buttonChecked by remember { mutableStateOf(false) }
ToggleButton(
checked = buttonChecked,
leadingIcon = { Icon(FavoriteIcon) },
onCheckedChange = { buttonChecked = it }
) {
Text(if (buttonChecked) "On" else "Off")
}Aquí puedes ver todos nuestros tipos de botones en uso para crear este control multimedia.
Ahora es fácil probar tu IU en diferentes fondos.
El emulador de lentes te permite colocar fotos de 360° y desplazarte por ellas para que puedas probarlas en diferentes fondos, fondos con muchos elementos, claros, oscuros, etc.
Los grupos de botones son ideales para usarlos en una vista de detalles como esta para mostrar acciones contextuales.
Cuando se presiona, puedes usar transiciones de elementos compartidos para navegar a un estado de visor detallado y, luego, usar el gesto atrás del sistema para volver a la vista anterior.
Qué lindo.
Además, agregamos un indicador de entrada de voz y diálogos de alerta.
Así que, sí, hay muchas novedades.
Pero ahora que vimos algunas de las novedades, compilemos algo.
Además de usar Glimmer para crear lentes inteligentes, podemos usarlo para crear experiencias para lentes de realidad extendida con cable.
Queremos que crees experiencias que aumenten el mundo que te rodea, comenzando con anclajes basados en ubicaciones del mundo real.
Imagina que estás en una ciudad nueva, tal vez de viaje para asistir a una conferencia de desarrolladores, y quieres encontrar un lugar para comer en la ciudad.
Podrías pedirle a tu app de navegación favorita que busque restaurantes cercanos y sus calificaciones.
La información se puede superponer directamente en la ubicación, lo que proporciona contexto, inteligencia y orientación.
Aquí puedes ver nuestro componente de Glimmer más reciente, llamado Icon Marker.
Te permite destacar elementos clave del mundo físico y vincularlos con otros componentes de Glimmer, como esta tarjeta.
En este ejemplo, se anotan puntos de interés con las APIs de Google Maps, Places y Geospatial.
Hola, Dereck, mostremos cómo configurar esto.
¡Hagámoslo!
Bien.
Bien.
Como mencionó Paul antes, comencemos a compilar.
Por lo tanto, usaremos la documentación para desarrolladores en developer.android.com/xr y veremos la biblioteca de componentes de Jetpack Compose Glimmer.
Por lo tanto, podrás encontrar una descripción general visual de todos los componentes que están disponibles en Glimmer.
Comencemos con esta parte de la IU que Paul mostró antes.
Por lo tanto, usaremos la IU de Glimmer para crear un indicador espacial y un cuadro de información.
Comencemos por identificar los componentes que usaremos.
A la izquierda, verás un marcador de ícono y, a la derecha, usaremos una tarjeta que describirá el lugar.
Por lo tanto, si vas a Android Studio, podrás obtener una vista previa de este panel como lo harías con cualquier otro componente de Compose y ver los cambios en tiempo real.
Como se mencionó antes, puedes obtener una vista previa de la IU en todos los tipos de entornos de fondo y asegurarte de que el contraste de la IU sea suficiente en varios tipos de situaciones, como entornos oscuros y claros.
Ahora que se configuró el lienzo, crearemos una fila, colocaremos un marcador de ícono con un poco de espaciado y una tarjeta con un texto de muestra.
Y ahí lo tienes.
Si te gusta la vista previa en 2D, podemos empezar a marcar algo en el mundo real, y lo haremos espacializando la IU.
Esto significa que lo diseñaremos en 3D en lugar de 2D.
Por lo tanto, las IU espaciales se encuentran en subespacios, que son espacios en 3D frente al usuario.
Subspace {
SpatialPanel {
VideoPlayer()
}
}De forma predeterminada, un subespacio dispone su contenido a una distancia cómoda del usuario.
Antes de mostrarte cómo usar la API de Geospatial, coloquemos un objeto cerca.
Por lo tanto, el elemento componible de subespacio del que hablé antes tiene un parámetro opcional que te permite especificar un objetivo de seguimiento.
val state by
qrCode.state.collectAsState()
Subspace(FollowTarget.anchor(anchor)) {
SpatialPanel {
Row {
IndicatorDot()
Spacer()
QrCodeCard(state)
}
}
}Por lo tanto, para adjuntar algo al mundo real, primero crearás un ancla que se obtiene de ARCore para Jetpack XR.
En este ejemplo, uso la función de reconocimiento de códigos QR.
Esto te permitirá leer y seguir códigos QR, de modo que tu contenido pueda seguir ese código QR en el espacio, y adjuntaremos la IU que vimos antes.
Bien.
Te mostré cómo colocar contenido digital en el mundo real cercano.
Volvamos al ejemplo de Paul.
En mi caso, soy de los Países Bajos.
No conozco muy bien Mountain View.
Por lo tanto, después de este evento, quiero ayudarte a crear una app que te permita encontrar cafeterías divertidas.
Veamos cómo podemos ayudar a nuestros usuarios a encontrar algunos con la API de Geospatial, que ahora se encuentra en ARCore para Jetpack XR.
Por lo tanto, la API de Geospatial que se lanzó originalmente para smartphones ya está disponible en Android XR.
Usa datos de Google Street View y los combina con mediciones de GPS para posicionar el dispositivo en las áreas admitidas.
Por lo tanto, en los lentes inteligentes, la API de Geospatial se puede usar para determinar con precisión la ubicación y la dirección del usuario, y habilita casos de uso de navegación paso a paso de alta precisión.
En dispositivos inmersivos, como lentes de RE con cable, la API de Geospatial te permite crear experiencias que se posicionan con precisión en el mundo real.
Por ejemplo, puedes fijar contenido espacial en edificios o crear recorridos especializados por la ciudad.
Comencemos a usar la API de Geospatial con estos tres pasos.
En el primero, crearás un proyecto de Google Cloud Platform y configurarás la autenticación en tu app.
Luego, cuando la API de Geospatial esté configurada y habilitada en tu app, le pedirás permiso al usuario para recuperar su ubicación con la precisión detallada.
Luego, después de configurar la sesión para usar la API de Geospatial, todo estará listo.
Por lo tanto, podrás encontrar todos los detalles en nuestra documentación para desarrolladores sobre la API de Geospatial en nuestra página para desarrolladores de Android XR recientemente actualizada.
Comencemos por usar la ubicación del usuario para pedirle a la API de Places de Google Maps Platform que busque cafeterías excelentes cerca del usuario.
En primer lugar, ¿dónde está el usuario?
Le pediremos a la API de Geospatial que obtenga actualizaciones continuas sobre la ubicación del usuario.
produceState<AppStatus>(Initializing) {
val geospatial = Geospatial.getInstance(session)
val wait = geospatial.state.onEach {
value = WaitingForGeospatial(it.geospatialTrackingState)
}.first {
it.geospatialTrackingState == Geospatial.GeospatialTrackingState.RUNNING
}
val accuracyWait = geospatial.state.onEach {
value = WaitingForGeospatialAccuracy(it)
}.first { it.horizontalAccuracy < horizontalAccuracyRequirement }
value = WaitingForPlacesResults
val result = client.findNearby(context, accWait.geospatialPose)
value = Done(result)
}Por lo tanto, accederemos al objeto geoespacial a través de la sesión de XR.
Después de eso, esperaremos a que Geospatial se ponga en marcha mientras inicializa los sensores de los centros necesarios y se conecta a la API de Cloud.
Luego, esperaremos a que se alcance un nivel de precisión determinado.
Por lo tanto, los niveles de precisión pueden mejorar mientras se reciben las mediciones del GPS o cuando el sistema de posicionamiento visual encuentra coincidencias en la ubicación del usuario.
Esperar a que se alcance un nivel de precisión alto es importante para garantizar que las experiencias basadas en la ubicación se fijen en el lugar correcto y que los resultados correspondan al área cercana del usuario.
Por último, estamos listos para realizar la búsqueda con la API de Places usando los valores de ubicación de la API de Geospatial.
val bounds = CircularBounds.newInstance(
LatLng(pose.latitude, pose.longitude),
100.0, // radius, in meters
)
val placeFields = listOf(
Place.Field.ID, Place.Field.DISPLAY_NAME,
Place.Field.RATING, Place.Field.LOCATION,
)
return placesClient.searchNearby(
SearchNearbyRequest.builder(bounds, placeFields)
.setIncludedTypes(listOf(PlaceTypes.CAFE))
.setMaxResultCount(10)
.build()
).await()Por lo tanto, usaremos un SearchNearbyRequest con la API de Places para consultar lugares alrededor de la posición del usuario que obtuvimos antes, y encontraremos resultados en un radio de 100 m.
Obtendremos el ID del lugar, su nombre visible, su calificación y, por supuesto, su ubicación.
Por último, realizaremos la búsqueda con los parámetros especificados, incluido un filtro de tipo de lugar.
Eso es todo en cuanto a los datos. Volvamos a la IU.
Por lo tanto, tendremos que averiguar cómo colocar nuestro marcador y cuadro de información de antes, y para eso, usaremos anclas geoespaciales.
Volvamos al código. Una vez más, accederemos a nuestra API de Geospatial con nuestra sesión de XR y, luego, crearemos un ancla geoespacial con una latitud y una longitud específicas que se obtuvieron de los resultados de la búsqueda anterior.
val anchorResult by produceState<AnchorResult?>(null) {
val geospatial = Geospatial.getInstance(session)
value = geospatial.createAnchorOnSurface(
place.location.latitude, place.location.longitude,
surface = GeospatialSurface.TERRAIN,
altitudeAboveSurface = 3.0,
eastUpSouthQuaternion = Quaternion()
)
}En este ejemplo, especificamos que la altitud sea de 3 m sobre el terreno en esas coordenadas, pero también puedes adjuntarla a techos compatibles, lo que significa que el valor de la altitud se determinará en relación con el techo del edificio en esa ubicación.
Por lo tanto, también puedes rotar el ancla con un cuaternión, que especifica los valores de rotación del ancla.
Sin embargo, en este ejemplo, usaremos los valores predeterminados porque haremos que la IU rote para mirar al usuario.
Luego, adjuntaremos la IU de Glimmer de antes a este anclaje, y eso te dará los componentes básicos para una experiencia con anclaje espacial.
when (anchorResult) {
is AnchorCreateSuccess ->
Subspace(FollowTarget.anchor(anchorResult.anchor)) {
SpatialPanel(SubspaceModifier.rotateToLookAtUser()) {
PlaceIndicator(place)
}
}
else ->
Log.e("Demo", "Failed to create anchor $anchorResult")
}Por lo tanto, creemos que la API de Geospatial puede habilitar excelentes experiencias de orientación, y diseñar con Glimmer ayudará a crear experiencias del usuario fáciles de consultar, creadas especialmente para tus pantallas transparentes.
Para obtener más información sobre la API de Geospatial, visita la página developer.android.com/xr, que se actualizó recientemente.
Ahora le daré la palabra a Yasmine para que termine la presentación.
En esta sesión, repasamos algunas funciones de Android XR: ARCore para Jetpack XR y Jetpack Compose Glimmer.
Nos entusiasman las nuevas funciones que Android XR incorporará a los lentes de realidad extendida con cable.
Los desarrolladores ya están creando para Android XR, y esperamos ver tu trabajo creativo en Google Play Store.
Para comenzar, consulta nuestra documentación para desarrolladores, inspírate con nuestros experimentos de RE y sumérgete en los codelabs.
Tenemos muchas sesiones, demostraciones y talleres de RE excelentes planificados para todos ustedes.
Así que sintoniza y aprende a crear para Android XR en experiencias inmersivas y aumentadas.
En nombre del equipo de Android XR, muchas gracias a todos por estar aquí. Esperamos que disfruten el resto de su tiempo en la Android Developer Summit.
Así que, gracias.
android skills add display-glasses-with-jetpack-compose-glimmerval brandColor = Color(0xFFFFAABB)
val focusedColor = ButtonDefaults.focusedColor(brandColor)
Column(…) {
LeadingImageCard(
focusable = false,
…
) {
…
}
ButtonGroup(...) {
Button(
onClick = {},
leadingIcon = { … },
focusedColor = focusedColor,
) { Text("Add to cart") }
/* Rest of the buttons */
}
}val pagerState =
rememberGlimmerPagerState(pageCount = { 10 })
GlimmerHorizontalPager(pagerState) { page ->
/* Card Content */
}ButtonGroup {
Button(onClick = {}) { Text("Button 1") }
Button(onClick = {}) { Text("Button 2") }
Button(onClick = {}) { Text("Button 3") }
Button(onClick = {}) { Text("Button 4") }
Button(onClick = {}) { Text("Button 5") }
}var buttonChecked by remember { mutableStateOf(false) }
ToggleButton(
checked = buttonChecked,
leadingIcon = { Icon(FavoriteIcon) },
onCheckedChange = { buttonChecked = it }
) {
Text(if (buttonChecked) "On" else "Off")
}Subspace {
SpatialPanel {
VideoPlayer()
}
}val state by
qrCode.state.collectAsState()
Subspace(FollowTarget.anchor(anchor)) {
SpatialPanel {
Row {
IndicatorDot()
Spacer()
QrCodeCard(state)
}
}
}produceState<AppStatus>(Initializing) {
val geospatial = Geospatial.getInstance(session)
val wait = geospatial.state.onEach {
value = WaitingForGeospatial(it.geospatialTrackingState)
}.first {
it.geospatialTrackingState == Geospatial.GeospatialTrackingState.RUNNING
}
val accuracyWait = geospatial.state.onEach {
value = WaitingForGeospatialAccuracy(it)
}.first { it.horizontalAccuracy < horizontalAccuracyRequirement }
value = WaitingForPlacesResults
val result = client.findNearby(context, accWait.geospatialPose)
value = Done(result)
}val bounds = CircularBounds.newInstance(
LatLng(pose.latitude, pose.longitude),
100.0, // radius, in meters
)
val placeFields = listOf(
Place.Field.ID, Place.Field.DISPLAY_NAME,
Place.Field.RATING, Place.Field.LOCATION,
)
return placesClient.searchNearby(
SearchNearbyRequest.builder(bounds, placeFields)
.setIncludedTypes(listOf(PlaceTypes.CAFE))
.setMaxResultCount(10)
.build()
).await()val anchorResult by produceState<AnchorResult?>(null) {
val geospatial = Geospatial.getInstance(session)
value = geospatial.createAnchorOnSurface(
place.location.latitude, place.location.longitude,
surface = GeospatialSurface.TERRAIN,
altitudeAboveSurface = 3.0,
eastUpSouthQuaternion = Quaternion()
)
}when (anchorResult) {
is AnchorCreateSuccess ->
Subspace(FollowTarget.anchor(anchorResult.anchor)) {
SpatialPanel(SubspaceModifier.rotateToLookAtUser()) {
PlaceIndicator(place)
}
}
else ->
Log.e("Demo", "Failed to create anchor $anchorResult")
}