Nachdem Sie die erforderlichen Berechtigungen angefordert und erhalten haben, kann Ihre App auf die Hardware der Audio- oder Displaybrille zugreifen. Der Schlüssel für den Zugriff auf die Hardware der Brille (anstelle der Hardware des Smartphones) ist die Verwendung eines projizierten Kontexts.
Es gibt zwei Möglichkeiten, einen projizierten Kontext abzurufen, je nachdem, wo Ihr Code ausgeführt wird:
Projizierten Kontext abrufen, wenn Ihr Code in einer projizierten Aktivität ausgeführt wird
Wenn der Code Ihrer App in der projizierten Aktivität ausgeführt wird, ist der zugehörige Aktivitätskontext bereits ein projizierter Kontext. In diesem Szenario können Anrufe, die im Rahmen dieser Aktivität getätigt werden, bereits auf die Hardware der Brille zugreifen.
Projizierten Kontext für Code abrufen, der in einer Smartphone-App-Komponente ausgeführt wird
Wenn ein Teil Ihrer App außerhalb Ihrer projizierten Aktivität (z. B. eine Telefonaktivität oder ein Dienst) auf die Hardware der Brille zugreifen muss, muss er explizit einen projizierten Kontext abrufen. Verwenden Sie dazu die Methode 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 } }
Auf Gültigkeit prüfen
Fassen Sie den createProjectedDeviceContext-Aufruf in ProjectedContext.isProjectedDeviceConnected zusammen. Auch wenn diese Methode true zurückgibt, bleibt der projizierte Kontext für das verbundene Gerät gültig und Ihre Smartphone-App-Aktivität oder Ihr Dienst (z. B. ein CameraManager) kann auf die Hardware der KI-Brille zugreifen.
Beim Trennen der Verbindung bereinigen
Der projizierte Kontext ist an den Lebenszyklus des verbundenen Geräts gebunden und wird daher zerstört, wenn die Verbindung zum Gerät getrennt wird. Wenn die Verbindung zum Gerät getrennt wird, gibt ProjectedContext.isProjectedDeviceConnected den Wert false zurück. Ihre App sollte auf diese Änderung reagieren und alle Systemdienste (z. B. CameraManager) oder Ressourcen bereinigen, die von Ihrer App mit diesem projizierten Kontext erstellt wurden.
Bei erneuter Verbindung neu initialisieren
Wenn die Brille die Verbindung wiederherstellt, kann Ihre App mit createProjectedDeviceContext eine weitere projizierte Kontextinstanz abrufen und dann alle Systemdienste oder Ressourcen mit dem neuen projizierten Kontext neu initialisieren.
Audio mit dem Mikrofon der Brille aufnehmen
Sie haben zwei Möglichkeiten, Audio über die Brille aufzunehmen:
- Verwenden Sie einen projizierten Kontext.
- Verwenden Sie das Bluetooth Hands-Free Profile (HFP).
Aufnahmemethode auswählen
Die von Ihnen gewählte Methode hängt davon ab, ob Sie eine hochwertige, XR-spezifische Audioverarbeitung oder eine standardmäßige Bluetooth-Audioeingabe benötigen.
| Aufzeichnungsmethode | Mikrofonzugriff | Gängige Anwendungsfälle |
|---|---|---|
Projizierter Kontext |
Mehrere Mikrofone |
Wenn Sie mit einem projizierten Kontext aufzeichnen, kann Ihre App auf mehrere Mikrofone der Brille und ihre spezielle Hardware zugreifen, z. B.:
|
Bluetooth HFP |
Einzelnes Mikrofon |
Basiert auf dem Bluetooth Hands-Free Profile (HFP) für sofortige Kompatibilität. In diesem Modus werden die Brille und das Smartphone über die Standardprofile Headset und Advanced Audio Distribution Profile (A2DP) verbunden. Die Brille funktioniert dann wie ein typisches Bluetooth-Peripheriegerät. Wenn Ihre App bereits für die Standard-Bluetooth-Aufnahme entwickelt wurde, können Sie mit dieser Methode Audio von der Brille aufnehmen, ohne XR-spezifische Funktionen zu integrieren. |
Audio mit einem projizierten Kontext aufzeichnen
Wenn Sie Audio mit einem projizierten Kontext aufzeichnen möchten, fordern Sie zuerst die erforderlichen Laufzeitberechtigungen an und zeichnen Sie dann das Audio mit der AudioRecord API auf, wie in den folgenden Abschnitten beschrieben.
Laufzeitberechtigungen anfordern
Wenn Sie auf mehrere Mikrofone auf der Brille zugreifen möchten, müssen Sie Audioberechtigungen speziell für das projizierte Gerät anfordern. Die standardmäßige, auf das Smartphone beschränkte RECORD_AUDIO-Berechtigung, die ein Nutzer für Ihre App auf seinem Mobilgerät erteilt hat, reicht nicht aus.
So fordern Sie die Berechtigungen an:
- Deklarieren Sie die Berechtigung
RECORD_AUDIOin der Manifestdatei Ihrer App. Fordern Sie die Berechtigungen mit Gerätebereich für die Projektion auf eine der folgenden Arten an, je nachdem, wo Ihr Code ausgeführt wird:
- Codeausführung über eine projizierte Aktivität: Verwenden Sie
ActivityResultLaunchermitProjectedPermissionsResultContract. Weitere Informationen zur Verwendung dieser Methode finden Sie im Abschnitt Berechtigungs-Launcher registrieren und in den nachfolgenden Abschnitten des Leitfadens zum Anfordern von Hardwareberechtigungen. - Code wird über eine Aktivität auf dem Host-Smartphone ausgeführt: Verwenden Sie
Activity#requestPermissions(permissions, requestCode, deviceId)und geben Sie die Geräte-ID an, die Sie überprojectedDeviceContexterhalten haben. Weitere Informationen finden Sie im Abschnitt Nutzerfluss für Berechtigungsanfragen im Leitfaden zum Anfordern von Hardwareberechtigungen.
- Codeausführung über eine projizierte Aktivität: Verwenden Sie
AudioRecord mit einem projizierten Kontext initialisieren
Damit Audio von der Brille und nicht vom Host-Smartphone aufgenommen wird, müssen Sie das AudioRecord-Objekt mit dem projizierten Gerätekontext verknüpfen.
Im folgenden Code wird AudioRecord.Builder verwendet und projectedDeviceContext an die Methode setContext übergeben:
// 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()
Wichtige Punkte zum Code
Sie können die Audioquelle auf
CAMCORDER,VOICE_RECOGNITION,VOICE_COMMUNICATIONoderUNPROCESSEDfestlegen, um die Audioverarbeitung an Ihren spezifischen Anwendungsfall anzupassen.Verwenden Sie beispielsweise
VOICE_COMMUNICATION, wenn für Ihren Anwendungsfall eine automatische Rauschunterdrückung erforderlich ist.VOICE_RECOGNITIONwird mit akustischer Echounterdrückung (AEC) verarbeitet. Wenn Sie unbearbeitetes Audio benötigen, wählen SieUNPROCESSEDoderCAMCORDERaus.Damit die Brille kompatibel ist, muss im
audioFormat-Objekt eine Abtastrate von 16 kHz und eine Kanalkonfiguration von Mono oder Stereo (mitCHANNEL_IN_MONOoderCHANNEL_IN_STEREO) definiert sein.Es gibt keine feste Anforderung für die Puffergröße. Rufen Sie die Mindestpuffergröße ab, um die wahrgenommene Latenz zu minimieren.
Nach Gebrauch reinigen
Wenn Ihre App das Mikrofon nicht mehr benötigt oder die Aktivität beendet wird, rufen Sie stop und release für das AudioRecord-Objekt auf.
Laufzeitberechtigungen vor der Aufnahme prüfen
Bevor Sie startRecording aufrufen, prüfen Sie anhand des projizierten Kontexts, ob der Nutzer die Mikrofonberechtigung für die Brille erteilt hat.
Audio über Bluetooth HFP aufnehmen
Wenn Sie Audio über Bluetooth HFP aufzeichnen möchten, fordern Sie zuerst die erforderlichen Laufzeitberechtigungen an und zeichnen Sie dann das Audio mit der AudioManager API auf, wie in den folgenden Abschnitten beschrieben.
Berechtigungen anfordern
Wie bei jedem Standard-Bluetooth-Audiogerät werden die Berechtigungen RECORD_AUDIO, BLUETOOTH_CONNECT und andere zugehörige Berechtigungen vom Smartphone und nicht vom verbundenen Gerät (z. B. Audiobrille oder Displaybrille) gesteuert.
So fordern Sie die Berechtigungen an:
Deklarieren Sie die folgenden Berechtigungen in der Manifestdatei Ihrer App:
Fordern Sie sowohl die Berechtigung
RECORD_AUDIOals auch die BerechtigungBLUETOOTH_CONNECTzur Laufzeit über den Standardablauf für Android-Berechtigungen an.
Audio mit AudioManager weiterleiten
Nachdem der Nutzer Ihrer App die erforderlichen Laufzeitberechtigungen erteilt hat, verwenden Sie die AudioManager API, um das Kommunikationsgerät auf TYPE_BLUETOOTH_SCO festzulegen und das Audio über Bluetooth HFP zu leiten. Dadurch wird das System angewiesen, Audio von dem Bluetooth-Peripheriegerät abzurufen.
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()
Bild mit der Kamera der Brille aufnehmen
Wenn Sie ein Bild mit der Kamera der Brille aufnehmen möchten, richten Sie den CameraX-Anwendungsfall ImageCapture für die Kamera der Brille ein und binden Sie ihn mit dem richtigen Kontext für Ihre App:
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)) }
Wichtige Punkte zum Code
- Ruft eine Instanz von
ProcessCameraProvidermit dem Kontext des Geräts mit der Projektionsfläche ab. - Im Rahmen des projizierten Kontexts wird die primäre, nach außen gerichtete Kamera der Brille beim Auswählen einer Kamera der
DEFAULT_BACK_CAMERAzugeordnet. - Bei einer Vorabprüfung wird
cameraProvider.hasCamera(cameraSelector)verwendet, um zu prüfen, ob die ausgewählte Kamera auf dem Gerät verfügbar ist, bevor fortgefahren wird. - Verwendet Camera2 Interop mit
Camera2CameraInfo, um die zugrunde liegendeCameraCharacteristics#SCALER_STREAM_CONFIGURATION_MAPzu lesen. Das kann für erweiterte Prüfungen der unterstützten Auflösungen nützlich sein. - Ein benutzerdefinierter
ResolutionSelectorwird erstellt, um die Auflösung des Ausgabebilds fürImageCapturepräzise zu steuern. - Erstellt einen
ImageCapture-Anwendungsfall, der mit einem benutzerdefiniertenResolutionSelectorkonfiguriert ist. - Bindet den
ImageCapture-Anwendungsfall an den Lebenszyklus der Aktivität. Dadurch wird das Öffnen und Schließen der Kamera automatisch basierend auf dem Status der Aktivität verwaltet (z. B. wird die Kamera angehalten, wenn die Aktivität pausiert wird).
Nachdem die Kamera der Brille eingerichtet wurde, können Sie mit der ImageCapture-Klasse von CameraX ein Bild aufnehmen. In der CameraX-Dokumentation finden Sie Informationen zur Verwendung von takePicture zum Aufnehmen eines Bildes.
Video mit der Kamera der Brille aufnehmen
Wenn Sie mit der Kamera der Brille ein Video statt eines Bildes aufnehmen möchten, ersetzen Sie die ImageCapture-Komponenten durch die entsprechenden VideoCapture-Komponenten und ändern Sie die Logik für die Aufnahme.
Die wichtigsten Änderungen betreffen die Verwendung eines anderen Anwendungsfalls, das Erstellen einer anderen Ausgabedatei und das Starten der Aufnahme mit der entsprechenden Videomethodik.
Weitere Informationen zur VideoCapture API und ihrer Verwendung finden Sie in der Dokumentation zur Videoaufnahme mit CameraX.
In der folgenden Tabelle sind die empfohlene Auflösung und Bildrate je nach Anwendungsfall Ihrer App aufgeführt:
| Anwendungsfall | Auflösung | Framerate |
|---|---|---|
| Videokommunikation | 1280 × 720 | 15 fps |
| Computer Vision | 640 x 480 | 10 fps |
| KI-Videostreaming | 640 x 480 | 1 fps |
Über eine projizierte Aktivität auf die Hardware eines Smartphones zugreifen
Eine projizierte Aktivität kann auch auf die Hardware des Smartphones (z. B. die Kamera oder das Mikrofon) zugreifen. Dazu muss sie mit createHostDeviceContext(context) den Kontext des Hostgeräts (Smartphone) abrufen:
@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 } }
Wenn Sie in einer Hybrid-App (einer App, die sowohl für Smartphones als auch für Brillen entwickelt wurde) auf Hardware oder Ressourcen zugreifen, die für das Hostgerät (Smartphone) spezifisch sind, müssen Sie explizit den richtigen Kontext auswählen, damit Ihre App auf die richtige Hardware zugreifen kann:
- Verwenden Sie den Kontext
Activityvom SmartphoneActivityoderProjectedContext.createHostDeviceContext, um den Kontext des Smartphones abzurufen. - Verwenden Sie nicht
getApplicationContext, da der Anwendungskontext möglicherweise fälschlicherweise den Kontext der Brille zurückgibt, wenn eine projizierte Aktivität die zuletzt gestartete Komponente war.