Setelah Anda meminta dan diberikan izin yang diperlukan, aplikasi Anda dapat mengakses hardware di kacamata audio atau kacamata tampilan. Kunci untuk mengakses hardware kacamata (bukan hardware ponsel), adalah menggunakan konteks yang diproyeksikan.
Ada dua cara utama untuk mendapatkan konteks yang diproyeksikan, bergantung pada tempat kode Anda dieksekusi:
Mendapatkan konteks yang diproyeksikan jika kode Anda berjalan dalam aktivitas yang diproyeksikan
Jika kode aplikasi Anda berjalan dari dalam aktivitas yang diproyeksikan, konteks aktivitasnya sendiri sudah merupakan konteks yang diproyeksikan. Dalam skenario ini, panggilan yang dilakukan dalam aktivitas tersebut sudah dapat mengakses hardware kacamata.
Mendapatkan konteks yang diproyeksikan untuk kode yang berjalan dalam komponen aplikasi ponsel
Jika bagian aplikasi Anda di luar aktivitas yang diproyeksikan (seperti aktivitas ponsel atau layanan) perlu mengakses hardware kacamata, bagian tersebut harus secara eksplisit mendapatkan konteks yang diproyeksikan. Untuk melakukannya, gunakan metode
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 } }
Memeriksa validitas
Gabungkan panggilan createProjectedDeviceContext dalam
ProjectedContext.isProjectedDeviceConnected. Meskipun metode ini menampilkan true, konteks yang diproyeksikan tetap valid untuk perangkat yang terhubung, dan aktivitas atau layanan aplikasi ponsel Anda (seperti CameraManager) dapat mengakses hardware kacamata AI.
Membersihkan saat koneksi terputus
Konteks yang diproyeksikan terikat dengan siklus proses perangkat yang terhubung, sehingga akan dihancurkan saat perangkat terputus. Saat perangkat terputus,
ProjectedContext.isProjectedDeviceConnected akan menampilkan false. Aplikasi Anda harus memproses perubahan ini dan membersihkan layanan sistem (seperti CameraManager) atau resource yang dibuat aplikasi Anda menggunakan konteks yang diproyeksikan tersebut.
Menginisialisasi ulang saat terhubung kembali
Saat kacamata terhubung kembali, aplikasi Anda dapat memperoleh instance konteks yang diproyeksikan lainnya menggunakan createProjectedDeviceContext, lalu menginisialisasi ulang layanan atau resource sistem menggunakan konteks yang diproyeksikan baru.
Merekam audio dengan mikrofon kacamata
Anda dapat merekam audio dari kacamata menggunakan dua metode berbeda:
- Menggunakan konteks yang diproyeksikan.
- Menggunakan Profil Handsfree (HFP) Bluetooth.
Memilih metode perekaman
Metode yang Anda pilih bergantung pada apakah Anda memerlukan pemrosesan audio dengan fidelitas tinggi dan khusus XR, atau input audio Bluetooth standar.
| Metode perekaman | Akses mikrofon | Kasus penggunaan umum |
|---|---|---|
Konteks yang Diproyeksikan |
Beberapa mikrofon |
Perekaman menggunakan konteks yang diproyeksikan memungkinkan aplikasi Anda mengakses beberapa mikrofon dari kacamata dan fitur hardware khususnya, seperti:
|
HFP Bluetooth |
Mikrofon tunggal |
Mengandalkan Profil Handsfree (HFP) Bluetooth untuk kompatibilitas langsung dan siap pakai. Dalam mode ini, kacamata terhubung ke ponsel menggunakan profil Headset dan Profil Distribusi Audio Lanjutan (A2DP) standar, yang berfungsi seperti periferal Bluetooth biasa. Jika aplikasi Anda sudah didesain untuk perekaman Bluetooth standar, Anda dapat menggunakan metode ini untuk merekam audio dari kacamata tanpa mengintegrasikan kemampuan khusus XR. |
Merekam audio menggunakan konteks yang diproyeksikan
Untuk merekam audio menggunakan konteks yang diproyeksikan, pertama-tama minta izin runtime yang diperlukan, lalu rekam audio menggunakan AudioRecord API, seperti
dijelaskan di bagian berikut.
Meminta izin runtime
Untuk mengakses beberapa mikrofon di kacamata, Anda harus meminta izin audio khusus untuk perangkat yang diproyeksikan. Izin RECORD_AUDIO standar dan cakupan ponsel yang telah diberikan pengguna untuk aplikasi Anda di perangkat selulernya tidak cukup.
Ikuti langkah-langkah berikut untuk meminta izin:
- Deklarasikan izin
RECORD_AUDIOdi file manifes aplikasi Anda. Minta izin cakupan perangkat yang diproyeksikan dengan salah satu cara berikut, bergantung pada tempat kode Anda dieksekusi:
- Kode yang dieksekusi dari aktivitas yang diproyeksikan: Gunakan
ActivityResultLauncherdenganProjectedPermissionsResultContract. Untuk mengetahui informasi selengkapnya tentang cara menggunakan metode ini, lihat bagian mendaftarkan peluncur izin dan bagian berikutnya dalam panduan untuk meminta izin hardware. - Kode yang dieksekusi dari aktivitas ponsel host: Gunakan
Activity#requestPermissions(permissions, requestCode, deviceId)dan berikan ID perangkat yang diperoleh dariprojectedDeviceContext, seperti yang dijelaskan di bagian memahami alur pengguna permintaan izin dalam panduan untuk meminta izin hardware.
- Kode yang dieksekusi dari aktivitas yang diproyeksikan: Gunakan
Menginisialisasi AudioRecord dengan konteks yang diproyeksikan
Untuk memastikan bahwa audio direkam dari kacamata, bukan ponsel host, Anda harus mengaitkan objek AudioRecord dengan konteks perangkat yang diproyeksikan.
Kode berikut menggunakan AudioRecord.Builder dan meneruskan
projectedDeviceContext ke metode 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()
Poin-poin penting tentang kode
Anda dapat menetapkan sumber audio ke
CAMCORDER,VOICE_RECOGNITION,VOICE_COMMUNICATION, atauUNPROCESSEDuntuk menyesuaikan pemrosesan audio dengan kasus penggunaan tertentu.Misalnya, gunakan
VOICE_COMMUNICATIONjika kasus penggunaan Anda memerlukan pengurangan noise otomatis.VOICE_RECOGNITIONdiproses dengan peredam gema akustik (AEC). Dan jika Anda memerlukan audio mentah yang tidak diubah, pilihUNPROCESSEDatauCAMCORDER.Untuk memastikan kompatibilitas dengan kacamata, objek
audioFormatharus menentukan frekuensi sampel 16 kHz dan konfigurasi saluran mono atau stereo (menggunakanCHANNEL_IN_MONOatauCHANNEL_IN_STEREO).Meskipun tidak ada persyaratan tetap untuk ukuran buffer, dapatkan ukuran buffer minimum untuk meminimalkan latensi yang dirasakan.
Membersihkan setelah digunakan
Jika aplikasi Anda tidak lagi memerlukan mikrofon, atau saat aktivitas dihentikan,
panggil stop dan release pada objek AudioRecord.
Memeriksa izin runtime sebelum merekam
Sebelum memanggil startRecording, pastikan pengguna telah memberikan
izin mikrofon untuk kacamata menggunakan konteks yang diproyeksikan.
Merekam audio menggunakan HFP Bluetooth
Untuk merekam audio menggunakan HFP Bluetooth, pertama-tama minta izin runtime yang diperlukan, lalu rekam audio menggunakan AudioManager API, seperti
yang dijelaskan di bagian berikut.
Meminta izin
Seperti halnya perangkat audio Bluetooth standar, RECORD_AUDIO,
BLUETOOTH_CONNECT, dan izin terkait lainnya dikontrol oleh
ponsel, bukan perangkat yang terhubung (seperti kacamata audio atau kacamata tampilan).
Ikuti langkah-langkah berikut untuk meminta izin:
Deklarasikan izin berikut di file manifes aplikasi Anda:
Minta izin
RECORD_AUDIOdanBLUETOOTH_CONNECTsaat runtime menggunakan alur izin Android standar.
Menggunakan AudioManager untuk merutekan audio
Setelah pengguna memberikan izin runtime yang diperlukan untuk aplikasi Anda, gunakan API
AudioManager untuk menetapkan perangkat komunikasi ke
TYPE_BLUETOOTH_SCO guna merutekan audio melalui HFP Bluetooth. Tindakan ini mengarahkan sistem untuk mengambil audio dari periferal 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()
Mengambil gambar dengan kamera kacamata
Untuk mengambil gambar dengan kamera kacamata, siapkan dan ikat kasus penggunaan CameraX's
ImageCapture ke kamera kacamata menggunakan konteks yang benar untuk aplikasi Anda:
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)) }
Poin-poin penting tentang kode
- Mendapatkan instance
ProcessCameraProvidermenggunakan konteks perangkat yang diproyeksikan. - Dalam cakupan konteks yang diproyeksikan, kamera utama kacamata yang menghadap ke luar dipetakan ke
DEFAULT_BACK_CAMERAsaat memilih kamera. - Pemeriksaan pra-pengikatan menggunakan
cameraProvider.hasCamera(cameraSelector)untuk memverifikasi bahwa kamera yang dipilih tersedia di perangkat sebelum melanjutkan. - Menggunakan Camera2 Interop dengan
Camera2CameraInfountuk membacaCameraCharacteristics#SCALER_STREAM_CONFIGURATION_MAPyang mendasarinya, yang dapat berguna untuk pemeriksaan lanjutan pada resolusi yang didukung. - `
ResolutionSelector` kustom dibuat untuk mengontrol resolusi gambar output secara tepat untuk `ImageCapture`. - Membuat kasus penggunaan
ImageCaptureyang dikonfigurasi denganResolutionSelectorkustom. - Mengikat kasus penggunaan
ImageCaptureke siklus proses aktivitas. Tindakan ini secara otomatis mengelola pembukaan dan penutupan kamera berdasarkan status aktivitas (misalnya, menghentikan kamera saat aktivitas dijeda).
Setelah kamera kacamata disiapkan, Anda dapat mengambil gambar dengan class ImageCapture CameraX. Lihat dokumentasi CameraX untuk mempelajari
cara menggunakan takePicture untuk mengambil gambar.
Merekam video dengan kamera kacamata
Untuk merekam video, bukan gambar, dengan kamera kacamata, ganti komponen
ImageCapture dengan komponen VideoCapture yang sesuai
dan ubah logika eksekusi pengambilan.
Perubahan utama melibatkan penggunaan kasus penggunaan yang berbeda, pembuatan file output yang berbeda, dan memulai pengambilan menggunakan metode perekaman video yang sesuai.
Untuk mengetahui informasi selengkapnya tentang VideoCapture API dan cara menggunakannya, lihat
dokumentasi pengambilan video CameraX.
Tabel berikut menunjukkan resolusi dan kecepatan frame yang direkomendasikan, bergantung pada kasus penggunaan aplikasi Anda:
| Kasus penggunaan | Resolusi | Kecepatan frame |
|---|---|---|
| Komunikasi Video | 1280 x 720 | 15 FPS |
| Computer Vision | 640 x 480 | 10 FPS |
| Streaming Video AI | 640 x 480 | 1 FPS |
Mengakses hardware ponsel dari aktivitas yang diproyeksikan
Aktivitas yang diproyeksikan juga dapat mengakses hardware ponsel (seperti
kamera atau mikrofon) dengan menggunakan createHostDeviceContext(context) untuk mendapatkan
konteks perangkat host (ponsel):
@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 } }
Saat mengakses hardware atau resource yang khusus untuk perangkat host (ponsel) dalam aplikasi hybrid (aplikasi yang berisi pengalaman seluler dan kacamata), Anda harus secara eksplisit memilih konteks yang benar untuk memastikan aplikasi Anda dapat mengakses hardware yang benar:
- Gunakan konteks
ActivitydariActivityponsel atauProjectedContext.createHostDeviceContextuntuk mendapatkan konteks ponsel. - Jangan gunakan
getApplicationContextkarena konteks aplikasi dapat salah menampilkan konteks kacamata jika aktivitas yang diproyeksikan adalah komponen yang paling baru diluncurkan.