Android XR

Tworzenie funkcji zakotwiczonych w świecie na przewodowych okularach XR

Głośniki

Informacje

Informacje o wystąpieniu

Wyjdź poza przestrzeń ekranu i zakotwicz aplikacje w świecie rzeczywistym dzięki Androidowi XR. Podczas tej sesji dowiesz się, jak tworzyć aplikacje na Androida XR z uwzględnieniem danych geoprzestrzennych za pomocą Jetpack Compose Glimmer i interfejsu Geospatial API ARCore. Zaczniemy od omówienia, jak uzyskać pozycję geoprzestrzenną urządzenia za pomocą ARCore. Następnie zobaczysz, jak minimalistyczne komponenty interfejsu Glimmer można zakotwiczyć w lokalizacjach w świecie rzeczywistym za pomocą systemu pozycjonowania wizualnego (VPS). Połączymy je, aby stworzyć proste, uwzględniające lokalizację środowisko przestrzenne.

Podsumowanie

  • Android XR wprowadza cyfrowe doświadczenia do środowisk fizycznych na różnych urządzeniach, od inteligentnych okularów po przewodowe okulary XR. Istniejące aplikacje 2D zgodne z Androidem działają na goglach XR i przewodowych okularach XR przy minimalnych modyfikacjach.
  • Jetpack Compose Glimmer udostępnia komponenty i style dostosowane do wyświetlaczy optycznych. Wbudowane funkcje pomocnicze dostosowują kolory marki do odcieni bezpiecznych dla okularów, aby zachować widoczność i kontrast w różnych warunkach oświetleniowych.
  • Biblioteka Glimmer dodaje nowe komponenty, takie jak pagery i grupy przycisków. Interfejs Depth API pomaga oddzielić elementy ułożone jeden na drugim na przezroczystych wyświetlaczach, na których kolor czarny jest przezroczysty.
  • Komponenty Subspace umieszczają interfejsy w przestrzeni 3D i mogą podążać za kotwicami ARCore za pomocą celu śledzenia, takiego jak śledzony kod QR.
  • Interfejs Geospatial API w ARCore łączy dane Street View i pomiary GPS, aby dokładnie określać pozycję urządzeń na obsługiwanych obszarach. Komponenty Glimmer możesz dołączać do rzeczywistych kotwic geoprzestrzennych, używając współrzędnych z zapytań Places API.

Zasoby

Gemini Notebook

Tematy

  • android-xr
  • jetpack-compose
  • przebłysk
  • arcore
  • geospatial-api
  • augmented-reality

Transkrypcja

Pobierz transkrypcję

Wprowadzenie do Androida XR

  • Cześć wszystkim, witamy na pierwszej sesji dotyczącej Androida XR na Android Dev Summit.

  • Tak!

  • Cieszymy się, że tak wielu z Was interesuje się Androidem XR.

  • Nazywam się Yasmine Quintana Evjen, a razem ze mną są Paul Hoover i Dereck Bridie, którzy dołączą do nas w trakcie rozmowy.

  • Tematem tej sesji jest Android XR oraz to, jak za pomocą Jetpack Compose Glimmer i ARCore dla Jetpack XR tworzyć rozwiązania zakotwiczone przestrzennie w świecie rzeczywistym.

  • Najpierw jednak krótko przedstawimy Androida XR.

  • Android XR to nowa platforma, która zmienia sposób, w jaki użytkownicy wchodzą w interakcję z aplikacją, wykraczając poza ekran i obejmując przestrzeń wokół nich.

  • Projektujemy platformę tak, aby przenosić cyfrowe doświadczenia bezpośrednio do środowiska fizycznego.

  • Może to być nakładka, która wzbogaca rzeczywistość, lub nieskończone, immersyjne środowisko.

  • To ten sam otwarty ekosystem Androida, który budujemy razem od lat, ale teraz przestrzeń jest tak duża jak pokój i tak rozległa, jak tylko pozwala na to wyobraźnia.

  • XR to dla nas całe spektrum urządzeń – od inteligentnych okularów, które zapewniają lekkie, rozszerzone wrażenia dzięki funkcjom audio i wyświetlaczowi, po immersyjne gogle i przewodowe okulary XR, które zapewniają skupienie, produktywność i możliwości immersyjnej rozrywki.

  • Najnowszym urządzeniem z rodziny Androida XR są okulary XREAL AURA, które można już kupić.

  • To urządzenie jest lekkie i wydajne, dzięki czemu umożliwia korzystanie z XR w znanym środowisku Androida.

  • Nasza wizja platformy jest taka, że jeśli już tworzysz aplikacje na Androida, możesz też tworzyć aplikacje na XR.

  • Chcemy ułatwić Ci tworzenie aplikacji na Androida XR, korzystając z tego samego modelu aplikacji, biblioteki i narzędzi, które już znasz.

  • Twoja dotychczasowa aplikacja 2D zgodna z Androidem będzie działać na goglach z Androidem XR i przewodowych okularach XR bez żadnych modyfikacji lub z niewielkimi zmianami.

Pakiet SDK Jetpack XR i narzędzia dla programistów

  • Aby umożliwić tworzenie niesamowitych środowisk XR, które będą działać naturalnie, udostępniamy zestaw bibliotek stworzonych specjalnie na platformę XR.

  • Deweloperzy, którzy są przyzwyczajeni do tworzenia treści na innych platformach immersyjnych, mogą korzystać z OpenXR i naszego wsparcia dla silników 3D, takich jak Unity, Godot i Unreal.

  • Porozmawiajmy o tworzeniu aplikacji za pomocą bibliotek Androida.

  • Udostępniamy pakiet SDK Jetpack XR, czyli zestaw bibliotek Kotlin, który pomaga rozszerzyć aplikacje na XR w trybie immersyjnym i rzeczywistości rozszerzonej.

  • Przyjrzyjmy się naszym bibliotekom immersyjnym.

  • W tym roku nasze biblioteki Jetpack XR osiągnęły wersję beta i stabilną, co daje Ci pewność, że możesz zacząć tworzyć immersyjne środowiska XR.

  • Warstwa percepcji to ARCore dla Jetpack XR.

  • Ta biblioteka dostarcza informacji o świecie rzeczywistym, np. o tym, gdzie znajdują się płaskie powierzchnie, takie jak stół, i jak ułożone są dłonie użytkowników.

  • Może śledzić punkty w przestrzeni 3D i udostępniać te informacje aplikacjom, jeśli oczywiście mają one odpowiednie uprawnienia.

  • Jest też Jetpack XR SceneCore, który udostępnia graf sceny do umieszczania treści przestrzennych.

  • Ta biblioteka umożliwia prezentowanie elementów w przestrzeni 3D i zapewnia zachowania przestrzenne, które pozwalają na precyzyjne umieszczanie elementów w grafie sceny.

  • Z kolei Jetpack Compose for XR udostępnia nowe elementy kompozycyjne podprzestrzeni stworzone specjalnie z myślą o 3D.

  • Programiści aplikacji na Androida, którzy znają już układy Compose, takie jak tworzenie z użyciem wierszy i kolumn, uznają, że układanie interfejsu przestrzennego jest intuicyjne.

  • Tworząc wiersz przestrzenny, możesz deklaratywnie ułożyć wiele paneli przestrzennych obok siebie bez konieczności ręcznego obliczania ich pozycji lub rozmiaru.

  • Za pomocą komponentów podprzestrzeni, takich jak panel przestrzenny lub orbiter, możesz łatwo rozszerzyć komponenty 2D na tę nową przestrzeń 3D.

  • Oczywiście elementy kompozycyjne podprzestrzeni mogą nie tylko wyświetlać treści 2D.

  • Compose for XR obsługuje filmy przestrzenne i modele 3D za pomocą przestrzennej powierzchni zewnętrznej i przestrzennego modelu glTF.

Tworzenie interfejsu za pomocą Jetpack Compose Glimmer

  • Porozmawiajmy teraz o aplikacjach rozszerzonych.

  • Dzięki bibliotece Jetpack Projected możesz ulepszyć istniejącą aplikację mobilną, aby korzystała z inteligentnych okularów, takich jak czujniki i wyświetlacz.

  • Na koniec Jetpack Compose Glimmer, czyli zestaw narzędzi do tworzenia interfejsu użytkownika, który umożliwia tworzenie aplikacji na Androida XR zoptymalizowanych pod kątem okularów.

  • Jetpack Compose Glimmer zawiera style i gotowe do użycia komponenty do tworzenia aplikacji XR.

  • W zeszłym roku wprowadziliśmy Glimmer i nadal rozwijamy jego możliwości, w tym umiejętność agentową, która pomaga tworzyć aplikacje na okulary z wyświetlaczem za pomocą Glimmer.

    android skills add display-glasses-with-jetpack-compose-glimmer
  • Od momentu wprowadzenia tej funkcji słuchamy Waszych opinii.

  • Otrzymaliśmy od Was prośby o rozszerzenie Jetpack Compose Glimmer poza inteligentne okulary.

  • Z przyjemnością informujemy, że wprowadziliśmy zmiany, które ułatwią korzystanie z funkcji zakotwiczonych przestrzennie w Glimmer. Omówimy je później w tej prezentacji.

  • Prace nad optymalizacją Glimmer rozpoczęliśmy od utworzenia nowej powierzchni do wyświetlania na różnych urządzeniach XR, od wyświetlacza po przewodowe okulary XR.

  • Dodanie tej nowej platformy zapewnia większą elastyczność w dostosowywaniu interfejsu.

  • Aby zwiększyć rozpoznawalność marki Twojej aplikacji, dodaliśmy spersonalizowane kolory do komponentów Glimmer.

  • Używaj ich, aby wyrazić styl swojej marki w aplikacjach XR.

  • Nasza biblioteka Jetpack Compose Glimmer udostępnia funkcje pomocnicze, które konwertują kolory docelowe i kolory marki na kolory bezpieczne dla okularów.

  • Ma to na celu zapewnienie optymalnej widoczności na wyświetlaczach optycznych.

  • Zobaczmy, jak to działa w praktyce.

  • Chcemy zmienić kolor przycisku, na którym jest ustawiony fokus, w tej aplikacji do okularów. Wybiorę jeden z moich ulubionych kolorów.

    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 */
       }
    }
  • Zauważ, że gdy kolor zostanie dodany do kodu, funkcje pomocnicze Glimmer dostosują go do koloru bezpiecznego dla okularów, aby zapewnić optymalną widoczność.

  • Wszystko to jest wbudowane w naszą bibliotekę Jetpack Compose Glimmer, dzięki czemu możesz tworzyć interfejsy, które wyglądają świetnie w różnych środowiskach.

  • W Android Studio możesz użyć emulatora, aby zobaczyć, jak interfejs okularów będzie wyglądać na różnych tła, np. w jasnym, ciemnym lub ruchliwym otoczeniu.

  • Te zmiany to dopiero początek.

  • OK, nie.

  • Mój zespół powiedział mi, że nikt się z tego nie zaśmieje, ale i tak go zachowałem.

  • Teraz Paul opowie Wam więcej o Jetpack Compose Glimmer.

Zasady projektowania interfejsu okularów z wyświetlaczem

  • Testuję, wszystko w porządku.

  • Muszę kliknąć przycisk Dalej.

  • Dziękuję, Yasmine.

  • OK.

  • Jak wspomniała Yasmine, Jetpack Compose Glimmer zapewnia podstawowe elementy do tworzenia aplikacji na okulary z wyświetlaczem.

  • Nasze komponenty integrują sprawdzone metody opracowane dla wyświetlaczy optycznych, a my wbudowaliśmy je bezpośrednio w interfejsy API, aby umożliwić wszystkim deweloperom tworzenie pięknych i funkcjonalnych rozwiązań optycznych.

  • Podczas projektowania i tworzenia aplikacji na urządzenia z optycznym systemem przeziernym, takie jak to, musisz pamiętać o kilku kwestiach.

  • Po pierwsze, jest to wyświetlacz przezroczysty, co oznacza, że nie ma czerni.

  • Gdy świat jest widoczny, czerń jest przezroczysta.

  • Nie możesz więc liczyć na to, że ciemne tło zapewni kontrast dla interfejsu, tekstu i treści.

  • Mimo że kolor czarny jest niewidoczny, możesz go używać do zasłaniania innych treści cyfrowych.

  • W interfejsie Glimmer mamy interfejs Depth API, który umożliwia łatwe oddzielanie elementów interfejsu od siebie i tworzenie poczucia hierarchii.

  • Tutaj możesz zobaczyć, jak głębia kart w tym stosie pomaga im zachować odrębność.

  • Nasi naukowcy zajmujący się widzeniem podali docelowe współczynniki kontrastu dla tekstu i ikon, które są widoczne w większości warunków oświetleniowych.

  • Jak pokazała Yasmine, możemy to wbudować lub wbudowaliśmy to w platformę, aby po dostosowaniu kolorów powierzchni można było mieć pewność, że interfejs będzie czytelny dla użytkowników.

  • Te i inne sprawdzone metody uwzględniliśmy w naszej rozszerzającej się bibliotece komponentów.

  • Niedawno wprowadziliśmy wiele nowych komponentów.

  • Jednym z naszych najnowszych dodatków jest komponent pagera z wbudowanymi płynnymi przejściami i gestami.

    val pagerState =
      rememberGlimmerPagerState(pageCount = { 10 })
    
    GlimmerHorizontalPager(pagerState) { page ->
       /* Card Content */
    }
  • Jeśli znasz poziomy pager Compose, ten komponent będzie Ci bardzo dobrze znany.

  • Wprowadziliśmy też 4 nowe warianty kart, które można bezproblemowo integrować z paginatorami, stosami i innymi elementami interfejsu Glimmer.

  • Karty z obrazem na początku i karty z obrazem na końcu zawierają tekst i obrazy obok siebie.

  • Używaj tych wariantów kart, gdy masz taką samą ilość tekstu i informacji wizualnych.

  • Karta z obrazem została wydzielona jako osobny wariant karty.

  • Najlepiej używać go, gdy masz duży obraz z jednym wierszem tekstu lub podpisem, jak w przypadku tego selektora filmów.

  • Wariant karty działania zawiera pojedynczy przycisk.

  • Używaj tego, gdy chcesz, aby użytkownicy wiedzieli dokładnie, co się stanie, gdy aktywują kartę, np. w tym selektorze miejsca docelowego.

  • Grupy przycisków umożliwiają umieszczenie wielu przycisków na małej przestrzeni.

    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") }
    }
  • Dzięki temu prostemu kodowi uzyskasz piękne przejścia, zachowania przewijania, scrimy, które ukrywają krawędzie i wyświetlają się tylko wtedy, gdy są potrzebne, oraz dane wejściowe, które są automatycznie kierowane z okularów do elementu sterującego.

  • Grupy przycisków mogą też zawierać nasze nowe przyciski ikon i przyciski przełączania.

    var buttonChecked by remember { mutableStateOf(false) }
    
    ToggleButton(
      checked = buttonChecked,
      leadingIcon = { Icon(FavoriteIcon) },
      onCheckedChange = { buttonChecked = it }
    ) {
      Text(if (buttonChecked) "On" else "Off")
    }
  • Tutaj możesz zobaczyć wszystkie typy przycisków użyte do utworzenia tych opcji sterowania multimediami.

  • Teraz możesz łatwo testować interfejs na różnych tła.

  • Emulator okularów pozwala na wstawianie zdjęć sferycznych i przesuwanie ich, dzięki czemu można przetestować okulary na różnych tłach, tłach z dużą ilością elementów, jasnych i ciemnych.

  • Grupy przycisków świetnie nadają się do wyświetlania czynności kontekstowych w widoku szczegółowym, takim jak ten.

  • Po kliknięciu możesz użyć przejść ze wspólnymi elementami, aby przejść do szczegółowego stanu przeglądarki, a następnie użyć systemowego gestu wstecz, aby wrócić do poprzedniego widoku.

  • Bardzo miło.

  • Dodaliśmy też wskaźnik wprowadzania głosowego i okna alertów.

  • Tak więc mamy dużo nowości.

  • Teraz, gdy już poznaliśmy niektóre z nowych funkcji, zbudujmy coś.

Omówienie kotwic opartych na lokalizacji

  • Oprócz tworzenia inteligentnych okularów możemy używać Glimmer do tworzenia funkcji na przewodowe okulary XR.

  • Chcemy, abyście tworzyli rozwiązania, które wzbogacają otaczający nas świat, zaczynając od kotwic opartych na lokalizacji w świecie rzeczywistym.

  • Wyobraź sobie, że jesteś w nowym mieście, na przykład na konferencji dla deweloperów, i chcesz znaleźć coś do jedzenia.

  • Możesz poprosić ulubioną aplikację do nawigacji o znalezienie pobliskich restauracji i ich ocen.

  • Informacje mogą być nakładane bezpośrednio na lokalizację, co zapewnia kontekst, dane i wskazówki.

  • Tutaj możesz zobaczyć nasz najnowszy komponent Glimmer o nazwie Icon Marker.

  • Pozwala wyróżnić kluczowe elementy w świecie fizycznym i połączyć je z innymi komponentami Glimmer, takimi jak ta karta.

  • W tym przykładzie dodaje adnotacje do ciekawych miejsc za pomocą interfejsów Mapy Google, Miejsc i Geospatial API.

  • Dereck, pokażmy, jak to skonfigurować.

  • Zróbmy to.

  • OK.

  • OK.

  • Jak wspomniał Paul, zacznijmy tworzyć.

  • Skorzystamy z dokumentacji dla deweloperów na stronie developer.android.com/xr i przyjrzymy się bibliotece komponentów Glimmer w Jetpack Compose.

  • Znajdziesz tam wizualny przegląd wszystkich komponentów dostępnych w Glimmer.

  • Zacznijmy od tego elementu interfejsu, który Paul pokazał wcześniej.

  • Użyjemy więc interfejsu Glimmer, aby utworzyć wskaźnik przestrzenny i pole informacyjne.

  • Zacznijmy od określenia komponentów, których będziemy używać.

  • Po lewej stronie zobaczysz znacznik ikony, a po prawej – kartę z opisem miejsca.

  • Jeśli przejdziesz do Android Studio, możesz wyświetlić podgląd tego panelu tak samo jak każdego innego komponentu Compose i zobaczyć zmiany w czasie rzeczywistym.

  • Jak wspomniano wcześniej, możesz wyświetlić podgląd interfejsu w różnych środowiskach tła i upewnić się, że kontrast interfejsu jest wystarczający w różnych scenariuszach, np. w ciemnym i jasnym otoczeniu.

  • Po skonfigurowaniu obszaru roboczego utworzymy wiersz, umieścimy w nim znacznik ikony z odstępami i kartę z przykładowym tekstem.

  • I to wszystko.

  • Jeśli podgląd 2D Ci odpowiada, możemy zacząć oznaczać coś w świecie rzeczywistym. Zrobimy to, nadając interfejsowi charakter przestrzenny.

  • Oznacza to, że układ będzie trójwymiarowy, a nie dwuwymiarowy.

  • Interfejsy przestrzenne są więc zawarte w podprzestrzeniach, czyli przestrzeniach 3D przed użytkownikiem.

    Subspace {
      SpatialPanel {
        VideoPlayer()
      }
    }
  • Domyślnie podprzestrzeń rozmieszcza zawartość w wygodnej odległości od użytkownika.

  • Zanim pokażemy Ci, jak korzystać z interfejsu Geospatial API, umieśćmy coś w pobliżu.

  • Komponent podprzestrzeni, o którym mówiłem wcześniej, ma opcjonalny parametr, który pozwala określić cel śledzenia.

    val state by
      qrCode.state.collectAsState()
    Subspace(FollowTarget.anchor(anchor)) {
      SpatialPanel {
        Row {
          IndicatorDot()
          Spacer()
          QrCodeCard(state)
        }
      }
    }
  • Aby dołączyć coś do świata rzeczywistego, najpierw utwórz kotwicę uzyskaną z ARCore dla Jetpack XR.

  • W tym przykładzie używam funkcji rozpoznawania kodu QR.

  • Dzięki temu możesz odczytywać i śledzić kody QR, dzięki czemu Twoje treści mogą podążać za kodem QR w przestrzeni, a my dołączymy interfejs, który widzieliśmy wcześniej.

Integracja Geospatial API i Google Maps Platform

  • OK.

  • Pokazałem, jak umieszczać treści cyfrowe w świecie rzeczywistym w pobliżu.

  • Wróćmy do przykładu Pawła.

  • Osobiście pochodzę z Holandii.

  • Nie znam zbyt dobrze Mountain View.

  • Po tym wydarzeniu chcę pomóc Ci stworzyć aplikację, która pomoże Ci znaleźć ciekawe kawiarnie.

  • Zobaczmy, jak możemy pomóc użytkownikom znaleźć niektóre z nich za pomocą interfejsu Geospatial API, który jest teraz dostępny w ARCore for Jetpack XR.

  • Dlatego interfejs Geospatial API, który pierwotnie został wprowadzony na smartfonach, już jest dostępny również na Androidzie XR.

  • Wykorzystuje dane z Google Street View i łączy je z pomiarami GPS, aby określić pozycję urządzenia na obsługiwanych obszarach.

  • Dzięki temu w inteligentnych okularach interfejs Geospatial API może służyć do dokładnego określania lokalizacji i kierunku użytkownika, co umożliwia precyzyjne korzystanie ze szczegółowych wskazówek dojazdu.

  • Z kolei na urządzeniach immersyjnych, takich jak przewodowe okulary XR, interfejs Geospatial API umożliwia tworzenie treści, które są dokładnie pozycjonowane w świecie rzeczywistym.

  • Możesz na przykład zakotwiczyć treści przestrzenne na budynkach lub utworzyć specjalistyczne wycieczki po mieście.

  • Zacznijmy od 3 kroków, które pozwolą Ci rozpocząć korzystanie z interfejsu Geospatial API.

  • Najpierw utworzysz projekt Google Cloud Platform i skonfigurujesz uwierzytelnianie w aplikacji.

  • Następnie, gdy interfejs Geospatial API zostanie skonfigurowany i włączony w aplikacji, poprosisz użytkownika o zgodę na pobieranie jego lokalizacji z dużą dokładnością.

  • Następnie skonfiguruj sesję, aby korzystała z interfejsu Geospatial API.

  • Wszystkie szczegóły dotyczące interfejsu Geospatial API znajdziesz w dokumentacji dla deweloperów na naszej nowo odświeżonej stronie dla deweloperów Androida XR.

  • Zacznijmy od użycia lokalizacji użytkownika, aby poprosić interfejs Places API z Google Maps Platform o znalezienie świetnych kawiarni w pobliżu użytkownika.

  • Po pierwsze, gdzie znajduje się użytkownik?

  • Poprosimy interfejs Geospatial API o ciągłe aktualizowanie lokalizacji użytkownika.

    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)
    
    }
  • Dlatego uzyskamy dostęp do obiektu geoprzestrzennego przez sesję XR.

  • Następnie poczekamy, aż Geospatial zacznie działać, zainicjuje niezbędne czujniki i połączy się z interfejsem Cloud API.

  • Następnie poczekamy na osiągnięcie określonego poziomu dokładności.

  • Dzięki temu poziom dokładności może się poprawić, gdy pojawią się pomiary GPS lub gdy system pozycjonowania wizualnego znajdzie dopasowania w lokalizacji użytkownika.

  • Oczekiwanie na Wysoką dokładność jest ważne, aby mieć pewność, że funkcje związane z lokalizacją są zakotwiczone we właściwym miejscu, a wyniki pochodzą z pobliskiego obszaru użytkownika.

  • Następnie możemy przeprowadzić wyszukiwanie za pomocą interfejsu Places API, używając wartości lokalizacji z interfejsu Geospatial API.

    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()
  • Dlatego użyjemy SearchNearbyRequest z interfejsem Places API, aby wyszukać miejsca wokół lokalizacji użytkownika, którą uzyskaliśmy wcześniej, i znaleźć wyniki w promieniu 100 metrów.

  • Pobierzemy identyfikator miejsca, jego wyświetlaną nazwę, ocenę i oczywiście lokalizację.

  • Na koniec przeprowadzimy wyszukiwanie z określonymi parametrami, w tym z filtrem typu miejsca.

  • To wszystko, jeśli chodzi o dane. Wróćmy do interfejsu.

  • Musimy więc wymyślić, jak umieścić nasz znacznik i pole informacyjne z wcześniejszego etapu. W tym celu użyjemy kotwic geoprzestrzennych.

  • Wracając do kodu, ponownie uzyskamy dostęp do interfejsu Geospatial API za pomocą sesji XR, a następnie utworzymy kotwicę geoprzestrzenną, używając określonej szerokości i długości geograficznej, które zostały wcześniej uzyskane z wyników wyszukiwania.

    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()
     )
    }
  • W tym przykładzie określiliśmy wysokość na 3 metry nad terenem przy tych współrzędnych, ale można też dołączyć do obsługiwanych dachów, co oznacza, że wartość wysokości zostanie określona względem dachu budynku w tej lokalizacji.

  • Możesz też obrócić kotwicę za pomocą kwaternionu, który określa wartości obrotu kotwicy.

  • W tym przykładzie użyjemy jednak wartości domyślnych, ponieważ interfejs będzie się obracać w stronę użytkownika.

  • Następnie do tej kotwicy dołączymy interfejs Glimmer, co zapewni podstawowe elementy do stworzenia środowiska zakotwiczonego przestrzennie.

    when (anchorResult) {
     is AnchorCreateSuccess ->
       Subspace(FollowTarget.anchor(anchorResult.anchor)) {
         SpatialPanel(SubspaceModifier.rotateToLookAtUser()) {
           PlaceIndicator(place)
         }
       }
    
     else ->
       Log.e("Demo", "Failed to create anchor $anchorResult")
    }
  • Uważamy, że interfejs Geospatial API może ułatwić znajdowanie drogi, a projektowanie z wykorzystaniem Glimmer pomoże tworzyć interfejsy użytkownika, które będą łatwe do odczytania i będą przeznaczone specjalnie na przezroczyste wyświetlacze.

  • Więcej informacji o interfejsie Geospatial API znajdziesz na niedawno zaktualizowanej stronie developer.android.com/xr.

  • Teraz oddaję głos Yasmine, która zakończy prezentację.

Podsumowanie i materiały dla deweloperów

  • Podczas tej sesji omówiliśmy kilka funkcji Androida XR: ARCore dla Jetpack XR i Jetpack Compose Glimmer.

  • Z niecierpliwością czekamy na nowe funkcje, które Android XR wprowadzi na przewodowych okularach XR.

  • Deweloperzy już tworzą aplikacje na Androida XR. Mamy nadzieję, że Twoje kreatywne dzieło pojawi się w Sklepie Google Play.

  • Aby zacząć, zapoznaj się z naszą dokumentacją dla deweloperów, zainspiruj się naszymi eksperymentami z XR i wypróbuj ćwiczenia z programowania.

  • Przygotowaliśmy dla Was mnóstwo świetnych sesji, prezentacji i warsztatów dotyczących XR.

  • Zapraszamy do oglądania i poznawania sposobów tworzenia aplikacji na Androida XR w trybie immersyjnym i rzeczywistości rozszerzonej.

  • W imieniu zespołu Androida XR dziękuję wszystkim za obecność i życzę miłego spędzenia czasu podczas Android Dev Summit.

  • Dziękuję.

Kod

Fragmenty kodu

Fragment kodu powłoki w 04:39 w filmie „Tworzenie interfejsu za pomocą Jetpack Compose Glimmer”
android skills add display-glasses-with-jetpack-compose-glimmer
Fragment kodu w języku Kotlin w 05:49 w filmie „Tworzenie interfejsu za pomocą Jetpack Compose Glimmer”
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 */
   }
}
Fragment kodu w języku Kotlin w 08:37 w filmie „Zasady projektowania interfejsu okularów z wyświetlaczem”
val pagerState =
  rememberGlimmerPagerState(pageCount = { 10 })

GlimmerHorizontalPager(pagerState) { page ->
   /* Card Content */
}
Fragment kodu w języku Kotlin w 09:50 w filmie „Zasady projektowania interfejsu okularów z wyświetlaczem”
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") }
}
Fragment kodu w języku Kotlin w 10:13 w filmie „Zasady projektowania interfejsu na potrzeby okularów z wyświetlaczem”
var buttonChecked by remember { mutableStateOf(false) }

ToggleButton(
  checked = buttonChecked,
  leadingIcon = { Icon(FavoriteIcon) },
  onCheckedChange = { buttonChecked = it }
) {
  Text(if (buttonChecked) "On" else "Off")
}
Fragment kodu w języku Kotlin w 13:55 w filmie „Omówienie kotwic opartych na lokalizacji”
Subspace {
  SpatialPanel {
    VideoPlayer()
  }
}
Fragment kodu w języku Kotlin w 14:16 w filmie „Overview of location-based anchors”
val state by
  qrCode.state.collectAsState()
Subspace(FollowTarget.anchor(anchor)) {
  SpatialPanel {
    Row {
      IndicatorDot()
      Spacer()
      QrCodeCard(state)
    }
  }
}
Fragment kodu w języku Kotlin w 16:43 w filmie „Geospatial API and Google Maps Platform integration”
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)

}
Fragment kodu w języku Kotlin w 17:27 w filmie „Geospatial API and Google Maps Platform integration”
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()
Fragment kodu w Kotlinie w 18:09 w filmie „Geospatial API and Google Maps Platform integration”
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()
 )
}
Fragment kodu w języku Kotlin w 18:53 w filmie „Geospatial API and Google Maps Platform integration”
when (anchorResult) {
 is AnchorCreateSuccess ->
   Subspace(FollowTarget.anchor(anchorResult.anchor)) {
     SpatialPanel(SubspaceModifier.rotateToLookAtUser()) {
       PlaceIndicator(place)
     }
   }

 else ->
   Log.e("Demo", "Failed to create anchor $anchorResult")
}