Ses gözlüklerindeki ve ekran gözlüklerindeki donanıma erişmek için yansıtılan bağlamı kullanma

Uygun XR cihazlar
Bu kılavuz, bu tür XR cihazları için deneyimler geliştirmenize yardımcı olur.
Ses ve
Görüntü Arabirimli Gözlük

Gerekli izinleri isteyip aldıktan sonra uygulamanız, ses arabirimli gözlük veya görüntü arabirimli gözlükteki donanıma erişebilir. Telefonun donanımı yerine gözlüğün donanımına erişmek için yansıtılan bağlam kullanmanız gerekir.

Kodunuzun nerede yürütüldüğüne bağlı olarak, yansıtılan bağlamı elde etmenin iki temel yolu vardır:

Kodunuz yansıtılan bir etkinlikte çalışıyorsa yansıtılan bağlamı alma

Uygulamanızın kodu yansıtılan etkinliğinizin içinden çalışıyorsa kendi etkinlik bağlamı zaten yansıtılan bağlamdır. Bu senaryoda, söz konusu etkinlikte yapılan çağrılar gözlüğün donanımına erişebilir.

Telefon uygulaması bileşeninde çalışan kod için öngörülen bağlamı alma

Uygulamanızın, yansıtılan etkinliğinizin dışında kalan bir bölümünün (ör. telefon etkinliği veya hizmet) gözlüğün donanımına erişmesi gerekiyorsa açıkça yansıtılan bir bağlam elde etmesi gerekir. Bunun için createProjectedDeviceContext yöntemini kullanın:

@RequiresApi(Build.VERSION_CODES.BAKLAVA)
@OptIn(ExperimentalProjectedApi::class, ExperimentalCoroutinesApi::class)
private fun monitorProjectedConnectivity(activity: ComponentActivity) {
    activity.lifecycleScope.launch {
        // Before creating a projected context, check to see if the projected device is connected.
        // While this method returns true, the projected context remains valid.
        ProjectedContext.isProjectedDeviceConnected(activity, coroutineContext)
            .collectLatest { isConnected ->
                if (isConnected) {
                    // From a phone Activity or Service, get a context for the audio and display glasses.
                    // Re-initialize on reconnect: Obtain another context instance.
                    val projectedContext = try {
                        ProjectedContext.createProjectedDeviceContext(activity)
                    } catch (e: IllegalStateException) {
                        Log.e(TAG, "Failed to create projected context", e)
                        return@collectLatest
                    }

                    // Use the projectedContext to initialize system services (e.g., CameraManager).
                    Log.i(TAG, "Projected device connected. Initializing hardware...")
                } else {
                    // The projected context is destroyed when the device disconnects.
                    // Clean up on disconnect: Listen for 'false' and release resources.
                    Log.i(TAG, "Projected device disconnected. Cleaning up hardware resources...")
                }
            }
    }
}

Geçerlilik kontrolü

createProjectedDeviceContext çağrısını ProjectedContext.isProjectedDeviceConnected içine alın. Bu yöntem true döndürürken yansıtılan bağlam, bağlı cihaz için geçerli kalır ve telefon uygulaması etkinliğiniz veya hizmetiniz (ör. CameraManager) yapay zeka destekli akıllı gözlük donanımına erişebilir.

Bağlantı kesildiğinde temizle

Yansıtılan bağlam, bağlı cihazın yaşam döngüsüne bağlıdır. Bu nedenle, cihazın bağlantısı kesildiğinde yok edilir. Cihazın bağlantısı kesildiğinde ProjectedContext.isProjectedDeviceConnected, false değerini döndürür. Uygulamanız bu değişikliği dinlemeli ve uygulamanızın bu yansıtılan bağlamı kullanarak oluşturduğu tüm sistem hizmetlerini (ör. CameraManager) veya kaynakları temizlemelidir.

Yeniden bağlanıldığında yeniden başlatma

Gözlük yeniden bağlandığında uygulamanız, createProjectedDeviceContext kullanarak başka bir yansıtılan bağlam örneği alabilir ve ardından yeni yansıtılan bağlamı kullanarak tüm sistem hizmetlerini veya kaynaklarını yeniden başlatabilir.

Gözlüğün mikrofonuyla ses kaydetme

Bluetooth Eller Serbest Profili'ni (HFP) kullanarak gözlükten ses kaydedebilirsiniz. Bu modda gözlük, standart mikrofonlu kulaklık ve Gelişmiş Ses Dağıtım Profili (A2DP) profillerini kullanarak telefona bağlanır ve tipik bir Bluetooth çevre birimi gibi çalışır. Uygulamanız standart Bluetooth kaydı için tasarlanmışsa ek bir geliştirme yapmadan gözlükten ses kaydetmek için bu yöntemi kullanabilirsiniz.

Bluetooth HFP kullanarak ses kaydetme

Bluetooth HFP kullanarak ses kaydetmek için önce gerekli çalışma zamanı izinlerini isteyin, ardından aşağıdaki bölümlerde açıklandığı gibi AudioManager API'yi kullanarak sesi kaydedin.

İzin iste

Tüm standart Bluetooth ses sistemlerinde olduğu gibi RECORD_AUDIO, BLUETOOTH_CONNECT ve diğer ilgili izinler, bağlı cihaz (ör. sesli gözlük veya görüntülü gözlük) tarafından değil, telefon tarafından kontrol edilir.

İzinleri istemek için aşağıdaki adımları uygulayın:

  1. Uygulamanızın manifest dosyasında aşağıdaki izinleri beyan edin:

  2. Standart Android izin akışını kullanarak çalışma zamanında hem RECORD_AUDIO hem de BLUETOOTH_CONNECT izinlerini isteyin.

Sesi yönlendirmek için AudioManager'ı kullanma

Kullanıcı, uygulamanıza gerekli çalışma zamanı izinlerini verdikten sonra sesi Bluetooth HFP üzerinden yönlendirmek için AudioManager API'yi kullanarak iletişim cihazını TYPE_BLUETOOTH_SCO olarak ayarlayın. Bu, sisteme Bluetooth çevre biriminden ses alması talimatını verir.

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()

Akıllı gözlüğün kamerasıyla fotoğraf çekme

Gözlüğün kamerasıyla görüntü yakalamak için CameraX'in ImageCapture kullanım alanını ayarlayın ve uygulamanız için doğru bağlamı kullanarak gözlüğün kamerasına bağlayın:

private fun startCameraOnGlasses(activity: ComponentActivity) {
    activity.lifecycleScope.launch {
        // Before creating a projected context, check to see if the projected device is connected.
        ProjectedContext.isProjectedDeviceConnected(activity, coroutineContext)
            .collectLatest { isConnected ->
                if (isConnected) {
                    // 1. Get the CameraProvider using the projected context.
                    // When using the projected context, DEFAULT_BACK_CAMERA maps to the audio and display glasses' camera.
                    val projectedContext = try {
                        ProjectedContext.createProjectedDeviceContext(activity)
                    } catch (e: IllegalStateException) {
                        Log.e(TAG, "Projected context could not be created", e)
                        return@collectLatest
                    }

                    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))
                }
            }
    }
}

Kodla ilgili önemli noktalar

  • Yansıtılan cihaz bağlamını kullanarak ProcessCameraProvider örneği alır.
  • Yansıtılan bağlamın kapsamında, kamera seçerken gözlüğün birincil, dışa bakan kamerası DEFAULT_BACK_CAMERA ile eşleşir.
  • Ön bağlama kontrolü, devam etmeden önce seçilen kameranın cihazda mevcut olduğunu doğrulamak için cameraProvider.hasCamera(cameraSelector) kullanır.
  • Temel CameraCharacteristics#SCALER_STREAM_CONFIGURATION_MAP'yi okumak için Camera2CameraInfo ile Camera2 Interop'u kullanır. Bu, desteklenen çözünürlüklerle ilgili gelişmiş kontroller için yararlı olabilir.
  • ImageCapture için çıkış görüntüsü çözünürlüğünü hassas bir şekilde kontrol etmek amacıyla özel bir ResolutionSelector oluşturulur.
  • Özel bir ResolutionSelector ile yapılandırılmış bir ImageCapture kullanım alanı oluşturur.
  • ImageCapture kullanım alanını etkinliğin yaşam döngüsüne bağlar. Bu, etkinliğin durumuna göre kameranın açılıp kapanmasını otomatik olarak yönetir (örneğin, etkinlik duraklatıldığında kamerayı durdurur).
  • Tek akışlı donanım için yapılandırma: Gözlük kamera ardışık düzeni, aynı anda tek bir etkin akışla sınırlıdır. ImageCapture tek başına bağlandığında CameraX, varsayılan olarak odaklama ve ölçüm için dahili bir yinelenen akış (MeteringRepeating) ekler. Bu da tek akışlı ardışık düzenin durmasına neden olur. ImageCapture öğesini önizleme akışı olmadan tek başına bağlamak için CameraXConfig.Builder üzerinde setRepeatingStreamForced(false) ayarını yaparak zorunlu tekrarlanan akışı devre dışı bırakın (örneğin, Application sınıfınızda CameraXConfig.Provider uygulayarak veya başlatmadan önce ProcessCameraProvider öğesini yapılandırarak).

Akıllı gözlüğün kamerası ayarlandıktan sonra CameraX'in ImageCapture sınıfıyla görüntü yakalayabilirsiniz. Resim çekmek için takePicture kullanma hakkında bilgi edinmek üzere CameraX belgelerini inceleyin.

Gözlüğün kamerasıyla video çekme

Gözlüğün kamerasıyla resim yerine video çekmek için ImageCapture bileşenlerini ilgili VideoCapture bileşenleriyle değiştirin ve çekim yürütme mantığını değiştirin.

Ana değişiklikler arasında farklı bir kullanım alanı kullanma, farklı bir çıkış dosyası oluşturma ve uygun video kayıt yöntemini kullanarak yakalama işlemini başlatma yer alır. VideoCapture API'si ve kullanımı hakkında daha fazla bilgi için CameraX'in video yakalama belgelerine bakın.

Aşağıdaki tabloda, uygulamanızın kullanım alanına bağlı olarak önerilen çözünürlük ve kare hızı gösterilmektedir:

Kullanım alanı Çözünürlük Kare hızı
Video İletişimi 1280 x 720 15 FPS
Bilgisayarlı Görü 640 x 480 10 FPS
Yapay Zeka Video Akışı 640 x 480 1 FPS

Yansıtılan bir etkinlikten telefonun donanımına erişme

Yansıtılan etkinlik, ana makine cihazın (telefon) bağlamını almak için createHostDeviceContext(context) kullanarak telefonun donanımına (ör. kamera veya mikrofon) da erişebilir:

@OptIn(ExperimentalProjectedApi::class)
private fun getPhoneContext(activity: ComponentActivity): Context? {
    return try {
        // From a projected Activity, get a context for the phone.
        ProjectedContext.createHostDeviceContext(activity)
    } catch (e: IllegalStateException) {
        Log.e(TAG, "Failed to create host device context", e)
        null
    }
}

Karma bir uygulamada (hem mobil hem de gözlük deneyimleri içeren bir uygulama) ana cihaza (telefon) özgü donanıma veya kaynaklara erişirken uygulamanızın doğru donanıma erişebilmesi için doğru bağlamı açıkça seçmeniz gerekir: