بعد طلب الأذونات اللازمة والحصول عليها، يمكن لتطبيقك الوصول إلى الأجهزة في النظارات الصوتية أو نظارات بشاشة عرض. للوصول إلى أجهزة النظارات (بدلاً من أجهزة الهاتف)، يجب استخدام سياق معروض.
هناك طريقتان أساسيتان للحصول على سياق متوقّع، وذلك حسب مكان تنفيذ الرمز البرمجي:
الحصول على سياق مسقَط إذا كان الرمز البرمجي يعمل في نشاط مسقَط
إذا كان رمز تطبيقك يعمل من داخل النشاط المتوقّع، سيكون سياق النشاط الخاص به سياقًا متوقّعًا. في هذا السيناريو، يمكن للمكالمات التي يتم إجراؤها ضمن هذا النشاط الوصول إلى أجهزة النظّارة الذكية.
الحصول على سياق معروض لتشغيل الرمز في أحد مكونات تطبيق الهاتف
إذا كان جزء من تطبيقك خارج نطاق النشاط المعروض (مثل نشاط على الهاتف أو خدمة) يحتاج إلى الوصول إلى أجهزة النظارات، يجب أن يحصل صراحةً على سياق معروض. لإجراء ذلك، استخدِم طريقة
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 } }
التحقّق من الصلاحية
يمكنك تضمين طلب createProjectedDeviceContext ضمن ProjectedContext.isProjectedDeviceConnected. على الرغم من أنّ هذا الإجراء يعرض true، يظل السياق المعروض صالحًا للجهاز المتصل، ويمكن لتطبيق الهاتف أو الخدمة (مثل CameraManager) الوصول إلى أجهزة نظارات الذكاء الاصطناعي.
تنظيف البيانات عند قطع الاتصال
يرتبط السياق المعروض بدورة حياة الجهاز المتصل، لذا يتم تدميره عند قطع اتصال الجهاز. وعند قطع اتصال الجهاز، تعرض الدالة ProjectedContext.isProjectedDeviceConnected القيمة false. ويجب أن يستجيب تطبيقك لهذا التغيير وأن يمحو أي خدمات نظام (مثل CameraManager) أو موارد أنشأها تطبيقك باستخدام هذا السياق المعروض.
إعادة التهيئة عند إعادة الاتصال
عند إعادة توصيل النظارات الذكية، يمكن لتطبيقك الحصول على مثيل آخر من سياق العرض المتوقّع باستخدام createProjectedDeviceContext، ثم إعادة إعداد أي خدمات تابعة لنظام التشغيل أو موارد باستخدام سياق العرض المتوقّع الجديد.
تسجيل الصوت باستخدام ميكروفون النظارات
يمكنك تسجيل الصوت من النظارات باستخدام طريقتَين مختلفتَين:
- استخدِم سياقًا متوقّعًا.
- استخدام ملف البلوتوث بدون لمس الجهاز (HFP)
اختيار إحدى طرق التسجيل
تعتمد الطريقة التي تختارها على ما إذا كنت بحاجة إلى معالجة صوتية عالية الدقة ومخصّصة للواقع الممتد، أو إدخال صوتي عادي عبر البلوتوث.
| طريقة التسجيل | الوصول إلى الميكروفون | حالة الاستخدام الشائعة |
|---|---|---|
السياق المتوقّع |
ميكروفونات متعدّدة |
يتيح لك التسجيل باستخدام سياق معروض أن يصل تطبيقك إلى عدة ميكروفونات من النظارات وميزات الأجهزة المتخصصة، مثل:
|
Bluetooth HFP |
ميكروفون واحد |
تعتمد هذه النظارات على ملف البلوتوث بدون لمس الجهاز (HFP) لتوفير توافق فوري وجاهز للاستخدام. في هذا الوضع، تتصل النظارات بالهاتف باستخدام ملفات "سماعة الرأس" و"ملف توزيع الصوت المتقدّم" (A2DP) العادية، وتعمل مثل أي جهاز طرفي عادي متوافق مع البلوتوث. إذا كان تطبيقك مصمّمًا لتسجيل الصوت باستخدام البلوتوث العادي، يمكنك استخدام هذه الطريقة لتسجيل الصوت من النظارات بدون دمج أي إمكانات خاصة بالواقع الممتد. |
تسجيل الصوت باستخدام سياق معروض
لتسجيل الصوت باستخدام سياق مُسقَط، عليك أولاً طلب أذونات وقت التشغيل المطلوبة، ثم تسجيل الصوت باستخدام واجهة برمجة التطبيقات AudioRecord، كما هو موضّح في الأقسام التالية.
طلب أذونات التشغيل
للوصول إلى ميكروفونات متعددة على النظّارة الذكية، يجب طلب أذونات الصوت بشكل خاص للجهاز المعروض، لأنّ إذن RECORD_AUDIO النطاق العادي للهاتف الذي يمنحه المستخدم لتطبيقك على جهازه الجوّال لا يكفي.
اتّبِع الخطوات التالية لطلب الأذونات:
- عليك تعريف إذن
RECORD_AUDIOفي ملف بيان تطبيقك. اطلب الأذونات التي تسري على الجهاز المعروض بإحدى الطرق التالية، وذلك حسب مكان تنفيذ الرمز البرمجي:
- تنفيذ الرمز من نشاط معروض: استخدِم
ActivityResultLauncherمعProjectedPermissionsResultContract. لمزيد من المعلومات حول استخدام هذه الطريقة، يُرجى الاطّلاع على قسم تسجيل مشغّل الأذونات والأقسام اللاحقة في دليل طلب أذونات الأجهزة. - تنفيذ الرمز من نشاط على هاتف مضيف: استخدِم
Activity#requestPermissions(permissions, requestCode, deviceId)وقدِّم رقم تعريف الجهاز الذي تم الحصول عليه منprojectedDeviceContext، كما هو موضّح في قسم فهم مسار المستخدم لطلب الحصول على إذن من دليل طلب أذونات الأجهزة.
- تنفيذ الرمز من نشاط معروض: استخدِم
تهيئة AudioRecord باستخدام سياق متوقّع
لضمان تسجيل الصوت من النظّارة الذكية بدلاً من هاتف المضيف، يجب ربط عنصر AudioRecord بسياق الجهاز المعروض.
يستخدم الرمز التالي AudioRecord.Builder ويمرّر projectedDeviceContext إلى طريقة 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()
نقاط أساسية حول الرمز
يمكنك ضبط مصدر الصوت على
CAMCORDERأوVOICE_RECOGNITIONأوVOICE_COMMUNICATIONأوUNPROCESSEDلتخصيص معالجة الصوت حسب حالة الاستخدام المحدّدة.على سبيل المثال، استخدِم
VOICE_COMMUNICATIONإذا كانت حالة الاستخدام تتطلّب تقليل الضوضاء تلقائيًا. تتم معالجةVOICE_RECOGNITIONباستخدام ميزة إلغاء الصدى الصوتي (AEC). وإذا كنت بحاجة إلى صوت خام وغير معدَّل، اختَرUNPROCESSEDأوCAMCORDER.لضمان التوافق مع النظارات، يجب أن يحدّد العنصر
audioFormatمعدّل عيّنات يبلغ 16 كيلو هرتز وإعدادات قناة أحادية أو استريو (باستخدامCHANNEL_IN_MONOأوCHANNEL_IN_STEREO).استخدِم
AudioRecord.getMinBufferSize()لتحديد الحد الأدنى لحجم ذاكرة التخزين المؤقت اللازم لإنشاء العنصرAudioRecord. ومع ذلك، لتجنُّب انقطاع الصوت من النظّارة الذكية، عليك القراءة من هذا المخزن المؤقت على شكل أجزاء قصيرة ومتكررة (بشكل مثالي، شرائح مدتها 20 ملي ثانية) بدلاً من انتظار امتلاء المخزن المؤقت بالكامل.
التنظيف بعد الاستخدام
عندما لا يحتاج تطبيقك إلى الميكروفون أو عند إيقاف النشاط،
اتّصِل بالدالتَين stop وrelease في العنصر AudioRecord.
التحقّق من أذونات التشغيل قبل التسجيل
قبل استدعاء startRecording، تأكَّد من أنّ المستخدم قد منح
إذن الوصول إلى الميكروفون للنظارات باستخدام السياق المعروض.
تسجيل الصوت باستخدام بروتوكول HFP عبر البلوتوث
لتسجيل الصوت باستخدام Bluetooth HFP، عليك أولاً طلب أذونات وقت التشغيل المطلوبة، ثم تسجيل الصوت باستخدام واجهة برمجة التطبيقات AudioManager، كما هو موضّح في الأقسام التالية.
طلب الحصول على الأذونات
كما هو الحال مع أي جهاز صوتي عادي يتضمّن بلوتوث، يتم التحكّم في أذونات RECORD_AUDIO وBLUETOOTH_CONNECT والأذونات الأخرى ذات الصلة من خلال الهاتف وليس الجهاز المتصل (مثل النظارات الصوتية أو نظارات بشاشة عرض).
اتّبِع الخطوات التالية لطلب الأذونات:
أدرِج الأذونات التالية في ملف بيان تطبيقك:
يجب طلب الإذنَين
RECORD_AUDIOوBLUETOOTH_CONNECTفي وقت التشغيل باستخدام مسار أذونات Android العادي.
استخدام AudioManager لتوجيه الصوت
بعد أن يمنح المستخدم تطبيقك أذونات التشغيل اللازمة، استخدِم واجهة برمجة التطبيقات
AudioManager لضبط جهاز الاتصال على
TYPE_BLUETOOTH_SCO من أجل توجيه الصوت من خلال بروتوكول HFP عبر البلوتوث، ما يؤدي إلى توجيه النظام لاسترداد الصوت من جهاز البلوتوث الطرفي.
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()
التقاط صورة باستخدام كاميرا النظارات
لالتقاط صورة باستخدام كاميرا النظارات، عليك إعداد حالة استخدام ImageCapture CameraX وربطها بكاميرا النظارات باستخدام السياق الصحيح لتطبيقك:
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)) }
نقاط أساسية حول الرمز
- يحصل على مثيل من
ProcessCameraProviderباستخدام سياق الجهاز المعروض. - ضمن نطاق السياق المعروض، يتم ربط الكاميرا الأساسية الخارجية في النظارات بالرمز
DEFAULT_BACK_CAMERAعند اختيار كاميرا. - يستخدم فحص الربط المسبق
cameraProvider.hasCamera(cameraSelector)للتأكّد من أنّ الكاميرا المحدّدة متاحة على الجهاز قبل المتابعة. - يستخدم Camera2 Interop مع
Camera2CameraInfoلقراءةCameraCharacteristics#SCALER_STREAM_CONFIGURATION_MAPالأساسي، وهو ما قد يكون مفيدًا لإجراء عمليات تحقّق متقدّمة من درجات الدقة المتوافقة. - تم تصميم
ResolutionSelectorمخصّص للتحكّم بدقة في دقة الصورة الناتجة عنImageCapture. - تنشئ هذه الطريقة
ImageCaptureحالة استخدام تم ضبطها باستخدامResolutionSelectorمخصّص. - يربط هذا الإجراء حالة استخدام
ImageCaptureبدورة حياة النشاط، ما يؤدي إلى إدارة فتح الكاميرا وإغلاقها تلقائيًا استنادًا إلى حالة النشاط (على سبيل المثال، إيقاف الكاميرا مؤقتًا عند إيقاف النشاط مؤقتًا).
بعد إعداد كاميرا النظارات، يمكنك التقاط صورة باستخدام الفئة ImageCapture في CameraX. راجِع مستندات CameraX للتعرّف على كيفية استخدام takePicture من أجل التقاط صورة.
تسجيل فيديو باستخدام كاميرا النظارات
لالتقاط فيديو بدلاً من صورة باستخدام كاميرا النظارات، استبدِل المكوّنات ImageCapture بمكوّنات VideoCapture المقابلة وعدِّل منطق تنفيذ عملية الالتقاط.
تتضمّن التغييرات الرئيسية استخدام حالة استخدام مختلفة، وإنشاء ملف إخراج مختلف، وبدء عملية الالتقاط باستخدام طريقة تسجيل الفيديو المناسبة.
لمزيد من المعلومات حول واجهة برمجة التطبيقات VideoCapture وكيفية استخدامها، يُرجى الاطّلاع على
مستندات التقاط الفيديو في CameraX.
يعرض الجدول التالي درجة الدقة وعدد اللقطات في الثانية المقترَحان استنادًا إلى حالة استخدام تطبيقك:
| حالة الاستخدام | درجة الدقة | عدد اللقطات في الثانية |
|---|---|---|
| التواصل عبر الفيديو | 1280 x 720 | 15 لقطة في الثانية |
| الرؤية الحاسوبية | 640 x 480 | 10 لقطات في الثانية |
| بث الفيديو بالذكاء الاصطناعي | 640 x 480 | لقطة واحدة في الثانية |
الوصول إلى أجهزة الهاتف من نشاط معروض
يمكن لنشاط مُسقَط أيضًا الوصول إلى أجهزة الهاتف (مثل الكاميرا أو الميكروفون) باستخدام createHostDeviceContext(context) للحصول على سياق الجهاز المضيف (الهاتف):
@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 } }
عند الوصول إلى أجهزة أو مراجع خاصة بالجهاز المضيف (الهاتف) في تطبيق مختلط (تطبيق يتضمّن تجارب على الأجهزة الجوّالة والنظارات)، يجب اختيار السياق الصحيح بشكل صريح للتأكّد من أنّ تطبيقك يمكنه الوصول إلى الأجهزة الصحيحة:
- استخدِم سياق
Activityمن الهاتفActivityأوProjectedContext.createHostDeviceContextللحصول على سياق الهاتف. - لا تستخدِم
getApplicationContextلأنّ سياق التطبيق قد يعرض بشكل غير صحيح سياق النظارات إذا كان النشاط المعروض هو المكوّن الذي تم إطلاقه مؤخرًا.