Gdy poprosisz o niezbędne uprawnienia i je uzyskasz, Twoja aplikacja będzie mieć dostęp do sprzętu okularów audio lub okularów z wyświetlaczem. Kluczem do uzyskania dostępu do hardware okularów (zamiast hardware telefonu) jest użycie prognozowanego kontekstu.
Istnieją 2 podstawowe sposoby uzyskiwania prognozowanego kontekstu, w zależności od tego, gdzie jest wykonywany kod:
Jak uzyskać prognozowany kontekst, jeśli kod jest uruchamiany w prognozowanej aktywności
Jeśli kod aplikacji jest uruchamiany w ramach prognozowanej aktywności, własny kontekst aktywności jest już prognozowanym kontekstem. W takim przypadku połączenia wykonywane w ramach tej aktywności mogą już korzystać z hardware okularów.
Jak uzyskać prognozowany kontekst w przypadku kodu uruchamianego w komponencie aplikacji na telefon
Jeśli część aplikacji poza prognozowaną aktywnością (np. aktywność na telefonie lub usługa) musi mieć dostęp do hardware okularów, musi wyraźnie uzyskać prognozowany kontekst. Aby to zrobić, użyj metody createProjectedDeviceContext:
@OptIn(ExperimentalProjectedApi::class) private fun getGlassesContext(context: Context): Context? { return try { // From a phone Activity or Service, get a context for the AI glasses. ProjectedContext.createProjectedDeviceContext(context) } catch (e: IllegalStateException) { Log.e(TAG, "Failed to create projected device context", e) null } }
Sprawdź ważność
Umieść wywołanie createProjectedDeviceContext w ProjectedContext.isProjectedDeviceConnected. Chociaż ta metoda zwraca wartość
true, przewidywany kontekst pozostaje ważny dla połączonego urządzenia, a aktywność aplikacji na telefonie lub usługi (np. CameraManager) może uzyskać dostęp do sprzętu okularów z AI.
Zwalnianie miejsca po odłączeniu
Przewidywany kontekst jest powiązany z cyklem życia połączonego urządzenia, więc jest usuwany po rozłączeniu urządzenia. Gdy urządzenie zostanie odłączone, ProjectedContext.isProjectedDeviceConnected zwróci wartość false. Aplikacja powinna nasłuchiwać tej zmiany i zwalniać miejsce zajmowane przez wszystkie usługi systemowe (np. CameraManager) lub zasoby, które utworzyła przy użyciu tego kontekstu.
Ponowna inicjalizacja po ponownym połączeniu
Gdy okulary ponownie się połączą, aplikacja może uzyskać kolejną instancję kontekstu projecji za pomocą metody createProjectedDeviceContext, a następnie ponownie zainicjować dowolne usługi systemowe lub zasoby za pomocą nowego kontekstu projecji.
Nagrywanie dźwięku za pomocą mikrofonu okularów
Dźwięk z okularów możesz nagrywać na 2 sposoby:
- Użyj prognozowanego kontekstu.
- Używaj profilu zestawu głośnomówiącego Bluetooth (HFP).
Wybieranie metod nagrywania
Wybrana metoda zależy od tego, czy potrzebujesz przetwarzania dźwięku o wysokiej wierności, specyficznego dla XR, czy standardowego wejścia audio Bluetooth.
| Metoda nagrywania | Dostęp do mikrofonu | Typowy przypadek użycia |
|---|---|---|
Kontekst prognozowany |
Wiele mikrofonów |
Nagrywanie z użyciem prognozowanego kontekstu umożliwia aplikacji dostęp do wielu mikrofonów okularów i ich specjalistycznych funkcji sprzętowych, takich jak:
|
Bluetooth HFP |
Jeden mikrofon |
Korzysta z profilu Bluetooth Hands-Free Profile (HFP), co zapewnia natychmiastową zgodność po wyjęciu z pudełka. W tym trybie okulary łączą się z telefonem za pomocą standardowych profili zestawu słuchawkowego i A2DP (Advanced Audio Distribution Profile), działając jak typowe urządzenie peryferyjne Bluetooth. Jeśli Twoja aplikacja jest już przeznaczona do nagrywania za pomocą standardowego Bluetootha, możesz użyć tej metody do nagrywania dźwięku z okularów bez integrowania żadnych funkcji specyficznych dla XR. |
Nagrywanie dźwięku z użyciem kontekstu prognozowanego
Aby nagrywać dźwięk przy użyciu kontekstu projekcji, najpierw poproś o wymagane uprawnienia w czasie działania, a potem nagraj dźwięk za pomocą interfejsu AudioRecord API, jak opisano w kolejnych sekcjach.
Wysyłanie prośby o uprawnienia w czasie działania
Aby uzyskać dostęp do wielu mikrofonów w okularach, musisz poprosić o uprawnienia do dźwięku
w przypadku projektowanego urządzenia. Standardowe uprawnienia w zakresie telefonuRECORD_AUDIO, które użytkownik przyznał Twojej aplikacji na urządzeniu mobilnym, są niewystarczające.
Aby poprosić o uprawnienia, wykonaj te czynności:
- Zadeklaruj uprawnienie
RECORD_AUDIOw pliku manifestu aplikacji. Poproś o uprawnienia w zakresie urządzenia docelowego w jeden z tych sposobów, w zależności od tego, gdzie jest wykonywany kod:
- Kod wykonywany w ramach prognozowanej aktywności: użyj elementu
ActivityResultLauncherz elementemProjectedPermissionsResultContract. Więcej informacji o korzystaniu z tej metody znajdziesz w sekcji Rejestrowanie narzędzia do uruchamiania uprawnień i w kolejnych sekcjach przewodnika dotyczącym proszenia o uprawnienia do sprzętu. - Kod wykonywany w ramach aktywności na telefonie hosta: użyj
Activity#requestPermissions(permissions, requestCode, deviceId)i podaj identyfikator urządzenia uzyskany zprojectedDeviceContext, zgodnie z opisem w sekcji Poznaj proces prośby o uprawnienia w przewodniku dotyczącym proszenia o uprawnienia do sprzętu.
- Kod wykonywany w ramach prognozowanej aktywności: użyj elementu
Inicjowanie AudioRecord za pomocą kontekstu prognozowanego
Aby dźwięk był nagrywany z okularów, a nie z telefonu hosta, musisz powiązać obiekt AudioRecord z kontekstem projektowanego urządzenia.
Poniższy kod używa metody AudioRecord.Builder i przekazuje wartość projectedDeviceContext do metody setContext:
// Initialize AudioRecord with projected device context val audioRecord = AudioRecord.Builder() .setAudioSource(MediaRecorder.AudioSource.CAMCORDER) .setAudioFormat(audioFormat) .setBufferSizeInBytes(bufferSize) // pass in the projected device context .setContext(projectedDeviceContext) .build() audioRecord.startRecording()
Najważniejsze informacje o kodzie
Możesz ustawić źródło dźwięku na
CAMCORDER,VOICE_RECOGNITION,VOICE_COMMUNICATIONlubUNPROCESSED, aby dostosować przetwarzanie dźwięku do konkretnego zastosowania.Jeśli na przykład w Twoim przypadku użycia wymagana jest automatyczna redukcja szumów, użyj
VOICE_COMMUNICATION.VOICE_RECOGNITIONjest przetwarzany z użyciem akustycznego usuwania echa (AEC). Jeśli potrzebujesz surowego, nieprzetworzonego dźwięku, wybierzUNPROCESSEDlubCAMCORDER.Aby zapewnić zgodność z okularami, obiekt
audioFormatmusi mieć zdefiniowaną częstotliwość próbkowania 16 kHz i konfigurację kanałów mono lub stereo (za pomocąCHANNEL_IN_MONOlubCHANNEL_IN_STEREO).Nie ma stałego wymogu dotyczącego rozmiaru bufora, ale uzyskaj minimalny rozmiar bufora, aby zminimalizować odczuwalne opóźnienie.
Zwalniaj miejsce po użyciu
Gdy aplikacja nie potrzebuje już mikrofonu lub gdy aktywność zostanie zatrzymana, wywołaj metody stop i release na obiekcie AudioRecord.
Sprawdzanie uprawnień w czasie działania przed rozpoczęciem nagrywania
Przed wywołaniem funkcji startRecording sprawdź, czy użytkownik przyznał okularom dostęp do mikrofonu, korzystając z przewidywanego kontekstu.
Nagrywanie dźwięku za pomocą Bluetooth HFP
Aby nagrywać dźwięk za pomocą profilu Bluetooth HFP, najpierw poproś o wymagane uprawnienia w czasie działania, a następnie nagraj dźwięk za pomocą interfejsu AudioManager API, jak opisano w kolejnych sekcjach.
Wyślij prośbę o uprawnienia
Podobnie jak w przypadku każdego standardowego urządzenia audio Bluetooth, uprawnienia RECORD_AUDIO,BLUETOOTH_CONNECT i inne powiązane uprawnienia są kontrolowane przez telefon, a nie przez połączone urządzenie (np. okulary audio lub okulary z wyświetlaczem).
Aby poprosić o uprawnienia, wykonaj te czynności:
Zadeklaruj te uprawnienia w pliku manifestu aplikacji:
Poproś o uprawnienia
RECORD_AUDIOiBLUETOOTH_CONNECTw czasie działania aplikacji, korzystając ze standardowego przepływu uprawnień Androida.
Używanie klasy AudioManager do kierowania dźwięku
Gdy użytkownik przyzna Twojej aplikacji niezbędne uprawnienia w czasie działania, użyj interfejsu AudioManager API, aby ustawić urządzenie komunikacyjne na TYPE_BLUETOOTH_SCO i przekierować dźwięk przez Bluetooth HFP. Dzięki temu system będzie pobierać dźwięk z urządzenia peryferyjnego Bluetooth.
val audioManager = context.getSystemService(AudioManager::class.java) ?: return val devices = audioManager.getDevices(AudioManager.GET_DEVICES_INPUTS) val hfpDevice = devices.find { it.type == AudioDeviceInfo.TYPE_BLUETOOTH_SCO } hfpDevice?.let { device -> val audioRecord = AudioRecord.Builder() .setAudioSource(MediaRecorder.AudioSource.VOICE_COMMUNICATION) .setAudioFormat(audioFormat) .setBufferSizeInBytes(bufferSize) .build() // Route recording to the Bluetooth device audioRecord.setPreferredDevice(device) audioManager.setCommunicationDevice(device) audioRecord.startRecording()
Robienie zdjęć aparatem okularów
Aby zrobić zdjęcie aparatem okularów, skonfiguruj i powiąż przypadek użycia ImageCapture CameraX z aparatem okularów, używając kontekstu odpowiedniego dla aplikacji:
private fun startCameraOnGlasses(activity: ComponentActivity) { // 1. Get the CameraProvider using the projected context. // When using the projected context, DEFAULT_BACK_CAMERA maps to the AI glasses' camera. val projectedContext = try { ProjectedContext.createProjectedDeviceContext(activity) } catch (e: IllegalStateException) { Log.e(TAG, "AI Glasses context could not be created", e) return } val cameraProviderFuture = ProcessCameraProvider.getInstance(projectedContext) cameraProviderFuture.addListener({ val cameraProvider: ProcessCameraProvider = cameraProviderFuture.get() val cameraSelector = CameraSelector.DEFAULT_BACK_CAMERA // 2. Check for the presence of a camera. if (!cameraProvider.hasCamera(cameraSelector)) { Log.w(TAG, "The selected camera is not available.") return@addListener } // 3. Query supported streaming resolutions using Camera2 Interop. val cameraInfo = cameraProvider.getCameraInfo(cameraSelector) val camera2CameraInfo = Camera2CameraInfo.from(cameraInfo) val cameraCharacteristics = camera2CameraInfo.getCameraCharacteristic( CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP ) // 4. Define the resolution strategy. val targetResolution = Size(1920, 1080) val resolutionStrategy = ResolutionStrategy( targetResolution, ResolutionStrategy.FALLBACK_RULE_CLOSEST_LOWER ) val resolutionSelector = ResolutionSelector.Builder() .setResolutionStrategy(resolutionStrategy) .build() // 5. If you have other continuous use cases bound, such as Preview or ImageAnalysis, // you can use Camera2 Interop's CaptureRequestOptions to set the FPS val fpsRange = Range(30, 60) val captureRequestOptions = CaptureRequestOptions.Builder() .setCaptureRequestOption(CaptureRequest.CONTROL_AE_TARGET_FPS_RANGE, fpsRange) .build() // 6. Initialize the ImageCapture use case with options. val imageCapture = ImageCapture.Builder() // Optional: Configure resolution, format, etc. .setResolutionSelector(resolutionSelector) .build() try { // Unbind use cases before rebinding. cameraProvider.unbindAll() // Bind use cases to camera using the Activity as the LifecycleOwner. cameraProvider.bindToLifecycle( activity, cameraSelector, imageCapture ) } catch (exc: Exception) { Log.e(TAG, "Use case binding failed", exc) } }, ContextCompat.getMainExecutor(activity)) }
Najważniejsze informacje o kodzie
- Pobiera instancję
ProcessCameraProviderza pomocą prognozowanego kontekstu urządzenia. - W zakresie prognozowanego kontekstu główny aparat okularów skierowany na zewnątrz jest mapowany na
DEFAULT_BACK_CAMERApodczas wybierania aparatu. - Sprawdzanie przed powiązaniem wykorzystuje
cameraProvider.hasCamera(cameraSelector), aby zanim przejdziesz dalej, było wiadomo, czy wybrany aparat jest dostępny na urządzeniu. - Używa Camera2 Interop z
Camera2CameraInfodo odczytywania podstawowejCameraCharacteristics#SCALER_STREAM_CONFIGURATION_MAP, co może być przydatne w przypadku zaawansowanych kontroli obsługiwanych rozdzielczości. - Niestandardowy
ResolutionSelectorzostał opracowany z myślą o precyzyjnym kontrolowaniu rozdzielczości obrazu wyjściowego w przypadkuImageCapture. - Tworzy przypadek użycia
ImageCaptureskonfigurowany za pomocą niestandardowegoResolutionSelector. - Wiąże przypadek użycia
ImageCapturez cyklem życia aktywności. Ta funkcja automatycznie zarządza otwieraniem i zamykaniem aparatu na podstawie stanu aktywności (np. zatrzymuje aparat, gdy aktywność jest wstrzymana).
Po skonfigurowaniu aparatu w okularach możesz zrobić zdjęcie za pomocą klasy ImageCapture CameraX. Więcej informacji o używaniu takePicture do robienia zdjęć znajdziesz w dokumentacji CameraX.
Nagrywanie filmów aparatem okularów
Aby nagrać film zamiast zrobić zdjęcie za pomocą aparatu w okularach, zastąp komponenty ImageCapture odpowiednimi komponentami VideoCapture i zmodyfikuj logikę wykonywania przechwytywania.
Główne zmiany polegają na zastosowaniu innego przypadku użycia, utworzeniu innego pliku wyjściowego i rozpoczęciu przechwytywania za pomocą odpowiedniej metody nagrywania wideo.
Więcej informacji o interfejsie VideoCapture API i sposobie jego używania znajdziesz w dokumentacji CameraX dotyczącej nagrywania filmów.
W tabeli poniżej znajdziesz zalecaną rozdzielczość i liczbę klatek w zależności od przypadku użycia aplikacji:
| Przypadek użycia | Rozdzielczość | Liczba klatek |
|---|---|---|
| Komunikacja wideo | 1280 x 720 | 15 kl./s |
| Rozpoznawanie obrazów | 640 x 480 | 10 kl./s |
| Strumieniowe przesyłanie filmów wygenerowanych przez AI | 640 x 480 | 1 kl./s |
Dostęp do sprzętu telefonu z poziomu wyświetlanej aktywności
Projektowana aktywność może też uzyskać dostęp do sprzętu telefonu (np. aparatu lub mikrofonu) za pomocą createHostDeviceContext(context), aby zdobyć kontekst urządzenia hosta (telefonu):
@OptIn(ExperimentalProjectedApi::class) private fun getPhoneContext(activity: ComponentActivity): Context? { return try { // From an AI glasses Activity, get a context for the phone. ProjectedContext.createHostDeviceContext(activity) } catch (e: IllegalStateException) { Log.e(TAG, "Failed to create host device context", e) null } }
Gdy w aplikacji hybrydowej (zawierającej zarówno funkcje mobilne, jak i funkcje na okulary) uzyskujesz dostęp do sprzętu lub zasobów, które są specyficzne dla urządzenia hosta (telefonu), musisz wyraźnie wybrać odpowiedni kontekst, aby aplikacja mogła mieć dostęp do właściwego sprzętu:
- Aby uzyskać kontekst telefonu, użyj kontekstu
ActivityzActivitytelefonu lubProjectedContext.createHostDeviceContext. - Nie używaj
getApplicationContext, ponieważ kontekst aplikacji może nieprawidłowo zwrócić kontekst okularów, jeśli ostatnio uruchomionym komponentem była prognozowana aktywność.