إنشاء نشاطك الأول لنظارات الصوت والنظارات المزودة بشاشة عرض

أجهزة XR المشمولة
تساعدك هذه الإرشادات في إنشاء تجارب لهذه الأنواع من أجهزة XR.
نظارات الصوت
والعرض

تستند التجارب المعزّزة للنظارات الصوتية والنظارات المزودة بشاشة إلى Activityواجهة برمجة التطبيقات الحالية لإطار عمل Android، كما تتضمّن مفاهيم إضافية لدعم الجوانب الفريدة لهذه النظارات. على عكس سماعات الرأس التي تعمل بتقنية الواقع الممتد والتي تشغّل حزمة APK كاملة على الجهاز، تستخدم نظارات الصوت والنظارات المزودة بشاشة نشاطًا مخصّصًا يتم تشغيله داخل التطبيق الحالي على هاتفك. ويتم عرض هذا النشاط من الجهاز المضيف إلى النظارات.

لإنشاء تجربة تطبيقك على النظارات الصوتية والنظارات المزودة بشاشة، عليك توسيع نطاق تطبيقك الحالي على الهاتف من خلال إنشاء Activity جديد معروض. يمثّل هذا النشاط نقطة الدخول الرئيسية لتشغيل تطبيقك على النظارات. ويبسّط هذا النهج عملية التطوير لأنّه يتيح لك مشاركة منطق النشاط التجاري وإعادة استخدامه بين تجارب الهاتف والنظارات.

التوافق مع الإصدارات

راجِع متطلبات التوافق مع حزمة تطوير البرامج (SDK) لنظام التشغيل Android الخاصة بحزمة Jetpack XR SDK.

الطلبات التابعة

أضِف اعتماديات المكتبة التالية لنظّارة الصوت ونظّارة العرض:

أنيق

dependencies {
    implementation "androidx.xr.runtime:runtime:1.0.0-alpha14"
    implementation "androidx.xr.glimmer:glimmer:1.0.0-alpha12"
    implementation "androidx.xr.glimmer:glimmer-google-fonts:1.0.0-alpha12"
    implementation "androidx.xr.projected:projected:1.0.0-alpha07"
    implementation "androidx.xr.arcore:arcore:1.0.0-alpha13"
}

Kotlin

dependencies {
    implementation("androidx.xr.runtime:runtime:1.0.0-alpha14")
    implementation("androidx.xr.glimmer:glimmer:1.0.0-alpha12")
    implementation("androidx.xr.glimmer:glimmer-google-fonts:1.0.0-alpha12")
    implementation("androidx.xr.projected:projected:1.0.0-alpha07")
    implementation("androidx.xr.arcore:arcore:1.0.0-alpha13")
}

تعريف النشاط في ملف البيان الخاص بتطبيقك

وكما هو الحال مع أنواع الأنشطة الأخرى، عليك تعريف نشاطك في ملف بيان التطبيق ليتمكّن النظام من رؤيته وتشغيله.

<application>
  <activity
      android:name="com.example.xr.projected.GlassesMainActivity"
      android:exported="true"
      android:requiredDisplayCategory="xr_projected"
      android:label="Example activity for audio glasses and display glasses">
      <intent-filter>
          <action android:name="android.intent.action.MAIN" />
      </intent-filter>
  </activity>
</application>

النقاط الرئيسية حول الرمز

  • تحدّد هذه السمة القيمة xr_projected للسمة android:requiredDisplayCategory لإعلام النظام بأنّ هذا النشاط يجب أن يستخدم سياقًا معروضًا للوصول إلى الأجهزة من جهاز متصل.

إنشاء نشاطك

بعد ذلك، ستنشئ نشاطًا صغيرًا يمكنه عرض محتوى على النظارات الذكية التي تعمل بالذكاء الاصطناعي كلما تم تشغيل الشاشة.

@OptIn(ExperimentalProjectedApi::class)
class GlassesMainActivity : 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@GlassesMainActivity)
            isVisualUiSupported = projectedDeviceController.capabilities.contains(CAPABILITY_VISUAL_UI)

            val controller = ProjectedDisplayController.create(this@GlassesMainActivity)
            displayController = controller
            val observer = GlassesLifecycleObserver(
                context = this@GlassesMainActivity,
                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))
    }
}

النقاط الرئيسية حول الرمز

تنفيذ الدالة المركّبة

يشير النشاط الذي أنشأته إلى دالة مركّبة 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, GlassesMainActivity::class.java)
context.startActivity(intent, options.toBundle())

تنشئ طريقة createProjectedActivityOptions في ProjectedContext الخيارات اللازمة لبدء نشاطك في سياق متوقّع. يمكن أن تكون المَعلمة context سياقًا من الهاتف أو نظارات الواقع الافتراضي.

الخطوات التالية

بعد إنشاء نشاطك الأول لنظارات الصوت والنظارات الذكية، يمكنك استكشاف طرق أخرى لتوسيع وظائفه: