تستند التجارب المعزّزة للنظارات الصوتية ونظارات بشاشة عرض إلى Activityواجهة برمجة التطبيقات الحالية لإطار عمل Android، وتتضمّن مفاهيم إضافية لدعم الجوانب الفريدة لهذه النظارات. على عكس سمّاعات الرأس التي تعمل بنظام الواقع الممتد والتي تشغّل حزمة APK كاملة على الجهاز، تستخدم نظارات الصوت والنظارات المزودة بشاشة نشاطًا مخصّصًا يتم تشغيله داخل التطبيق الحالي على هاتفك، ويتم عرض هذا النشاط من الجهاز المضيف إلى النظارات.
لإنشاء تجربة تطبيقك على النظارات الصوتية ونظارات بشاشة عرض، عليك توسيع نطاق تطبيقك الحالي على تطبيق الهاتف من خلال إنشاء Activity جديد معروض. يمثّل هذا النشاط نقطة الدخول الرئيسية لتشغيل تطبيقك على النظارات. ويبسّط هذا النهج عملية التطوير لأنّه يتيح لك مشاركة منطق النشاط التجاري وإعادة استخدامه بين تجارب الهاتف والنظارات.
التوافق مع الإصدارات
اطّلِع على متطلبات التوافق مع حزمة تطوير البرامج (SDK) لنظام التشغيل Android الخاصة بحزمة Jetpack XR SDK.
الطلبات التابعة
أضِف اعتماديات المكتبة التالية للنظارات الصوتية ونظارات العرض:
أنيق
dependencies {
implementation "androidx.xr.runtime:runtime:1.0.0-alpha15"
implementation "androidx.xr.glimmer:glimmer:1.0.0-alpha13"
implementation "androidx.xr.glimmer:glimmer-google-fonts:1.0.0-alpha13"
implementation "androidx.xr.projected:projected:1.0.0-alpha08"
implementation "androidx.xr.arcore:arcore:1.0.0-alpha14"
}
Kotlin
dependencies {
implementation("androidx.xr.runtime:runtime:1.0.0-alpha15")
implementation("androidx.xr.glimmer:glimmer:1.0.0-alpha13")
implementation("androidx.xr.glimmer:glimmer-google-fonts:1.0.0-alpha13")
implementation("androidx.xr.projected:projected:1.0.0-alpha08")
implementation("androidx.xr.arcore:arcore:1.0.0-alpha14")
}
تحديد النشاط في ملف البيان الخاص بتطبيقك
وكما هو الحال مع أنواع الأنشطة الأخرى، عليك تعريف نشاطك في ملف بيان التطبيق ليتمكّن النظام من رؤيته وتشغيله.
<application>
<activity
android:name="com.example.xr.projected.ProjectedMainActivity"
android:exported="true"
android:requiredDisplayCategory="android.hardware.display.category.XR_PROJECTED"
android:label="Example activity for audio glasses and display glasses">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.XR_PROJECTED_LAUNCHER"/>
</intent-filter>
</activity>
</application>
نقاط أساسية حول الرمز
- تحدّد هذه السمة القيمة
android.hardware.display.category.XR_PROJECTEDللسمةandroid:requiredDisplayCategoryلإعلام النظام بأنّ هذا النشاط معروض ويمكن عرضه على نظارات الصوت ونظارات بشاشة عرض. - تضبط
android.intent.action.MAINهذا النشاط كنشاط الإطلاق التلقائي. android.intent.category.XR_PROJECTED_LAUNCHERهي فئة متخصّصة تتيح لـ "أوامر Gemini الصوتية" العثور على نشاطك المعروض.عندما يوجّه المستخدم طلبًا صوتيًا باستخدام اسم التطبيق (على سبيل المثال، "افتح عيّنة كتالوج الذكاء الاصطناعي" أو "شغِّل عيّنة كتالوج الذكاء الاصطناعي" أو "ابدأ عيّنة كتالوج الذكاء الاصطناعي")، يستخدم النظام هذه الفئة لتحديد موقع النشاط المحدّد وبدء تشغيله على النظارات الصوتية أو نظارات بشاشة عرض.
إنشاء نشاطك
بعد ذلك، ستنشئ نشاطًا صغيرًا يمكنه عرض محتوى على النظارات الذكية التي تعمل بالذكاء الاصطناعي كلما تم تشغيل الشاشة.
@OptIn(ExperimentalProjectedApi::class) class ProjectedMainActivity : ComponentActivity() { private var displayController: ProjectedDisplayController? = null private var isVisualUiSupported by mutableStateOf(false) private var areVisualsOn by mutableStateOf(true) private var isPermissionDenied by mutableStateOf(false) // Register the permissions launcher using the ProjectedPermissionsResultContract. private val requestPermissionLauncher: ActivityResultLauncher<List<ProjectedPermissionsRequestParams>> = registerForActivityResult(ProjectedPermissionsResultContract()) { results -> if (results[Manifest.permission.CAMERA] == true) { isPermissionDenied = false initializeGlassesFeatures() } else { // Handle permission denial. isPermissionDenied = true } } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) lifecycle.addObserver(object : DefaultLifecycleObserver { override fun onDestroy(owner: LifecycleOwner) { displayController?.close() displayController = null } }) if (hasCameraPermission()) { initializeGlassesFeatures() } else { requestHardwarePermissions() } setContent { GlimmerTheme { HomeScreen( areVisualsOn = areVisualsOn, isVisualUiSupported = isVisualUiSupported, isPermissionDenied = isPermissionDenied, onRetryPermission = { requestHardwarePermissions() }, onClose = { finish() } ) } } } private fun initializeGlassesFeatures() { lifecycleScope.launch { // Check device capabilities val projectedDeviceController = ProjectedDeviceController.create(this@ProjectedMainActivity) isVisualUiSupported = projectedDeviceController.capabilities.contains(CAPABILITY_VISUAL_UI) val controller = ProjectedDisplayController.create(this@ProjectedMainActivity) displayController = controller val observer = GlassesLifecycleObserver( context = this@ProjectedMainActivity, controller = controller, onVisualsChanged = { visualsOn -> areVisualsOn = visualsOn } ) lifecycle.addObserver(observer) } } private fun hasCameraPermission(): Boolean { return ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED } private fun requestHardwarePermissions() { val params = ProjectedPermissionsRequestParams( permissions = listOf(Manifest.permission.CAMERA), rationale = "Camera access is required to overlay digital content on your physical environment." ) requestPermissionLauncher.launch(listOf(params)) } }
نقاط أساسية حول الرمز
- يوافق على استخدام واجهات برمجة التطبيقات التي تتطلّب الموافقة من مكتبة Jetpack Projected.
- توسّع
ProjectedMainActivityنطاقComponentActivity، كما هو متوقّع في تطوير التطبيقات على الأجهزة الجوّالة. - بما أنّ بعض النظارات لا تتضمّن شاشة عرض، يتم التحقّق مما إذا كان الجهاز يتضمّن شاشة عرض باستخدام
ProjectedDeviceController. - تحدّد كتلة
setContentضِمن الدالةonCreateجذر شجرة واجهة المستخدم القابلة للإنشاء الخاصة بالنشاط. ستنفّذ الدالة البرمجية المركّبةHomeScreenباستخدام Jetpack Compose Glimmer. - يتم إعداد واجهة المستخدم أثناء تنفيذ طريقة
onCreateالخاصة بالنشاط (راجِع دورة حياة النشاط المتوقّعة). - للاستعداد للميزات المرتبطة بالكاميرا التي تصل إلى أجهزة النظارات، تطلب النظارات أذونات الوصول إلى الأجهزة من خلال تسجيل مشغّل الأذونات وتحديد الدالتَين
hasCameraPermissionوrequestHardwarePermissionsوالتحقّق من منح الأذونات قبل استدعاءinitializeGlassesFeatures.
تنفيذ الدالة المركّبة
يشير النشاط الذي أنشأته إلى دالة مركّبة HomeScreen يجب تنفيذها. يستخدم الرمز البرمجي التالي Jetpack Compose Glimmer لتحديد دالة مركّبة يمكنها عرض بعض النصوص على شاشة النظّارة الذكية:
@Composable fun HomeScreen( areVisualsOn: Boolean, isVisualUiSupported: Boolean, isPermissionDenied: Boolean, onRetryPermission: () -> Unit, onClose: () -> Unit, modifier: Modifier = Modifier ) { Box( modifier = modifier .surface() .focusable(false) .fillMaxSize(), contentAlignment = Alignment.Center ) { if (isPermissionDenied) { Card( title = { Text("Permission Required") }, action = { Button(onClick = onClose) { Text("Exit") } } ) { Text("Camera access is needed to use AI glasses features.") Button(onClick = onRetryPermission) { Text("Retry") } } } else if (isVisualUiSupported) { Card( title = { Text("Android XR") }, action = { Button(onClick = onClose) { Text("Close") } } ) { if (areVisualsOn) { Text("Hello, AI Glasses!") } else { Text("Display is off. Audio guidance active.") } } } else { Text("Audio Guidance Mode Active") } } }
نقاط أساسية حول الرمز
- كما حدّدت في نشاطك السابق، تتضمّن الدالة
HomeScreenالمحتوى القابل للإنشاء الذي يراه المستخدم عندما تكون شاشة النظارات مفعّلة. - يعرض مكوّن Glimmer
Textفي Jetpack Compose النص "مرحبًا، نظارات الذكاء الاصطناعي" على شاشة النظارات. - يغلق Jetpack Compose Glimmer
Buttonالنشاط من خلال استدعاءfinish()عبرonCloseفي النشاط المعروض.
التحقّق مما إذا كانت النظارات الصوتية أو نظارات بشاشة عرض متصلة
لتحديد ما إذا كانت نظارات الصوت أو نظارات بشاشة عرض متصلة بهاتف المستخدم قبل إطلاق نشاطك، استخدِم طريقة ProjectedContext.isProjectedDeviceConnected. تعرض هذه الطريقة
Flow<Boolean> التي يمكن لتطبيقك مراقبتها للحصول على آخر الأخبار حول
حالة الاتصال في الوقت الفعلي.
بدء نشاطك
بعد إنشاء نشاط أساسي، يمكنك تشغيله على نظارتك. للوصول إلى أجهزة النظّارة الذكية، يجب أن يبدأ تطبيقك النشاط بخيارات معيّنة تطلب من النظام استخدام سياق معروض، كما هو موضّح في الرمز التالي:
val options = ProjectedContext.createProjectedActivityOptions(context) val intent = Intent(context, ProjectedMainActivity::class.java) context.startActivity(intent, options.toBundle())
تنشئ طريقة createProjectedActivityOptions في ProjectedContext
الخيارات اللازمة لبدء نشاطك في سياق متوقّع.
يمكن أن تكون المَعلمة context سياقًا من الهاتف أو نظارات الواقع الافتراضي.
الخطوات التالية
بعد إنشاء نشاطك الأول لنظارات الصوت والنظارات المزودة بشاشة، يمكنك استكشاف طرق أخرى لتوسيع نطاق وظائفه:
- التعامل مع مصدر إخراج الصوت باستخدام ميزة "تحويل النص إلى كلام"
- التعامل مع إدخال الصوت باستخدام ميزة "التعرّف التلقائي على الكلام"
- إنشاء واجهة مستخدم باستخدام Jetpack Compose Glimmer
- الوصول إلى الأجهزة على النظارات الصوتية والنظارات بشاشة عرض