Dopo aver richiesto e ottenuto le autorizzazioni necessarie, la tua app può accedere all'hardware degli occhiali audio o degli occhiali con display. La chiave per accedere all'hardware degli occhiali (anziché a quello dello smartphone) è utilizzare un contesto proiettato.
Esistono due modi principali per ottenere un contesto proiettato, a seconda di dove viene eseguito il codice:
Ottieni un contesto proiettato se il codice viene eseguito in un'attività proiettata
Se il codice della tua app viene eseguito dall'attività proiettata, il suo contesto di attività è già un contesto proiettato. In questo scenario, le chiamate effettuate all'interno di questa attività possono già accedere all'hardware degli occhiali.
Ottenere un contesto proiettato per il codice in esecuzione in un componente dell'app per smartphone
Se una parte della tua app al di fuori dell'attività proiettata (ad esempio un'attività telefonica o un servizio) deve accedere all'hardware degli occhiali, deve ottenere esplicitamente un contesto proiettato. Per farlo, utilizza il metodo 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 } }
Controllare la validità
Inserisci la chiamata createProjectedDeviceContext all'interno di
ProjectedContext.isProjectedDeviceConnected. Anche se questo metodo restituisce
true, il contesto proiettato rimane valido per il dispositivo connesso e l'attività o il servizio dell'app per smartphone (ad esempio CameraManager) può accedere all'hardware degli occhiali AI.
Liberare spazio alla disconnessione
Il contesto proiettato è legato al ciclo di vita del dispositivo connesso, quindi viene eliminato quando il dispositivo si disconnette. Quando il dispositivo si disconnette, ProjectedContext.isProjectedDeviceConnected restituisce false. La tua app deve rimanere in ascolto per questa modifica e liberare spazio per eventuali servizi di sistema (ad esempio un CameraManager) o risorse che la tua app ha creato utilizzando quel contesto proiettato.
Reinizializza alla riconnessione
Quando gli occhiali si riconnettono, l'app può ottenere un'altra istanza di contesto proiettata utilizzando createProjectedDeviceContext e poi reinizializzare qualsiasi servizio o risorsa di sistema utilizzando il nuovo contesto proiettato.
Registrare audio con il microfono degli occhiali
Puoi registrare l'audio dagli occhiali utilizzando due metodi distinti:
- Utilizza un contesto proiettato.
- Utilizza il profilo Bluetooth Hands-Free (HFP).
Scegliere un metodo di registrazione
Il metodo che scegli dipende dal fatto che tu abbia bisogno di un'elaborazione audio ad alta fedeltà e specifica per la realtà estesa o di un ingresso audio Bluetooth standard.
| Metodo di registrazione | Accesso al microfono | Caso d'uso comune |
|---|---|---|
Contesto proiettato |
Più microfoni |
La registrazione utilizzando un contesto proiettato consente alla tua app di accedere a più microfoni degli occhiali e alle relative funzionalità hardware specializzate, ad esempio:
|
Bluetooth HFP |
Microfono singolo |
Si basa sul profilo Bluetooth Hands-Free (HFP) per una compatibilità immediata e pronta all'uso. In questa modalità, gli occhiali si connettono allo smartphone utilizzando i profili standard per cuffie e Advanced Audio Distribution Profile (A2DP), funzionando come una tipica periferica Bluetooth. Se la tua app è già progettata per la registrazione Bluetooth standard, puoi utilizzare questo metodo per registrare l'audio dagli occhiali senza integrare funzionalità specifiche per XR. |
Registrare l'audio utilizzando un contesto proiettato
Per registrare l'audio utilizzando un contesto proiettato, richiedi prima le autorizzazioni di runtime richieste, quindi registra l'audio utilizzando l'API AudioRecord, come descritto nelle sezioni seguenti.
Richiedere autorizzazioni di runtime
Per accedere a più microfoni sugli occhiali, devi richiedere le autorizzazioni audio
specificamente per il dispositivo proiettato. L'autorizzazione standard con ambito telefono
RECORD_AUDIO che un utente ha concesso per la tua app sul suo dispositivo
mobile non è sufficiente.
Segui questi passaggi per richiedere le autorizzazioni:
- Dichiara l'autorizzazione
RECORD_AUDIOnel file manifest dell'app. Richiedi le autorizzazioni con ambito dispositivo proiettato in uno dei seguenti modi, a seconda di dove viene eseguito il codice:
- Esecuzione del codice da un'attività proiettata: utilizza
ActivityResultLauncherconProjectedPermissionsResultContract. Per ulteriori informazioni sull'utilizzo di questo metodo, consulta la sezione Registrare il launcher delle autorizzazioni e le sezioni successive della guida per richiedere le autorizzazioni hardware. - Codice in esecuzione da un'attività dello smartphone host: utilizza
Activity#requestPermissions(permissions, requestCode, deviceId)e fornisci l'ID dispositivo ottenuto daprojectedDeviceContext, come descritto nella sezione Comprendere il flusso utente della richiesta di autorizzazione della guida per la richiesta di autorizzazioni hardware.
- Esecuzione del codice da un'attività proiettata: utilizza
Inizializza AudioRecord con un contesto proiettato
Per assicurarti che l'audio venga registrato dagli occhiali anziché dallo smartphone host,
devi associare l'oggetto AudioRecord al contesto del dispositivo proiettato.
Il seguente codice utilizza AudioRecord.Builder e passa
projectedDeviceContext al metodo 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()
Punti chiave sul codice
Puoi impostare la sorgente audio su
CAMCORDER,VOICE_RECOGNITION,VOICE_COMMUNICATIONoUNPROCESSEDper adattare l'elaborazione audio al tuo caso d'uso specifico.Ad esempio, utilizza
VOICE_COMMUNICATIONse il tuo caso d'uso richiede la riduzione automatica del rumore.VOICE_RECOGNITIONviene elaborato con la cancellazione dell'eco acustico (AEC). Se hai bisogno di audio grezzo e inalterato, selezionaUNPROCESSEDoCAMCORDER.Per garantire la compatibilità con gli occhiali, l'oggetto
audioFormatdeve definire una frequenza di campionamento di 16 kHz e una configurazione dei canali mono o stereo (utilizzandoCHANNEL_IN_MONOoCHANNEL_IN_STEREO).Utilizza
AudioRecord.getMinBufferSize()per determinare la dimensione minima del buffer per creare l'oggettoAudioRecord. Tuttavia, per evitare interruzioni audio dagli occhiali, devi leggere da questo buffer in blocchi brevi e frequenti (idealmente segmenti di 20 ms) anziché attendere che l'intero buffer si riempia.
Pulizia dopo l'uso
Quando l'app non ha più bisogno del microfono o quando l'attività viene interrotta,
chiama stop e release sull'oggetto AudioRecord.
Controllare le autorizzazioni di runtime prima della registrazione
Prima di chiamare startRecording, verifica che l'utente abbia concesso l'autorizzazione microfono per gli occhiali utilizzando il contesto proiettato.
Registrare audio utilizzando Bluetooth HFP
Per registrare l'audio utilizzando Bluetooth HFP, richiedi prima le autorizzazioni di runtime richieste, quindi registra l'audio utilizzando l'API AudioManager, come descritto nelle sezioni seguenti.
Richiedi autorizzazioni
Come per qualsiasi dispositivo audio Bluetooth standard, il RECORD_AUDIO,
BLUETOOTH_CONNECT e altre autorizzazioni correlate sono controllati dallo
smartphone e non dal dispositivo connesso (ad esempio occhiali audio o occhiali con display).
Segui questi passaggi per richiedere le autorizzazioni:
Dichiara le seguenti autorizzazioni nel file manifest dell'app:
Richiedi le autorizzazioni
RECORD_AUDIOeBLUETOOTH_CONNECTin fase di runtime utilizzando il flusso di autorizzazioni Android standard.
Utilizzare AudioManager per instradare l'audio
Dopo che l'utente ha concesso alla tua app le autorizzazioni di runtime necessarie, utilizza l'API
AudioManager per impostare il dispositivo di comunicazione su
TYPE_BLUETOOTH_SCO per indirizzare l'audio tramite Bluetooth HFP. In questo modo
il sistema recupera l'audio dalla periferica 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()
Acquisire un'immagine con la videocamera degli occhiali
Per acquisire un'immagine con la fotocamera degli occhiali, configura e associa lo
ImageCapture caso d'uso di CameraX alla fotocamera degli occhiali utilizzando il contesto
corretto per la tua 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)) }
Punti chiave sul codice
- Ottiene un'istanza di
ProcessCameraProviderutilizzando il contesto del dispositivo proiettato. - Nell'ambito del contesto proiettato, la fotocamera principale degli occhiali,
rivolta verso l'esterno, viene mappata su
DEFAULT_BACK_CAMERAquando si seleziona una fotocamera. - Un controllo pre-binding utilizza
cameraProvider.hasCamera(cameraSelector)per verificare che la videocamera selezionata sia disponibile sul dispositivo prima di procedere. - Utilizza Camera2 Interop con
Camera2CameraInfoper leggere l'CameraCharacteristics#SCALER_STREAM_CONFIGURATION_MAPsottostante, che può essere utile per controlli avanzati sulle risoluzioni supportate. - Un
ResolutionSelectorpersonalizzato è progettato per controllare con precisione la risoluzione dell'immagine di output perImageCapture. - Crea un caso d'uso
ImageCaptureconfigurato con unResolutionSelectorpersonalizzato. - Collega lo scenario d'uso
ImageCaptureal ciclo di vita dell'attività. In questo modo, l'apertura e la chiusura della videocamera vengono gestite automaticamente in base allo stato dell'attività (ad esempio, la videocamera viene arrestata quando l'attività viene sospesa).
Dopo aver configurato la videocamera degli occhiali, puoi acquisire un'immagine con la classe
ImageCapture di CameraX. Consulta la documentazione di CameraX per scoprire come utilizzare takePicture per acquisire un'immagine.
Acquisire un video con la videocamera degli occhiali
Per acquisire un video anziché un'immagine con la videocamera degli occhiali, sostituisci i
componenti ImageCapture con i componenti VideoCapture corrispondenti
e modifica la logica di esecuzione dell'acquisizione.
Le modifiche principali riguardano l'utilizzo di un caso d'uso diverso, la creazione di un file di output diverso e l'avvio dell'acquisizione utilizzando il metodo di registrazione video appropriato.
Per saperne di più sull'API VideoCapture e su come utilizzarla, consulta la
documentazione relativa all'acquisizione video di CameraX.
La tabella seguente mostra la risoluzione e la frequenza fotogrammi consigliate a seconda del caso d'uso della tua app:
| Caso d'uso | Risoluzione | Frequenza fotogrammi |
|---|---|---|
| Comunicazione video | 1280 x 720 | 15 f/s |
| Computer Vision | 640 x 480 | 10 f/s |
| Streaming video AI | 640 x 480 | 1 FPS |
Accedere all'hardware di uno smartphone da un'attività proiettata
Un'attività proiettata può anche accedere all'hardware dello smartphone (ad esempio la
fotocamera o il microfono) utilizzando createHostDeviceContext(context) per ottenere
il contesto del dispositivo host (smartphone):
@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 } }
Quando accedi a hardware o risorse specifici del dispositivo host (smartphone) in un'app ibrida (un'app che contiene esperienze sia per dispositivi mobili che per occhiali), devi selezionare esplicitamente il contesto corretto per assicurarti che la tua app possa accedere all'hardware corretto:
- Utilizza il contesto
Activitydello smartphoneActivityoProjectedContext.createHostDeviceContextper ottenere il contesto dello smartphone. - Non utilizzare
getApplicationContextperché il contesto dell'applicazione può restituire in modo errato il contesto degli occhiali se un'attività proiettata era il componente avviato più di recente.